diff --git a/worlds/gl/GauntletLegendsClient.py b/worlds/gl/GauntletLegendsClient.py index 07732f5da65f..be540e44297e 100644 --- a/worlds/gl/GauntletLegendsClient.py +++ b/worlds/gl/GauntletLegendsClient.py @@ -625,8 +625,6 @@ async def scout_locations(self, ctx: "GauntletLegendsContext") -> None: "locations": [ location.id for location in raw_locations - if "Chest" not in location.name - and ("Barrel" not in location.name or "Barrel of Gold" in location.name) ], "create_as_hint": 0, }, diff --git a/worlds/gl/Options.py b/worlds/gl/Options.py index 6e1068ac1a1b..c41240f553b9 100644 --- a/worlds/gl/Options.py +++ b/worlds/gl/Options.py @@ -3,6 +3,18 @@ from Options import Choice, PerGameCommonOptions, StartInventoryPool, Toggle, Range +class PlayerCount(Range): + """ + Select how many players will be playing this world locally. + If 3 players will be active then change this to 3, etc. + """ + + display_name = "Local Players" + range_start = 1 + range_end = 4 + default = 1 + + class ChestBarrels(Choice): """ Choose how you want Chests and Barrels to be randomized. @@ -201,6 +213,7 @@ class UnlockCharacterFour(Choice): @dataclass class GLOptions(PerGameCommonOptions): start_inventory_from_pool: StartInventoryPool + local_players: PlayerCount chests_barrels: ChestBarrels obelisks: Obelisks mirror_shards: MirrorShards diff --git a/worlds/gl/Rom.py b/worlds/gl/Rom.py index 7832e2ccb4fb..a7ba243a9542 100644 --- a/worlds/gl/Rom.py +++ b/worlds/gl/Rom.py @@ -53,9 +53,9 @@ class LevelData: objects: List[bytearray] chests: List[bytearray] end: bytes - obelisk_items: int = 0 - obelisk_chests: int = 0 - item: int = 0 + items_replaced_by_obelisks: int = 0 + chests_replaced_by_obelisks: int = 0 + obelisks_replaced_by_items: int = 0 def __init__(self): self.items = [] @@ -128,7 +128,7 @@ def patch_items(caller: APProcedurePatch, rom: bytes): + bytes([0x0, 0x0, 0x0, 0x0]), ] del data.objects[index] - data.item += 1 + data.obelisks_replaced_by_items += 1 except Exception as e: print(item[0]) print(e) @@ -171,10 +171,10 @@ def patch_items(caller: APProcedurePatch, rom: bytes): ] if chest_barrel(location_name): del data.chests[j - (len(data.items) + data.obelisk_items + data.obelisk_chests)] - data.obelisk_chests += 1 + data.chests_replaced_by_obelisks += 1 else: del data.items[j - data.obelisk_items] - data.obelisk_items += 1 + data.items_replaced_by_obelisks += 1 else: if chest_barrel(location_name): data.chests[j - (len(data.items) + data.obelisk_items + data.obelisk_chests)][12] = item_dict[item[0]][0] @@ -286,23 +286,25 @@ def get_level_data(stream: io.BytesIO, size: int) -> (io.BytesIO, LevelData): data.stream.seek(i) data.chests += [bytearray(data.stream.read(16))] data.end = data.stream.read() - return (stream, data) + return stream, data # Format a LevelData object back into a bytes object # Format is header, items, spawners, objects, barrels/chests, then traps. def level_data_reformat(data: LevelData) -> bytes: stream = io.BytesIO() - obelisk_offset = 24 * (data.obelisk_items - data.item) + obelisk_offset = 24 * (data.items_replaced_by_obelisks + data.chests_replaced_by_obelisks - data.obelisks_replaced_by_items) + item_offset = 12 * (data.obelisks_replaced_by_items - data.items_replaced_by_obelisks) + chest_offset = 16 * data.chests_replaced_by_obelisks stream.write(int.to_bytes(0x5C, 4, "big")) - stream.write(int.to_bytes(data.spawner_addr + (12 * (data.item - data.obelisk_items)), 4, "big")) - stream.write(int.to_bytes(data.spawner_addr + (12 * (data.item - data.obelisk_items)), 4, "big")) - stream.write(int.to_bytes(data.obj_addr + (12 * (data.item - data.obelisk_items)), 4, "big")) - stream.write(int.to_bytes(data.end_addr + ((12 * (data.item - data.obelisk_items)) + obelisk_offset - (data.obelisk_chests * 16)), 4, "big")) - stream.write(int.to_bytes(data.portal_addr + ((12 * (data.item - data.obelisk_items)) + obelisk_offset - (data.obelisk_chests * 16)), 4, "big")) - stream.write(int.to_bytes(data.chest_addr + ((12 * (data.item - data.obelisk_items)) + obelisk_offset), 4, "big")) - stream.write(int.to_bytes(data.end_addr2 + ((12 * (data.item - data.obelisk_items)) + obelisk_offset - (data.obelisk_chests * 16)), 4, "big")) - stream.write(int.to_bytes(data.end_addr3 + ((12 * (data.item - data.obelisk_items)) + obelisk_offset - (data.obelisk_chests * 16)), 4, "big")) + stream.write(int.to_bytes(data.spawner_addr + item_offset, 4, "big")) + stream.write(int.to_bytes(data.spawner_addr + item_offset, 4, "big")) + stream.write(int.to_bytes(data.obj_addr + item_offset, 4, "big")) + stream.write(int.to_bytes(data.end_addr + item_offset + obelisk_offset - chest_offset, 4, "big")) + stream.write(int.to_bytes(data.portal_addr + item_offset + obelisk_offset - chest_offset, 4, "big")) + stream.write(int.to_bytes(data.chest_addr + item_offset + obelisk_offset, 4, "big")) + stream.write(int.to_bytes(data.end_addr2 + item_offset + obelisk_offset - chest_offset, 4, "big")) + stream.write(int.to_bytes(data.end_addr3 + item_offset + obelisk_offset - chest_offset, 4, "big")) stream.seek(1, 1) stream.write(bytes([len(data.items)])) stream.write(bytes([0x0, 0x0, 0x0])) diff --git a/worlds/gl/__init__.py b/worlds/gl/__init__.py index 33ea5bd0e9e3..4e4398c6665b 100644 --- a/worlds/gl/__init__.py +++ b/worlds/gl/__init__.py @@ -78,6 +78,9 @@ class GauntletLegendsWorld(World): disabled_locations: typing.List[LocationData] + def generate_early(self) -> None: + self.options.max_difficulty_value.value = max(self.options.max_difficulty_value.value, self.options.local_players.value) + def create_regions(self) -> None: self.disabled_locations = [] if self.options.chests_barrels == 0: @@ -85,19 +88,22 @@ def create_regions(self) -> None: location.name for location in all_locations if "Chest" in location.name or ("Barrel" in location.name and "Barrel of Gold" not in location.name) + and location.name not in self.disabled_locations ] elif self.options.chests_barrels == 1: self.disabled_locations += [ location.name for location in all_locations if "Barrel" in location.name and "Barrel of Gold" not in location.name + and location.name not in self.disabled_locations ] elif self.options.chests_barrels == 2: - self.disabled_locations += [location.name for location in all_locations if "Chest" in location.name] + self.disabled_locations += [location.name for location in all_locations if "Chest" in location.name and location.name not in self.disabled_locations] if self.options.max_difficulty_toggle: self.disabled_locations += [location.name for location in all_locations - if location.difficulty > self.options.max_difficulty_value] + if location.difficulty > self.options.max_difficulty_value + and location.name not in self.disabled_locations] create_regions(self) connect_regions(self) @@ -149,6 +155,7 @@ def fill_slot_data(self) -> dict: ] return { "player": self.player, + "players": self.options.local_players.value, "shards": shard_values, "speed": self.options.permanent_speed.value, "keys": self.options.infinite_keys.value, @@ -169,8 +176,6 @@ def create_items(self) -> None: if "Mirror" in item.item_name and self.options.mirror_shards == 0: continue freq = item_frequencies.get(item.item_name, 1) - if freq is None: - freq = 1 required_items += [item.item_name for _ in range(freq)] for item_name in required_items: @@ -200,8 +205,6 @@ def create_items(self) -> None: freq = item_frequencies.get(item.item_name, 1) if item.item_name == "Anti-Death Halo" and (self.options.traps_choice.value == self.options.traps_choice.option_only_death or self.options.traps_choice.value == self.options.traps_choice.option_all_active) and self.options.traps_frequency.value == self.options.traps_frequency.option_extreme: freq *= 2 - if freq is None: - freq = 1 filler_items += [item.item_name for _ in range(freq)] remaining = len(all_locations) - len(required_items) - len(self.disabled_locations) - (2 if not self.options.infinite_keys else 0) @@ -225,8 +228,10 @@ def set_rules(self) -> None: ) def pre_fill(self) -> None: - self.random.shuffle(self.multiworld.get_unfilled_locations(self.player)) - fast_fill(self.multiworld, self.death, self.multiworld.get_unfilled_locations(self.player)) + if len(self.death) != 0: + locations = self.multiworld.get_unfilled_locations(self.player) + self.random.shuffle(locations) + fast_fill(self.multiworld, self.death, locations) def create_item(self, name: str) -> GLItem: item = item_table[name]