From 4aab317665d5d2d9a72d2bc5ea9446639eaf887e Mon Sep 17 00:00:00 2001 From: Silvris <58583688+Silvris@users.noreply.github.com> Date: Mon, 9 Sep 2024 08:56:15 -0500 Subject: [PATCH 01/21] ALTTP: Plando (#2904) fixes (#3834) --- Options.py | 14 ++++++++++++++ worlds/alttp/Options.py | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Options.py b/Options.py index ecde6275f1ea..b79714635d9e 100644 --- a/Options.py +++ b/Options.py @@ -973,7 +973,19 @@ def from_any(cls, data: PlandoTextsFromAnyType) -> Self: if random.random() < float(text.get("percentage", 100)/100): at = text.get("at", None) if at is not None: + if isinstance(at, dict): + if at: + at = random.choices(list(at.keys()), + weights=list(at.values()), k=1)[0] + else: + raise OptionError("\"at\" must be a valid string or weighted list of strings!") given_text = text.get("text", []) + if isinstance(given_text, dict): + if not given_text: + given_text = [] + else: + given_text = random.choices(list(given_text.keys()), + weights=list(given_text.values()), k=1) if isinstance(given_text, str): given_text = [given_text] texts.append(PlandoText( @@ -981,6 +993,8 @@ def from_any(cls, data: PlandoTextsFromAnyType) -> Self: given_text, text.get("percentage", 100) )) + else: + raise OptionError("\"at\" must be a valid string or weighted list of strings!") elif isinstance(text, PlandoText): if random.random() < float(text.percentage/100): texts.append(text) diff --git a/worlds/alttp/Options.py b/worlds/alttp/Options.py index 20dd18038a14..bd87cbf2c3ea 100644 --- a/worlds/alttp/Options.py +++ b/worlds/alttp/Options.py @@ -728,7 +728,7 @@ class ALttPPlandoConnections(PlandoConnections): entrances = set([connection[0] for connection in ( *default_connections, *default_dungeon_connections, *inverted_default_connections, *inverted_default_dungeon_connections)]) - exits = set([connection[1] for connection in ( + exits = set([connection[0] for connection in ( *default_connections, *default_dungeon_connections, *inverted_default_connections, *inverted_default_dungeon_connections)]) From 09c7f5f909e6ca23de5bc58f8174a1794b07f817 Mon Sep 17 00:00:00 2001 From: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com> Date: Mon, 9 Sep 2024 17:36:27 +0200 Subject: [PATCH 02/21] The Witness: Bump Required Client Version (#3891) The newest release of the Witness client connects with 0.5.1 https://github.com/NewSoupVi/The-Witness-Randomizer-for-Archipelago/releases/tag/7.0.0p10 --- worlds/witness/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/witness/__init__.py b/worlds/witness/__init__.py index cdb17a483b1e..b4b38c883e7d 100644 --- a/worlds/witness/__init__.py +++ b/worlds/witness/__init__.py @@ -61,7 +61,7 @@ class WitnessWorld(World): item_name_groups = static_witness_items.ITEM_GROUPS location_name_groups = static_witness_locations.AREA_LOCATION_GROUPS - required_client_version = (0, 4, 5) + required_client_version = (0, 5, 1) player_logic: WitnessPlayerLogic player_locations: WitnessPlayerLocations From 170aedba8fbe35765289a8628b89acf9fd1515ec Mon Sep 17 00:00:00 2001 From: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com> Date: Mon, 9 Sep 2024 17:36:47 +0200 Subject: [PATCH 03/21] The Witness: Fix hints always displaying the Witness player (#3861) * The Witness: Fix hints always displaying the Witness player Got a bit too trigger happy with changing instances of `world.multiworld.player_name` to `world.player_name` - Some of these were actually *supposed* to be other players. Alternate title: The Witness doesn't have a Silph Scope * that one i guess --- worlds/witness/hints.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/worlds/witness/hints.py b/worlds/witness/hints.py index 2c5f816b2bc2..99e8eea2eb89 100644 --- a/worlds/witness/hints.py +++ b/worlds/witness/hints.py @@ -220,7 +220,7 @@ def try_getting_location_group_for_location(world: "WitnessWorld", hint_loc: Loc def word_direct_hint(world: "WitnessWorld", hint: WitnessLocationHint) -> WitnessWordedHint: location_name = hint.location.name if hint.location.player != world.player: - location_name += " (" + world.player_name + ")" + location_name += " (" + world.multiworld.get_player_name(hint.location.player) + ")" item = hint.location.item @@ -229,7 +229,7 @@ def word_direct_hint(world: "WitnessWorld", hint: WitnessLocationHint) -> Witnes item_name = item.name if item.player != world.player: - item_name += " (" + world.player_name + ")" + item_name += " (" + world.multiworld.get_player_name(item.player) + ")" hint_text = "" area: Optional[str] = None From 7ff201e32c859eeb1b3e07ee087f11da3249f833 Mon Sep 17 00:00:00 2001 From: Spineraks Date: Tue, 10 Sep 2024 17:01:36 +0200 Subject: [PATCH 04/21] Yacht Dice: add get_filler_item_name (#3916) * Add the yacht dice (from other git) world to the yacht dice fork * Update .gitignore * Removed zillion because it doesn't work * Update .gitignore * added zillion again... * Now you can have 0 extra fragments * Added alt categories, also options * Added item categories * Extra categories are now working! :dog: * changed options and added exceptions * Testing if I change the generate.py * Revert "Testing if I change the generate.py" This reverts commit 7c2b3df6170dcf8d8f36a1de9fcbc9dccdec81f8. * ignore gitignore * Delete .gitignore * Update .gitignore * Update .gitignore * Update logic, added multiplicative categories * Changed difficulties * Update offline mode so that it works again * Adjusted difficulty * New version of the apworld, with 1000 as final score, always Will still need to check difficulty and weights of adding items. Website is not ready yet, so this version is not usable yet :) * Changed yaml and small bug fixes Fix when goal and max are same Options: changed chance to weight * no changes, just whitespaces * changed how logic works Now you put an array of mults and the cpu gets a couple of tries * Changed logic, tweaked a bit too * Preparation for 2.0 * logic tweak * Logic for alt categories properly now * Update setup_en.md * Update en_YachtDice.md * Improve performance of add_distributions * Formatting style * restore gitignore to APMW * Tweaked generation parameters and methods * Version 2.0.3 manual input option max score in logic always 2.0.3 faster gen * Comments and editing * Renamed setup guide * Improved create_items code * init of locations: remove self.event line * Moved setting early items to generate_early * Add my name to CODEOWNERS * Added Yacht Dice to the readme in list of games * Improve performance of Yacht Dice * newline * Improve typing * This is actually just slower lol * Update worlds/yachtdice/Items.py Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> * Update Options.py * Styling * finished text whichstory option * removed roll and rollfragments; not used * import; worlds not world :) * Option groups! * ruff styling, fix * ruff format styling! * styling and capitalization of options * small comment * Cleaned up the "state_is_a_list" a little bit * RUFF :dog: * Changed filling the itempool for efficiency Now, we start with 17 extra items in the item pool, it's quite likely you need at least 17 items (~80%?). And then afterwards, we delete items if we overshoot the target of 1000, and add items if we haven't reached an achievable score of 1000 yet. Also, no need to recompute the entire logic when adding points. * :dog: * Removed plando "fix" * Changed indent of score multiplier * faster location function * Comments to docstrings * fixed making location closest to goal_score be goal_score * options format * iterate keys and values of a dict together * small optimization ListState * faster collection of categories * return arguments instead of making a list (will :dog: later) * Instead of turning it into a tuple, you can just make a tuple literal * remove .keys() * change .random and used enumerate * some readability improvements * Remove location "0", we don't use that one * Remove lookup_id_to_name entirely I for sure don't use it, and as far as I know it's not one of the mandatory functions for AP, these are item_name_to_id and location_name_to_id. * .append instead of += for single items, percentile function changed Also an extra comment for location ids. * remove ) too many * Removed sorted from category list * Hash categories (which makes it slower :( ) Maybe I messed up or misunderstood... I'll revert this right away since it is 2x slower, probably because of sorted instead of sort? * Revert "Hash categories (which makes it slower :( )" This reverts commit 34f2c1aed8c8813b2d9c58896650b82a810d3578. * temporary push: 40% faster generation test Small changes in logic make the generation 40% faster. I'll have to think about how big the changes are. I suspect they are rather limited. If this is the way to go, I'll remove the temp file and redo the YachtWeights file, I'll remove the functions there and just put the new weights here. * Add Points item category * Reverse changes of bad idea :) * ruff :dog: * Use numpy and pmf function to speed up gen Numpy has a built-in way to sum probability mass functions (pmf). This shaves of 60% of the generation time :D * Revert "Use numpy and pmf function to speed up gen" This reverts commit 9290191cb323ae92321d6c2cfcfe8c27370f439b. * Step inbetween to change the weights * Changed the weights to make it faster 135 -> 81 seconds on 100 random yamls * Adjusted max_dist, split dice_simulation function * Removed nonlocal and pass arguments instead * Change "weight-lists" to Dict[str, float] * Removed the return from ini_locations. Also added explanations to cat_weights * Choice options; dont'use .value (will ruff later) * Only put important options in slotdata * :dog: * Add Dict import * Split the cache per player, limit size to 400. * :dog: * added , because of style * Update apworld version to 2.0.6 2.0.5 is the apworld I released on github to be tested I never separately released 2.0.4. * Multiple smaller code improvements - changed names in YachtWeights so we don't need to translate them in Rules anymore - we now remember which categories are present in the game, and also put this in slotdata. This we do because only one of two categories is present in a game. If for some reason both are present (plando/getitem/startinventory), we now know which category to ignore - * :dog: ruff * Mostly minimize_extra_items improvements - Change logic, generation is now even faster (0.6s per default yaml). - Made the option 'minimize_extra_items' do a lot more, hopefully this makes the impact of Yacht Dice a little bit less, if you want that. Here's what is also does now: - you start with 2 dice and 2 rolls - there will be less locations/items at the start of you game * ruff :dog: * Removed printing options * Reworded some option descriptions * Yacht Dice: setup: change release-link to latest On the installation page, link to the latest release, instead of the page with all releases * Several fixes and changes -change apworld version -Removed the extra roll (this was not intended) -change extra_points_added to a mutable list to that it actually does something -removed variables multipliers_added and items_added -Rules, don't order by quantity, just by mean_score -Changed the weights in general to make it faster * :dog: * Revert setup to what it was (latest, without S) * remove temp weights file, shouldn't be here * Made sure that there is not too many step score multipliers. Too many step score multipliers lead to gen fails too, probably because you need many categories for them to actually help a lot. So it's hard to use them at the start of the game. * add filler item name --------- Co-authored-by: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com> Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> --- worlds/yachtdice/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/worlds/yachtdice/__init__.py b/worlds/yachtdice/__init__.py index 3a79eff04046..d86ee3382d33 100644 --- a/worlds/yachtdice/__init__.py +++ b/worlds/yachtdice/__init__.py @@ -466,6 +466,9 @@ def create_regions(self): menu.exits.append(connection) connection.connect(board) self.multiworld.regions += [menu, board] + + def get_filler_item_name(self) -> str: + return "Good RNG" def set_rules(self): """ From 874392756b706bc07f4c1ff9429ed0b16e52abd3 Mon Sep 17 00:00:00 2001 From: Bryce Wilson Date: Wed, 11 Sep 2024 04:20:07 -0700 Subject: [PATCH 05/21] Pokemon Emerald: Add normalize encounter rate option to slot data (#3917) --- worlds/pokemon_emerald/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/worlds/pokemon_emerald/__init__.py b/worlds/pokemon_emerald/__init__.py index abdee26f572f..d281dde23cb0 100644 --- a/worlds/pokemon_emerald/__init__.py +++ b/worlds/pokemon_emerald/__init__.py @@ -711,6 +711,7 @@ def fill_slot_data(self) -> Dict[str, Any]: "trainersanity", "modify_118", "death_link", + "normalize_encounter_rates", ) slot_data["free_fly_location_id"] = self.free_fly_location_id slot_data["hm_requirements"] = self.hm_requirements From c9f1a21bd2b8888e5b4dc75123c19b0a016ee261 Mon Sep 17 00:00:00 2001 From: Bryce Wilson Date: Wed, 11 Sep 2024 04:22:04 -0700 Subject: [PATCH 06/21] BizHawkClient: Remove `run_gui` in favor of `make_gui` (#3910) --- CommonClient.py | 2 +- worlds/_bizhawk/context.py | 12 ++++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/CommonClient.py b/CommonClient.py index 911de4226dc3..6bdd8fc819da 100644 --- a/CommonClient.py +++ b/CommonClient.py @@ -662,7 +662,7 @@ def handle_connection_loss(self, msg: str) -> None: logger.exception(msg, exc_info=exc_info, extra={'compact_gui': True}) self._messagebox_connection_loss = self.gui_error(msg, exc_info[1]) - def make_gui(self) -> type: + def make_gui(self) -> typing.Type["kvui.GameManager"]: """To return the Kivy App class needed for run_gui so it can be overridden before being built""" from kvui import GameManager diff --git a/worlds/_bizhawk/context.py b/worlds/_bizhawk/context.py index 234faf3b65cf..896c8fb7b504 100644 --- a/worlds/_bizhawk/context.py +++ b/worlds/_bizhawk/context.py @@ -59,14 +59,10 @@ def __init__(self, server_address: Optional[str], password: Optional[str]): self.bizhawk_ctx = BizHawkContext() self.watcher_timeout = 0.5 - def run_gui(self): - from kvui import GameManager - - class BizHawkManager(GameManager): - base_title = "Archipelago BizHawk Client" - - self.ui = BizHawkManager(self) - self.ui_task = asyncio.create_task(self.ui.async_run(), name="UI") + def make_gui(self): + ui = super().make_gui() + ui.base_title = "Archipelago BizHawk Client" + return ui def on_package(self, cmd, args): if cmd == "Connected": From 7621889b8b626e89947d6258ddd5ade65d434ddb Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 11 Sep 2024 04:22:53 -0700 Subject: [PATCH 07/21] DS3: Add nex3 as a world maintainer (#3882) I've already discussed this with @Marechal-L and gotten his approval. --- docs/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CODEOWNERS b/docs/CODEOWNERS index 28dcc6736283..ee7fd7ed863b 100644 --- a/docs/CODEOWNERS +++ b/docs/CODEOWNERS @@ -46,7 +46,7 @@ /worlds/clique/ @ThePhar # Dark Souls III -/worlds/dark_souls_3/ @Marechal-L +/worlds/dark_souls_3/ @Marechal-L @nex3 # Donkey Kong Country 3 /worlds/dkc3/ @PoryGone From ed948e3e5b60ea67d126b7ef06a60dcccd71f4aa Mon Sep 17 00:00:00 2001 From: Mysteryem Date: Fri, 13 Sep 2024 15:02:13 +0100 Subject: [PATCH 08/21] sm64ex: Add missing indirect condition for BitFS randomized entrance (#3926) The Bowser in the Fire Sea randomized entrance has an access rule that requires being able to reach "DDD: Board Bowser's Sub", but being able to reach a location also requires being able to reach the region that location is in, so an indirect condition is required. --- worlds/sm64ex/Regions.py | 4 ++-- worlds/sm64ex/Rules.py | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/worlds/sm64ex/Regions.py b/worlds/sm64ex/Regions.py index 6fc2d74b96dc..52126bcf9ff7 100644 --- a/worlds/sm64ex/Regions.py +++ b/worlds/sm64ex/Regions.py @@ -246,10 +246,10 @@ def create_regions(world: MultiWorld, options: SM64Options, player: int): regBitS.subregions = [bits_top] -def connect_regions(world: MultiWorld, player: int, source: str, target: str, rule=None): +def connect_regions(world: MultiWorld, player: int, source: str, target: str, rule=None) -> Entrance: sourceRegion = world.get_region(source, player) targetRegion = world.get_region(target, player) - sourceRegion.connect(targetRegion, rule=rule) + return sourceRegion.connect(targetRegion, rule=rule) def create_region(name: str, player: int, world: MultiWorld) -> SM64Region: diff --git a/worlds/sm64ex/Rules.py b/worlds/sm64ex/Rules.py index 9add8d9b2932..1535f9ca1fde 100644 --- a/worlds/sm64ex/Rules.py +++ b/worlds/sm64ex/Rules.py @@ -92,9 +92,12 @@ def set_rules(world, options: SM64Options, player: int, area_connections: dict, connect_regions(world, player, "Hazy Maze Cave", randomized_entrances_s["Cavern of the Metal Cap"]) connect_regions(world, player, "Basement", randomized_entrances_s["Vanish Cap under the Moat"], rf.build_rule("GP")) - connect_regions(world, player, "Basement", randomized_entrances_s["Bowser in the Fire Sea"], - lambda state: state.has("Power Star", player, star_costs["BasementDoorCost"]) and - state.can_reach("DDD: Board Bowser's Sub", 'Location', player)) + entrance = connect_regions(world, player, "Basement", randomized_entrances_s["Bowser in the Fire Sea"], + lambda state: state.has("Power Star", player, star_costs["BasementDoorCost"]) and + state.can_reach("DDD: Board Bowser's Sub", 'Location', player)) + # Access to "DDD: Board Bowser's Sub" does not require access to other locations or regions, so the only region that + # needs to be registered is its parent region. + world.register_indirect_condition(world.get_location("DDD: Board Bowser's Sub", player).parent_region, entrance) connect_regions(world, player, "Menu", "Second Floor", lambda state: state.has("Second Floor Key", player) or state.has("Progressive Key", player, 2)) From 5530d181da643beb96abd915c259f6c22cb9dc7f Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Mon, 16 Sep 2024 06:48:13 +0200 Subject: [PATCH 09/21] Core: update version number (#3944) --- Utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Utils.py b/Utils.py index f89330cf7c65..d6709431d32c 100644 --- a/Utils.py +++ b/Utils.py @@ -46,7 +46,7 @@ def as_simple_string(self) -> str: return ".".join(str(item) for item in self) -__version__ = "0.5.0" +__version__ = "0.5.1" version_tuple = tuplize_version(__version__) is_linux = sys.platform.startswith("linux") From 84805a4e541c6dc2a0d95b8c3609b1faf9c240db Mon Sep 17 00:00:00 2001 From: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> Date: Mon, 16 Sep 2024 08:30:47 -0400 Subject: [PATCH 10/21] HK: XBox doesn't exist #3932 --- worlds/hk/docs/setup_en.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/hk/docs/setup_en.md b/worlds/hk/docs/setup_en.md index c046785038d8..21cdcb68b3a9 100644 --- a/worlds/hk/docs/setup_en.md +++ b/worlds/hk/docs/setup_en.md @@ -15,7 +15,7 @@ ### What to do if Lumafly fails to find your installation directory 1. Find the directory manually. * Xbox Game Pass: - 1. Enter the XBox app and move your mouse over "Hollow Knight" on the left sidebar. + 1. Enter the Xbox app and move your mouse over "Hollow Knight" on the left sidebar. 2. Click the three points then click "Manage". 3. Go to the "Files" tab and select "Browse...". 4. Click "Hollow Knight", then "Content", then click the path bar and copy it. From ee12dda3611cdf016d8e8a8633a32333a3f47a13 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Mon, 16 Sep 2024 12:06:20 -0400 Subject: [PATCH 11/21] Lingo: Added missing connection from The Tenacious -> Hub Room (#3947) --- worlds/lingo/data/LL1.yaml | 4 +++- worlds/lingo/data/generated.dat | Bin 149166 -> 149230 bytes 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/worlds/lingo/data/LL1.yaml b/worlds/lingo/data/LL1.yaml index 16a1573b1d56..bbed1464530b 100644 --- a/worlds/lingo/data/LL1.yaml +++ b/worlds/lingo/data/LL1.yaml @@ -482,7 +482,9 @@ Crossroads: door: Crossroads Entrance The Tenacious: - door: Tenacious Entrance + - door: Tenacious Entrance + - room: The Tenacious + door: Shortcut to Hub Room Near Far Area: True Hedge Maze: door: Shortcut to Hedge Maze diff --git a/worlds/lingo/data/generated.dat b/worlds/lingo/data/generated.dat index e2d3d06bec9642cf1b782cc3751ec542c322b558..789fc0856d62853f80fa5bd332b87c6fdd63ee60 100644 GIT binary patch delta 32275 zcmb__33!x6(y*OlW^&&*NhT)=R}$_!PLjz1$xKKlatKI-NO%PTgy4mOH((I3k@f`f zSZ@&V78Dc}4=yk826xwERm9I-{ai)Jw;hL!e_QRkdfWQ^k_H@a9kUwC5% ztKq9}%vNw8-+to&fG=N_$j3aHNTojN`{h*$eAudf&m@KY;t zR6m`(_saeR&o1<=G-_Z_&*Jr0ukVg&Zfm=^ZIRPGfAON$`7_(r8d!gt;@GEbt0dYh`;O@!R4ko{BPWx2@?Mf`Jo zc=s3}V@dZ&B7UsuY!CmXdrXAt%-7_qoa5F8^+ACBRG8^@=mstIJqLeerb@5_rctyP8OUe&xEV47vItzS_FxfStr`|8!lx z!X3`-x8M%CZy8EESim>mqS>#CzjVt`-H*rH=nm)yID#MATgdyDk+v1nGOv-uME z9&tZ|&F1-+{w@S(|fW>)g4z;U(O6>kuGs)vW_^ z{b)_nZ4P82)MvAc}Dw0+$n?n~z^)+2A_ zAB#cFa*-VM-e#Y(sH|8o6rO7Z94LYMH2V+e`cC7cGxWQY&$w+&yjpx$gIg~?-+EhG zj7d|c@hh|yC-Q>Q^+9}_|8`rwVuPr9_j&GmXHmu6)|nS;s`!lCGhoI`ZyyV5_2TVX z7JbUUxZMlzk~`8^xj0b35UK5sTd@=VxAr!LXc-URh*whm z#&j5E^SvV+^4N@y;iWew@*NxTdN=|9t>ITcpU8)Aic`#3%NsY9LfzGyMnm08o3yO( z@vk;D&5}DE>hU)DYOU;Jn^@=ZDe11R21iw$tHJ9rPZC`pA=PzF^2DE!#kz3;t#?5? zf8idKP=DSdB}sm8r*Su*x>?_tUHqEOZm8e8xerjgtsCpN@br5%yIjM^-aAy?86j2P zi5`AvbBXiW(D2ZGLsY{Dc+~wa zmpW3lbye=uj+6wdBc)eG0taa}kK$`9)A_^qC-v96xW3EyLGn>#bKA_0`LmqWb1jnU z1>}HYzOYX^4}D<7ICb=z8(sS7$#wR*T@BU^3h}w!0f4Z&hDMjK&h7DOhFtMLQ9qN2 zPUD}FhzMW%91xZPG`veR2V#4<&M$Lo=MtL7Tq+!8QkXmyLnGucX2XhU61 zttLPc-t4Ll=p>{W#EP{|NTmiS7V73({}LZ*p*)=L`-_&XC-`@NsZUnBRPAkPvg{IX zc(8xE*{<8xytsAFqE0O`|K#f*M9KcvgT?+Eq^o@n>IvcQIkVdr2izdxJmsOGrE;LK zI%iRH+mg2V<}SmYg;jQREn0k8>!MlfyIGxH^eZ2-oZpT83!{`0lo^W$U0l5%RAAe~ zvB-q6{gQb9hvO=63~O&jJm8u-S$C9sZri-J`HMT9u0?IF>y2bp+qOb#Xn4hf(i3FV ziVKb~eJz*pJrASmxo|^CM5_gJ|4F@)BY4Fl62o#!#YR}WJ@`nfIvh3p$43gOI}5VB z(nXQ(MO~f1W7a$NC*;|_eBFpNzIQw7n~tZlfwZEh zG|7MP`ln{6%b_Bh>!yInY@P=yl0GJU-&2FK&G9;o-k~&JuzUV2eJUe(_R|BiWEXZ{ zt=sALNQ%LxSUGL|3N(*+$J1$P^14fo1Cs0wt(}Y87G1X9GY#w(zWHgSYWLIW>Q2o@ zoW9~*H%sU28Qk#VXR`3XEt{3XXFL-FfCbN>*>>kMIq=_|&!Db$?3ug}q(b!nC(Fo( zAzaOH{{;xQ9x!N_5oTTn{NiV^n>Ej>ZtnRfOXlx9i-UdoEZQ)qo-Km^7Cl!2v`%_1 zPhH;^#?kdHo=c+9dF#kbzVEq6|Hr?di}HUIJ#VRLdA=`nb@lUFNxz&w_B@hs@cB{2 z=9+gITjiQZRe&zmIcHW|zz*NQ2fyGME>}MkuJUHhp(ML)&F)IL@U6l+6fA`%4dE}m z(AO%bpXWclpykhB9jTV3 zxzX!Atx1Np$zQ3-#V_@amQC#SZXYmDbI)=5SsVSxzjRugfvE{e+4ocEgjpvWPdM;pm|0+5;4&7JC`|XKQl%2;b_S9%e zT2bGkH3*WVAl0D3u{~M>w(+3XOcmXVd~icHzx1^tK;HaXqUw1u-|<>wqTD!}x5?$H zG5dDD-V4PBDs+=xPl+`-rpx#Rmz2lWWoX)Ooy?j6xfv0;U~rcSDa7H(f3H}0Y3auyL`m7rI@sTXG#;HB~x-kbn~M!uEqmfc4;)itB^?wY@7PG_5z z-y{WsrH_S2nfIj)vyx~Bd_To%&H+iX{b=$Tb zs8ie4#Mwc#HisM>Y@I5oY<@zf@@ozjXeUl}-Ff;<(P_Hnkb{4E(4o%OEr(*Ev$#XW z^xqsl^-w_}u3(RehE;Fqy` z(UaTAjaSjSXwjTD(^*)MNC$lL;beZ*kp$@emLsT|A2^~%Mr zIPhXE>UQ*W*N^5)KSIvj_7O^axXF^wsC@w&eTi6de&S<&-%jH(pP)wW{iMKe%y!2%qk@NKqSG~VW?QFu3flQ?pNxYgozTS!lKf&D zgf+({W+L*;`$EOn}7UyxOKEC{KV&awEKDd7vCY9h zSAcoq`)f%kEhwMxbs})!-G9weLhVof%3sGJEfc1uh^0$ee90NNHkO@blyH90-*7n} z{ad<~s%dAVDk+IpHTLgGJml{v6Z`)?)!Oi8)v&(pqQwEzabm+9zPmMvU-$R%(D2~j z!3Gk?uVDEDE&7R{8()?^MOJq#f@h&w0iBidabMA_f0~xTw|$jlJ?kgVM!{(Q-B+~9 zYhl}99sSJEX~gh_Uu(OfmaqQWEls&YGn4ptUng3-?;S2z^0W#1c>Xul)J)tH8k8zZ zma=fZ_M343<3y)XE*smL;O1*JUovL!9p9ie@Xv3YepZ6xNXYOs1}hL~3|`Dfev4XR z_qS=*7ZOj~B>`f;i|_sxIWF!WseaqO+ve+lS4-2ZUxxdV%gev*#b^Cvn!dK0EZ3Hp zznm3|{mWUz&@&07Npeo$GM$aiuK)^e#?H&cHhWMmpYh#d?G0>d!DkTDBF6#xh#glk za4~!zZguNlE&C35P`R7eeUAcl<@c!k{pEY@CVz0HMtvIb$Sy3gaQT| z*Ly28&4|doKjDkOlArY6=ktv}HEU;hqqjNWg(So>G*NG_a@SSDvw-_7s@MrSuLQmU&M(+6N=Ul}dZOOnL1)Tky$TR*qT&+ZCb0eH% z>nt5jZg>c5c5C|NtSrPxfQ1@`;c6al?I*-SmBjE_4_q9cIPgDt%)xo<<={vCC#k+r zjOlS8soL|e2)xKtwe>Vo^+vOrzxJ;JtCYx>ms^08*vP|w84XKV^Gkr^tBqgvi`LU0 z;hTOzUGeBIsQaNO!1=58mp;R*e(ei&3x1sibyq*1z`yxbzl-nTr+!5T&xrp;pA6Kc zbQ&|yK*Aw@`~Qv^sss}HjRID1yxZMq1=uQFb@gt|T|Dx)0oExVjG$X5V2WSyDZh;! zr27Z2fVI2O%6e~Qz@7z}?9JZ}?ELdHbpAU}`}df^vU3}Re0X}u!fJ1m^@YiN>i{qB zs(<&llK$-(dXM3U|BVl=Nxu)U-og>w`@7a6Gx);aQ89Vuck~&n$OOghg(<}CYkyDS zC4Zo_t^8wv-`Cbum{Su@HF0 z)Iv-yNu#Stvz$m}EVIa@5ww{z55aNzd17~LP?}i6*dQgkf&w2^gBx4{iy!O?e9eS< z?ddYt9!WxzsncjV^Ux3C(D@eo+F4ZH!55s05QBo);9=@ufr%O*Eo~q`dw@clT0CZC zg4h_ul1HjFBi^wxiLG?#z`;XC5XizOep+XU<3UW@j=M!vFe~xO&TP$AN&475Reyl#n1s+t_gZ&|l$yP9bgwM!>fN>+beg zKzS${T6|hgh!{Jp{1F=ig+P{1*%+t6p~7j;MDgP(J8hqH3d zqM38efWv`EZC`#YC0)E3&LYG?I~%My1}>fP=CdME6Yi~O2Bxg^1%gAH$JYfa1)&>NhO*ys>5eifUDVhTD%1u@CseiDj6y>Ih<*H%CPsy`C|OPyGs9Vd)zze_^|daV-DbvZT^_Km<&EkP z{ue5URJ*Uq+fq@lcb#BiRV|J6b(LTWXnL=TVBm9`SXZz0T>%gz7%Rli2qGBD8R9F1 z*NBKnmZL6>O-Njxm>$WJg$I}^UsH!?U+)@?8(d=spNMUdtQYjZKa!2~&%e`nM=@Bq z`_h@MOIjDt>6ot_w@-~DQ z0T--}yv-NmVwiT5fUJsPU^0u{F)Y=5Lz*rev$v`)FfhjkdX(G$mOB~ShBkuC(o7*2qK74F2L-*kE>KUg zRk!p6g){(~X1C8`83f&m-(#89uu?^49Ba^1uBh|YG`SiBl^k28tJ2q^`yg7v!A$bG zE1LYAOkgmM1GNK(k6v8@7b~>7sijWeL)6YTyusA~2AU#M_SI9=f;q3uBg_3Da^hJ= zxC}NJnFjFybK+SZ{s-@~b@41g&CFiWe3WQMuV%TFn55dROxG~tF)5l zaQVCqmgfhe*Y0t*fQlS22SBVr%)t5E8k->QwkIOu1+tGNV%sPeMgq(5tI1tPzF3!F zYPT1O#}gRD8;Tzj;B=E6BT5t5IJQJwk;tmx4@+Wc5U>Gp!v=k%hJ{XSsslkJKeGWP zCy5oqpYcg7J8=oDWzn?t^8euQS-H}x3XdeQp>m|uNJj&( zJuQ(MPK-|mRYMLfI+B@9mea%k^ejfF(kx)9 zQpJo^7Og0-Qy@Mdl@);4ekc|0_9f!!RF(^WBC1(hbPYZ{s0{_Q3B?dNd#%VwV|jqF zZUIY!ugUOr=7nFIMhg$ElI##1Mw{_g8tZjt?0-w_H4|BRST{H)K(fdolSEfKONIe) z0mCcYzKL#-d}?ZujtW=h_=)KL0tienO?;nD^T3Y8i8Zv`3pXLWob^ia$GnHtdVS7H zG?1|6u|}3Fp2%R~Fiy^eEKwZDpfPt{%4%X|XnRq&j9|x+A7nXVdM{ek-FHCC?p~k^ zDHeFD7mJ57u4$B~rfIUX5!@Z{psutPQRDFGr%?ec0 z%xmG?wR?^A88W1hY!_H9d?Hf>M%XJL=E@0qAOKK3T;&AaV2Sv~$qK|HIV@Z}Uk*y( zXHFUupd)2vnIoimqN`E&B(BP#WAj!H8zpw-(EdWX7Lbv@lT;)-JeZjg>uShCB$KKY ze_{Beq}3nDu5gP@T!Zzp%&JQTv;#9x;JQM$5&B2s+gx*rA|wbx*o_oXmd6r-Kv>xU zqOlLNiOch7GZ|1E2L6tg3RA*`+iJl|1Dg!G#tEfU7(5=t9uedfxDeEAsUz41~Wd;Yfk_S3a@qgn2AI#tX~lESlIpXE8)*z!<>Cg+&&TS)`l` zJ*bhPq-OkmDyA4%!|la)1t2cUMQ9<*me0fCm8fDb>S&$S39rLd?n=L%jL|S+dLe^A zPO+enmBh;6d{}e}!Vj2vFLAJt*cNBrhZY^NjmHAFO@`=$Gy_>77uUad#iFG*i8KVs zokw)zmB21!pgs^m0r>$$j1-^urk(dMKVln4L_rb5TS}d`3yREf;Jj^;b=U^JJ}IJU z&{@wYCfNez+zrc_UF|C%%`2v1ZZ0Od05YynjOhcy`{iP0SNDzv=0K~Q0W8;E<(fkG zMC2zYfwVy77 zg8Pv_n%piDYd@XOveF=)p90N`Ajn{5`y640Uph{1A} zVwe_1ceiaIEXut8dbxAVI}uu;UO~vdTF~a9bL=V70amEHxf$#^fIxMTiPZw0euZTZ zMgu^8IODsFEs9)9CqTHL`?iSV1Bge6=8_VV6ai&>C}>xzPqDIu${2t)kEp}N=)ra^eaH6%yeSw;&8H%uOc8%h}rGAT`b zQ^pb`ev*hA$Pz|bI11uvW>6^{?1;{#*l*)T~!4gqCwN6B)o zopJ+YC(g7k}_!4JCc;u@~D9qQFlvWI{t3MDGvH{j@>+h zW%Sol)zKr{fRuCvH1%=8hp;kOm*qpX_zxFf4I#;bASwQFIHN+84rQr;S~rxmTx*NX zLm7$)*z+6_JB-=<97Nx!31OWBjxt16!8{1_^I;~h0pSB3Ar$(EvmC(&i7$pp?u8nW zIb4?qiK%88TDV*4E3iLo9~0qeX{bOQ2vf^W2TD~MF|j`_;^2FvA{UBdfl~NWCgMi2 zr16Rqb+$!QN5{PNt!NR*CH{ZOfe;Hv%F}%zbBY&6O671jhyWKnowkEmB#J>@bVfP= zgkg9lM2sCp)5lt2jM9pwQ|ubWLhRo8{bP(1%A3r73f z-Zh#=jN8D{;ihX0%LX>x>k=FD#+UcHcI_mlYssAXt}s}XRd zmGnmi$}7Ye;vRac()xUn-fBuxG`U!+{Horw_1q%UMGyp9QDB}#^Pl|Y<2T;0R}u;!6sOZ-f8Q#b<5&mX+f+c=4I7ws zwo`){62<^=poS$a`_DMJmSfQkPPP`znmqfk)UUNom+`z@!%!K`eOl8J)Z_3ZBObhF zn=#*H3w0W=tL8AYv?Nhi=}RrJ3Rc2&@~IYwWec#{AyXCB54By{0Bg|NQW>lY{~I*| zyzIzZA1y+8Sicct>sWI1ZxRC&DX>G_QmNZxMK^&>l>&1s=nW8p1|W+9@j=ziD3O!I zx^7itX_6tE=qw6s1mZ+D!rE@L&eE)(8i{f#uo2#@MaMaT_iNX9mX9y9$Ht~_sAo|WjaQy7 zit9nUq+Szvf0rFv`R4_#V6;L+ol%i0HrIplI#=wjXXv9L;dY|l6oOG<^q4;ppZH-~ zq(TjD0DDL7Q4sj&<~4xZ(;{I5g!zsJQjJMk5BIRls4$9tCg&vdr{U;{pfWmn#Ii}u zApvA5^%9s8tv-yVK3=LuupIudr7)@wy-Sm#8wuG0PWvH zl{3=O2q|2e8ia`DjdUZMO~OK(8z4FYXHO0+%>BTGIrxU#4NZ)DRE$g@aEQ#(dyA0a9Bz0yaPE5v$Z$XeVXc+c~JjZ`jv@v)K= zN}z#Dqo<8Hx`m;&1KW`v=52tOD;gA8Hyk3T;C7(h!S6^1K6};Rn>3Jxkg(8Ncw97n zvLfO<@-`CRm&!72a}_*i!3(!;W)H3f_lD{t+*VUpQ)BW3x?8FJr{|gHM*QjTH{6EK zJ;)eR&4pjFNV7M2rCAIeq}lPZKrCj}Oo z$7#2qL%cM}@2UPw9xfvI&q<_*k&atBS(kHcn9KuY06YgCj$|42W@J`02Z_j?li3Kx z(qW;KA+(DY3(Wg$d+6Tk5hgL!JEGG;@6wDF&0R7b6 zsHwySPvln;Q%Y|EqrCgb+sQMmAuvq zEJsMR?B`FT(M+p3dVwFgR#g07WG@X|CAbf{FPI^brlr$||=egLvPZ8|HnYKs7x zIGqjY0V=%++&>#1XSQUxTPZ=mM>qRU?5l{NCju?S> zA^JG6R+d{L2>{2Ij+G)^rpvhfEX)bapC~SBCFZ09a!0E))zBb(X(|avf*)@sUchpM zox)$G9RPqrme!$v8l%&taOBoQ*RpbOFO!#pK1tuQcf@=WBN?L z^|)%LX|INbdMn*hUSl8Pix`&s!DOFl0yLlGY&owljfnxB zsyr->3Hnd6T7fT(S>oVq{q9f_rUqKrTa*nYWrAc$Lp#Y5Je0g3GDcj}PF!bxA8IFS z`ulcPj)w|O{)#!W`EK+(Phz3Sq;N9|%gqpOYpAPIk{IgJ?B)v{^3%yVnxArP80D+j zTGSiFf~Zb}g=*nMiVfb&@MAG_FT*D1mbafo!5HG61W)pQE+lm0k+)mW*Ker7^frv+d7!u+GzCZno=a{G);#Df=;$G>;1W;D3?6|f+cny-L_D6#ze>vIj7cg;TZ>g z(m2KFa8;)?&K!W%nki*H^ddizTHiV?)&P_rcs@~41(;xkRsiHKim~&_43WuJm;pw~ zTKImW%VA>Id=PpBz#pK(3V7sM%);@hNTz2&8dZ7(MAGOHFs};3iXd$Z!s*2p$g+}u zQbofe77>gF4&;xCs0ECE>;%h$7qGnafCa}bQWozy4zX|nT`H&>+3`rgYKUzMXq~}H zTAC#1Y~3;(1?M%RoS2bkqh8*JU-pn+ z@M}O)q1D9q>Osw~ovbu-oa)pxdjba6n~v^l-A!V0moY`P@+aNFB9Y{1^h$HY#0G_M zDPec(-;dIpMUP2#a`sC88lAaHVus4!p3;cQ9c(UZ1oGp5cm>OexlQ(Ceb&NwG|k+x z;)Z1`IjPgQPvZH1#tNnZPi6S>siz(#VlQDyG22v+&0hFfF~CBvXp#g>O7SY!V5paB(-YP5@XFoJnb!kOFJ97%97b+Arx2&BVbn|^HNwV zI}Mdv7uw*immMMwG?h7YMcoj7LJ#kx27DsJBQPW)rc3p$We!pggs2ocP-%5DB;+qW zkdZ!^lgg5d05i#5S?j`Q09**1Q$nZlxoiZl;Sipr1%D1Ku}YEy%e%4;f~&|-fI9)J zVr0&hqrT4VtOrvOyMyVbz)V%{TWBq)=VXW>5rt-2+kX^=`qvJ~i6Rpx<4N{FHeQV1 z8AX}{hrBy6{tCFrC`}OMqfE?<4VCrTGm^#5E0jZKR!XE?V`a}mN?}1@9vig5 zu?KtHWN6@y!_yCh_^Ca~&XDN@?keS+4>;F44K7Dqautj2As2LI|3Zsg)^|)muWA`$ zma!41_fag4E(4?P?qw|MES0@ahYt~T%UMcK_Zd($6jl!YPx254L#wQS z^<>w8;N zqr|FZVDs*r#OysPY3%ZASz*tq>WUcT3i{Y{Eh~su>4^~U3zISZF&WA;d9}#A4q|CG zsA*`Yo3yh~Hzbub@60Nx#x^zG|0`^1&6s`Xp1E%a8t3%$P4Z}i#|BhOkCYrB1<<2# z=k@G<_yaFR_%zb`BD~sW-Uu;hO5O8A#Qqz|48u1iJEcrQry3t$eW0RP%)F62n*i?} z8zJgS4XxdxsTcO(&ia%D6P8YpW zn#n66&dW^qgSHD-vQnT4s*6w^rffq1!*{WrlKUi1kwa$o$*LkTbrm_4pmlGtdKJBO zfWr|(9-(EjaNY#L7?edP8xRENzThU-4_{kIh{6X>(`+FQ+(hP=1|)^4oH^(O-I)np zMh@jp!Mh1qobs(5g$?+!T=1ih~5%lA_~^pMRMK9($^ zSCeH8eSnh^SitX6%@qq))0DAJ+_jn(7|T&%^%NEfugy3Lg3y1eA_(NC*V8OrPxuKO zBVDGlt_n_yj=0OSgC4BA$+P>K1d#RSD=r^EUP8hWsH;AYQfcV47~XIb{cIGYHO~A2T0sZza+96 zOGcYmyH=Xla50Jx){>1!N zTSn6YKMa4(6Fg*QR-SnA7KjnbzeVcsmo4cP)eS$_Xoh5q3vOWp@g9xoN*9mcLUsnU zr~!V&XUcH!F4Vf3q`m+gAOmQ^j(gyQR>I319U<&WK1-dB)@8`7Nw}U=cBJZ8J~(|Yw%YP$cFKGv)>Sfb{F@08O})HObhGi{U8fm~eBlT`CO|#J$Y5geFphq(n>j!sj#x z+SYWR$hR{vWt!?-@?isr$chCSrV@HEiBk5FBQCg$d`xshVDhAb+tia1z_TGFOlC^L zIisvcz#&yvWjw+sdX+@!-C7psiu3QLCji_DG3{Et-f)|!JV$H;D+^cI3`JBZykkQ~ zrRod<0mnn01>7GR5*-eL4Qyn}I0h_=z`l7iOK^nXuY)6+2@+d2%FRQvKHf-vSR^sR zOoCLyK;H<`+GC6EtcmeUrHz*(i9U1Va zuV}o7d~sL~3za#DYGASD9+N^KLS`PCMERejh^Q+li5!Tc{1-W5iom&xd7G&lJ#&nP zrM)<~S-xW60-V}xE`TG1vJRmO>^NSh5W*c#34xlpo9>mn2d&`}rxZX{1)r4vr#?aB zklA;-<(T{AUejXhbT?h8p7U^M@!EZKkpQuIpqbG25#i4J^}6KQFhxzb?Ydu{aGcPi z_tQm)_@Ixe(8=%bXJv^xWzA9L^~yXgXb*n8zOXLn&J$NYK;2^*mR;tp0m?QrBH$fy z=mGLqK^j7l3L>JSRJf@qwP`BKxKAp|xKAp|xKAkJODI8P+$WK6#e*ambU9o_Ci%Z* z_x^7=hW}d*WPXbU4^iaO=7(5s9k=%o&6psvH!KS0!+u2B$%Kdfh{I%vAR->LAdVm~ zdqmFV5&xoKQ7sA-#S@QE=YUugs#XPyYE`hPRt1Z4EW(v5Lq&6_-nN0IdRQn+%ZMgC z<}nuD!#MLj#tP--`i@xs7(_8x)FQCT9(jxv&{GNJGa5mIte02_r6O9@HfZttW6YUH znhCN~j~3P#DY)u}qv3by`;dPKgXe8Yh#fpsr*>D zN+8`3FR!!MD^a588TokJAG9ZUmmmR+en_$A8H#xZ>pexh{0w9mk}`mNfI5j7|18P% z9E`VJ`YbINZWM$T$<$f7{sAB

U5jJVz{w%7y6n9Ni@HWmP7_!i>?bhpSWv&*7)( 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 delta 32247 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 UkY1CJx8$FF&puxoG}7^Z0FSwKuK)l5 From ce42e42af78676758f6bd30c1cceb576ebf1fcb6 Mon Sep 17 00:00:00 2001 From: Silvris <58583688+Silvris@users.noreply.github.com> Date: Tue, 17 Sep 2024 07:36:05 -0500 Subject: [PATCH 12/21] Core: fix single player item links (#3721) * fix single player item links * Make a variable and fix weird spacing * use advancement instead of classification --------- Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> --- BaseClasses.py | 2 ++ Fill.py | 20 +++++++++----------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index b40b872f0c8c..a5de1689a7fe 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -342,6 +342,8 @@ def find_common_pool(players: Set[int], shared_pool: Set[str]) -> Tuple[ region = Region("Menu", group_id, self, "ItemLink") self.regions.append(region) locations = region.locations + # ensure that progression items are linked first, then non-progression + self.itempool.sort(key=lambda item: item.advancement) for item in self.itempool: count = common_item_count.get(item.player, {}).get(item.name, 0) if count: diff --git a/Fill.py b/Fill.py index e2fcff00358e..706cca657457 100644 --- a/Fill.py +++ b/Fill.py @@ -475,28 +475,26 @@ def mark_for_locking(location: Location): nonlocal lock_later lock_later.append(location) + single_player = multiworld.players == 1 and not multiworld.groups + if prioritylocations: # "priority fill" fill_restrictive(multiworld, multiworld.state, prioritylocations, progitempool, - single_player_placement=multiworld.players == 1, swap=False, on_place=mark_for_locking, - name="Priority") + single_player_placement=single_player, swap=False, on_place=mark_for_locking, name="Priority") accessibility_corrections(multiworld, multiworld.state, prioritylocations, progitempool) defaultlocations = prioritylocations + defaultlocations if progitempool: # "advancement/progression fill" if panic_method == "swap": - fill_restrictive(multiworld, multiworld.state, defaultlocations, progitempool, - swap=True, - name="Progression", single_player_placement=multiworld.players == 1) + fill_restrictive(multiworld, multiworld.state, defaultlocations, progitempool, swap=True, + name="Progression", single_player_placement=single_player) elif panic_method == "raise": - fill_restrictive(multiworld, multiworld.state, defaultlocations, progitempool, - swap=False, - name="Progression", single_player_placement=multiworld.players == 1) + fill_restrictive(multiworld, multiworld.state, defaultlocations, progitempool, swap=False, + name="Progression", single_player_placement=single_player) elif panic_method == "start_inventory": - fill_restrictive(multiworld, multiworld.state, defaultlocations, progitempool, - swap=False, allow_partial=True, - name="Progression", single_player_placement=multiworld.players == 1) + fill_restrictive(multiworld, multiworld.state, defaultlocations, progitempool, swap=False, + allow_partial=True, name="Progression", single_player_placement=single_player) if progitempool: for item in progitempool: logging.debug(f"Moved {item} to start_inventory to prevent fill failure.") From b8d23ec5956cbf8313c328e4f3f9f9d08c9e0492 Mon Sep 17 00:00:00 2001 From: Mysteryem Date: Tue, 17 Sep 2024 13:41:56 +0100 Subject: [PATCH 13/21] MMBN3: Add missing indirect conditions (#3931) Entrances to SciLab_Cyberworld and Yoka_Cyberworld had logic for being able to reach SciLab_Overworld, but did not register this indirect condition. Entrances to Beach_Cyberworld had logic for being able to reach Yoka_Overworld, but did not register this indirect condition. Entrances to Undernet and Secret_Area had logic for having a high enough explore score, but explore score is calculated based on the accessibility of a number of regions and no indirect conditions were being registered for these regions. --- worlds/mmbn3/__init__.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/worlds/mmbn3/__init__.py b/worlds/mmbn3/__init__.py index 97725e728bae..6d28b101c377 100644 --- a/worlds/mmbn3/__init__.py +++ b/worlds/mmbn3/__init__.py @@ -97,6 +97,28 @@ def create_regions(self) -> None: add_item_rule(loc, lambda item: not item.advancement) region.locations.append(loc) self.multiworld.regions.append(region) + + # Regions which contribute to explore score when accessible. + explore_score_region_names = ( + RegionName.WWW_Island, + RegionName.SciLab_Overworld, + RegionName.SciLab_Cyberworld, + RegionName.Yoka_Overworld, + RegionName.Yoka_Cyberworld, + RegionName.Beach_Overworld, + RegionName.Beach_Cyberworld, + RegionName.Undernet, + RegionName.Deep_Undernet, + RegionName.Secret_Area, + ) + explore_score_regions = [self.get_region(region_name) for region_name in explore_score_region_names] + + # Entrances which use explore score in their logic need to register all the explore score regions as indirect + # conditions. + def register_explore_score_indirect_conditions(entrance): + for explore_score_region in explore_score_regions: + self.multiworld.register_indirect_condition(explore_score_region, entrance) + for region_info in regions: region = name_to_region[region_info.name] for connection in region_info.connections: @@ -119,6 +141,7 @@ def create_regions(self) -> None: entrance.access_rule = lambda state: \ state.has(ItemName.CSciPas, self.player) or \ state.can_reach(RegionName.SciLab_Overworld, "Region", self.player) + self.multiworld.register_indirect_condition(self.get_region(RegionName.SciLab_Overworld), entrance) if connection == RegionName.Yoka_Cyberworld: entrance.access_rule = lambda state: \ state.has(ItemName.CYokaPas, self.player) or \ @@ -126,16 +149,19 @@ def create_regions(self) -> None: state.can_reach(RegionName.SciLab_Overworld, "Region", self.player) and state.has(ItemName.Press, self.player) ) + self.multiworld.register_indirect_condition(self.get_region(RegionName.SciLab_Overworld), entrance) if connection == RegionName.Beach_Cyberworld: entrance.access_rule = lambda state: state.has(ItemName.CBeacPas, self.player) and\ state.can_reach(RegionName.Yoka_Overworld, "Region", self.player) - + self.multiworld.register_indirect_condition(self.get_region(RegionName.Yoka_Overworld), entrance) if connection == RegionName.Undernet: entrance.access_rule = lambda state: self.explore_score(state) > 8 and\ state.has(ItemName.Press, self.player) + register_explore_score_indirect_conditions(entrance) if connection == RegionName.Secret_Area: entrance.access_rule = lambda state: self.explore_score(state) > 12 and\ state.has(ItemName.Hammer, self.player) + register_explore_score_indirect_conditions(entrance) if connection == RegionName.WWW_Island: entrance.access_rule = lambda state:\ state.has(ItemName.Progressive_Undernet_Rank, self.player, 8) From 4692e6f08aa9c7cea764be0f079e506e17c695b0 Mon Sep 17 00:00:00 2001 From: Silvris <58583688+Silvris@users.noreply.github.com> Date: Tue, 17 Sep 2024 07:42:19 -0500 Subject: [PATCH 14/21] MM2: fix Air Shooter minimum damage #3922 --- worlds/mm2/rules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/mm2/rules.py b/worlds/mm2/rules.py index c30688f2adbe..eddd09927445 100644 --- a/worlds/mm2/rules.py +++ b/worlds/mm2/rules.py @@ -37,7 +37,7 @@ minimum_weakness_requirement: Dict[int, int] = { 0: 1, # Mega Buster is free 1: 14, # 2 shots of Atomic Fire - 2: 1, # 14 shots of Air Shooter, although you likely hit more than one shot + 2: 2, # 14 shots of Air Shooter 3: 4, # 9 uses of Leaf Shield, 3 ends up 1 damage off 4: 1, # 56 uses of Bubble Lead 5: 1, # 224 uses of Quick Boomerang From 1c0cec0de2311f818c1a19b4f0d91219e0d9c852 Mon Sep 17 00:00:00 2001 From: digiholic Date: Tue, 17 Sep 2024 06:42:48 -0600 Subject: [PATCH 15/21] [OSRS] Adds Description to OSRS World #3921 --- worlds/osrs/__init__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/worlds/osrs/__init__.py b/worlds/osrs/__init__.py index 1b7ca9c1e0f4..49aa1666084e 100644 --- a/worlds/osrs/__init__.py +++ b/worlds/osrs/__init__.py @@ -33,6 +33,12 @@ class OSRSWeb(WebWorld): class OSRSWorld(World): + """ + The best retro fantasy MMORPG on the planet. Old School is RuneScape but… older! This is the open world you know and love, but as it was in 2007. + The Randomizer takes the form of a Chunk-Restricted f2p Ironman that takes a brand new account up through defeating + the Green Dragon of Crandor and earning a spot in the fabled Champion's Guild! + """ + game = "Old School Runescape" options_dataclass = OSRSOptions options: OSRSOptions @@ -635,7 +641,7 @@ def can_gold(state): else: return lambda state: can_tan(state) or (can_silver(state) and can_smelt_silver(state)) or \ (can_gold(state) and can_smelt_gold(state)) - if skill.lower() == "Cooking": + if skill.lower() == "cooking": if self.options.brutal_grinds or level < 15: return lambda state: state.can_reach(RegionNames.Milk, "Region", self.player) or \ state.can_reach(RegionNames.Egg, "Region", self.player) or \ From f8d3c26e3c6e4972f7845c6cd10bede41d8fd7cf Mon Sep 17 00:00:00 2001 From: Bryce Wilson Date: Tue, 17 Sep 2024 05:43:22 -0700 Subject: [PATCH 16/21] Pokemon Emerald: Fix unguarded wonder trade write (#3939) --- worlds/pokemon_emerald/client.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/worlds/pokemon_emerald/client.py b/worlds/pokemon_emerald/client.py index cda829def9d9..d742b8936f14 100644 --- a/worlds/pokemon_emerald/client.py +++ b/worlds/pokemon_emerald/client.py @@ -133,6 +133,7 @@ class PokemonEmeraldClient(BizHawkClient): latest_wonder_trade_reply: dict wonder_trade_cooldown: int wonder_trade_cooldown_timer: int + queued_received_trade: Optional[str] death_counter: Optional[int] previous_death_link: float @@ -153,6 +154,7 @@ def initialize_client(self): self.previous_death_link = 0 self.ignore_next_death_link = False self.current_map = None + self.queued_received_trade = None async def validate_rom(self, ctx: "BizHawkClientContext") -> bool: from CommonClient import logger @@ -548,22 +550,29 @@ async def handle_wonder_trade(self, ctx: "BizHawkClientContext", guards: Dict[st (sb1_address + 0x37CC, [1], "System Bus"), ]) elif trade_is_sent != 0 and wonder_trade_pokemon_data[19] != 2: - # Game is waiting on receiving a trade. See if there are any available trades that were not - # sent by this player, and if so, try to receive one. - if self.wonder_trade_cooldown_timer <= 0 and f"pokemon_wonder_trades_{ctx.team}" in ctx.stored_data: + # Game is waiting on receiving a trade. + if self.queued_received_trade is not None: + # Client is holding a trade, ready to write it into the game + success = await bizhawk.guarded_write(ctx.bizhawk_ctx, [ + (sb1_address + 0x377C, json_to_pokemon_data(self.queued_received_trade), "System Bus"), + ], [guards["SAVE BLOCK 1"]]) + + # Notify the player if it was written, otherwise hold it for the next loop + if success: + logger.info("Wonder trade received!") + self.queued_received_trade = None + + elif self.wonder_trade_cooldown_timer <= 0 and f"pokemon_wonder_trades_{ctx.team}" in ctx.stored_data: + # See if there are any available trades that were not sent by this player. If so, try to receive one. if any(item[0] != ctx.slot for key, item in ctx.stored_data.get(f"pokemon_wonder_trades_{ctx.team}", {}).items() if key != "_lock" and orjson.loads(item[1])["species"] <= 386): - received_trade = await self.wonder_trade_receive(ctx) - if received_trade is None: + self.queued_received_trade = await self.wonder_trade_receive(ctx) + if self.queued_received_trade is None: self.wonder_trade_cooldown_timer = self.wonder_trade_cooldown self.wonder_trade_cooldown *= 2 self.wonder_trade_cooldown += random.randrange(0, 500) else: - await bizhawk.write(ctx.bizhawk_ctx, [ - (sb1_address + 0x377C, json_to_pokemon_data(received_trade), "System Bus"), - ]) - logger.info("Wonder trade received!") self.wonder_trade_cooldown = 5000 else: From ec50b0716aa280c7bf4ce7a13691de8dc95f8b34 Mon Sep 17 00:00:00 2001 From: qwint Date: Tue, 17 Sep 2024 07:44:32 -0500 Subject: [PATCH 17/21] Core: Add color conversions for colorama/terminal output #3940 --- NetUtils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NetUtils.py b/NetUtils.py index c451fa3f8460..4776b228db17 100644 --- a/NetUtils.py +++ b/NetUtils.py @@ -273,7 +273,8 @@ def _handle_color(self, node: JSONMessagePart): color_codes = {'reset': 0, 'bold': 1, 'underline': 4, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33, 'blue': 34, 'magenta': 35, 'cyan': 36, 'white': 37, 'black_bg': 40, 'red_bg': 41, 'green_bg': 42, 'yellow_bg': 43, - 'blue_bg': 44, 'magenta_bg': 45, 'cyan_bg': 46, 'white_bg': 47} + 'blue_bg': 44, 'magenta_bg': 45, 'cyan_bg': 46, 'white_bg': 47, + 'plum': 35, 'slateblue': 34, 'salmon': 31,} # convert ui colors to terminal colors def color_code(*args): From 96542fb2d891ff26c86b8a652907980ea3686702 Mon Sep 17 00:00:00 2001 From: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> Date: Tue, 17 Sep 2024 09:08:15 -0400 Subject: [PATCH 18/21] Blasphemous: Move pre_fill to create_items #3901 --- worlds/blasphemous/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/worlds/blasphemous/__init__.py b/worlds/blasphemous/__init__.py index b110c316da48..67031710e4eb 100644 --- a/worlds/blasphemous/__init__.py +++ b/worlds/blasphemous/__init__.py @@ -199,8 +199,6 @@ def create_items(self): self.multiworld.itempool += pool - - def pre_fill(self): self.place_items_from_dict(unrandomized_dict) if self.options.thorn_shuffle == "vanilla": @@ -335,4 +333,4 @@ class BlasphemousItem(Item): class BlasphemousLocation(Location): - game: str = "Blasphemous" \ No newline at end of file + game: str = "Blasphemous" From dae3fe188d253bfd8340a15ff5b44a8189413008 Mon Sep 17 00:00:00 2001 From: Mysteryem Date: Tue, 17 Sep 2024 14:11:35 +0100 Subject: [PATCH 19/21] OOT: Fix incorrect region accessibility after update_reachable_regions() (#3712) `CollectionState.update_reachable_regions()` un-stales the state for all players, but when checking `OOTRegion.can_reach()`, it would only update OOT's age region accessibility when the state was stale, so if the state was always un-staled by `update_reachable_regions()` immediately before `OOTRegion.can_reach()`, OOT's age region accessibility would never update. This patch fixes the issue by replacing use of CollectionState.stale with a separate stale state dictionary specific to OOT that is only un-staled by `_oot_update_age_reachable_regions()`. OOT's collect() and remove() implementations have been updated to stale the new OOT-specific state. --- worlds/oot/Regions.py | 2 +- worlds/oot/Rules.py | 13 +++++++++---- worlds/oot/__init__.py | 9 +++++++-- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/worlds/oot/Regions.py b/worlds/oot/Regions.py index 5d5cc9b13822..4a3d7e416a15 100644 --- a/worlds/oot/Regions.py +++ b/worlds/oot/Regions.py @@ -64,7 +64,7 @@ def get_scene(self): return None def can_reach(self, state): - if state.stale[self.player]: + if state._oot_stale[self.player]: stored_age = state.age[self.player] state._oot_update_age_reachable_regions(self.player) state.age[self.player] = stored_age diff --git a/worlds/oot/Rules.py b/worlds/oot/Rules.py index 4bbf15435cfe..36563a3f9f27 100644 --- a/worlds/oot/Rules.py +++ b/worlds/oot/Rules.py @@ -8,12 +8,17 @@ from .Items import oot_is_item_of_type from .LocationList import dungeon_song_locations -from BaseClasses import CollectionState +from BaseClasses import CollectionState, MultiWorld from worlds.generic.Rules import set_rule, add_rule, add_item_rule, forbid_item from ..AutoWorld import LogicMixin class OOTLogic(LogicMixin): + def init_mixin(self, parent: MultiWorld): + # Separate stale state for OOTRegion.can_reach() to use because CollectionState.update_reachable_regions() sets + # `self.state[player] = False` for all players without updating OOT's age region accessibility. + self._oot_stale = {player: True for player, world in parent.worlds.items() + if parent.worlds[player].game == "Ocarina of Time"} def _oot_has_stones(self, count, player): return self.has_group("stones", player, count) @@ -92,9 +97,9 @@ def _oot_reach_at_time(self, regionname, tod, already_checked, player): return False # Store the age before calling this! - def _oot_update_age_reachable_regions(self, player): - self.stale[player] = False - for age in ['child', 'adult']: + def _oot_update_age_reachable_regions(self, player): + self._oot_stale[player] = False + for age in ['child', 'adult']: self.age[player] = age rrp = getattr(self, f'{age}_reachable_regions')[player] bc = getattr(self, f'{age}_blocked_connections')[player] diff --git a/worlds/oot/__init__.py b/worlds/oot/__init__.py index ee78958b2dbe..94587a41a0f2 100644 --- a/worlds/oot/__init__.py +++ b/worlds/oot/__init__.py @@ -1301,6 +1301,7 @@ def write_spoiler(self, spoiler_handle: typing.TextIO) -> None: # the appropriate number of keys in the collection state when they are # picked up. def collect(self, state: CollectionState, item: OOTItem) -> bool: + state._oot_stale[self.player] = True if item.advancement and item.special and item.special.get('alias', False): alt_item_name, count = item.special.get('alias') state.prog_items[self.player][alt_item_name] += count @@ -1313,8 +1314,12 @@ def remove(self, state: CollectionState, item: OOTItem) -> bool: state.prog_items[self.player][alt_item_name] -= count if state.prog_items[self.player][alt_item_name] < 1: del (state.prog_items[self.player][alt_item_name]) + state._oot_stale[self.player] = True return True - return super().remove(state, item) + changed = super().remove(state, item) + if changed: + state._oot_stale[self.player] = True + return changed # Helper functions @@ -1389,7 +1394,7 @@ def get_state_with_complete_itempool(self): # If free_scarecrow give Scarecrow Song if self.free_scarecrow: all_state.collect(self.create_item("Scarecrow Song"), prevent_sweep=True) - all_state.stale[self.player] = True + all_state._oot_stale[self.player] = True return all_state From 97be5f1dde63e4fbec51f8973a184a7c66dd6a37 Mon Sep 17 00:00:00 2001 From: Rensen3 <127029481+Rensen3@users.noreply.github.com> Date: Tue, 17 Sep 2024 15:13:19 +0200 Subject: [PATCH 20/21] YGO06: slotdata fix (#3953) * YGO06: fix slot data for universal tracker * YGO06: put Extremely Low Deck Bonus after Low Deck Bonus --- worlds/yugioh06/__init__.py | 2 +- worlds/yugioh06/rules.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/worlds/yugioh06/__init__.py b/worlds/yugioh06/__init__.py index 1cf44f090fed..a39b52cd09d5 100644 --- a/worlds/yugioh06/__init__.py +++ b/worlds/yugioh06/__init__.py @@ -430,7 +430,7 @@ def fill_slot_data(self) -> Dict[str, Any]: "final_campaign_boss_campaign_opponents": self.options.final_campaign_boss_campaign_opponents.value, "fourth_tier_5_campaign_boss_campaign_opponents": - self.options.fourth_tier_5_campaign_boss_unlock_condition.value, + self.options.fourth_tier_5_campaign_boss_campaign_opponents.value, "third_tier_5_campaign_boss_campaign_opponents": self.options.third_tier_5_campaign_boss_campaign_opponents.value, "number_of_challenges": self.options.number_of_challenges.value, diff --git a/worlds/yugioh06/rules.py b/worlds/yugioh06/rules.py index a804c7e7286a..0b46e0b5d0b0 100644 --- a/worlds/yugioh06/rules.py +++ b/worlds/yugioh06/rules.py @@ -39,10 +39,10 @@ def set_rules(world): "No Trap Cards Bonus": lambda state: yugioh06_difficulty(state, player, 2), "No Damage Bonus": lambda state: state.has_group("Campaign Boss Beaten", player, 3), "Low Deck Bonus": lambda state: state.has_any(["Reasoning", "Monster Gate", "Magical Merchant"], player) and - yugioh06_difficulty(state, player, 3), + yugioh06_difficulty(state, player, 2), "Extremely Low Deck Bonus": lambda state: state.has_any(["Reasoning", "Monster Gate", "Magical Merchant"], player) and - yugioh06_difficulty(state, player, 2), + yugioh06_difficulty(state, player, 3), "Opponent's Turn Finish Bonus": lambda state: yugioh06_difficulty(state, player, 2), "Exactly 0 LP Bonus": lambda state: yugioh06_difficulty(state, player, 2), "Reversal Finish Bonus": lambda state: yugioh06_difficulty(state, player, 2), From 5aea8d4ab56fcb063ce672d44bcf1d97229d705d Mon Sep 17 00:00:00 2001 From: Bryce Wilson Date: Tue, 17 Sep 2024 06:14:05 -0700 Subject: [PATCH 21/21] Pokemon Emerald: Update changelog (#3952) --- worlds/pokemon_emerald/CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/worlds/pokemon_emerald/CHANGELOG.md b/worlds/pokemon_emerald/CHANGELOG.md index 0437c0dae8ff..6a1844e79fde 100644 --- a/worlds/pokemon_emerald/CHANGELOG.md +++ b/worlds/pokemon_emerald/CHANGELOG.md @@ -1,3 +1,21 @@ +# 2.3.0 + +### Features + +- Added a Swedish translation of the setup guide. +- The client communicates map transitions to any trackers connected to the slot. +- Added the player's Normalize Encounter Rates option to slot data for trackers. + +### Fixes + +- Fixed a logic issue where the "Mauville City - Coin Case from Lady in House" location only required a Harbor Mail if +the player randomized NPC gifts. +- The Dig tutor has its compatibility percentage raised to 50% if the player's TM/tutor compatibility is set lower. +- A Team Magma Grunt in the Space Center which could become unreachable while trainersanity is active by overlapping +with another NPC was moved to an unoccupied space. +- Fixed a problem where the client would crash on certain operating systems while using certain python versions if the +player tried to wonder trade. + # 2.2.0 ### Features @@ -175,6 +193,7 @@ turn to face you when you run. species equally likely to appear, but makes rare encounters less rare. - Added `Trick House` location group. - Removed `Postgame Locations` location group. +- Added a Spanish translation of the setup guide. ### QoL