New Game: Zillion (#1081)

* Option RangeWithSpecialMax

* amendment to typing in web options

* compare string with number

* lots of work on zillion

* fix zillion fill logic

* fix a few more issues in zillion fill logic

* can make zillion patch and use it

* put multi items in zillion rom

* work on ZillionClient

* logging and auth in client

* work on sending and receiving items

* implement item_handling flag

* fix locations ids to NuktiServer package

* use rewrite of zri

* cache logic rule data for performance

* use new id maps

* fix some problems with the big recent merge

* ZillionClient: use new context manager for Memory class

* fix ItemClassification for Zillion items
and some debug statements for asserts,
documentation on running scripts for manual testing
type correction in CommonContext

* fix some issues in client, start on docs, put rescue and item ram addresses in slot data

* use new location name system
fix item locations getting out of sync in progression balancing

* zillion client can read slot name from game

* zillion: new item names

* remove extra unneeded import

* newer options (room gen and starting cards)

* update comment in zillion patch

* zillion non static regions

* change some logging, update some comments

* allow ZillionClient to exit in certain situations

* todo note to fix options doc strings

* don't force auto forfeit

* rework validation of floppy requirement and item counts
and fix race condition in generate_output

* reorganize Zillion component structure
with System class

* documentation updates for Zillion

* attempt inno_setup.iss

* remove todo comment for something done

* update comment

* rework item count zillion options
and some small cleanups

* fix location check count

* data package version 1

* Zillion can pass unit tests without rom

* fix freeze if closing ZillionClient while it's waiting for server login

* specify commit hash for zilliandomizer package

* some changes to options validation

* Zillion doors saved on multiworld server

* add missing function in inno_setup
and name of vanilla continues in options

* rework zillion sync task and context

* Apply documentation suggestions from SoldierofOrder

Co-authored-by: SoldierofOrder <107806872+SoldierofOrder@users.noreply.github.com>

* update zillion package

* workaround for asyncio udp bug

There is a bug in Python in Windows
https://github.com/python/cpython/issues/91227
that makes it so if I look for RetroArch before it's ready, it breaks the asyncio udp transport system.

As a workaround, we don't look for RetroArch until the user asks for it with /sms

* a few of the smaller suggestions from review

* logic only looks at my locations
instead of all the multiworld locations

* some adjustments from pull request discussion
and some unit tests

* patch webhost changes from pull request discussion

* zillion logic tests

* better vblr test

* test interaction of character rescue items with logic

* move unit tests to new worlds folder

* comment improvements

* fix minor logic issue
and add memory read timeout

* capitalization in option display names
Opa-Opa is a proper noun

* redirect zz stdout to debug

* fix option validation bug making unbeatable seeds

* remove line that does nothing

* attach logic cache to world

Co-authored-by: SoldierofOrder <107806872+SoldierofOrder@users.noreply.github.com>
Co-authored-by: Doug Hoskisson <doughoskisson@novuslabs.com>
This commit is contained in:
Doug Hoskisson
2022-10-20 10:41:11 -07:00
committed by GitHub
parent ed76c13961
commit 265ee7098a
27 changed files with 1871 additions and 1 deletions

View File

@@ -0,0 +1,144 @@
from . import ZillionTestBase
class TestGoalVanilla(ZillionTestBase):
options = {
"start_char": "JJ",
"jump_levels": "vanilla",
"gun_levels": "vanilla",
"floppy_disk_count": 7,
"floppy_req": 6,
}
def test_floppies(self):
self.collect_by_name(["Apple", "Champ", "Red ID Card"])
self.assertBeatable(False) # 0 floppies
floppies = self.get_items_by_name("Floppy Disk")
win = self.get_item_by_name("Win")
self.collect(floppies[:-2]) # 1 too few
self.assertEqual(self.count("Floppy Disk"), 5)
self.assertBeatable(False)
self.collect(floppies[-2:-1]) # exact
self.assertEqual(self.count("Floppy Disk"), 6)
self.assertBeatable(True)
self.remove([win]) # reset
self.collect(floppies[-1:]) # 1 extra
self.assertEqual(self.count("Floppy Disk"), 7)
self.assertBeatable(True)
def test_with_everything(self):
self.collect_by_name(["Apple", "Champ", "Red ID Card", "Floppy Disk"])
self.assertBeatable(True)
def test_no_jump(self):
self.collect_by_name(["Champ", "Red ID Card", "Floppy Disk"])
self.assertBeatable(False)
def test_no_gun(self):
self.collect_by_name(["Apple", "Red ID Card", "Floppy Disk"])
self.assertBeatable(False)
def test_no_red(self):
self.collect_by_name(["Apple", "Champ", "Floppy Disk"])
self.assertBeatable(False)
class TestGoalBalanced(ZillionTestBase):
options = {
"start_char": "JJ",
"jump_levels": "balanced",
"gun_levels": "balanced",
}
def test_jump(self):
self.collect_by_name(["Red ID Card", "Floppy Disk", "Zillion"])
self.assertBeatable(False) # not enough jump
opas = self.get_items_by_name("Opa-Opa")
self.collect(opas[:1]) # too few
self.assertEqual(self.count("Opa-Opa"), 1)
self.assertBeatable(False)
self.collect(opas[1:])
self.assertBeatable(True)
def test_guns(self):
self.collect_by_name(["Red ID Card", "Floppy Disk", "Opa-Opa"])
self.assertBeatable(False) # not enough gun
guns = self.get_items_by_name("Zillion")
self.collect(guns[:1]) # too few
self.assertEqual(self.count("Zillion"), 1)
self.assertBeatable(False)
self.collect(guns[1:])
self.assertBeatable(True)
class TestGoalRestrictive(ZillionTestBase):
options = {
"start_char": "JJ",
"jump_levels": "restrictive",
"gun_levels": "restrictive",
}
def test_jump(self):
self.collect_by_name(["Champ", "Red ID Card", "Floppy Disk", "Zillion"])
self.assertBeatable(False) # not enough jump
self.collect_by_name("Opa-Opa")
self.assertBeatable(False) # with all opas, jj champ can't jump
self.collect_by_name("Apple")
self.assertBeatable(True)
def test_guns(self):
self.collect_by_name(["Apple", "Red ID Card", "Floppy Disk", "Opa-Opa"])
self.assertBeatable(False) # not enough gun
self.collect_by_name("Zillion")
self.assertBeatable(False) # with all guns, jj apple can't gun
self.collect_by_name("Champ")
self.assertBeatable(True)
class TestGoalAppleStart(ZillionTestBase):
""" creation of character rescue items has some special interactions with logic """
options = {
"start_char": "Apple",
"jump_levels": "balanced",
"gun_levels": "low",
"zillion_count": 5
}
def test_guns_jj_first(self):
""" with low gun levels, 5 Zillion is enough to get JJ to gun 3 """
self.collect_by_name(["JJ", "Red ID Card", "Floppy Disk", "Opa-Opa"])
self.assertBeatable(False) # not enough gun
self.collect_by_name("Zillion")
self.assertBeatable(True)
def test_guns_zillions_first(self):
""" with low gun levels, 5 Zillion is enough to get JJ to gun 3 """
self.collect_by_name(["Zillion", "Red ID Card", "Floppy Disk", "Opa-Opa"])
self.assertBeatable(False) # not enough gun
self.collect_by_name("JJ")
self.assertBeatable(True)
class TestGoalChampStart(ZillionTestBase):
""" creation of character rescue items has some special interactions with logic """
options = {
"start_char": "Champ",
"jump_levels": "low",
"gun_levels": "balanced",
"opa_opa_count": 5,
"opas_per_level": 1
}
def test_jump_jj_first(self):
""" with low jump levels, 5 level-ups is enough to get JJ to jump 3 """
self.collect_by_name(["JJ", "Red ID Card", "Floppy Disk", "Zillion"])
self.assertBeatable(False) # not enough jump
self.collect_by_name("Opa-Opa")
self.assertBeatable(True)
def test_jump_opa_first(self):
""" with low jump levels, 5 level-ups is enough to get JJ to jump 3 """
self.collect_by_name(["Opa-Opa", "Red ID Card", "Floppy Disk", "Zillion"])
self.assertBeatable(False) # not enough jump
self.collect_by_name("JJ")
self.assertBeatable(True)

View File

@@ -0,0 +1,26 @@
from test.worlds.zillion import ZillionTestBase
from worlds.zillion.options import ZillionJumpLevels, ZillionGunLevels, validate
from zilliandomizer.options import VBLR_CHOICES
class OptionsTest(ZillionTestBase):
auto_construct = False
def test_validate_default(self) -> None:
self.world_setup()
validate(self.world, 1)
def test_vblr_ap_to_zz(self) -> None:
""" all of the valid values for the AP options map to valid values for ZZ options """
for option_name, vblr_class in (
("jump_levels", ZillionJumpLevels),
("gun_levels", ZillionGunLevels)
):
for value in vblr_class.name_lookup.values():
self.options = {option_name: value}
self.world_setup()
zz_options, _item_counts = validate(self.world, 1)
assert getattr(zz_options, option_name) in VBLR_CHOICES
# TODO: test validate with invalid combinations of options

View File

@@ -0,0 +1,5 @@
from test.worlds.test_base import WorldTestBase
class ZillionTestBase(WorldTestBase):
game = "Zillion"