Skip to content

Commit c3fca02

Browse files
committed
Merge branch 'main' into smw-main
2 parents e3c0e08 + 6e38126 commit c3fca02

File tree

388 files changed

+81747
-12784
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

388 files changed

+81747
-12784
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@
99
*.apmc
1010
*.apz5
1111
*.aptloz
12+
*.apemerald
1213
*.pyc
1314
*.pyd
1415
*.sfc
1516
*.z64
1617
*.n64
1718
*.nes
19+
*.smc
1820
*.sms
1921
*.gb
2022
*.gbc

BaseClasses.py

+20-8
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ def extend(self, regions: Iterable[Region]):
113113
for region in regions:
114114
self.region_cache[region.player][region.name] = region
115115

116+
def add_group(self, new_id: int):
117+
self.region_cache[new_id] = {}
118+
self.entrance_cache[new_id] = {}
119+
self.location_cache[new_id] = {}
120+
116121
def __iter__(self) -> Iterator[Region]:
117122
for regions in self.region_cache.values():
118123
yield from regions.values()
@@ -220,6 +225,7 @@ def add_group(self, name: str, game: str, players: Set[int] = frozenset()) -> Tu
220225
return group_id, group
221226
new_id: int = self.players + len(self.groups) + 1
222227

228+
self.regions.add_group(new_id)
223229
self.game[new_id] = game
224230
self.player_types[new_id] = NetUtils.SlotType.group
225231
world_type = AutoWorld.AutoWorldRegister.world_types[game]
@@ -617,7 +623,7 @@ class CollectionState():
617623
additional_copy_functions: List[Callable[[CollectionState, CollectionState], CollectionState]] = []
618624

619625
def __init__(self, parent: MultiWorld):
620-
self.prog_items = {player: Counter() for player in parent.player_ids}
626+
self.prog_items = {player: Counter() for player in parent.get_all_ids()}
621627
self.multiworld = parent
622628
self.reachable_regions = {player: set() for player in parent.get_all_ids()}
623629
self.blocked_connections = {player: set() for player in parent.get_all_ids()}
@@ -708,37 +714,43 @@ def sweep_for_events(self, key_only: bool = False, locations: Optional[Iterable[
708714
assert isinstance(event.item, Item), "tried to collect Event with no Item"
709715
self.collect(event.item, True, event)
710716

717+
# item name related
711718
def has(self, item: str, player: int, count: int = 1) -> bool:
712719
return self.prog_items[player][item] >= count
713720

714-
def has_all(self, items: Set[str], player: int) -> bool:
721+
def has_all(self, items: Iterable[str], player: int) -> bool:
715722
"""Returns True if each item name of items is in state at least once."""
716723
return all(self.prog_items[player][item] for item in items)
717724

718-
def has_any(self, items: Set[str], player: int) -> bool:
725+
def has_any(self, items: Iterable[str], player: int) -> bool:
719726
"""Returns True if at least one item name of items is in state at least once."""
720727
return any(self.prog_items[player][item] for item in items)
721728

722729
def count(self, item: str, player: int) -> int:
723730
return self.prog_items[player][item]
724731

732+
def item_count(self, item: str, player: int) -> int:
733+
Utils.deprecate("Use count instead.")
734+
return self.count(item, player)
735+
736+
# item name group related
725737
def has_group(self, item_name_group: str, player: int, count: int = 1) -> bool:
726738
found: int = 0
739+
player_prog_items = self.prog_items[player]
727740
for item_name in self.multiworld.worlds[player].item_name_groups[item_name_group]:
728-
found += self.prog_items[player][item_name]
741+
found += player_prog_items[item_name]
729742
if found >= count:
730743
return True
731744
return False
732745

733746
def count_group(self, item_name_group: str, player: int) -> int:
734747
found: int = 0
748+
player_prog_items = self.prog_items[player]
735749
for item_name in self.multiworld.worlds[player].item_name_groups[item_name_group]:
736-
found += self.prog_items[player][item_name]
750+
found += player_prog_items[item_name]
737751
return found
738752

739-
def item_count(self, item: str, player: int) -> int:
740-
return self.prog_items[player][item]
741-
753+
# Item related
742754
def collect(self, item: Item, event: bool = False, location: Optional[Location] = None) -> bool:
743755
if location:
744756
self.locations_checked.add(location)

CommonClient.py

+8-2
Original file line numberDiff line numberDiff line change
@@ -737,7 +737,8 @@ async def process_server_cmd(ctx: CommonContext, args: dict):
737737
elif 'InvalidGame' in errors:
738738
ctx.event_invalid_game()
739739
elif 'IncompatibleVersion' in errors:
740-
raise Exception('Server reported your client version as incompatible')
740+
raise Exception('Server reported your client version as incompatible. '
741+
'This probably means you have to update.')
741742
elif 'InvalidItemsHandling' in errors:
742743
raise Exception('The item handling flags requested by the client are not supported')
743744
# last to check, recoverable problem
@@ -758,6 +759,7 @@ async def process_server_cmd(ctx: CommonContext, args: dict):
758759
ctx.slot_info = {int(pid): data for pid, data in args["slot_info"].items()}
759760
ctx.hint_points = args.get("hint_points", 0)
760761
ctx.consume_players_package(args["players"])
762+
ctx.stored_data_notification_keys.add(f"_read_hints_{ctx.team}_{ctx.slot}")
761763
msgs = []
762764
if ctx.locations_checked:
763765
msgs.append({"cmd": "LocationChecks",
@@ -836,10 +838,14 @@ async def process_server_cmd(ctx: CommonContext, args: dict):
836838

837839
elif cmd == "Retrieved":
838840
ctx.stored_data.update(args["keys"])
841+
if ctx.ui and f"_read_hints_{ctx.team}_{ctx.slot}" in args["keys"]:
842+
ctx.ui.update_hints()
839843

840844
elif cmd == "SetReply":
841845
ctx.stored_data[args["key"]] = args["value"]
842-
if args["key"].startswith("EnergyLink"):
846+
if ctx.ui and f"_read_hints_{ctx.team}_{ctx.slot}" == args["key"]:
847+
ctx.ui.update_hints()
848+
elif args["key"].startswith("EnergyLink"):
843849
ctx.current_energy_link_value = args["value"]
844850
if ctx.ui:
845851
ctx.ui.set_new_energy_link_value()

Fill.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def fill_restrictive(world: MultiWorld, base_state: CollectionState, locations:
112112

113113
location.item = None
114114
placed_item.location = None
115-
swap_state = sweep_from_pool(base_state, [placed_item] if unsafe else [])
115+
swap_state = sweep_from_pool(base_state, [placed_item, *item_pool] if unsafe else item_pool)
116116
# unsafe means swap_state assumes we can somehow collect placed_item before item_to_place
117117
# by continuing to swap, which is not guaranteed. This is unsafe because there is no mechanic
118118
# to clean that up later, so there is a chance generation fails.
@@ -471,7 +471,7 @@ def mark_for_locking(location: Location):
471471
raise FillError(
472472
f"Not enough filler items for excluded locations. There are {len(excludedlocations)} more locations than items")
473473

474-
restitempool = usefulitempool + filleritempool
474+
restitempool = filleritempool + usefulitempool
475475

476476
remaining_fill(world, defaultlocations, restitempool)
477477

@@ -792,6 +792,9 @@ def failed(warning: str, force: typing.Union[bool, str]) -> None:
792792
block['force'] = 'silent'
793793
if 'from_pool' not in block:
794794
block['from_pool'] = True
795+
elif not isinstance(block['from_pool'], bool):
796+
from_pool_type = type(block['from_pool'])
797+
raise Exception(f'Plando "from_pool" has to be boolean, not {from_pool_type} for player {player}.')
795798
if 'world' not in block:
796799
target_world = False
797800
else:

Generate.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from BaseClasses import seeddigits, get_seed, PlandoOptions
2121
from Main import main as ERmain
2222
from settings import get_settings
23-
from Utils import parse_yamls, version_tuple, __version__, tuplize_version, user_path
23+
from Utils import parse_yamls, version_tuple, __version__, tuplize_version
2424
from worlds.alttp import Options as LttPOptions
2525
from worlds.alttp.EntranceRandomizer import parse_arguments
2626
from worlds.alttp.Text import TextTable
@@ -53,6 +53,9 @@ def mystery_argparse():
5353
help='List of options that can be set manually. Can be combined, for example "bosses, items"')
5454
parser.add_argument("--skip_prog_balancing", action="store_true",
5555
help="Skip progression balancing step during generation.")
56+
parser.add_argument("--skip_output", action="store_true",
57+
help="Skips generation assertion and output stages and skips multidata and spoiler output. "
58+
"Intended for debugging and testing purposes.")
5659
args = parser.parse_args()
5760
if not os.path.isabs(args.weights_file_path):
5861
args.weights_file_path = os.path.join(args.player_files_path, args.weights_file_path)
@@ -127,6 +130,13 @@ def main(args=None, callback=ERmain):
127130
player_id += 1
128131

129132
args.multi = max(player_id - 1, args.multi)
133+
134+
if args.multi == 0:
135+
raise ValueError(
136+
"No individual player files found and number of players is 0. "
137+
"Provide individual player files or specify the number of players via host.yaml or --multi."
138+
)
139+
130140
logging.info(f"Generating for {args.multi} player{'s' if args.multi > 1 else ''}, "
131141
f"{seed_name} Seed {seed} with plando: {args.plando}")
132142

@@ -143,6 +153,7 @@ def main(args=None, callback=ERmain):
143153
erargs.outputname = seed_name
144154
erargs.outputpath = args.outputpath
145155
erargs.skip_prog_balancing = args.skip_prog_balancing
156+
erargs.skip_output = args.skip_output
146157

147158
settings_cache: Dict[str, Tuple[argparse.Namespace, ...]] = \
148159
{fname: (tuple(roll_settings(yaml, args.plando) for yaml in yamls) if args.samesettings else None)

0 commit comments

Comments
 (0)