Skip to content

Commit

Permalink
Merge branch 'main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
gaithern authored Aug 13, 2024
2 parents c9bfbb2 + 50330cf commit 20b32eb
Show file tree
Hide file tree
Showing 70 changed files with 12,238 additions and 3,123 deletions.
41 changes: 21 additions & 20 deletions BaseClasses.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from __future__ import annotations

import collections
import copy
import itertools
import functools
import logging
Expand Down Expand Up @@ -290,6 +289,8 @@ def set_item_links(self):

def link_items(self) -> None:
"""Called to link together items in the itempool related to the registered item link groups."""
from worlds import AutoWorld

for group_id, group in self.groups.items():
def find_common_pool(players: Set[int], shared_pool: Set[str]) -> Tuple[
Optional[Dict[int, Dict[str, int]]], Optional[Dict[str, int]]
Expand All @@ -300,15 +301,15 @@ def find_common_pool(players: Set[int], shared_pool: Set[str]) -> Tuple[
if item.player in counters and item.name in shared_pool:
counters[item.player][item.name] += 1
classifications[item.name] |= item.classification

for player in players.copy():
if all([counters[player][item] == 0 for item in shared_pool]):
players.remove(player)
del (counters[player])

if not players:
return None, None

for item in shared_pool:
count = min(counters[player][item] for player in players)
if count:
Expand All @@ -318,19 +319,19 @@ def find_common_pool(players: Set[int], shared_pool: Set[str]) -> Tuple[
for player in players:
del (counters[player][item])
return counters, classifications

common_item_count, classifications = find_common_pool(group["players"], group["item_pool"])
if not common_item_count:
continue

new_itempool: List[Item] = []
for item_name, item_count in next(iter(common_item_count.values())).items():
for _ in range(item_count):
new_item = group["world"].create_item(item_name)
# mangle together all original classification bits
new_item.classification |= classifications[item_name]
new_itempool.append(new_item)

region = Region("Menu", group_id, self, "ItemLink")
self.regions.append(region)
locations = region.locations
Expand All @@ -341,16 +342,16 @@ def find_common_pool(players: Set[int], shared_pool: Set[str]) -> Tuple[
None, region)
loc.access_rule = lambda state, item_name = item.name, group_id_ = group_id, count_ = count: \
state.has(item_name, group_id_, count_)

locations.append(loc)
loc.place_locked_item(item)
common_item_count[item.player][item.name] -= 1
else:
new_itempool.append(item)

itemcount = len(self.itempool)
self.itempool = new_itempool

while itemcount > len(self.itempool):
items_to_add = []
for player in group["players"]:
Expand Down Expand Up @@ -717,14 +718,14 @@ def update_reachable_regions(self, player: int):

def copy(self) -> CollectionState:
ret = CollectionState(self.multiworld)
ret.prog_items = copy.deepcopy(self.prog_items)
ret.reachable_regions = {player: copy.copy(self.reachable_regions[player]) for player in
self.reachable_regions}
ret.blocked_connections = {player: copy.copy(self.blocked_connections[player]) for player in
self.blocked_connections}
ret.events = copy.copy(self.events)
ret.path = copy.copy(self.path)
ret.locations_checked = copy.copy(self.locations_checked)
ret.prog_items = {player: counter.copy() for player, counter in self.prog_items.items()}
ret.reachable_regions = {player: region_set.copy() for player, region_set in
self.reachable_regions.items()}
ret.blocked_connections = {player: entrance_set.copy() for player, entrance_set in
self.blocked_connections.items()}
ret.events = self.events.copy()
ret.path = self.path.copy()
ret.locations_checked = self.locations_checked.copy()
for function in self.additional_copy_functions:
ret = function(self, ret)
return ret
Expand Down Expand Up @@ -1126,9 +1127,9 @@ def can_fill(self, state: CollectionState, item: Item, check_access=True) -> boo
and (not check_access or self.can_reach(state))))

def can_reach(self, state: CollectionState) -> bool:
# self.access_rule computes faster on average, so placing it first for faster abort
# Region.can_reach is just a cache lookup, so placing it first for faster abort on average
assert self.parent_region, "Can't reach location without region"
return self.access_rule(state) and self.parent_region.can_reach(state)
return self.parent_region.can_reach(state) and self.access_rule(state)

def place_locked_item(self, item: Item):
if self.item:
Expand Down
15 changes: 10 additions & 5 deletions Main.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
# Because some worlds don't actually create items during create_items this has to be as late as possible.
if any(getattr(multiworld.worlds[player].options, "start_inventory_from_pool", None) for player in multiworld.player_ids):
new_items: List[Item] = []
old_items: List[Item] = []
depletion_pool: Dict[int, Dict[str, int]] = {
player: getattr(multiworld.worlds[player].options,
"start_inventory_from_pool",
Expand All @@ -169,20 +170,24 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
depletion_pool[item.player][item.name] -= 1
# quick abort if we have found all items
if not target:
new_items.extend(multiworld.itempool[i+1:])
old_items.extend(multiworld.itempool[i+1:])
break
else:
new_items.append(item)
old_items.append(item)

# leftovers?
if target:
for player, remaining_items in depletion_pool.items():
remaining_items = {name: count for name, count in remaining_items.items() if count}
if remaining_items:
raise Exception(f"{multiworld.get_player_name(player)}"
logger.warning(f"{multiworld.get_player_name(player)}"
f" is trying to remove items from their pool that don't exist: {remaining_items}")
assert len(multiworld.itempool) == len(new_items), "Item Pool amounts should not change."
multiworld.itempool[:] = new_items
# find all filler we generated for the current player and remove until it matches
removables = [item for item in new_items if item.player == player]
for _ in range(sum(remaining_items.values())):
new_items.remove(removables.pop())
assert len(multiworld.itempool) == len(new_items + old_items), "Item Pool amounts should not change."
multiworld.itempool[:] = new_items + old_items

multiworld.link_items()

Expand Down
29 changes: 1 addition & 28 deletions Options.py
Original file line number Diff line number Diff line change
Expand Up @@ -1236,6 +1236,7 @@ def as_dict(self, *option_names: str, casing: str = "snake") -> typing.Dict[str,
:param option_names: names of the options to return
:param casing: case of the keys to return. Supports `snake`, `camel`, `pascal`, `kebab`
"""
assert option_names, "options.as_dict() was used without any option names."
option_results = {}
for option_name in option_names:
if option_name in type(self).type_hints:
Expand Down Expand Up @@ -1517,31 +1518,3 @@ def yaml_dump_scalar(scalar) -> str:

with open(os.path.join(target_folder, game_name + ".yaml"), "w", encoding="utf-8-sig") as f:
f.write(res)


if __name__ == "__main__":

from worlds.alttp.Options import Logic
import argparse

map_shuffle = Toggle
compass_shuffle = Toggle
key_shuffle = Toggle
big_key_shuffle = Toggle
hints = Toggle
test = argparse.Namespace()
test.logic = Logic.from_text("no_logic")
test.map_shuffle = map_shuffle.from_text("ON")
test.hints = hints.from_text('OFF')
try:
test.logic = Logic.from_text("overworld_glitches_typo")
except KeyError as e:
print(e)
try:
test.logic_owg = Logic.from_text("owg")
except KeyError as e:
print(e)
if test.map_shuffle:
print("map_shuffle is on")
print(f"Hints are {bool(test.hints)}")
print(test)
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ Currently, the following games are supported:
* Aquaria
* Yu-Gi-Oh! Ultimate Masters: World Championship Tournament 2006
* A Hat in Time
* Old School Runescape
* Kingdom Hearts 1

For setup and instructions check out our [tutorials page](https://archipelago.gg/tutorial/).
Expand Down
3 changes: 3 additions & 0 deletions docs/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@
# Ocarina of Time
/worlds/oot/ @espeon65536

# Old School Runescape
/worlds/osrs @digiholic

# Overcooked! 2
/worlds/overcooked2/ @toasterparty

Expand Down
1 change: 0 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@
"Adventure",
"ArchipIDLE",
"Archipelago",
"ChecksFinder",
"Clique",
"Final Fantasy",
"Lufia II Ancient Cave",
Expand Down
7 changes: 6 additions & 1 deletion worlds/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,12 @@ def load(self) -> bool:
else: # TODO: remove with 3.8 support
mod = importer.load_module(os.path.basename(self.path).rsplit(".", 1)[0])

mod.__package__ = f"worlds.{mod.__package__}"
if mod.__package__ is not None:
mod.__package__ = f"worlds.{mod.__package__}"
else:
# load_module does not populate package, we'll have to assume mod.__name__ is correct here
# probably safe to remove with 3.8 support
mod.__package__ = f"worlds.{mod.__name__}"
mod.__name__ = f"worlds.{mod.__name__}"
sys.modules[mod.__name__] = mod
with warnings.catch_warnings():
Expand Down
2 changes: 1 addition & 1 deletion worlds/aquaria/Items.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def __init__(self, id: int, count: int, type: ItemType, group: ItemGroup):
"Mutant Costume": ItemData(698020, 1, ItemType.JUNK, ItemGroup.COLLECTIBLE), # collectible_mutant_costume
"Baby Nautilus": ItemData(698021, 1, ItemType.NORMAL, ItemGroup.UTILITY), # collectible_nautilus
"Baby Piranha": ItemData(698022, 1, ItemType.NORMAL, ItemGroup.UTILITY), # collectible_piranha
"Arnassi Armor": ItemData(698023, 1, ItemType.NORMAL, ItemGroup.UTILITY), # collectible_seahorse_costume
"Arnassi Armor": ItemData(698023, 1, ItemType.PROGRESSION, ItemGroup.UTILITY), # collectible_seahorse_costume
"Seed Bag": ItemData(698024, 1, ItemType.JUNK, ItemGroup.COLLECTIBLE), # collectible_seed_bag
"King's Skull": ItemData(698025, 1, ItemType.JUNK, ItemGroup.COLLECTIBLE), # collectible_skull
"Song Plant Spore": ItemData(698026, 1, ItemType.JUNK, ItemGroup.COLLECTIBLE), # collectible_spore_seed
Expand Down
20 changes: 16 additions & 4 deletions worlds/aquaria/Locations.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class AquariaLocations:
"Home Water, bulb below the grouper fish": 698058,
"Home Water, bulb in the path below Nautilus Prime": 698059,
"Home Water, bulb in the little room above the grouper fish": 698060,
"Home Water, bulb in the end of the left path from the Verse Cave": 698061,
"Home Water, bulb in the end of the path close to the Verse Cave": 698061,
"Home Water, bulb in the top left path": 698062,
"Home Water, bulb in the bottom left room": 698063,
"Home Water, bulb close to Naija's Home": 698064,
Expand All @@ -67,7 +67,7 @@ class AquariaLocations:

locations_song_cave = {
"Song Cave, Erulian spirit": 698206,
"Song Cave, bulb in the top left part": 698071,
"Song Cave, bulb in the top right part": 698071,
"Song Cave, bulb in the big anemone room": 698072,
"Song Cave, bulb in the path to the singing statues": 698073,
"Song Cave, bulb under the rock in the path to the singing statues": 698074,
Expand Down Expand Up @@ -152,6 +152,9 @@ class AquariaLocations:

locations_arnassi_path = {
"Arnassi Ruins, Arnassi Statue": 698164,
}

locations_arnassi_cave_transturtle = {
"Arnassi Ruins, Transturtle": 698217,
}

Expand Down Expand Up @@ -269,9 +272,12 @@ class AquariaLocations:
}

locations_forest_bl = {
"Kelp Forest bottom left area, Transturtle": 698212,
}

locations_forest_bl_sc = {
"Kelp Forest bottom left area, bulb close to the spirit crystals": 698054,
"Kelp Forest bottom left area, Walker Baby": 698186,
"Kelp Forest bottom left area, Transturtle": 698212,
}

locations_forest_br = {
Expand Down Expand Up @@ -370,7 +376,7 @@ class AquariaLocations:

locations_sun_temple_r = {
"Sun Temple, first bulb of the temple": 698091,
"Sun Temple, bulb on the left part": 698092,
"Sun Temple, bulb on the right part": 698092,
"Sun Temple, bulb in the hidden room of the right part": 698093,
"Sun Temple, Sun Key": 698182,
}
Expand Down Expand Up @@ -402,6 +408,9 @@ class AquariaLocations:
"Abyss right area, bulb in the middle path": 698110,
"Abyss right area, bulb behind the rock in the middle path": 698111,
"Abyss right area, bulb in the left green room": 698112,
}

locations_abyss_r_transturtle = {
"Abyss right area, Transturtle": 698214,
}

Expand Down Expand Up @@ -499,6 +508,7 @@ class AquariaLocations:
**AquariaLocations.locations_skeleton_path_sc,
**AquariaLocations.locations_arnassi,
**AquariaLocations.locations_arnassi_path,
**AquariaLocations.locations_arnassi_cave_transturtle,
**AquariaLocations.locations_arnassi_crab_boss,
**AquariaLocations.locations_sun_temple_l,
**AquariaLocations.locations_sun_temple_r,
Expand All @@ -509,6 +519,7 @@ class AquariaLocations:
**AquariaLocations.locations_abyss_l,
**AquariaLocations.locations_abyss_lb,
**AquariaLocations.locations_abyss_r,
**AquariaLocations.locations_abyss_r_transturtle,
**AquariaLocations.locations_energy_temple_1,
**AquariaLocations.locations_energy_temple_2,
**AquariaLocations.locations_energy_temple_3,
Expand All @@ -530,6 +541,7 @@ class AquariaLocations:
**AquariaLocations.locations_forest_tr,
**AquariaLocations.locations_forest_tr_fp,
**AquariaLocations.locations_forest_bl,
**AquariaLocations.locations_forest_bl_sc,
**AquariaLocations.locations_forest_br,
**AquariaLocations.locations_forest_boss,
**AquariaLocations.locations_forest_boss_entrance,
Expand Down
Loading

0 comments on commit 20b32eb

Please sign in to comment.