From 86a55c78377246872359e15af8fafa73ff0de867 Mon Sep 17 00:00:00 2001 From: el-u <109771707+el-u@users.noreply.github.com> Date: Sat, 22 Jul 2023 07:49:23 +0200 Subject: [PATCH] lufia2ac: code cleanup (#1971) --- worlds/lufia2ac/__init__.py | 50 ++++++++------------ worlds/lufia2ac/basepatch/basepatch.asm | 22 +-------- worlds/lufia2ac/basepatch/basepatch.bsdiff4 | Bin 7745 -> 8365 bytes 3 files changed, 21 insertions(+), 51 deletions(-) diff --git a/worlds/lufia2ac/__init__.py b/worlds/lufia2ac/__init__.py index bcf8e75d..f14048a2 100644 --- a/worlds/lufia2ac/__init__.py +++ b/worlds/lufia2ac/__init__.py @@ -1,13 +1,11 @@ import base64 import itertools import os -import settings - from enum import IntFlag -from random import Random from typing import Any, ClassVar, Dict, get_type_hints, Iterator, List, Set, Tuple -from BaseClasses import Entrance, Item, ItemClassification, Location, MultiWorld, Region, Tutorial +import settings +from BaseClasses import Item, ItemClassification, Location, MultiWorld, Region, Tutorial from Options import AssembleOptions from Utils import __version__ from worlds.AutoWorld import WebWorld, World @@ -103,11 +101,9 @@ class L2ACWorld(World): def create_regions(self) -> None: menu = Region("Menu", self.player, self.multiworld) - menu.exits.append(Entrance(self.player, "AncientDungeonEntrance", menu)) self.multiworld.regions.append(menu) ancient_dungeon = Region("AncientDungeon", self.player, self.multiworld, "Ancient Dungeon") - ancient_dungeon.exits.append(Entrance(self.player, "FinalFloorEntrance", ancient_dungeon)) item_count: int = int(self.o.blue_chest_count) if self.o.shuffle_capsule_monsters: item_count += len(self.item_name_groups["Capsule monsters"]) @@ -115,11 +111,11 @@ class L2ACWorld(World): item_count += len(self.item_name_groups["Party members"]) for location_name, location_id in itertools.islice(l2ac_location_name_to_id.items(), item_count): ancient_dungeon.locations.append(L2ACLocation(self.player, location_name, location_id, ancient_dungeon)) - prog_chest_access = L2ACItem("Progressive chest access", ItemClassification.progression, None, self.player) for i in range(CHESTS_PER_SPHERE, item_count, CHESTS_PER_SPHERE): chest_access = \ L2ACLocation(self.player, f"Chest access {i + 1}-{i + CHESTS_PER_SPHERE}", None, ancient_dungeon) - chest_access.place_locked_item(prog_chest_access) + chest_access.place_locked_item( + L2ACItem("Progressive chest access", ItemClassification.progression, None, self.player)) ancient_dungeon.locations.append(chest_access) for iris in self.item_name_groups["Iris treasures"]: treasure_name: str = f"Iris treasure {self.item_name_to_id[iris] - self.item_name_to_id['Iris sword'] + 1}" @@ -138,14 +134,12 @@ class L2ACWorld(World): final_floor.locations.append(boss) self.multiworld.regions.append(final_floor) - self.multiworld.get_entrance("AncientDungeonEntrance", self.player) \ - .connect(self.multiworld.get_region("AncientDungeon", self.player)) - self.multiworld.get_entrance("FinalFloorEntrance", self.player) \ - .connect(self.multiworld.get_region("FinalFloor", self.player)) + menu.connect(ancient_dungeon, "AncientDungeonEntrance") + ancient_dungeon.connect(final_floor, "FinalFloorEntrance") def create_items(self) -> None: - item_pool: List[str] = self.multiworld.random.choices(sorted(self.item_name_groups["Blue chest items"]), - k=self.o.blue_chest_count - self.o.custom_item_pool.count) + item_pool: List[str] = self.random.choices(sorted(self.item_name_groups["Blue chest items"]), + k=self.o.blue_chest_count - self.o.custom_item_pool.count) item_pool += [item_name for item_name, count in self.o.custom_item_pool.items() for _ in range(count)] if self.o.shuffle_capsule_monsters: @@ -155,9 +149,7 @@ class L2ACWorld(World): item_pool += self.item_name_groups["Party members"] self.o.blue_chest_count.value += len(self.item_name_groups["Party members"]) for item_name in item_pool: - item_data: ItemData = l2ac_item_table[item_name] - item_id: int = items_start_id + item_data.code - self.multiworld.itempool.append(L2ACItem(item_name, item_data.classification, item_id, self.player)) + self.multiworld.itempool.append(self.create_item(item_name)) def set_rules(self) -> None: for i in range(1, self.o.blue_chest_count): @@ -270,7 +262,7 @@ class L2ACWorld(World): def get_filler_item_name(self) -> str: return ["Potion", "Hi-Magic", "Miracle", "Hi-Potion", "Potion", "Ex-Potion", "Regain", "Ex-Magic", "Hi-Magic"][ - (self.multiworld.random.randrange(9) + self.multiworld.random.randrange(9)) // 2] + (self.random.randrange(9) + self.random.randrange(9)) // 2] # end of overridden AutoWorld.py methods @@ -324,33 +316,31 @@ class L2ACWorld(World): index_set: Set[int] = set(used_indices) used_pointers: List[bytes] = [pointer for index, pointer in enumerate(pointers) if index in index_set] - slot_random: Random = self.multiworld.per_slot_randoms[self.player] - d: int = 2 * 6 if self.o.enemy_floor_numbers == EnemyFloorNumbers.option_shuffle: - constrained_shuffle(used_formations, d, random=slot_random) + constrained_shuffle(used_formations, d, random=self.random) elif self.o.enemy_floor_numbers == EnemyFloorNumbers.option_randomize: - used_formations = constrained_choices(used_formations, d, k=len(used_formations), random=slot_random) + used_formations = constrained_choices(used_formations, d, k=len(used_formations), random=self.random) if self.o.enemy_sprites == EnemySprites.option_shuffle: - slot_random.shuffle(used_sprites) + self.random.shuffle(used_sprites) elif self.o.enemy_sprites == EnemySprites.option_randomize: - used_sprites = slot_random.choices(tuple(dict.fromkeys(used_sprites)), k=len(used_sprites)) + used_sprites = self.random.choices(tuple(dict.fromkeys(used_sprites)), k=len(used_sprites)) elif self.o.enemy_sprites == EnemySprites.option_singularity: - used_sprites = [slot_random.choice(tuple(dict.fromkeys(used_sprites)))] * len(used_sprites) + used_sprites = [self.random.choice(tuple(dict.fromkeys(used_sprites)))] * len(used_sprites) elif self.o.enemy_sprites.sprite: used_sprites = [self.o.enemy_sprites.sprite] * len(used_sprites) if self.o.enemy_movement_patterns == EnemyMovementPatterns.option_shuffle_by_pattern: - slot_random.shuffle(used_pointers) + self.random.shuffle(used_pointers) elif self.o.enemy_movement_patterns == EnemyMovementPatterns.option_randomize_by_pattern: - used_pointers = slot_random.choices(tuple(dict.fromkeys(used_pointers)), k=len(used_pointers)) + used_pointers = self.random.choices(tuple(dict.fromkeys(used_pointers)), k=len(used_pointers)) elif self.o.enemy_movement_patterns == EnemyMovementPatterns.option_shuffle_by_sprite: - slot_random.shuffle(used_indices) + self.random.shuffle(used_indices) elif self.o.enemy_movement_patterns == EnemyMovementPatterns.option_randomize_by_sprite: - used_indices = slot_random.choices(tuple(dict.fromkeys(used_indices)), k=len(used_indices)) + used_indices = self.random.choices(tuple(dict.fromkeys(used_indices)), k=len(used_indices)) elif self.o.enemy_movement_patterns == EnemyMovementPatterns.option_singularity: - used_indices = [slot_random.choice(tuple(dict.fromkeys(used_indices)))] * len(used_indices) + used_indices = [self.random.choice(tuple(dict.fromkeys(used_indices)))] * len(used_indices) elif self.o.enemy_movement_patterns.sprite: used_indices = [indices[self.o.enemy_movement_patterns.sprite - 128]] * len(used_indices) diff --git a/worlds/lufia2ac/basepatch/basepatch.asm b/worlds/lufia2ac/basepatch/basepatch.asm index d5275c74..aeae6846 100644 --- a/worlds/lufia2ac/basepatch/basepatch.asm +++ b/worlds/lufia2ac/basepatch/basepatch.asm @@ -192,26 +192,6 @@ ReportLocationCheck: ; report event flag based goal completion -pushpc -org $D09000 - DB $00,$01,$01,$02,$01,$02,$02,$03,$01,$02,$02,$03,$02,$03,$03,$04, \ - $01,$02,$02,$03,$02,$03,$03,$04,$02,$03,$03,$04,$03,$04,$04,$05, \ - $01,$02,$02,$03,$02,$03,$03,$04,$02,$03,$03,$04,$03,$04,$04,$05, \ - $02,$03,$03,$04,$03,$04,$04,$05,$03,$04,$04,$05,$04,$05,$05,$06, \ - $01,$02,$02,$03,$02,$03,$03,$04,$02,$03,$03,$04,$03,$04,$04,$05, \ - $02,$03,$03,$04,$03,$04,$04,$05,$03,$04,$04,$05,$04,$05,$05,$06, \ - $02,$03,$03,$04,$03,$04,$04,$05,$03,$04,$04,$05,$04,$05,$05,$06, \ - $03,$04,$04,$05,$04,$05,$05,$06,$04,$05,$05,$06,$05,$06,$06,$07, \ - $01,$02,$02,$03,$02,$03,$03,$04,$02,$03,$03,$04,$03,$04,$04,$05, \ - $02,$03,$03,$04,$03,$04,$04,$05,$03,$04,$04,$05,$04,$05,$05,$06, \ - $02,$03,$03,$04,$03,$04,$04,$05,$03,$04,$04,$05,$04,$05,$05,$06, \ - $03,$04,$04,$05,$04,$05,$05,$06,$04,$05,$05,$06,$05,$06,$06,$07, \ - $02,$03,$03,$04,$03,$04,$04,$05,$03,$04,$04,$05,$04,$05,$05,$06, \ - $03,$04,$04,$05,$04,$05,$05,$06,$04,$05,$05,$06,$05,$06,$06,$07, \ - $03,$04,$04,$05,$04,$05,$05,$06,$04,$05,$05,$06,$05,$06,$06,$07, \ - $04,$05,$05,$06,$05,$06,$06,$07,$05,$06,$06,$07,$06,$07,$07,$08 -pullpc - Goal: TDC LDA $0797 ; load EV flags $C8-$CF (iris sword, iris shield, ..., iris pot) @@ -223,7 +203,7 @@ Goal: STA $F02031 ; report boss victory goal TYA AND.b #$01 ; test iris tiara - ADC $D09000,X ; test remaining iris items via lookup table + ADC $97B418,X ; test remaining iris items via predefined lookup table for number of bits set in a byte CMP $D08017 ; compare with number of treasures required BMI + LDA.b #$01 diff --git a/worlds/lufia2ac/basepatch/basepatch.bsdiff4 b/worlds/lufia2ac/basepatch/basepatch.bsdiff4 index 33a8452210aeadd5150dd890d36172c89e06b1a8..51478e5d525636d62f8a4d5755a2c1d36905fb2a 100644 GIT binary patch literal 8365 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 literal 7745 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