From b7437fa9fd45692aa78bcb4b5a4142432941e375 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Thu, 2 Jan 2025 03:41:42 -0600 Subject: [PATCH 01/14] Restoring some native dungeon item behavior for glitched modes Also fixes issues with HC BK (item collection in other dungeons, acting like a small key on BnC) --- Rom.py | 4 +++- data/base2current.bps | Bin 134014 -> 133996 bytes source/dungeon/EnemyList.py | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index 261db448..67369cfa 100644 --- a/Rom.py +++ b/Rom.py @@ -43,7 +43,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '1fde4fa24bc9d3efe450c3bc30e4cf2c' +RANDOMIZERBASEHASH = 'e2e7241bfb0085ab7ba12167565e814a' class JsonRom(object): @@ -439,6 +439,8 @@ def patch_rom(world, rom, player, team, is_mystery=False): location.pot.item = valid_pot_items[location.item.name] else: code = itemid + if world.pottery[player] == 'none' or location.locked: + code = handle_native_dungeon(location, itemid) standing_item_flag = 0x80 if location.item.player != player: standing_item_flag |= 0x40 diff --git a/data/base2current.bps b/data/base2current.bps index b8544acb14b79983294fa51d205def14fdd139ac..30a30355cec10c78c85615ae6f4c6f967a314bc2 100644 GIT binary patch delta 1865 zcmWkudr(t%7S74zCIkf$VF^fxk%UFXB1E)45Vx$NqENBz_`nBSTh_3kQWaSX_a>mZ z!I0?B@PlS)Fp-krreu4y*C0qnySmIcEACip*|p=NyJ&o+qmB|Dchhrb&iUqi-<+BA z{c$E+Bu`r;mdmi`Z&y|UI2kEF9)w@q+C3Mg3Uu%>(y_2K&z)3^{G;Sdx&*;#erXgj8H!L)|IHtXzl8-D!=D7;51*%9(029-gb{e1$rKS7eb96sl27t!WUrW|pa0M=ylv|u{=LkT9Ml=2c&zalv z9RO?5;YO$iWf) zNYBPsb&2M132A#&SeSNjfPpR$2{b*~LREJ5m zSx2j1)!{sJZ}eS=L5ZP!AkdD`Z(s*H6iSPy9=UTf+P>qoK0lYvy=e1uzRO5~&V>qL zGlH=-P=VHsb z@oKu!RbYONc9$5eU@j`IFSiw~cuW=GwegRinNi-j288Iqcv{AR`)J9((T?@>k6O>T zAShO@n`keLd+OOLNQWUo7~#EHWP4`VaY5m9G&rsh$3JyEIZ7v>;P|HagQhIP{nV6A zxE&@9(V`HM-z(e>v}Ga#c*rm@J9Tc>F#d4Cuu0F2aX5B;E3M}SY?}40I82xJ)E8E;aJ;zlsEaFSjKbXntB&9PAcILm{Mlz;Ew})+R zfa`oHz*~=jN+OtjwDIyZ^ge4i2HD|O;ci_WM_3EKMSuq4x5Ru*t0toS8(TX zK%!H9E~OhUH^F|W!d??7lPh`$Q-hMh4vTWo>y@7=^~)@TKgr7{r=P9ZgIW>Ld!`+SIo~q*dpdbteco!YPIJT~8(p>DJd&yY#15X5bS>Zm3 z*O_7Ato^;D+)r2}t&1NMJQpeQ)%B87JBhUxk*}eb6!?tzv>9^biDf41&%7aNr}2x= zi@&56;5C(BQZrlfrnFFTwvG6;8EW7oTxo$N{Qb*uw*}_K?Z|Dh$86VA1$Oy%eapca z&9MpkLiULTE(t+{yRDD|pRo6>5D*c$QFeRP2kcE7M9hznR=Q{EUk7?DfaES{}x!Km786r6fT9c zf68wA9&Upux6Fl;*+oCV5{Rx&?>+sHd&!N2^M-Q+ChtJ?lx53)vU1!H9OCdCmb(Mb d(#1vl&R=b2B0$d;@ z@HhNW7K4dMD2vd!>LpYPrR`E?nKkaP*0QBjAG_FyN?kjZ@VI;Z|C#@M|M|W-XU>^( zW||~RO%m$}oc#RjZ2+IsQeFfIvPQ3MKnlJQKBhbQUVuZWQFv1l_lv`Ou}!`M=|ws? zOZSTc0Y0Kf#VS6WMUSPEkV}vG|0j?K59z+>)ga#Jb$C5gC4DdED1Zy?j!X5w=yl+< zFynZGJdAFQ+YDevQ}MAO41)x}w!XZ_Y_2K4=B=h4B5A^_prkh@90HKhf00EBC5NxN zyx!)X6X?29v9gSD^@+VN4(TUi1fXq zK+?&5m-wJZlk_;(j?ODP0WQ+NU!B_Q7c?!i{P(U*#-OR+2cvV0Z{7{=h?!08StiiF zc7uJ==aCGrolP3HqrT_g791x{zJoX?rrIFKI%d(sBZH)$o!&IPb$ZwI2h*j~Rnxt1 zKRX+3;U1s9SBV9QRR*P9Fo=SpstrnHG$<`bOK~|S!mAA`ynfK2vcEToKIBLhN2)AF zO7-k4&O|@Xy$(?*!j};hXlY0#HDf}1TaT6)(-O04GI}h4zy+wpml$Ds(3PfX`G(cI zhgmiIq-B_OkD!&P&!^)?ZPAwne@5Hq(;x{|&c6l|=*xLomL#yow12`GmqEGpT}0b( zK<4As;@;km1+46 z6`{n1ELet)EhK8r+(B>t8x`bGKj?cF__Jd9j>R@z)J#t^zHOa+;}fcBnNq zrx|lZ!`JK(Th;wX^!LRN1M`zbWJ@n=XM0(uH3g+EZ4s{^x-da>p~@vW6wqBu)!~qa z*M`9>kj5Mcg8&}rnZwKB90=rY`)cM%1T^rLp9-&Vd;A^4jqN`N%l=tQCiT`~g0V$` zjVC&#sIZ?@Fxpr+rV*-@mEAn%QWhMIfz$YT{Fd=TVct2Pg77FNX`az6 zly%~#6sUy9->@SMj;jH4W_pENZk36nT)tNR6M?K;D`NNlk5wA=qN2#`|6NWdE7~ z^4-C7@tDFB+tEHodi=N$9zmCa3@F_3%#Dq;PzFVKsTQ=sUtLcZb4w^4wtK=}6pz={ z!9LLyru(4mCPsCDgpTVjIfGv@!^dz2pErXXO7T}_h~`Q1@RS*}+&8cu*6;!Xa9TZV zic|!&*WT~(6_Pq9{B>5`@~aq8hDqlc+)xh@VsgMm3Wn-l{nY}QNb-EvaY@X zGK{J4BRSZPU$?;e732UJKP<9Jnm7C+B3z`hCo!5>vlpi6Y~#vEDY7{bW#52oQ;2=3_(pHQG?FSIA#JUacGIlkYL|~yC=XV zJ-*LnPGA#Q`w=GlUvLYA+wxpk$tb^vjS!?)4_vs%{*RsJ?hWTQTznf!mSv>=WMkRy lS;S&F^XN7_Qj51AyZX0!CW5)c Date: Thu, 2 Jan 2025 05:17:26 -0600 Subject: [PATCH 02/14] Allow flute in rain state for glitched modes with starting preactivated flute --- InitialSram.py | 5 +++++ Rom.py | 2 +- data/base2current.bps | Bin 133996 -> 133996 bytes 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/InitialSram.py b/InitialSram.py index 001c9f9d..8d1685fc 100644 --- a/InitialSram.py +++ b/InitialSram.py @@ -114,6 +114,11 @@ class InitialSram: equip[0x37B] = 1 starting_magic = 0x80 + if world.mode[player] == 'standard' and world.logic[player] not in ['noglitches', 'minorglitches']: + if (startingstate.has('Ocarina', player) and world.flute_mode[player] == 'active') \ + or startingstate.has('Ocarina (Activated)', player): + self.pre_set_overworld_flag(0x18, 0x20) + if startingstate.has('Return Old Man', player): self._initial_sram_bytes[0x410] |= 0x01 diff --git a/Rom.py b/Rom.py index 67369cfa..c39c3f58 100644 --- a/Rom.py +++ b/Rom.py @@ -43,7 +43,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = 'e2e7241bfb0085ab7ba12167565e814a' +RANDOMIZERBASEHASH = '05a99da65a85d2093f1824a82a876fab' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index 30a30355cec10c78c85615ae6f4c6f967a314bc2..b871a6ba4cdf189e748d36b3ee87b0607da4afe8 100644 GIT binary patch delta 41 zcmV+^0M`HPmI&;Y2(Tan1lH3jEVCp7==TGd@_vI4`?nAK0aXwP5wJh`oO}uigb5Lj delta 41 zcmV+^0M`HPmI&;Y2(Tan1e(-kE3+g6==TE>g0O=R`?nAK0aXwPdS+n@;UqPgape%j From d479789274412c5932303f97812103b7bf749bde Mon Sep 17 00:00:00 2001 From: codemann8 Date: Thu, 2 Jan 2025 22:53:34 -0600 Subject: [PATCH 03/14] Fixed issue with OWG+ placing old man outside DM --- OverworldShuffle.py | 10 ++++++---- source/overworld/EntranceShuffle2.py | 6 +++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 8d61630d..235849aa 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -1479,7 +1479,7 @@ def build_sectors(world, player): return sectors -def build_accessible_region_list(world, start_region, player, build_copy_world=False, cross_world=False, region_rules=True, ignore_ledges = False): +def build_accessible_region_list(world, start_region, player, build_copy_world=False, cross_world=False, region_rules=True, ignore_ledges=False, restrictive_follower=False): from BaseClasses import CollectionState from Main import copy_world_premature from Items import ItemFactory @@ -1500,9 +1500,11 @@ def build_accessible_region_list(world, start_region, player, build_copy_world=F if flutespot.connected_region and flutespot.connected_region.name not in explored_regions: explore_region(flutespot.connected_region.name, flutespot.connected_region) elif exit.connected_region.name not in explored_regions \ - and (exit.connected_region.type == region.type - or exit.name in OWExitTypes['OWEdge'] or (cross_world and exit.name in (OWExitTypes['Portal'] + OWExitTypes['Mirror']))) \ - and (not region_rules or exit.access_rule(blank_state)) and (not ignore_ledges or exit.name not in (OWExitTypes['Ledge'] + OWExitTypes['OWG'])): + and (exit.connected_region.type == region.type or exit.name in OWExitTypes['OWEdge'] + or (cross_world and exit.name in (OWExitTypes['Portal'] + OWExitTypes['Mirror']))) \ + and (not region_rules or exit.access_rule(blank_state)) \ + and (not restrictive_follower or exit.spot_type != 'OWG') \ + and (not ignore_ledges or exit.name not in (OWExitTypes['Ledge'] + OWExitTypes['OWG'])): explore_region(exit.connected_region.name, exit.connected_region) if build_copy_world: diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index c65c4fb8..d693eae6 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -405,7 +405,7 @@ def do_old_man_cave_exit(entrances, exits, avail, cross_world): region_name = 'West Death Mountain (Top)' else: region_name = 'West Dark Death Mountain (Top)' - om_cave_options = list(get_accessible_entrances(region_name, avail, [], cross_world, True, True, True)) + om_cave_options = list(get_accessible_entrances(region_name, avail, [], cross_world, True, True, True, True)) om_cave_options = [e for e in om_cave_options if e in entrances and e != 'Old Man House (Bottom)'] if avail.swapped: om_cave_options = [e for e in om_cave_options if e not in Forbidden_Swap_Entrances] @@ -863,7 +863,7 @@ def get_nearby_entrances(avail, start_region): return candidates -def get_accessible_entrances(start_region, avail, assumed_inventory=[], cross_world=False, region_rules=True, exit_rules=True, include_one_ways=False): +def get_accessible_entrances(start_region, avail, assumed_inventory=[], cross_world=False, region_rules=True, exit_rules=True, include_one_ways=False, restrictive_follower=False): from Main import copy_world_premature from BaseClasses import CollectionState from Items import ItemFactory @@ -881,7 +881,7 @@ def get_accessible_entrances(start_region, avail, assumed_inventory=[], cross_wo for item in assumed_inventory: blank_state.collect(ItemFactory(item, avail.player), True) - explored_regions = list(build_accessible_region_list(base_world, start_region, avail.player, False, cross_world, region_rules, False)) + explored_regions = list(build_accessible_region_list(base_world, start_region, avail.player, False, cross_world, region_rules, False, restrictive_follower)) if include_one_ways: new_regions = list() From 9eba1d4c40518970021c23cccd856f7fdb551e60 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Thu, 2 Jan 2025 22:58:07 -0600 Subject: [PATCH 04/14] Fixed issue with prize shuffle where HMG uses cross dungeon paths to show prize on map --- BaseClasses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BaseClasses.py b/BaseClasses.py index 98f20269..d913721d 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -2820,7 +2820,7 @@ class Item(object): def explore_region(region): explored_regions.append(region.name) for ent in region.entrances: - if ent.parent_region is not None and ent.spot_type != 'OWG': + if ent.parent_region is not None and ent.spot_type not in ['OWG', 'HMG']: if ent.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld]: return ent elif ent.parent_region.name not in explored_regions: From 8ce8b93c8b46d959f7b746844bcf018bd590662c Mon Sep 17 00:00:00 2001 From: codemann8 Date: Fri, 3 Jan 2025 04:31:51 -0600 Subject: [PATCH 05/14] Fixed some inconsistencies with OWG starting boots considerations --- InitialSram.py | 3 +-- Main.py | 2 ++ OverworldShuffle.py | 15 ++++++++------- source/overworld/EntranceShuffle2.py | 2 ++ 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/InitialSram.py b/InitialSram.py index 8d1685fc..0aa25bc2 100644 --- a/InitialSram.py +++ b/InitialSram.py @@ -115,8 +115,7 @@ class InitialSram: starting_magic = 0x80 if world.mode[player] == 'standard' and world.logic[player] not in ['noglitches', 'minorglitches']: - if (startingstate.has('Ocarina', player) and world.flute_mode[player] == 'active') \ - or startingstate.has('Ocarina (Activated)', player): + if startingstate.has('Ocarina (Activated)', player): self.pre_set_overworld_flag(0x18, 0x20) if startingstate.has('Return Old Man', player): diff --git a/Main.py b/Main.py index 122ca5ba..1e64d179 100644 --- a/Main.py +++ b/Main.py @@ -619,6 +619,7 @@ def copy_world(world): ret.inaccessible_regions = world.inaccessible_regions.copy() ret.damage_table = world.damage_table ret.data_tables = world.data_tables # can be changed... + ret.settings = world.settings for player in range(1, world.players + 1): create_regions(ret, player) @@ -832,6 +833,7 @@ def copy_world_premature(world, player): ret.damage_table = world.damage_table ret.data_tables = world.data_tables # can be changed... ret.key_logic = world.key_logic.copy() + ret.settings = world.settings ret.is_copied_world = True diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 235849aa..d7fca96c 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -1384,11 +1384,10 @@ def can_reach_smith(world, player): region = world.get_region(region_name, player) for exit in region.exits: if not found and exit.connected_region is not None: - if exit.spot_type == 'Flute': - if any(map(lambda i: i.name == 'Ocarina (Activated)' and i.player == player, world.precollected_items)): - for flutespot in exit.connected_region.exits: - if flutespot.connected_region and flutespot.connected_region.name not in explored_regions: - explore_region(flutespot.connected_region.name, flutespot.connected_region) + if starting_flute and exit.spot_type == 'Flute': + for flutespot in exit.connected_region.exits: + if flutespot.connected_region and flutespot.connected_region.name not in explored_regions: + explore_region(flutespot.connected_region.name, flutespot.connected_region) elif exit.connected_region.name == 'Blacksmiths Hut' and exit.access_rule(blank_state): found = True return @@ -1410,6 +1409,7 @@ def can_reach_smith(world, player): found = False explored_regions = list() + starting_flute = any(map(lambda i: i.name == 'Ocarina (Activated)' and i.player == player, world.precollected_items)) if not world.is_bombshop_start(player): start_region = 'Links House' else: @@ -1494,7 +1494,7 @@ def build_accessible_region_list(world, start_region, player, build_copy_world=F region = base_world.get_region(region_name, player) for exit in region.exits: if exit.connected_region is not None: - if any(map(lambda i: i.name == 'Ocarina (Activated)' and i.player == player, base_world.precollected_items)) and exit.spot_type == 'Flute': + if starting_flute and exit.spot_type == 'Flute': fluteregion = exit.connected_region for flutespot in fluteregion.exits: if flutespot.connected_region and flutespot.connected_region.name not in explored_regions: @@ -1504,7 +1504,7 @@ def build_accessible_region_list(world, start_region, player, build_copy_world=F or (cross_world and exit.name in (OWExitTypes['Portal'] + OWExitTypes['Mirror']))) \ and (not region_rules or exit.access_rule(blank_state)) \ and (not restrictive_follower or exit.spot_type != 'OWG') \ - and (not ignore_ledges or exit.name not in (OWExitTypes['Ledge'] + OWExitTypes['OWG'])): + and (not ignore_ledges or not (exit.spot_type == 'OWG' or exit.name in OWExitTypes['Ledge'])): explore_region(exit.connected_region.name, exit.connected_region) if build_copy_world: @@ -1520,6 +1520,7 @@ def build_accessible_region_list(world, start_region, player, build_copy_world=F if base_world.mode[player] == 'standard': blank_state.collect(ItemFactory('Zelda Delivered', player), True) explored_regions = list() + starting_flute = any(map(lambda i: i.name == 'Ocarina (Activated)' and i.player == player, base_world.precollected_items)) explore_region(start_region) return explored_regions diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index d693eae6..e3e15854 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -878,6 +878,8 @@ def get_accessible_entrances(start_region, avail, assumed_inventory=[], cross_wo blank_state = CollectionState(base_world) if base_world.mode[avail.player] == 'standard': blank_state.collect(ItemFactory('Zelda Delivered', avail.player), True) + if base_world.logic[avail.player] in ['owglitches', 'hybridglitches', 'nologic']: + blank_state.collect(ItemFactory('Pegasus Boots', avail.player), True) for item in assumed_inventory: blank_state.collect(ItemFactory(item, avail.player), True) From 5fbcedbb244f15c2308f6b7ac9e0a9a34bc20c79 Mon Sep 17 00:00:00 2001 From: Kris Davie Date: Sun, 5 Jan 2025 09:30:32 +0100 Subject: [PATCH 06/14] Make sure we can actually reach clips --- UnderworldGlitchRules.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/UnderworldGlitchRules.py b/UnderworldGlitchRules.py index c1d046bb..1ffe30ed 100644 --- a/UnderworldGlitchRules.py +++ b/UnderworldGlitchRules.py @@ -173,20 +173,23 @@ def dungeon_reentry_rules( def underworld_glitches_rules(world, player): def mire_clip(state): torches = world.get_region("Mire Torches Top", player) - return state.can_dash_clip(torches, player) or ( - state.can_bomb_clip(torches, player) and state.has_fire_source(player) + return state.can_reach(torches, player) and ( + state.can_dash_clip(torches, player) + or (state.can_bomb_clip(torches, player) and state.has_fire_source(player)) ) def hera_clip(state): hera = world.get_region("Hera 4F", player) - return state.can_bomb_clip(hera, player) or state.can_dash_clip(hera, player) + return state.can_reach(hera) and ( + state.can_bomb_clip(hera, player) or state.can_dash_clip(hera, player) + ) # We use these plus functool.partial because lambdas don't work in loops properly. def bomb_clip(state, region, player): - return state.can_bomb_clip(region, player) + return state.can_reach(region, player) and state.can_bomb_clip(region, player) def dash_clip(state, region, player): - return state.can_dash_clip(region, player) + return state.can_reach(region, player) and state.can_dash_clip(region, player) # Bomb clips for clip in ( kikiskip_spots From 84fc0dc10ad67257d53eda146d32aa11f1c4bc0b Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 5 Jan 2025 03:25:07 -0600 Subject: [PATCH 07/14] Fixed issue with In-dungeon Prize shuffle placing multiple prizes within the same dungeon --- Fill.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Fill.py b/Fill.py index 6654c813..df7cadaf 100644 --- a/Fill.py +++ b/Fill.py @@ -36,6 +36,10 @@ def dungeon_tracking(world): for dungeon in world.dungeons: layout = world.dungeon_layouts[dungeon.player][dungeon.name] layout.dungeon_items = len([i for i in dungeon.all_items if i.is_inside_dungeon_item(world)]) + if world.prizeshuffle[dungeon.player] in ['dungeon', 'district'] and not dungeon.prize: + from Dungeons import dungeon_table + if dungeon_table[dungeon.name].prize: + layout.dungeon_items += 1 layout.free_items = layout.location_cnt - layout.dungeon_items From ee452578626cbb1afebee6df74799e8d67879254 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 5 Jan 2025 03:26:55 -0600 Subject: [PATCH 08/14] Minor issue with plandoing SW dropdowns in Follow Linked Drops --- source/overworld/EntranceShuffle2.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index e3e15854..64f1461b 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -1286,8 +1286,9 @@ def handle_skull_woods_drops(avail, pool, mode_cfg): keep_together = mode_cfg['keep_drops_together'] == 'on' if 'keep_drops_together' in mode_cfg else True if keep_together: for drop in ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)']: - target = drop_map[drop] - connect_entrance(drop, target, avail) + if drop in avail.entrances: + target = drop_map[drop] + connect_entrance(drop, target, avail) def handle_skull_woods_entrances(avail, pool): From ea5ad39a070702bbd736d434a6624b936bbbeaec Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 5 Jan 2025 03:42:52 -0600 Subject: [PATCH 09/14] Renaming keyword for Nearby dungeon item shuffle from 'district' to 'nearby' --- BaseClasses.py | 22 +++++++++++----------- Fill.py | 6 +++--- ItemList.py | 10 +++++----- Rom.py | 20 ++++++++++---------- mystery_example.yml | 10 +++++----- 5 files changed, 34 insertions(+), 34 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index d913721d..7f5c25e0 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1100,7 +1100,7 @@ class CollectionState(object): new_locations = True while new_locations: reachable_events = [location for location in locations if location.event and - (not key_only or (self.world.keyshuffle[location.item.player] in ['none', 'district'] and location.item.smallkey) or (self.world.bigkeyshuffle[location.item.player] in ['none', 'district'] and location.item.bigkey)) + (not key_only or (self.world.keyshuffle[location.item.player] in ['none', 'nearby'] and location.item.smallkey) or (self.world.bigkeyshuffle[location.item.player] in ['none', 'nearby'] and location.item.bigkey)) and location.can_reach(self)] reachable_events = self._do_not_flood_the_keys(reachable_events) new_locations = False @@ -2806,11 +2806,11 @@ class Item(object): or (self.map and world.mapshuffle[self.player] == 'none')) def is_near_dungeon_item(self, world): - return ((self.prize and world.prizeshuffle[self.player] == 'district') - or (self.smallkey and world.keyshuffle[self.player] == 'district') - or (self.bigkey and world.bigkeyshuffle[self.player] == 'district') - or (self.compass and world.compassshuffle[self.player] == 'district') - or (self.map and world.mapshuffle[self.player] == 'district')) + return ((self.prize and world.prizeshuffle[self.player] == 'nearby') + or (self.smallkey and world.keyshuffle[self.player] == 'nearby') + or (self.bigkey and world.bigkeyshuffle[self.player] == 'nearby') + or (self.compass and world.compassshuffle[self.player] == 'nearby') + or (self.map and world.mapshuffle[self.player] == 'nearby')) def get_map_location(self): if self.location: @@ -3644,10 +3644,10 @@ counter_mode = {"default": 0, "off": 1, "on": 2, "pickup": 3} access_mode = {"items": 0, "locations": 1, "none": 2} # byte 7: MMCC SSBB (maps, compass, small, big) -mapshuffle_mode = {'none': 0, 'off': 0, 'district': 2, 'wild': 3, 'on': 3} -compassshuffle_mode = {'none': 0, 'off': 0, 'district': 2, 'wild': 3, 'on': 3} -keyshuffle_mode = {'none': 0, 'off': 0, 'universal': 1, 'district': 2, 'wild': 3, 'on': 3} -bigkeyshuffle_mode = {'none': 0, 'off': 0, 'district': 2, 'wild': 3, 'on': 3} +mapshuffle_mode = {'none': 0, 'off': 0, 'nearby': 2, 'wild': 3, 'on': 3} +compassshuffle_mode = {'none': 0, 'off': 0, 'nearby': 2, 'wild': 3, 'on': 3} +keyshuffle_mode = {'none': 0, 'off': 0, 'universal': 1, 'nearby': 2, 'wild': 3, 'on': 3} +bigkeyshuffle_mode = {'none': 0, 'off': 0, 'nearby': 2, 'wild': 3, 'on': 3} # byte 8: HHHD DPEE (enemy_health, enemy_dmg, potshuffle, enemies) e_health = {"default": 0, "easy": 1, "normal": 2, "hard": 3, "expert": 4} @@ -3673,7 +3673,7 @@ flutespot_mode = {"vanilla": 0, "balanced": 1, "random": 2} flute_mode = {'normal': 0, 'active': 1} bow_mode = {'progressive': 0, 'silvers': 1, 'retro': 2, 'retro_silvers': 3} # reserved 8 modes? take_any_mode = {'none': 0, 'random': 1, 'fixed': 2} -prizeshuffle_mode = {'none': 0, 'dungeon': 1, 'district': 2, 'wild': 3} +prizeshuffle_mode = {'none': 0, 'dungeon': 1, 'nearby': 2, 'wild': 3} # additions # byte 14: POOT TKKK (pseudoboots, overworld_map, trap_door_mode, key_logic_algo) diff --git a/Fill.py b/Fill.py index df7cadaf..ce4feb87 100644 --- a/Fill.py +++ b/Fill.py @@ -36,7 +36,7 @@ def dungeon_tracking(world): for dungeon in world.dungeons: layout = world.dungeon_layouts[dungeon.player][dungeon.name] layout.dungeon_items = len([i for i in dungeon.all_items if i.is_inside_dungeon_item(world)]) - if world.prizeshuffle[dungeon.player] in ['dungeon', 'district'] and not dungeon.prize: + if world.prizeshuffle[dungeon.player] in ['dungeon', 'nearby'] and not dungeon.prize: from Dungeons import dungeon_table if dungeon_table[dungeon.name].prize: layout.dungeon_items += 1 @@ -51,7 +51,7 @@ def fill_dungeons_restrictive(world, shuffled_locations): or (item.smallkey and world.keyshuffle[item.player] != 'none') or (item.bigkey and world.bigkeyshuffle[item.player] != 'none')): item.advancement = True - elif (item.map and world.mapshuffle[item.player] not in ['none', 'district']) or (item.compass and world.compassshuffle[item.player] not in ['none', 'district']): + elif (item.map and world.mapshuffle[item.player] not in ['none', 'nearby']) or (item.compass and world.compassshuffle[item.player] not in ['none', 'nearby']): item.priority = True dungeon_items = [item for item in get_dungeon_item_pool(world) if item.is_inside_dungeon_item(world) or item.is_near_dungeon_item(world)] @@ -81,7 +81,7 @@ def fill_dungeons_restrictive(world, shuffled_locations): for attempt in range(15): try: for player in range(1, world.players + 1): - if world.prizeshuffle[player] == 'district': + if world.prizeshuffle[player] == 'nearby': dungeon_pool = [] for dungeon in world.dungeons: from Dungeons import dungeon_table diff --git a/ItemList.py b/ItemList.py index 5727c523..36f9b6c1 100644 --- a/ItemList.py +++ b/ItemList.py @@ -348,11 +348,11 @@ def generate_itempool(world, player): world.treasure_hunt_icon[player] = 'Triforce Piece' world.itempool.extend([item for item in get_dungeon_item_pool(world) if item.player == player - and ((item.prize and world.prizeshuffle[player] not in ['none', 'dungeon', 'district']) - or (item.smallkey and world.keyshuffle[player] not in ['none', 'district']) - or (item.bigkey and world.bigkeyshuffle[player] not in ['none', 'district']) - or (item.map and world.mapshuffle[player] not in ['none', 'district']) - or (item.compass and world.compassshuffle[player] not in ['none', 'district']))]) + and ((item.prize and world.prizeshuffle[player] not in ['none', 'dungeon', 'nearby']) + or (item.smallkey and world.keyshuffle[player] not in ['none', 'nearby']) + or (item.bigkey and world.bigkeyshuffle[player] not in ['none', 'nearby']) + or (item.map and world.mapshuffle[player] not in ['none', 'nearby']) + or (item.compass and world.compassshuffle[player] not in ['none', 'nearby']))]) if world.logic[player] == 'hybridglitches' and world.pottery[player] not in ['none', 'cave']: keys_to_remove = 2 diff --git a/Rom.py b/Rom.py index c39c3f58..53e889de 100644 --- a/Rom.py +++ b/Rom.py @@ -1278,28 +1278,28 @@ def patch_rom(world, rom, player, team, is_mystery=False): # m - enabled for inside maps # c - enabled for inside compasses # s - enabled for inside small keys - free_item_text = 0x40 if 'district' in [world.mapshuffle[player], world.compassshuffle[player], world.keyshuffle[player], world.bigkeyshuffle[player]] else 0x00 + free_item_text = 0x40 if 'nearby' in [world.mapshuffle[player], world.compassshuffle[player], world.keyshuffle[player], world.bigkeyshuffle[player]] else 0x00 rom.write_byte(0x18016A, free_item_text | 0x10 | ((0x20 if world.prizeshuffle[player] not in ['none', 'dungeon'] else 0x00) | (0x01 if world.keyshuffle[player] not in ['none', 'universal'] else 0x00) | (0x02 if world.compassshuffle[player] != 'none' else 0x00) | (0x04 if world.mapshuffle[player] != 'none' else 0x00) | (0x08 if world.bigkeyshuffle[player] != 'none' else 0x00))) # free roaming item text boxes - rom.write_byte(0x18003B, 0x01 if world.mapshuffle[player] not in ['none', 'district'] else 0x00) # maps showing crystals on overworld + rom.write_byte(0x18003B, 0x01 if world.mapshuffle[player] not in ['none', 'nearby'] else 0x00) # maps showing crystals on overworld # compasses showing dungeon count - compass_mode = 0x80 if world.compassshuffle[player] not in ['none', 'district'] else 0x00 + compass_mode = 0x80 if world.compassshuffle[player] not in ['none', 'nearby'] else 0x00 if world.clock_mode != 'none' or world.dungeon_counters[player] == 'off': pass elif world.dungeon_counters[player] == 'on': compass_mode |= 0x02 # always on - elif (world.compassshuffle[player] not in ['none', 'district'] or world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] != 'none' + elif (world.compassshuffle[player] not in ['none', 'nearby'] or world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] != 'none' or world.dungeon_counters[player] == 'pickup' or world.pottery[player] not in ['none', 'cave']): compass_mode |= 0x01 # show on pickup if world.overworld_map[player] == 'map': compass_mode |= 0x10 # show icon if map is collected elif world.overworld_map[player] == 'compass': compass_mode |= 0x20 # show icon if compass is collected - if world.prizeshuffle[player] not in ['none', 'dungeon', 'district']: + if world.prizeshuffle[player] not in ['none', 'dungeon', 'nearby']: compass_mode |= 0x40 # show icon if boss is defeated, hide if collected rom.write_byte(0x18003C, compass_mode) @@ -1335,7 +1335,7 @@ def patch_rom(world, rom, player, team, is_mystery=False): map_index = max(0, dungeon_index - 2) # write out dislocated coords - if map_index >= 0x02 and map_index < 0x18 and (world.overworld_map[player] != 'default' or world.prizeshuffle[player] not in ['none', 'dungeon', 'district']): + if map_index >= 0x02 and map_index < 0x18 and (world.overworld_map[player] != 'default' or world.prizeshuffle[player] not in ['none', 'dungeon', 'nearby']): owid_map = [0x1E, 0x30, 0xFF, 0x7B, 0x5E, 0x70, 0x40, 0x75, 0x03, 0x58, 0x47] x_map_position_generic = [0x03c0, 0x0740, 0xff00, 0x03c0, 0x01c0, 0x0bc0, 0x05c0, 0x09c0, 0x0ac0, 0x07c0, 0x0dc0] y_map_position_generic = [0xff00, 0xff00, 0xff00, 0x0fc0, 0x0fc0, 0x0fc0, 0x0fc0, 0x0fc0, 0xff00, 0x0fc0, 0x0fc0] @@ -1349,7 +1349,7 @@ def patch_rom(world, rom, player, team, is_mystery=False): write_int16(rom, snes_to_pc(0x0ABE2E)+(map_index*6)+6, y_map_position_generic[idx]) # write out icon coord data - if world.prizeshuffle[player] not in ['none', 'dungeon', 'district'] and dungeon_table[dungeon].prize: + if world.prizeshuffle[player] not in ['none', 'dungeon', 'nearby'] and dungeon_table[dungeon].prize: dungeon_obj = world.get_dungeon(dungeon, player) entrance = dungeon_obj.prize.get_map_location() coords = get_entrance_coords(entrance) @@ -1389,7 +1389,7 @@ def patch_rom(world, rom, player, team, is_mystery=False): # figure out compass entrances and what world (light/dark) write_int16s(rom, snes_to_pc(0x0ABE2E)+(map_index*6), coords) - if world.prizeshuffle[player] in ['none', 'dungeon', 'district'] and dungeon_table[dungeon].prize: + if world.prizeshuffle[player] in ['none', 'dungeon', 'nearby'] and dungeon_table[dungeon].prize: # prize location write_int16s(rom, snes_to_pc(0x0ABE2E)+(map_index*6)+8, coords) @@ -1428,7 +1428,7 @@ def patch_rom(world, rom, player, team, is_mystery=False): # b - Big Key # a - Small Key # - enable_menu_map_check = (world.overworld_map[player] != 'default' and world.shuffle[player] != 'vanilla') or world.prizeshuffle[player] not in ['none', 'dungeon', 'district'] + enable_menu_map_check = (world.overworld_map[player] != 'default' and world.shuffle[player] != 'vanilla') or world.prizeshuffle[player] not in ['none', 'dungeon', 'nearby'] rom.write_byte(0x180045, ((0x01 if world.keyshuffle[player] not in ['none', 'universal'] else 0x00) | (0x02 if world.bigkeyshuffle[player] != 'none' else 0x00) | (0x04 if world.mapshuffle[player] != 'none' or enable_menu_map_check else 0x00) @@ -2331,7 +2331,7 @@ def write_strings(rom, world, player, team): (crystal5, crystal6, greenpendant) = tuple([x.parent_region.dungeon.name for x in [crystal5, crystal6, greenpendant]]) tt['bomb_shop'] = 'Big Bomb?\nMy supply is blocked until you clear %s and %s.' % (crystal5, crystal6) tt['sahasrahla_bring_courage'] = 'I lost my family heirloom in %s' % greenpendant - elif world.prizeshuffle[player] == 'district': + elif world.prizeshuffle[player] == 'nearby': (crystal5, crystal6, greenpendant) = tuple([x.item.dungeon_object.name for x in [crystal5, crystal6, greenpendant]]) tt['bomb_shop'] = 'Big Bomb?\nThe crystals can be found near %s and %s.' % (crystal5, crystal6) tt['sahasrahla_bring_courage'] = 'I lost my family heirloom near %s' % greenpendant diff --git a/mystery_example.yml b/mystery_example.yml index 8fde1fa0..cdff6bb1 100644 --- a/mystery_example.yml +++ b/mystery_example.yml @@ -151,7 +151,7 @@ prize_shuffle: none: 1 dungeon: 1 - district: 1 + nearby: 1 wild: 1 dungeon_items: standard: 10 @@ -162,20 +162,20 @@ # for use when you aren't using the dungeon_items above # map_shuffle: # none: 1 - # district: 1 + # nearby: 1 # wild: 1 # compass_shuffle: # none: 1 - # district: 1 + # nearby: 1 # wild: 1 # smallkey_shuffle: # none: 5 - # district: 1 + # nearby: 1 # wild: 1 # universal: 1 # bigkey_shuffle: # none: 1 - # district: 1 + # nearby: 1 # wild: 1 dungeon_counters: on: 5 From c2a637e0b586be6c21cd121da56020efea036f75 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 5 Jan 2025 04:45:04 -0600 Subject: [PATCH 10/14] Added flipper and mire clip rules to hera clip, also some efficiency fixes --- UnderworldGlitchRules.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/UnderworldGlitchRules.py b/UnderworldGlitchRules.py index 1ffe30ed..24a92dd0 100644 --- a/UnderworldGlitchRules.py +++ b/UnderworldGlitchRules.py @@ -173,23 +173,21 @@ def dungeon_reentry_rules( def underworld_glitches_rules(world, player): def mire_clip(state): torches = world.get_region("Mire Torches Top", player) - return state.can_reach(torches, player) and ( - state.can_dash_clip(torches, player) + return (state.can_dash_clip(torches, player) or (state.can_bomb_clip(torches, player) and state.has_fire_source(player)) - ) + ) and state.can_reach(torches, player) def hera_clip(state): hera = world.get_region("Hera 4F", player) - return state.can_reach(hera) and ( - state.can_bomb_clip(hera, player) or state.can_dash_clip(hera, player) - ) + return (state.can_bomb_clip(hera, player) or state.can_dash_clip(hera, player)) \ + and state.has("Flippers", player) and state.can_reach(hera) and mire_clip(state) # We use these plus functool.partial because lambdas don't work in loops properly. def bomb_clip(state, region, player): - return state.can_reach(region, player) and state.can_bomb_clip(region, player) + return state.can_bomb_clip(region, player) and state.can_reach(region, player) def dash_clip(state, region, player): - return state.can_reach(region, player) and state.can_dash_clip(region, player) + return state.can_dash_clip(region, player) and state.can_reach(region, player) # Bomb clips for clip in ( kikiskip_spots @@ -236,18 +234,24 @@ def underworld_glitches_rules(world, player): # Allow mire big key to be used in Hera Rules.add_rule( world.get_entrance("Hera Startile Corner NW", player), - lambda state: mire_clip(state) and state.has("Big Key (Misery Mire)", player), + lambda state: state.has("Big Key (Misery Mire)", player) and mire_clip(state), combine="or", ) Rules.add_rule( world.get_location("Tower of Hera - Big Chest", player), - lambda state: mire_clip(state) and state.has("Big Key (Misery Mire)", player), + lambda state: state.has("Big Key (Misery Mire)", player) and mire_clip(state), combine="or", ) # This uses the mire clip because it's always expected to come from mire Rules.set_rule( world.get_entrance("Hera to Swamp Clip", player), - lambda state: mire_clip(state) and state.has("Flippers", player), + lambda state: state.has("Flippers", player) and mire_clip(state), + ) + Rules.add_rule( + world.get_location("Swamp Palace - Big Chest", player), + lambda state: (state.has("Big Key (Misery Mire)", player) or state.has("Big Key (Tower of Hera)", player)) \ + and state.has("Flippers", player) and mire_clip(state), + combine="or", ) # We need to set _all_ swamp doors to be openable with mire keys, otherwise the small key can't be behind them - 6 keys because of Pots # Flippers required for all of these doors to prevent locks when flooding @@ -266,9 +270,9 @@ def underworld_glitches_rules(world, player): ]: Rules.add_rule( world.get_entrance(door, player), - lambda state: mire_clip(state) + lambda state: state.has("Flippers", player) and state.has("Small Key (Misery Mire)", player, count=6) - and state.has("Flippers", player), + and mire_clip(state), combine="or", ) @@ -314,8 +318,8 @@ def underworld_glitches_rules(world, player): return ( state.can_reach("Old Man S&Q", "Entrance", player) and state.has("Flippers", player) - and mire_clip(state) and (hera_rule(state) or gt_rule(state)) + and mire_clip(state) ) Rules.add_rule( From 107eef9c6ab749026d1b00eb5a48bf4edd104789 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 5 Jan 2025 05:34:21 -0600 Subject: [PATCH 11/14] Fixed issue with MapCountDisplay flags not getting set when entering a dungeon and maps aren't wild --- Rom.py | 2 +- data/base2current.bps | Bin 133996 -> 133998 bytes 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index 53e889de..1260f616 100644 --- a/Rom.py +++ b/Rom.py @@ -43,7 +43,7 @@ from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '05a99da65a85d2093f1824a82a876fab' +RANDOMIZERBASEHASH = 'a84f59e5e76492f6a5693835823e6292' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index b871a6ba4cdf189e748d36b3ee87b0607da4afe8..8dc5625936213e64070a82f19f3e60a373e1bb10 100644 GIT binary patch delta 1627 zcmWlY4^UHg634Uo`yfI>LM`EsgolA*!D7&Am7x_-K%-#oA6x&lN)^UeP)_eeYSZ^3 zK^`_n_?0|uN>d*GN#MNLc{ooxd@Yk@t_2S>b&DDb&B;Fq?xO7Ra7&fCe*<~!{cQ$aW17C>x(}~%jSR7yjKDr5wB<+WPpsM5`xY0`~`O0GfmMPF$ zF16xX&YAKtfPq__`i&fd9F{Rl0)r?ss}CqNoTUR9_b4kYfko(HPJXoSHv45kHEF~5 zpy%g0;68VCE(@?4?akGTMFGy4TO$#KM9tT!=6Ocg!%9>0P5tr_A`l1^Q>{35hYehz zY#-S0%nv_dQr|{4pHYAOJY~zLZTKv-w&-0@qsv9lKqrD?Go)~2aru1N%Xe9P4|k>h zmKyT8GxP(g*5GG*F!_J1!9xR$n@Q4(;pe%*Lm$S%EN*?r=NM32!U>63s7`jb%n(*P z*;@{9jGH`@Aci@d`fQa^VD*7ZLWY)&K7bWm!Mz4)l&h}xCKcUfCegtCwTaTitKW1F zi27NY^ogShTr$E&6GN{bM1MuuJj^E3H0^O)LuJ!E#hpfKZYs5g`iqi8skpc@@Nr~i0Enw28DR~I>{ z*ltVP#tKqFmLtu?N?3axOqMlj)lIF+Wh+lNJYN zwV!Nhp$O;Gh@HfB=kJ_etDpbd>DAsNgJz#nuk!2dzungBd30m4RV+Qh?R=nw&8w1{ zDV#C!VV{^(MR)I8;$P;k@Ne`t_z(EKFNVyCcEL>%UIQb|JU|&-(tf1UHB$y;p$vA5 zy>>q%if^Vem<9b*hHG6vsuW;`05j|sIwNFeO3}X`J_kB9b80CV(YmQZup;NwX5r<+ zQ)RFll{_kdd8p~px-u-b#k%3ku9RZBkMM{(24hO^Wh=Z7Mo-%~aS@$L)|AlBEjO&y zgiD8$^z#9BU=~hN4M;+E#r={>zFk6F-KF*kRD&|7%U}koo6gL6cNi7^jH*`AKM|*< zC1VPFWw^tn3!Yjp$pW85h?t#5vLp9+(wM|F6MZqQS0o16yG?X53Qi|MGQz@H>bfm= z=(#{RclUnr3=p0Mv@{$KC(tY59KcXxIDK}WafsP8Z^*Vk@+iV2tvW*Qj|@2TR~=yl z(E6{)K;%HrTj*lAGInd8oIL7{xFX&N?|c^JKQ2yn6!NccgwGYQk9l${Y=;!yvK2UheE!)guwa6az8x?J=J3DnfR6a6C{wGI zj9Y8j!nZWSeR147c$4*<8m+O|?CBx1bDxs89e}SSs+m~J!K0MgewhB}S+^^XsXPcC zp=ZqdPzl0}IvbpU82Qe}N6+a!>}RBwZ)Ec9V1?OypB)}b;XKcM08cE4($uta;Ek=?3z)W|4Vx$ ZaxH=)49<^UhdoAY>Q2RyH$;oh{SWrf#_#|D delta 1645 zcmWNN4^R_#7RPz{Pa=Opq(@L>SsG{*C`MWl8CnDd+9-J1vleX;rE-mmJ*-j=<8C0D z1!Kft&1%y$y2u&um~gY&l~xYUyQ||>@8W6Ia-#^G1|sE9NKpj$GH>S1`_AXhoA39H z*=3`4+0lQ%$IU*KN&r~C|^qcUA< z#v@oq+}8jJY*zer2@JEcqy<8_gp##gupbR*)d1`Z?W_a1R=q>%&sv(2l@6RWjanVD@e~Yw! zVa1cbJVnRvKvu5~YyBH(&8Dn)ELxYhALgJNd9Q;LLB0{xERkQFAzu5K!K>L@H4h>o zlf6Ja6R8q>%sH84*gSfqr(p|0cw~4l+k528aEN6~otI>=pN;v0P!Qan?{1tEjCS^X zF~BKy>OzbFlGw4+JT`P0Sp?^(*OwywC9&xxQD*^ zMGp&*aH>F)+e7Ld>%0F%D6d9(WqAL88A2Lk->0*m&NO(6Cye9s`y8pLYAP`*CF~}t zI%~4a%(kfVQ)>#+1koohTNdW~KMrto1+kAgN?)IZfMsB2|^_e=p> zR?f4KIIZVqy#gXa-d?xVm+RB}Hv0DZ>V2MHPa0!w!8UF1XlPOLAyVV2yM&}_3#maS zQe!jOcGc50DhruJr*@M`u99w48iYwfm}E0iNs~tUZFK+nGEkzJ>0DTdKA2tsByvt~ zfsKftE`lAX=tT}_k>$nvMIqsh=1t$XMCVgohPDuAZ&=}2iq!M$=T0kIyMl@*Dhen^ z`8{)`!KKCtYPp~3S%4E{EfNwf%KP!ewgSrRF0=(u1=7wGfe2O4B&U8hfU^FBK3Yxv zWayX?j!W@(XPx@!(T-A~7QDjXMDNxS&h*xK<3fEDx-x@FUm0Z{AEaW?*vvebi{!If z&2P&eQfXjt>^cAU*TK-X*Q%4WjarvT+8Aqw#EyxHAF(zDy+4}@A?V<2;;V~vee`b@ z_gU-t7d)+6eS)gzdmI_7PtZZo{8zq*KXh&rx<0!#eDe|sani%Pcn{Ax(ooLJJgGXQ zjjlQ1MwXX2>|@Wp9F2lu`h*hhiC26)POqVBqaYu$=$%U z0;^CI+vUpOp1cChf@p|t=<+I<-ead8L}LGLA{KiN(=v`$!*PKGG!3p04X2wApQefz zYYv{1bM7M0%istXSPR7h*v+k64^g4w$tdo!k9QJzt~MF4zwKmh$18G zN_BV3GCJ-SP*Qodn~ZmTNO#N?EA-g{tl0LdLj3}yRc6uQ+B@f{Hr>7=>Gv@o6utb From d8899f3e6fad8e4e64c12a375eff044b482d2c2a Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 5 Jan 2025 05:41:04 -0600 Subject: [PATCH 12/14] Fixed issue with Free Terrain not requiring Pearl on DW water transitions --- Rules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rules.py b/Rules.py index 4020ce69..d5d14cfb 100644 --- a/Rules.py +++ b/Rules.py @@ -1306,7 +1306,7 @@ def ow_terrain_rules(world, player): ent = world.get_entrance(edge.name, player) if edge.terrain == Terrain.Land: set_rule(ent, lambda state: state.has('Flippers', player)) - if ent.parent_region.is_light_world == (world.mode[player] != 'inverted') and ent.connected_region.is_dark_world == (world.mode[player] != 'inverted'): + if ent.connected_region.is_dark_world == (world.mode[player] != 'inverted'): add_rule(ent, lambda state: state.has_Pearl(player)) for whirlpool_name in OWExitTypes['Whirlpool']: From 41080e73bdf725c7bc2fa7ee5fc22cf5db24200e Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 5 Jan 2025 07:32:04 -0600 Subject: [PATCH 13/14] Fixed issue with 2 extra SP keys in pool for HMG --- Fill.py | 36 ++++++++++++++++++++++++++++++++++-- ItemList.py | 16 ++-------------- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/Fill.py b/Fill.py index ce4feb87..068b1c26 100644 --- a/Fill.py +++ b/Fill.py @@ -44,7 +44,6 @@ def dungeon_tracking(world): def fill_dungeons_restrictive(world, shuffled_locations): - # with shuffled dungeon items they are distributed as part of the normal item pool for item in world.get_items(): if ((item.prize and world.prizeshuffle[item.player] != 'none') @@ -60,13 +59,46 @@ def fill_dungeons_restrictive(world, shuffled_locations): (bigs if i.bigkey else smalls if i.smallkey else prizes if i.prize else others).append(i) unplaced_smalls = list(smalls) for i in world.itempool: - if i.smallkey and world.keyshuffle[i.player] != 'none': + if i.smallkey and world.keyshuffle[i.player] not in ['none', 'nearby']: unplaced_smalls.append(i) def fill(base_state, items, locations, key_pool=None): fill_restrictive(world, base_state, locations, items, key_pool, True) all_state_base = world.get_all_state() + for player in range(1, world.players + 1): + if world.logic[player] == 'hybridglitches' and world.keyshuffle[i.player] in ['none', 'nearby'] \ + and world.pottery[player] not in ['none', 'cave']: + # remove 2 keys from main pool + count_to_remove = 2 + to_remove = [] + for wix, wi in enumerate(smalls): + if wi.name == 'Small Key (Swamp Palace)' and wi.player == player: + to_remove.append(wix) + if count_to_remove == len(to_remove): + break + for wix in reversed(to_remove): + del smalls[wix] + + # remove 2 swamp locations from pool + hybrid_locations = [] + to_remove = [] + for i, loc in enumerate(shuffled_locations): + if loc.name in ['Swamp Palace - Trench 1 Pot Key', 'Swamp Palace - Pot Row Pot Key'] and loc.player == player: + to_remove.append(i) + hybrid_locations.append(loc) + if count_to_remove == len(to_remove): + break + for i in reversed(to_remove): + shuffled_locations.pop(i) + + # place 2 HMG keys + hybrid_state_base = all_state_base.copy() + for x in bigs + smalls + prizes + others: + hybrid_state_base.collect(x, True) + hybrid_smalls = [ItemFactory('Small Key (Swamp Palace)', player)] * 2 + fill(hybrid_state_base, hybrid_smalls, hybrid_locations, unplaced_smalls) + big_state_base = all_state_base.copy() for x in smalls + prizes + others: big_state_base.collect(x, True) diff --git a/ItemList.py b/ItemList.py index 36f9b6c1..bed44e64 100644 --- a/ItemList.py +++ b/ItemList.py @@ -272,7 +272,8 @@ def generate_itempool(world, player): for _ in range(0, amt): pool.append('Rupees (20)') - if world.logic[player] == 'hybridglitches' and world.pottery[player] not in ['none', 'cave']: + if world.logic[player] == 'hybridglitches' and world.pottery[player] not in ['none', 'cave'] \ + and world.keyshuffle[player] not in ['none', 'nearby']: # In HMG force swamp smalls in pots to allow getting out of swamp palace placed_items['Swamp Palace - Trench 1 Pot Key'] = 'Small Key (Swamp Palace)' placed_items['Swamp Palace - Pot Row Pot Key'] = 'Small Key (Swamp Palace)' @@ -365,19 +366,6 @@ def generate_itempool(world, player): for wix in reversed(to_remove): del world.itempool[wix] - if world.logic[player] == 'hybridglitches' and world.pottery[player] not in ['none', 'cave']: - # In HMG force swamp smalls in pots to allow getting out of swamp palace - loc = world.get_location('Swamp Palace - Trench 1 Pot Key', player) - world.push_item(loc, ItemFactory('Small Key (Swamp Palace)', player), False) - loc.event = True - loc.locked = True - loc = world.get_location('Swamp Palace - Pot Row Pot Key', player) - world.push_item(loc, ItemFactory('Small Key (Swamp Palace)', player), False) - loc.event = True - loc.locked = True - world.itempool.remove(ItemFactory('Small Key (Swamp Palace)', player)) - world.itempool.remove(ItemFactory('Small Key (Swamp Palace)', player)) - # logic has some branches where having 4 hearts is one possible requirement (of several alternatives) # rather than making all hearts/heart pieces progression items (which slows down generation considerably) # We mark one random heart container as an advancement item (or 4 heart pieces in expert mode) From 3d9b2a5271f1bcfad882f80ca4694853266be2d5 Mon Sep 17 00:00:00 2001 From: codemann8 Date: Sun, 5 Jan 2025 07:48:04 -0600 Subject: [PATCH 14/14] Version bump 0.5.1.2 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f913b845..071aeafd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.5.1.2 +- Many fixes to HMG logic, generation, key collection issues +- Fixed issue with In-Dungeon Prizes getting placed in the same dungeon +- Fixed issue with Old Man getting placed outside of DM in glitched modes +- Fixed issue with Free Terrain water transitions not checking for Pearl requirement + ## 0.5.1.0 (+ Hotfix 0.5.1.1) - New "Nearby" Dungeon Item Shuffle option - Fixed issue with smith follower getting deleted incorrectly on s+q