mypy phase 1

This commit is contained in:
Silvris
2024-04-09 00:28:24 -05:00
parent c57b6d0e9f
commit 26e9658c4a
12 changed files with 126 additions and 110 deletions

View File

@@ -1,6 +1,6 @@
import struct
from .Options import KirbyFlavorPreset, GooeyFlavorPreset
from typing import TYPE_CHECKING, Optional, Dict, List
from typing import TYPE_CHECKING, Optional, Dict, List, Tuple
if TYPE_CHECKING:
from . import KDL3World
@@ -431,7 +431,7 @@ def get_palette_bytes(palette: Dict[str, str], target: List[str], offset: int, f
if hexcol.startswith("#"):
hexcol = hexcol.replace("#", "")
colint = int(hexcol, 16)
col = ((colint & 0xFF0000) >> 16, (colint & 0xFF00) >> 8, colint & 0xFF)
col: Tuple[int, ...] = ((colint & 0xFF0000) >> 16, (colint & 0xFF00) >> 8, colint & 0xFF)
col = tuple(int(int(factor*x) + offset) for x in col)
byte_data = rgb888_to_bgr555(col[0], col[1], col[2])
output_data.extend(bytearray(byte_data))

View File

@@ -84,7 +84,7 @@ deathlink_messages = defaultdict(lambda: " was defeated.", {
def cmd_gift(self: "SNIClientCommandProcessor"):
"""Toggles gifting for the current game."""
if not getattr(self.ctx, "gifting", None):
self.ctx.gifting = True
setattr(self.ctx, "gifting", True)
else:
self.ctx.gifting = not self.ctx.gifting
self.output(f"Gifting set to {self.ctx.gifting}")

View File

@@ -36,10 +36,10 @@ async def pop_object(ctx: "CommonContext", key: str, value: str) -> None:
async def initialize_giftboxes(ctx: "CommonContext", giftbox_key: str, motherbox_key: str, is_open: bool) -> None:
ctx.set_notify(motherbox_key, giftbox_key)
await update_object(ctx, f"Giftboxes;{ctx.team}", {f"{ctx.slot}":
{
"IsOpen": is_open,
**kdl3_gifting_options
}})
{
"IsOpen": is_open,
**kdl3_gifting_options
}})
setattr(ctx, "gifting", is_open)

View File

@@ -102,4 +102,4 @@ item_names = {
"Animal Friend": set(animal_friend_table),
}
lookup_name_to_id: typing.Dict[str, int] = {item_name: data.code for item_name, data in item_table.items() if data.code}
lookup_item_to_id: typing.Dict[str, int] = {item_name: data.code for item_name, data in item_table.items() if data.code}

View File

@@ -2,7 +2,7 @@ import typing
from pkgutil import get_data
import Utils
from typing import Optional, TYPE_CHECKING
from typing import Optional, TYPE_CHECKING, Tuple, Dict, List
import hashlib
import os
import struct
@@ -261,53 +261,56 @@ class RomData:
self.file = bytearray(file)
self.name = name
def read_byte(self, offset: int):
def read_byte(self, offset: int) -> int:
return self.file[offset]
def read_bytes(self, offset: int, length: int):
def read_bytes(self, offset: int, length: int) -> bytearray:
return self.file[offset:offset + length]
def write_byte(self, offset: int, value: int):
def write_byte(self, offset: int, value: int) -> None:
self.file[offset] = value
def write_bytes(self, offset: int, values: typing.Sequence) -> None:
def write_bytes(self, offset: int, values: typing.Sequence[int]) -> None:
self.file[offset:offset + len(values)] = values
def get_bytes(self) -> bytes:
return bytes(self.file)
def handle_level_sprites(stages, sprites, palettes):
def handle_level_sprites(stages: List[Tuple[int, ...]], sprites: List[bytearray], palettes: List[List[bytearray]]) \
-> Tuple[List[bytearray], List[bytearray]]:
palette_by_level = list()
for palette in palettes:
palette_by_level.extend(palette[10:16])
out_palettes = list()
for i in range(5):
for j in range(6):
palettes[i][10 + j] = palette_by_level[stages[i][j] - 1]
palettes[i] = [x for palette in palettes[i] for x in palette]
out_palettes.append(bytearray([x for palette in palettes[i] for x in palette]))
tiles_by_level = list()
for spritesheet in sprites:
decompressed = hal_decompress(spritesheet)
tiles = [decompressed[i:i + 32] for i in range(0, 2304, 32)]
tiles_by_level.extend([[tiles[x] for x in stage_tiles[stage]] for stage in stage_tiles])
out_sprites = list()
for world in range(5):
levels = [stages[world][x] - 1 for x in range(6)]
world_tiles: typing.List[typing.Optional[bytes]] = [None for _ in range(72)]
world_tiles: typing.List[bytes] = [bytes() for _ in range(72)]
for i in range(6):
for x in range(12):
world_tiles[stage_tiles[i][x]] = tiles_by_level[levels[i]][x]
sprites[world] = list()
out_sprites.append(bytearray())
for tile in world_tiles:
sprites[world].extend(tile)
out_sprites[world].extend(tile)
# insert our fake compression
sprites[world][0:0] = [0xe3, 0xff]
sprites[world][1026:1026] = [0xe3, 0xff]
sprites[world][2052:2052] = [0xe0, 0xff]
sprites[world].append(0xff)
return sprites, palettes
out_sprites[world][0:0] = [0xe3, 0xff]
out_sprites[world][1026:1026] = [0xe3, 0xff]
out_sprites[world][2052:2052] = [0xe0, 0xff]
out_sprites[world].append(0xff)
return out_sprites, out_palettes
def write_heart_star_sprites(rom: RomData):
def write_heart_star_sprites(rom: RomData) -> None:
compressed = rom.read_bytes(heart_star_address, heart_star_size)
decompressed = hal_decompress(compressed)
patch = get_data(__name__, os.path.join("data", "APHeartStar.bsdiff4"))
@@ -319,7 +322,7 @@ def write_heart_star_sprites(rom: RomData):
rom.write_bytes(0x3F0EBF, [0x00, 0xD0, 0x39])
def write_consumable_sprites(rom: RomData, consumables: bool, stars: bool):
def write_consumable_sprites(rom: RomData, consumables: bool, stars: bool) -> None:
compressed = rom.read_bytes(consumable_address, consumable_size)
decompressed = hal_decompress(compressed)
patched = bytearray(decompressed)
@@ -339,7 +342,7 @@ class KDL3PatchExtensions(APPatchExtension):
game = "Kirby's Dream Land 3"
@staticmethod
def apply_post_patch(_: APProcedurePatch, rom: bytes):
def apply_post_patch(_: APProcedurePatch, rom: bytes) -> bytes:
rom_data = RomData(rom)
target_language = rom_data.read_byte(0x3C020)
rom_data.write_byte(0x7FD9, target_language)
@@ -347,9 +350,9 @@ class KDL3PatchExtensions(APPatchExtension):
if rom_data.read_bytes(0x3D014, 1)[0] > 0:
stages = [struct.unpack("HHHHHHH", rom_data.read_bytes(0x3D020 + x * 14, 14)) for x in range(5)]
palettes = [rom_data.read_bytes(full_pal, 512) for full_pal in stage_palettes]
palettes = [[palette[i:i + 32] for i in range(0, 512, 32)] for palette in palettes]
read_palettes = [[palette[i:i + 32] for i in range(0, 512, 32)] for palette in palettes]
sprites = [rom_data.read_bytes(offset, level_sprites[offset]) for offset in level_sprites]
sprites, palettes = handle_level_sprites(stages, sprites, palettes)
sprites, palettes = handle_level_sprites(stages, sprites, read_palettes)
for addr, palette in zip(stage_palettes, palettes):
rom_data.write_bytes(addr, palette)
for addr, level_sprite in zip([0x1CA000, 0x1CA920, 0x1CB230, 0x1CBB40, 0x1CC450], sprites):
@@ -377,7 +380,7 @@ class KDL3ProcedurePatch(APProcedurePatch, APTokenMixin):
return get_base_rom_bytes()
def patch_rom(world: "KDL3World", patch: KDL3ProcedurePatch):
def patch_rom(world: "KDL3World", patch: KDL3ProcedurePatch) -> None:
patch.write_file("kdl3_basepatch.bsdiff4",
get_data(__name__, os.path.join("data", "kdl3_basepatch.bsdiff4")))
@@ -415,8 +418,8 @@ def patch_rom(world: "KDL3World", patch: KDL3ProcedurePatch):
music_map[8] = world.random.choice(music_choices)
for room in rooms:
room.music = music_map[room.music]
for room in room_music:
patch.write_token(APTokenTypes.WRITE, room + 2, bytes([music_map[room_music[room]]]))
for room_ptr in room_music:
patch.write_token(APTokenTypes.WRITE, room_ptr + 2, bytes([music_map[room_music[room_ptr]]]))
for i, old_music in zip(range(5), [25, 26, 28, 27, 30]):
# level themes
patch.write_token(APTokenTypes.WRITE, 0x133F2 + i, bytes([music_map[old_music]]))
@@ -563,13 +566,15 @@ def patch_rom(world: "KDL3World", patch: KDL3ProcedurePatch):
for addr in kirby_target_palettes:
target = kirby_target_palettes[addr]
palette = get_kirby_palette(world)
patch.write_token(APTokenTypes.WRITE, addr, get_palette_bytes(palette, target[0], target[1], target[2]))
if palette is not None:
patch.write_token(APTokenTypes.WRITE, addr, get_palette_bytes(palette, target[0], target[1], target[2]))
if world.options.gooey_flavor_preset.value != 0:
for addr in gooey_target_palettes:
target = gooey_target_palettes[addr]
palette = get_gooey_palette(world)
patch.write_token(APTokenTypes.WRITE, addr, get_palette_bytes(palette, target[0], target[1], target[2]))
if palette is not None:
patch.write_token(APTokenTypes.WRITE, addr, get_palette_bytes(palette, target[0], target[1], target[2]))
patch.write_file("token_patch.bin", patch.get_token_binary())

View File

@@ -56,10 +56,12 @@ class KDL3Room(Region):
patch.write_token(APTokenTypes.WRITE, self.pointer + address + 7,
animal_map[current_animal].to_bytes(1, "little"))
if local_items:
for location in self.locations:
if not location.address or not location.item or location.item.player != self.player:
for location in self.get_locations():
if location.item is None or location.item.player != self.player:
continue
item = location.item.code
if item is None:
continue
item_idx = item & 0x00000F
location_idx = location.address & 0xFFFF
if location_idx & 0xF00 in (0x300, 0x400, 0x500, 0x600):
@@ -84,7 +86,7 @@ class KDL3Room(Region):
load_len = len(self.entity_load)
for consumable in self.consumables:
location = next(x for x in self.locations if x.name == consumable["name"])
assert location.item
assert location.item is not None
is_progression = location.item.classification & ItemClassification.progression
if load_len == 8:
# edge case, there is exactly 1 room with 8 entities and only 1 consumable among them
@@ -126,5 +128,6 @@ class KDL3Room(Region):
vtype = 3
else:
vtype = 2
assert isinstance(consumable["pointer"], int)
patch.write_token(APTokenTypes.WRITE, self.pointer + consumable["pointer"] + 7,
vtype.to_bytes(1, "little"))

View File

@@ -10,7 +10,7 @@ if typing.TYPE_CHECKING:
def can_reach_boss(state: "CollectionState", player: int, level: int, open_world: int,
ow_boss_req: int, player_levels: typing.Dict[int, typing.Dict[int, int]]):
ow_boss_req: int, player_levels: typing.Dict[int, typing.List[int]]) -> bool:
if open_world:
return state.has(f"{LocationName.level_names_inverse[level]} - Stage Completion", player, ow_boss_req)
else:
@@ -86,7 +86,7 @@ ability_map: typing.Dict[str, typing.Callable[["CollectionState", int], bool]] =
}
def can_assemble_rob(state: "CollectionState", player: int, copy_abilities: typing.Dict[str, str]):
def can_assemble_rob(state: "CollectionState", player: int, copy_abilities: typing.Dict[str, str]) -> bool:
# check animal requirements
if not (can_reach_coo(state, player) and can_reach_kine(state, player)):
return False
@@ -103,7 +103,7 @@ def can_assemble_rob(state: "CollectionState", player: int, copy_abilities: typi
return can_reach_parasol(state, player) and can_reach_stone(state, player)
def can_fix_angel_wings(state: "CollectionState", player: int, copy_abilities: typing.Dict[str, str]):
def can_fix_angel_wings(state: "CollectionState", player: int, copy_abilities: typing.Dict[str, str]) -> bool:
can_reach = True
for enemy in {"Sparky", "Blocky", "Jumper Shoot", "Yuki", "Sir Kibble", "Haboki", "Boboo", "Captain Stitch"}:
can_reach = can_reach & ability_map[copy_abilities[enemy]](state, player)
@@ -310,14 +310,14 @@ def set_rules(world: "KDL3World") -> None:
LocationName.iceberg_dedede],
range(1, 6)):
set_rule(world.multiworld.get_location(boss_flag, world.player),
lambda state, i=i: (state.has("Heart Star", world.player, world.boss_requirements[i - 1])
and can_reach_boss(state, world.player, i,
lambda state, x=i: (state.has("Heart Star", world.player, world.boss_requirements[x - 1])
and can_reach_boss(state, world.player, x,
world.options.open_world.value,
world.options.ow_boss_requirement.value,
world.player_levels)))
set_rule(world.multiworld.get_location(purification, world.player),
lambda state, i=i: (state.has("Heart Star", world.player, world.boss_requirements[i - 1])
and can_reach_boss(state, world.player, i,
lambda state, x=i: (state.has("Heart Star", world.player, world.boss_requirements[x - 1])
and can_reach_boss(state, world.player, x,
world.options.open_world.value,
world.options.ow_boss_requirement.value,
world.player_levels)))
@@ -327,12 +327,12 @@ def set_rules(world: "KDL3World") -> None:
for level in range(2, 6):
set_rule(world.multiworld.get_entrance(f"To Level {level}", world.player),
lambda state, i=level: state.has(f"Level {i - 1} Boss Defeated", world.player))
lambda state, x=level: state.has(f"Level {x - 1} Boss Defeated", world.player))
if world.options.strict_bosses:
for level in range(2, 6):
add_rule(world.multiworld.get_entrance(f"To Level {level}", world.player),
lambda state, i=level: state.has(f"Level {i - 1} Boss Purified", world.player))
lambda state, x=level: state.has(f"Level {x - 1} Boss Purified", world.player))
if world.options.goal_speed == GoalSpeed.option_normal:
add_rule(world.multiworld.get_entrance("To Level 6", world.player),

View File

@@ -1,12 +1,13 @@
import logging
import typing
from BaseClasses import Tutorial, ItemClassification, MultiWorld, CollectionState
from BaseClasses import Tutorial, ItemClassification, MultiWorld, CollectionState, Item
from Fill import fill_restrictive
from Options import PerGameCommonOptions
from worlds.AutoWorld import World, WebWorld
from .Items import item_table, item_names, copy_ability_table, animal_friend_table, filler_item_weights, KDL3Item, \
trap_item_table, copy_ability_access_table, star_item_weights, total_filler_weights, animal_friend_spawn_table
trap_item_table, copy_ability_access_table, star_item_weights, total_filler_weights, animal_friend_spawn_table,\
lookup_item_to_id
from .Locations import location_table, KDL3Location, level_consumables, consumable_locations, star_locations
from .Names.AnimalFriendSpawns import animal_friend_spawns
from .Names.EnemyAbilities import vanilla_enemies, enemy_mapping, enemy_restrictive
@@ -19,7 +20,7 @@ from .Rules import set_rules
from .Rom import KDL3ProcedurePatch, get_base_rom_path, patch_rom, KDL3JHASH, KDL3UHASH
from .Client import KDL3SNIClient
from typing import Dict, TextIO, Optional, List
from typing import Dict, TextIO, Optional, List, Any
import os
import math
import threading
@@ -63,27 +64,27 @@ class KDL3World(World):
game = "Kirby's Dream Land 3"
options_dataclass: typing.ClassVar[typing.Type[PerGameCommonOptions]] = KDL3Options
options: KDL3Options
item_name_to_id = {item: item_table[item].code for item in item_table}
item_name_to_id = lookup_item_to_id
location_name_to_id = {location_table[location]: location for location in location_table}
item_name_groups = item_names
web = KDL3WebWorld()
settings: typing.ClassVar[KDL3Settings]
def __init__(self, multiworld: MultiWorld, player: int):
self.rom_name = None
self.rom_name: bytearray = bytearray()
self.rom_name_available_event = threading.Event()
super().__init__(multiworld, player)
self.copy_abilities: Dict[str, str] = vanilla_enemies.copy()
self.required_heart_stars: int = 0 # we fill this during create_items
self.boss_requirements: Dict[int, int] = dict()
self.boss_requirements: List[int] = []
self.player_levels = default_levels.copy()
self.stage_shuffle_enabled = False
self.boss_butch_bosses: List[Optional[bool]] = list()
self.rooms: Optional[List[KDL3Room]] = None
self.boss_butch_bosses: List[Optional[bool]] = []
self.rooms: List[KDL3Room] = []
create_regions = create_levels
def create_item(self, name: str, force_non_progression=False) -> KDL3Item:
def create_item(self, name: str, force_non_progression: bool = False) -> KDL3Item:
item = item_table[name]
classification = ItemClassification.filler
if item.progression and not force_non_progression:
@@ -93,7 +94,7 @@ class KDL3World(World):
classification = ItemClassification.trap
return KDL3Item(name, classification, item.code, self.player)
def get_filler_item_name(self, include_stars=True) -> str:
def get_filler_item_name(self, include_stars: bool = True) -> str:
if include_stars:
return self.random.choices(list(total_filler_weights.keys()),
weights=list(total_filler_weights.values()))[0]
@@ -107,7 +108,7 @@ class KDL3World(World):
self.options.ability_trap_weight.value])[0]
def get_restrictive_copy_ability_placement(self, copy_ability: str, enemies_to_set: typing.List[str],
level: int, stage: int):
level: int, stage: int) -> Optional[str]:
valid_rooms = [room for room in self.rooms if (room.level < level)
or (room.level == level and room.stage < stage)] # leave out the stage in question to avoid edge
valid_enemies = set()
@@ -118,7 +119,7 @@ class KDL3World(World):
return None # a valid enemy got placed by a more restrictive placement
return self.random.choice(sorted([enemy for enemy in valid_enemies if enemy not in placed_enemies]))
def get_pre_fill_items(self) -> List[KDL3Item]:
def get_pre_fill_items(self) -> List[Item]:
return [self.create_item(item)
for item in [*copy_ability_access_table.keys(), *animal_friend_spawn_table.keys()]]
@@ -214,7 +215,7 @@ class KDL3World(World):
animal_pool.remove("Kine Spawn") # should always have at least one in the pool
spawn.place_locked_item(self.create_item("Kine Spawn"))
locations = [self.multiworld.get_location(spawn, self.player) for spawn in spawns]
items = [self.create_item(animal) for animal in animal_pool]
items: List[Item] = [self.create_item(animal) for animal in animal_pool]
allstate = CollectionState(self.multiworld)
for item in [*copy_ability_table, *animal_friend_table, *["Heart Star" for _ in range(50)]]:
self.collect(allstate, self.create_item(item))
@@ -290,7 +291,7 @@ class KDL3World(World):
def generate_basic(self) -> None:
self.stage_shuffle_enabled = self.options.stage_shuffle > 0
goal = self.options.goal
goal = self.options.goal.value
goal_location = self.multiworld.get_location(LocationName.goals[goal], self.player)
goal_location.place_locked_item(KDL3Item("Love-Love Rod", ItemClassification.progression, None, self.player))
for level in range(1, 6):
@@ -310,7 +311,7 @@ class KDL3World(World):
else:
self.boss_butch_bosses = [False for _ in range(6)]
def generate_output(self, output_directory: str):
def generate_output(self, output_directory: str) -> None:
try:
patch = KDL3ProcedurePatch()
patch_rom(self, patch)
@@ -324,9 +325,10 @@ class KDL3World(World):
finally:
self.rom_name_available_event.set() # make sure threading continues and errors are collected
def modify_multidata(self, multidata: dict):
def modify_multidata(self, multidata: Dict[str, Any]) -> None:
# wait for self.rom_name to be available.
self.rom_name_available_event.wait()
assert isinstance(self.rom_name, bytes)
rom_name = getattr(self, "rom_name", None)
# we skip in case of error, so that the original error in the output thread is the one that gets raised
if rom_name:
@@ -341,21 +343,22 @@ class KDL3World(World):
spoiler_handle.write(f"{level} {i}: {location_table[stage].replace(' - Complete', '')}\n")
if self.options.animal_randomization:
spoiler_handle.write(f"\nAnimal Friends ({self.multiworld.get_player_name(self.player)}):\n")
for level in self.player_levels:
for lvl in self.player_levels:
for stage in range(6):
rooms = [room for room in self.rooms if room.level == level and room.stage == stage]
rooms = [room for room in self.rooms if room.level == lvl and room.stage == stage]
animals = []
for room in rooms:
animals.extend([location.item.name.replace(" Spawn", "")
for location in room.locations if "Animal" in location.name])
spoiler_handle.write(f"{location_table[self.player_levels[level][stage]].replace(' - Complete','')}"
for location in room.locations if "Animal" in location.name
and location.item is not None])
spoiler_handle.write(f"{location_table[self.player_levels[lvl][stage]].replace(' - Complete','')}"
f": {', '.join(animals)}\n")
if self.options.copy_ability_randomization:
spoiler_handle.write(f"\nCopy Abilities ({self.multiworld.get_player_name(self.player)}):\n")
for enemy in self.copy_abilities:
spoiler_handle.write(f"{enemy}: {self.copy_abilities[enemy].replace('No Ability', 'None').replace(' Ability', '')}\n")
def extend_hint_information(self, hint_data: Dict[int, Dict[int, str]]):
def extend_hint_information(self, hint_data: Dict[int, Dict[int, str]]) -> None:
if self.stage_shuffle_enabled:
regions = {LocationName.level_names[level]: level for level in LocationName.level_names}
level_hint_data = {}
@@ -365,6 +368,6 @@ class KDL3World(World):
self.player).name.replace(" - Complete", "")
stage_regions = [room for room in self.rooms if stage_name in room.name]
for region in stage_regions:
for location in [location for location in region.locations if location.address]:
for location in [location for location in list(region.get_locations()) if location.address]:
level_hint_data[location.address] = f"{regions[level]} {stage + 1 if stage < 6 else 'Boss'}"
hint_data[self.player] = level_hint_data

View File

@@ -2,10 +2,12 @@ import typing
from argparse import Namespace
from BaseClasses import MultiWorld, PlandoOptions, CollectionState
from test.TestBase import WorldTestBase
from test.bases import WorldTestBase
from test.general import gen_steps
from worlds import AutoWorld
from worlds.AutoWorld import call_all
# mypy: ignore-errors
# This is a copy of core code, and I'm not smart enough to solve the errors in here
class KDL3TestBase(WorldTestBase):

View File

@@ -10,7 +10,7 @@ class TestFastGoal(KDL3TestBase):
"filler_percentage": 0,
}
def test_goal(self):
def test_goal(self) -> None:
self.assertBeatable(False)
heart_stars = self.get_items_by_name("Heart Star")
self.collect(heart_stars[0:14])
@@ -35,7 +35,7 @@ class TestNormalGoal(KDL3TestBase):
"filler_percentage": 0,
}
def test_goal(self):
def test_goal(self) -> None:
self.assertBeatable(False)
heart_stars = self.get_items_by_name("Heart Star")
self.collect(heart_stars[0:14])
@@ -51,14 +51,14 @@ class TestNormalGoal(KDL3TestBase):
self.assertEqual(self.count("Heart Star"), 30, str(self.multiworld.seed))
self.assertBeatable(True)
def test_kine(self):
def test_kine(self) -> None:
self.collect_by_name(["Cutter", "Burning", "Heart Star"])
self.assertBeatable(False)
def test_cutter(self):
def test_cutter(self) -> None:
self.collect_by_name(["Kine", "Burning", "Heart Star"])
self.assertBeatable(False)
def test_burning(self):
def test_burning(self) -> None:
self.collect_by_name(["Cutter", "Kine", "Heart Star"])
self.assertBeatable(False)

View File

@@ -12,7 +12,7 @@ class TestLocations(KDL3TestBase):
# these ensure we can always reach all stages physically
}
def test_simple_heart_stars(self):
def test_simple_heart_stars(self) -> None:
self.run_location_test(LocationName.grass_land_muchi, ["ChuChu"])
self.run_location_test(LocationName.grass_land_chao, ["Stone"])
self.run_location_test(LocationName.grass_land_mine, ["Kine"])
@@ -23,9 +23,9 @@ class TestLocations(KDL3TestBase):
self.run_location_test(LocationName.sand_canyon_auntie, ["Clean"])
self.run_location_test(LocationName.sand_canyon_nyupun, ["ChuChu", "Cutter"])
self.run_location_test(LocationName.sand_canyon_rob, ["Stone", "Kine", "Coo", "Parasol", "Spark", "Ice"])
self.run_location_test(LocationName.sand_canyon_rob, ["Stone", "Kine", "Coo", "Parasol", "Clean", "Ice"]),
self.run_location_test(LocationName.sand_canyon_rob, ["Stone", "Kine", "Coo", "Parasol", "Spark", "Needle"]),
self.run_location_test(LocationName.sand_canyon_rob, ["Stone", "Kine", "Coo", "Parasol", "Clean", "Needle"]),
self.run_location_test(LocationName.sand_canyon_rob, ["Stone", "Kine", "Coo", "Parasol", "Clean", "Ice"])
self.run_location_test(LocationName.sand_canyon_rob, ["Stone", "Kine", "Coo", "Parasol", "Spark", "Needle"])
self.run_location_test(LocationName.sand_canyon_rob, ["Stone", "Kine", "Coo", "Parasol", "Clean", "Needle"])
self.run_location_test(LocationName.cloudy_park_hibanamodoki, ["Coo", "Clean"])
self.run_location_test(LocationName.cloudy_park_piyokeko, ["Needle"])
self.run_location_test(LocationName.cloudy_park_mikarin, ["Coo"])
@@ -36,7 +36,7 @@ class TestLocations(KDL3TestBase):
self.run_location_test(LocationName.iceberg_angel, ["Cutter", "Burning", "Spark", "Parasol", "Needle", "Clean",
"Stone", "Ice"])
def run_location_test(self, location: str, itempool: typing.List[str]):
def run_location_test(self, location: str, itempool: typing.List[str]) -> None:
items = itempool.copy()
while len(itempool) > 0:
self.assertFalse(self.can_reach_location(location), str(self.multiworld.seed))
@@ -59,7 +59,7 @@ class TestShiro(KDL3TestBase):
"plando_options": "connections"
}
def test_shiro(self):
def test_shiro(self) -> None:
self.assertFalse(self.can_reach_location("Iceberg 5 - Shiro"), str(self.multiworld.seed))
self.collect_by_name("Nago")
self.assertFalse(self.can_reach_location("Iceberg 5 - Shiro"), str(self.multiworld.seed))

View File

@@ -13,7 +13,7 @@ class TestCopyAbilityShuffle(KDL3TestBase):
"copy_ability_randomization": "enabled",
}
def test_goal(self):
def test_goal(self) -> None:
try:
self.assertBeatable(False)
heart_stars = self.get_items_by_name("Heart Star")
@@ -33,28 +33,28 @@ class TestCopyAbilityShuffle(KDL3TestBase):
# if assert beatable fails, this will catch and print the seed
raise AssertionError(f"Test failed, Seed:{self.multiworld.seed}") from ex
def test_kine(self):
def test_kine(self) -> None:
try:
self.collect_by_name(["Cutter", "Burning", "Heart Star"])
self.assertBeatable(False)
except AssertionError as ex:
raise AssertionError(f"Test failed, Seed:{self.multiworld.seed}") from ex
def test_cutter(self):
def test_cutter(self) -> None:
try:
self.collect_by_name(["Kine", "Burning", "Heart Star"])
self.assertBeatable(False)
except AssertionError as ex:
raise AssertionError(f"Test failed, Seed:{self.multiworld.seed}") from ex
def test_burning(self):
def test_burning(self) -> None:
try:
self.collect_by_name(["Cutter", "Kine", "Heart Star"])
self.assertBeatable(False)
except AssertionError as ex:
raise AssertionError(f"Test failed, Seed:{self.multiworld.seed}") from ex
def test_cutter_and_burning_reachable(self):
def test_cutter_and_burning_reachable(self) -> None:
rooms = self.multiworld.worlds[1].rooms
copy_abilities = self.multiworld.worlds[1].copy_abilities
sand_canyon_5 = self.multiworld.get_region("Sand Canyon 5 - 9", 1)
@@ -76,7 +76,7 @@ class TestCopyAbilityShuffle(KDL3TestBase):
else:
self.fail("Could not reach Burning Ability before Iceberg 4!")
def test_valid_abilities_for_ROB(self):
def test_valid_abilities_for_ROB(self) -> None:
# there exists a subset of 4-7 abilities that will allow us access to ROB heart star on default settings
self.collect_by_name(["Heart Star", "Kine", "Coo"]) # we will guaranteed need Coo, Kine, and Heart Stars to reach
# first we need to identify our bukiset requirements
@@ -87,13 +87,13 @@ class TestCopyAbilityShuffle(KDL3TestBase):
({"Stone Ability", "Burning Ability"}, {'Bukiset (Stone)', 'Bukiset (Burning)'}),
]
copy_abilities = self.multiworld.worlds[1].copy_abilities
required_abilities: List[Tuple[str]] = []
required_abilities: List[List[str]] = []
for abilities, bukisets in groups:
potential_abilities: List[str] = list()
for bukiset in bukisets:
if copy_abilities[bukiset] in abilities:
potential_abilities.append(copy_abilities[bukiset])
required_abilities.append(tuple(potential_abilities))
required_abilities.append(potential_abilities)
collected_abilities = list()
for group in required_abilities:
self.assertFalse(len(group) == 0, str(self.multiworld.seed))
@@ -122,7 +122,7 @@ class TestAnimalShuffle(KDL3TestBase):
"animal_randomization": "full",
}
def test_goal(self):
def test_goal(self) -> None:
try:
self.assertBeatable(False)
heart_stars = self.get_items_by_name("Heart Star")
@@ -142,33 +142,36 @@ class TestAnimalShuffle(KDL3TestBase):
# if assert beatable fails, this will catch and print the seed
raise AssertionError(f"Test failed, Seed:{self.multiworld.seed}") from ex
def test_kine(self):
def test_kine(self) -> None:
try:
self.collect_by_name(["Cutter", "Burning", "Heart Star"])
self.assertBeatable(False)
except AssertionError as ex:
raise AssertionError(f"Test failed, Seed:{self.multiworld.seed}") from ex
def test_cutter(self):
def test_cutter(self) -> None:
try:
self.collect_by_name(["Kine", "Burning", "Heart Star"])
self.assertBeatable(False)
except AssertionError as ex:
raise AssertionError(f"Test failed, Seed:{self.multiworld.seed}") from ex
def test_burning(self):
def test_burning(self) -> None:
try:
self.collect_by_name(["Cutter", "Kine", "Heart Star"])
self.assertBeatable(False)
except AssertionError as ex:
raise AssertionError(f"Test failed, Seed:{self.multiworld.seed}") from ex
def test_locked_animals(self):
self.assertTrue(self.multiworld.get_location("Ripple Field 5 - Animal 2", 1).item.name == "Pitch Spawn",
def test_locked_animals(self) -> None:
ripple_field_5 = self.multiworld.get_location("Ripple Field 5 - Animal 2", 1)
self.assertTrue(ripple_field_5.item is not None and ripple_field_5.item.name == "Pitch Spawn",
f"Multiworld did not place Pitch, Seed: {self.multiworld.seed}")
self.assertTrue(self.multiworld.get_location("Iceberg 4 - Animal 1", 1).item.name == "ChuChu Spawn",
iceberg_4 = self.multiworld.get_location("Iceberg 4 - Animal 1", 1)
self.assertTrue(iceberg_4.item is not None and iceberg_4.item.name == "ChuChu Spawn",
f"Multiworld did not place ChuChu, Seed: {self.multiworld.seed}")
self.assertTrue(self.multiworld.get_location("Sand Canyon 6 - Animal 1", 1).item.name in
sand_canyon_6 = self.multiworld.get_location("Sand Canyon 6 - Animal 1", 1)
self.assertTrue(sand_canyon_6.item is not None and sand_canyon_6.item.name in
{"Kine Spawn", "Coo Spawn"}, f"Multiworld did not place Coo/Kine, Seed: {self.multiworld.seed}")
@@ -183,10 +186,7 @@ class TestAllShuffle(KDL3TestBase):
"copy_ability_randomization": "enabled",
}
def world_setup(self, seed: Optional[int] = None) -> None:
super().world_setup(97344459114886422393)
def test_goal(self):
def test_goal(self) -> None:
try:
self.assertBeatable(False)
heart_stars = self.get_items_by_name("Heart Star")
@@ -206,36 +206,39 @@ class TestAllShuffle(KDL3TestBase):
# if assert beatable fails, this will catch and print the seed
raise AssertionError(f"Test failed, Seed:{self.multiworld.seed}") from ex
def test_kine(self):
def test_kine(self) -> None:
try:
self.collect_by_name(["Cutter", "Burning", "Heart Star"])
self.assertBeatable(False)
except AssertionError as ex:
raise AssertionError(f"Test failed, Seed:{self.multiworld.seed}") from ex
def test_cutter(self):
def test_cutter(self) -> None:
try:
self.collect_by_name(["Kine", "Burning", "Heart Star"])
self.assertBeatable(False)
except AssertionError as ex:
raise AssertionError(f"Test failed, Seed:{self.multiworld.seed}") from ex
def test_burning(self):
def test_burning(self) -> None:
try:
self.collect_by_name(["Cutter", "Kine", "Heart Star"])
self.assertBeatable(False)
except AssertionError as ex:
raise AssertionError(f"Test failed, Seed:{self.multiworld.seed}") from ex
def test_locked_animals(self):
self.assertTrue(self.multiworld.get_location("Ripple Field 5 - Animal 2", 1).item.name == "Pitch Spawn",
def test_locked_animals(self) -> None:
ripple_field_5 = self.multiworld.get_location("Ripple Field 5 - Animal 2", 1)
self.assertTrue(ripple_field_5.item is not None and ripple_field_5.item.name == "Pitch Spawn",
f"Multiworld did not place Pitch, Seed: {self.multiworld.seed}")
self.assertTrue(self.multiworld.get_location("Iceberg 4 - Animal 1", 1).item.name == "ChuChu Spawn",
iceberg_4 = self.multiworld.get_location("Iceberg 4 - Animal 1", 1)
self.assertTrue(iceberg_4.item is not None and iceberg_4.item.name == "ChuChu Spawn",
f"Multiworld did not place ChuChu, Seed: {self.multiworld.seed}")
self.assertTrue(self.multiworld.get_location("Sand Canyon 6 - Animal 1", 1).item.name in
sand_canyon_6 = self.multiworld.get_location("Sand Canyon 6 - Animal 1", 1)
self.assertTrue(sand_canyon_6.item is not None and sand_canyon_6.item.name in
{"Kine Spawn", "Coo Spawn"}, f"Multiworld did not place Coo/Kine, Seed: {self.multiworld.seed}")
def test_cutter_and_burning_reachable(self):
def test_cutter_and_burning_reachable(self) -> None:
rooms = self.multiworld.worlds[1].rooms
copy_abilities = self.multiworld.worlds[1].copy_abilities
sand_canyon_5 = self.multiworld.get_region("Sand Canyon 5 - 9", 1)
@@ -257,7 +260,7 @@ class TestAllShuffle(KDL3TestBase):
else:
self.fail("Could not reach Burning Ability before Iceberg 4!")
def test_valid_abilities_for_ROB(self):
def test_valid_abilities_for_ROB(self) -> None:
# there exists a subset of 4-7 abilities that will allow us access to ROB heart star on default settings
self.collect_by_name(["Heart Star", "Kine", "Coo"]) # we will guaranteed need Coo, Kine, and Heart Stars to reach
# first we need to identify our bukiset requirements
@@ -268,13 +271,13 @@ class TestAllShuffle(KDL3TestBase):
({"Stone Ability", "Burning Ability"}, {'Bukiset (Stone)', 'Bukiset (Burning)'}),
]
copy_abilities = self.multiworld.worlds[1].copy_abilities
required_abilities: List[Tuple[str]] = []
required_abilities: List[List[str]] = []
for abilities, bukisets in groups:
potential_abilities: List[str] = list()
for bukiset in bukisets:
if copy_abilities[bukiset] in abilities:
potential_abilities.append(copy_abilities[bukiset])
required_abilities.append(tuple(potential_abilities))
required_abilities.append(potential_abilities)
collected_abilities = list()
for group in required_abilities:
self.assertFalse(len(group) == 0, str(self.multiworld.seed))