From d23d2d8323a82141b9a282b7b5de74037c21adc5 Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 24 Feb 2023 15:45:37 -0700 Subject: [PATCH 1/7] Better support for customized start_inverntory with dungeon items (can be mixed with item pool) --- ItemList.py | 17 ++++++++++++++--- KeyDoorShuffle.py | 6 ++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/ItemList.py b/ItemList.py index b708b284..53524e6c 100644 --- a/ItemList.py +++ b/ItemList.py @@ -1014,8 +1014,19 @@ item_alternates = { def modify_pool_for_start_inventory(start_inventory, world, player): - # skips custom item pools - these shouldn't be adjusted if (world.customizer and world.customizer.get_item_pool()) or world.custom: + # custom item pools only adjust in dungeon items + for item in start_inventory: + if item.dungeon: + d = world.get_dungeon(item.dungeon, item.player) + match = next((i for i in d.all_items if i.name == item.name), None) + if match: + if match.map or match.compass: + d.dungeon_items.remove(match) + elif match.smallkey: + d.small_keys.remove(match) + elif match.bigkey and d.big_key == match: + d.big_key = None return for item in start_inventory: if item.player == player: @@ -1041,8 +1052,8 @@ def modify_pool_for_start_inventory(start_inventory, world, player): d.dungeon_items.remove(match) elif match.smallkey: d.small_keys.remove(match) - elif match.bigkey: - d.big_key.remove(match) + elif match.bigkey and d.big_key == match: + d.big_key = None def make_custom_item_pool(world, player, progressive, shuffle, difficulty, timer, goal, mode, swords, bombbag, customitemarray): diff --git a/KeyDoorShuffle.py b/KeyDoorShuffle.py index 2a39f38c..f8bda68e 100644 --- a/KeyDoorShuffle.py +++ b/KeyDoorShuffle.py @@ -2094,6 +2094,12 @@ def validate_key_placement(key_layout, world, player): if world.bigkeyshuffle[player]: max_counter = find_max_counter(key_layout) big_key_outside = bigkey_name not in (l.item.name for l in max_counter.free_locations if l.item) + for i in world.precollected_items: + if i.player == player and i.name == bigkey_name: + big_key_outside = True + break + if i.player == player and i.name == smallkey_name: + keys_outside += 1 for code, counter in key_layout.key_counters.items(): if len(counter.child_doors) == 0: From 7397d779ad8c87cb184b9c2cdc6af932705422b1 Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 24 Feb 2023 16:20:29 -0700 Subject: [PATCH 2/7] Changing colorizing pots defaults --- BaseClasses.py | 2 +- CLI.py | 2 +- Rom.py | 5 ++--- source/tools/MysteryUtils.py | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index dd39cfc9..0dc89695 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -142,7 +142,7 @@ class World(object): set_player_attr('pot_contents', None) set_player_attr('pseudoboots', False) set_player_attr('collection_rate', False) - set_player_attr('colorizepots', False) + set_player_attr('colorizepots', True) set_player_attr('pot_pool', {}) set_player_attr('decoupledoors', False) set_player_attr('door_type_mode', 'original') diff --git a/CLI.py b/CLI.py index 536543f2..d1bc47d3 100644 --- a/CLI.py +++ b/CLI.py @@ -205,7 +205,7 @@ def parse_settings(): 'keydropshuffle': False, 'dropshuffle': False, 'pottery': 'none', - 'colorizepots': False, + 'colorizepots': True, 'shufflepots': False, 'mapshuffle': False, 'compassshuffle': False, diff --git a/Rom.py b/Rom.py index 1dd05a8f..6be923da 100644 --- a/Rom.py +++ b/Rom.py @@ -1585,9 +1585,8 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): Room0127.write_to_rom(snes_to_pc(0x2B8000), rom) if world.pot_contents[player]: - colorize_pots = is_mystery or (world.pottery[player] not in ['vanilla', 'lottery'] - and (world.colorizepots[player] - or world.pottery[player] in ['reduced', 'clustered'])) + colorize_pots = (world.pottery[player] != 'vanilla' + and (world.colorizepots[player] or world.pottery[player] in ['reduced', 'clustered'])) if world.pot_contents[player].size() > 0x2800: raise Exception('Pot table is too big for current area') world.pot_contents[player].write_pot_data_to_rom(rom, colorize_pots) diff --git a/source/tools/MysteryUtils.py b/source/tools/MysteryUtils.py index 322ed447..2bdcdcd7 100644 --- a/source/tools/MysteryUtils.py +++ b/source/tools/MysteryUtils.py @@ -102,7 +102,7 @@ def roll_settings(weights): ret.dropshuffle = get_choice('dropshuffle') == 'on' or keydropshuffle ret.pottery = get_choice('pottery') if 'pottery' in weights else 'none' ret.pottery = 'keys' if ret.pottery == 'none' and keydropshuffle else ret.pottery - ret.colorizepots = get_choice('colorizepots') == 'on' + ret.colorizepots = get_choice_default('colorizepots', default='on') == 'on' ret.shufflepots = get_choice('pot_shuffle') == 'on' ret.mixed_travel = get_choice('mixed_travel') if 'mixed_travel' in weights else 'prevent' ret.standardize_palettes = (get_choice('standardize_palettes') if 'standardize_palettes' in weights From 6c0da515b440827c40050d9d916b1d51d899eb16 Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 24 Feb 2023 17:00:25 -0700 Subject: [PATCH 3/7] Fix dungeon_only + pottery modes --- source/item/FillUtil.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/source/item/FillUtil.py b/source/item/FillUtil.py index 0252673f..f581f3fb 100644 --- a/source/item/FillUtil.py +++ b/source/item/FillUtil.py @@ -3,7 +3,7 @@ import logging from collections import defaultdict from source.item.District import resolve_districts -from BaseClasses import PotItem, PotFlags +from BaseClasses import PotItem, PotFlags, LocationType from DoorShuffle import validate_vanilla_reservation from Dungeons import dungeon_table from Items import item_table, ItemFactory @@ -82,8 +82,8 @@ def create_item_pool_config(world): if pot.item not in [PotItem.Key, PotItem.Hole, PotItem.Switch]: item = pot_items[pot.item] descriptor = 'Large Block' if pot.flags & PotFlags.Block else f'Pot #{pot_index+1}' - location = f'{pot.room} {descriptor}' - config.static_placement[player][item].append(location) + loc = f'{pot.room} {descriptor}' + config.static_placement[player][item].append(loc) if world.shopsanity[player]: for item, locs in shop_vanilla_mapping.items(): config.static_placement[player][item].extend(locs) @@ -164,6 +164,10 @@ def create_item_pool_config(world): mode_grouping['Heart Containers'] + mode_grouping['GT Trash'] + mode_grouping['Small Keys'] + mode_grouping['Compasses'] + mode_grouping['Maps'] + mode_grouping['Key Drops'] + mode_grouping['Pot Keys'] + mode_grouping['Big Key Drops']) + dungeon_set = set(dungeon_set) + for loc in world.get_locations(): + if loc.parent_region.dungeon and loc.type in [LocationType.Pot, LocationType.Drop]: + dungeon_set.add(loc.name) for player in range(1, world.players + 1): config.item_pool[player] = determine_major_items(world, player) config.location_groups[0].locations = set(dungeon_set) From 3047826808deb0d5feebefb34945d8d56c4d52d8 Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 27 Feb 2023 14:41:43 -0700 Subject: [PATCH 4/7] Fix AllowAccidentalGlitches flag --- Rom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rom.py b/Rom.py index 6be923da..5b137612 100644 --- a/Rom.py +++ b/Rom.py @@ -1489,7 +1489,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(0x1800A3, 0x01) # enable correct world setting behaviour after agahnim kills rom.write_byte(0x1800A4, 0x01 if world.logic[player] != 'nologic' else 0x00) # enable POD EG fix rom.write_byte(0x180042, 0x01 if world.save_and_quit_from_boss else 0x00) # Allow Save and Quit after boss kill - rom.write_byte(0x180358, 0x01 if world.logic[player] == 'nologic' else 0x00) + rom.write_byte(0x180358, 0x01 if (world.logic[player] in ['owglitches', 'nologic']) else 0x00) # remove shield from uncle rom.write_bytes(0x6D253, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E]) From 1dc3b13449fa3fbcee462e70a7e97d7aaa231f90 Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 27 Feb 2023 14:42:52 -0700 Subject: [PATCH 5/7] Change info message to debug for cleaner output Possible generation error --- DungeonGenerator.py | 2 +- Rules.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/DungeonGenerator.py b/DungeonGenerator.py index e429719a..f97f8ecf 100644 --- a/DungeonGenerator.py +++ b/DungeonGenerator.py @@ -3572,7 +3572,7 @@ def check_for_valid_layout(builder, sector_list, builder_info): builder.exception_list = list(sector_list) return True, {}, package except (GenerationException, NeutralizingException, OtherGenException) as e: - logging.getLogger('').info(f'Bailing on this layout for {builder.name}', exc_info=1) + logging.getLogger('').debug(f'Bailing on this layout for {builder.name}', exc_info=1) builder.split_dungeon_map = None builder.valid_proposal = None if temp_builder.name == 'Hyrule Castle' and temp_builder.throne_door: diff --git a/Rules.py b/Rules.py index ed6367dd..5015c019 100644 --- a/Rules.py +++ b/Rules.py @@ -2110,6 +2110,8 @@ def eval_small_key_door_main(state, door_name, dungeon, player): if state.is_door_open(door_name, player): return True key_logic = state.world.key_logic[player][dungeon] + if door_name not in key_logic.door_rules: + return False door_rule = key_logic.door_rules[door_name] door_openable = False for ruleType, number in door_rule.new_rules.items(): From a75b36e2eb7d1e4388f3e2eaa994c71e9a27ce7d Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 1 Mar 2023 16:23:03 -0700 Subject: [PATCH 6/7] ER2 Minor issues --- source/overworld/EntranceShuffle2.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index a89e6085..51a702dd 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -1630,7 +1630,7 @@ default_dw = { 'Palace of Darkness Exit', 'Swamp Palace Exit', 'Turtle Rock Exit (Front)', 'Turtle Rock Ledge Exit (West)', 'Turtle Rock Ledge Exit (East)', 'Turtle Rock Isolated Ledge Exit', 'Bumper Cave Exit (Top)', 'Bumper Cave Exit (Bottom)', 'Superbunny Cave Exit (Top)', 'Superbunny Cave Exit (Bottom)', - 'Hookshot Cave Front Exit', 'Hookshot Cave Back Exit', 'Ganons Tower Exit', 'Pyramid Exit', + 'Hookshot Cave Front Exit', 'Hookshot Cave Back Exit', 'Ganons Tower Exit', 'Pyramid Exit', 'Bonk Fairy (Dark)', 'Dark Lake Hylia Healer Fairy', 'Dark Lake Hylia Ledge Healer Fairy', 'Dark Desert Healer Fairy', 'Dark Death Mountain Healer Fairy', 'Cave Shop (Dark Death Mountain)', 'Pyramid Fairy', 'East Dark World Hint', 'Palace of Darkness Hint', 'Big Bomb Shop', 'Village of Outcasts Shop', 'Dark Lake Hylia Shop', @@ -1653,7 +1653,7 @@ default_lw = { 'Spectacle Rock Cave Exit (Top)', 'Spectacle Rock Cave Exit (Peak)', 'Paradox Cave Exit (Bottom)', 'Paradox Cave Exit (Middle)', 'Paradox Cave Exit (Top)', 'Fairy Ascension Cave Exit (Bottom)', 'Fairy Ascension Cave Exit (Top)', 'Spiral Cave Exit', 'Spiral Cave Exit (Top)', 'Waterfall of Wishing', 'Dam', - 'Blinds Hideout', 'Lumberjack House', 'Bonk Fairy (Light)', 'Bonk Fairy (Dark)', 'Lake Hylia Healer Fairy', + 'Blinds Hideout', 'Lumberjack House', 'Bonk Fairy (Light)', 'Lake Hylia Healer Fairy', 'Swamp Healer Fairy', 'Desert Healer Fairy', 'Fortune Teller (Light)', 'Lake Hylia Fortune Teller', 'Kings Grave', 'Tavern', 'Chicken House', 'Aginahs Cave', 'Sahasrahlas Hut', 'Cave Shop (Lake Hylia)', 'Capacity Upgrade', 'Blacksmiths Hut', 'Sick Kids House', 'Lost Woods Gamble', 'Snitch Lady (East)', 'Snitch Lady (West)', 'Bush Covered House', @@ -1698,7 +1698,7 @@ DW_Entrances = ['Bumper Cave (Bottom)', 'Superbunny Cave (Top)', 'Superbunny Ca 'Dark Lake Hylia Ledge Hint', 'Chest Game', 'Dark Desert Fairy', 'Dark Lake Hylia Ledge Fairy', 'Fortune Teller (Dark)', 'Dark World Hammer Peg Cave', 'Pyramid Entrance', 'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', - 'Skull Woods Second Section Door (West)', + 'Skull Woods Second Section Door (West)', 'Ganons Tower', 'Inverted Dark Sanctuary', 'Inverted Links House', 'Inverted Agahnims Tower'] LW_Must_Exit = ['Desert Palace Entrance (East)'] From 00b06c53d64eb9832507148008e31f061b3c82e0 Mon Sep 17 00:00:00 2001 From: aerinon Date: Wed, 1 Mar 2023 16:28:47 -0700 Subject: [PATCH 7/7] Rom updates and version update --- Main.py | 2 +- RELEASENOTES.md | 8 ++++++++ Rom.py | 2 +- data/base2current.bps | Bin 93908 -> 93872 bytes 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Main.py b/Main.py index 7266628d..52073cce 100644 --- a/Main.py +++ b/Main.py @@ -34,7 +34,7 @@ from source.overworld.EntranceShuffle2 import link_entrances_new from source.tools.BPS import create_bps_from_data from source.classes.CustomSettings import CustomSettings -__version__ = '1.2.0.9-u' +__version__ = '1.2.0.10u' from source.classes.BabelFish import BabelFish diff --git a/RELEASENOTES.md b/RELEASENOTES.md index d5a492b1..6cb841e6 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -108,6 +108,14 @@ These are now independent of retro mode and have three options: None, Random, an * Bonk Fairy (Dark) # Bug Fixes and Notes +* 1.2.0.10u + * Fixed overrun issues with edge transitions + * Better support for customized start_inventory with dungeon items + * Colorized pots now available with lottery. Default is on. + * Dungeon_only support pottery + * Fix AllowAccidentalGlitches flag in OWG + * Potential fix for mirror portal and entering cave on same frame + * A few other minor issues, generation and graphical * 1.2.0.9-u * Disallowed standard exits (due to ER) are now graphically half blocked instead of missing * Graphical issues with Sanctuary and Swamp Hub lobbies are fixed diff --git a/Rom.py b/Rom.py index 5b137612..0d9b0849 100644 --- a/Rom.py +++ b/Rom.py @@ -37,7 +37,7 @@ from source.dungeon.RoomList import Room0127 JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '67279b96a589f09e3ba8393a5bc5f071' +RANDOMIZERBASEHASH = 'd981a1fc7408ecbb30fa44135c7239b7' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index be3d11ea1f94a232964df475e95479bc094ec0cc..b5582c3f917243a2eedb422ae856bbf82f7f21cd 100644 GIT binary patch delta 3335 zcmW+&3s@6Z+MY9+gd~J}2q~ft1E}dw6}-@B6)Cl=i#M#TYZWzG*alN8uC}YL!kNH; z0b(xGa3B#0mvIOb6N+23zbq81CEee@*|zR(-SyF8?JC=Cr&e3*y0V8pPoDX%=goKK z`@Z*?&#uWs__rPGe6!dV1O!P8P7a3HLGM2KsWttq=YXfZWLb;ItZNZX@fpy=IFE}E z@EvKlT+9m&apK6BotfzGKIn8!FzfB&=GqqK4Z9curnC_|bI>ai9kzLwx@hQP{=pA} z4)AY&7s;f&*C=Mz_c2XZ{LCAFiz)fEx~rhh&Q$Y0a+z*LqbT%Cilzg`Z(oGAkLpCq zYG)qs-;lardul`q+L^CD?iy)fu5euu#l#BM_665@CDnGL`1}GBM9>)K($uZGUELBmH$=aDkm%6^@m|c_kkh0pCAIA9Y zFv2k_e+SPL<4V^a7gq#a%;z=Y#NTaytrL$pUCh-c|3q4~i%G-IM=r*Q1%Yq)T#Yz| z*Hf~eu4|fZ+xPn4DwF-`{46|7ZghYST^8@iUFXJ zZ&iK+uJMhkk{IoA@dJU$KaP``xdZoA#Q;nU{37~Q05bX0no3gU7?>M#j0D#Q2IJnP zz()hGYljTrR|DDk>j5a`k1SsUei#TZKQN8Fg9esw?SGB@jvu)D;mb>^X0|BkQPt9tw{QMnZ~5 ze}ZbA5&nV*b-eZ)ej7wTVPDl%Rv^N%h?+j)Fo#xhe)ybO@Sn^od}}9EaHOB0 z6;=Tlpp??37@3r!1VA5jK{*x!E;_Rs%IJb>94}Z!ujkB?+^?Y9>w5oI0NcAU+-3~- zqQmXma4-S#l?BcG=DY`-mNnwIXD1Syvd!l)j;b9m?)b0(|eGru>fRV}K z4#Ow-RO^+0_6$csk(N-#}PJ~sjSO409>z*dY_ zF7Td{G(v1R%PF&Or_4}hcE?vhq-MOV4()mJ`{^(aw>!KYSGq&ehG$926Wb}F!w_eo z$8A5$hSj}Sx6tcs=#{)v=y?@bIC~a34c^CdRMa5!6mbeXh-8uRmO_f*6l#5057*-d zjx&xO$gct!UP*mjSdE{Y4G( z@(35l1n$9Tye>(>Un&}k2D893=*wvEv~G9j zciQkFr_OliAV)Q;_2pC#t!zZO8j!9i?_^~@BigJ1^RuFMKDaFBVz47H|H2dujhyA^unG?j0oLpWm4X zxrD3YCZvf0^Ols&NPulir{i_sclr(;iN7%lLeZmieNoRaaaSC(jM8I+F%~++7xuZ= z*Jz}$VT$@dOm5Zw;#`&T&On`Sb_>DB4(=>C7*FI-F~aZw>8u2 zeN*)>!E%F;g{U?{s`M?*_5SJPdzupc*%-`=*F#D}TsYQAj*a;ozIkXxEO4Y1W;P>` z*~~c=O=a)2;yS_%h-liLTK(t{MBm1OMO8w4I8KTRe)N4M%KPQNuasO5a#Msy_qhEe zrnw*7;YmsFJ+#$zAG_uBY8Q6ax#&+EMw8z3vukw9m3JX^W6R}Fomh=?(Mso3RFHB0 z8pVaZXNM>QjHm>P1DG~pj6k&=srAqsoR~bT+UE8TWSniC%FyEszO zXyWhm=A6A^atboci6(llQ=sWXPD24fUvTnikY(^f(Y$pCbG*_pNpOV8FbRye2&6j4 z%6;W!?pr-W(nTEU76^I+&yhkDHn4{K@1_7O_AR9OR+4^$=LkWAQl|gdLYX94fB>j) z*AIWE^^U)hh1Yb#Q7GW@_3Lj zPtBx2ZmElJZSXgI9sF0r|2KTh7V=j1J%_bnyy0fU_!NNSrt2U+eue)QosS1I6gRm6 zd_o+L2Vm!aRvDzQ6d}zPv*CCcT1EgN%@~mp5jow-L+yy%u>#t9c<5Gs(N~1;2Ctb$ z2tu0UKx_uG+CSvs6X;mW-+35Ezr*wOZk54hgP)pES)H3nc^j=bh7Klzy!fAZ%XmD$ z&EuYy=FEkkzlDYqLAGq?FKp=BM39^^cayCTM79R=zpo76mO1D4nqBNd6oaNEfhW}Y zJ8V9=#pbKnjq;N~di2YFHo`ABcCv6;7uuZ!azHNfC4uaOUCoXTwB~jAe%FVULpc(? z3;i_-6swjb;1!alI3rgJ7AK`5KHe%s5fRy%R1`><6MQoU9o2%FwA&-Q##u-pxdsX@1fzhJIlE%hO2mQ$8(1Ll4zV9 zxmQWwXhuhq!SvKkXwKn>maD`6L1GX6R5MoVi}-9SzkDM)lMLp76m%^a*06+VZ8>b>{PyD%+O*bYBc=J? zB~-94VhBo;;zhp{AH2&~2sUN#o})GpNf^%wq;T85xj~}Y3ZF2VW4SlqHZ0Y$O{&;; zzAIU5V`)c>Hxx}T^@g6N*Lg$xkf_67J4<%zAoqc`ZG%MzTNHF<;G(w8z2&S>&_-e7 zwzkcRR^gDR23rrbf9&QSwf6L+} zy758dD>`VeNw2h^mx~)bDnT8NC(yGdyB%{<-eC58Z=VruNCEnVd$q~%9H*uo-iINX zKv)6*SbFr9x7e*suP|IY5qTl_P!gqdMBzmDi!V0%%J!hv6p*c3vbvd#a#&p?yaK;K z`{t5xT3xnr@Olb39TmM>rp3)AQ&W5Qqvn~w7WdMT>PyZRv;9Q0#e70c8_c1B0+7>J zBV#(4O)f1)JJZ3lq@n_KrQ@cV(B*W{NtUcfzhS_$aY@c;k- delta 3375 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 HciH~|=+T7u