From 59e875dc6d45c6aa266515128aa7872fcb8bb334 Mon Sep 17 00:00:00 2001 From: aerinon Date: Mon, 26 Jul 2021 11:28:45 -0600 Subject: [PATCH 01/10] updated readme and version --- Main.py | 2 +- README.md | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 63 insertions(+), 6 deletions(-) diff --git a/Main.py b/Main.py index 8909301a..e92bbf61 100644 --- a/Main.py +++ b/Main.py @@ -28,7 +28,7 @@ from Fill import sell_potions, sell_keys, balance_multiworld_progression, balanc from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops from Utils import output_path, parse_player_names -__version__ = '0.4.0.12u' +__version__ = '0.4.0-dev' class EnemizerError(RuntimeError): diff --git a/README.md b/README.md index 31ff824b..e997d70d 100644 --- a/README.md +++ b/README.md @@ -38,9 +38,11 @@ Alternatively, run ```Gui.py``` for a simple graphical user interface. # Commonly Missed Things and Differences from other Randomizers +Most of these apply only when the door shuffle is not vanilla. + ### Starting Item -You start with a “Mirror Scroll”, a dumbed-down mirror that only works in dungeons, not the overworld and can’t erase blocks like the Mirror +You start with a “Mirror Scroll”, a dumbed-down mirror that only works in dungeons, not the overworld and can’t erase blocks like the Mirror. ### Navigation @@ -58,7 +60,7 @@ You start with a “Mirror Scroll”, a dumbed-down mirror that only works in du ### Boss Differences -* You have to find the attic floor and bomb it open and bring the maiden to the light to fight Blind. In cross dungeon door shuffle, the attic can be in any dungeon. If hints are on, there is a special one about a cracked floor. +* You have to find the attic floor and bomb it open and bring the maiden to the light to fight Blind. In cross dungeon door shuffle, the attic can be in any dungeon. If you bring the maiden to the boss arena, she will hint were the cracked floor can be found. If hints are on, there is a special one about the cracked floor. * GT Bosses do not respawn after killing them in this mode. * Enemizer change: The attic/maiden sequence is now active and required when Blind is the boss of Theives' Town even when bosses are shuffled. @@ -70,7 +72,7 @@ You start with a “Mirror Scroll”, a dumbed-down mirror that only works in du ### Misc -* Compass counts no longer function after you get the Triforce +* Compass counts no longer function after you get the Triforce (this is actually true in all randomizers) # Settings @@ -124,7 +126,7 @@ The rooms are left alone and it is up to the discretion of the player whether to #### Force -The two disjointed sections are forced to be in the same dungeon but the glitches are never logically required to complete that game. +The two disjointed sections are forced to be in the same dungeon but the glitches are never logically required to complete that game.cause then you would need time to check the map in a d ### Standardize Palettes (--standardize_palettes) No effect if door shuffle is not on crossed @@ -239,6 +241,37 @@ Arrow Capacity upgrades are now replaced by Rupees wherever it might end up. The Ten Arrows and 5 randomly selected Small Hearts or Blue Shields are replaced by the quiver item (represented by the Single Arrow in game.) 5 Red Potion refills are replaced by the Universal small key. It is assured that at least one shop sells Universal Small Keys. The quiver may thus not be found in shops. The quiver and small keys retain their original base price, but may be discounted. +## Logic Level + +### Overworld Glitches + +Set `--logic` to `owglitches` to make overworld glitches required in the logic. + +## Shuffle Links House + +In certain ER shuffles, (not dungeonssimple or dungeonsfulls), you can now control whether Links House is shuffled or remains vanilla. Previously, inverted seeds had this behavior and would shuffle links house, but now if will only do so if this is specified. Now, also works for open modes, but links house is never shuffled in standard mode. + +## Reduce Flashing + +Accessibility option to reducing some flashing animations in the game. + +## Pseudo-boots + +Option to start with ability to dash, but not able to make any boots required logical checks or traversal. + +## Experimental Features + +The treasure check counter is turned on. Also, you will start as a bunny if your spawn point is in the dark world. + +## Triforce Hunt Settings + +A collection of settings to control the triforce piece pool. + +* --triforce_goal_min: Minimum number of pieces to collect to win +* --triforce_goal_max: Maximum number of pieces to collect to win +* --triforce_pool_min: Minimum number of pieces in item pool +* --triforce_pool_max: Maximum number of pieces in item pool +* --triforce_min_difference: Minimum difference between pool and goal to win ## Seed @@ -280,6 +313,24 @@ Include mobs and pots drop in the item pool. (default: not enabled) Includes shop locations in the item pool. +``` +--pseudoboots +``` + +Start with dash ability, but no way to use boots to accomplish checks + +``` +--shufflelinks +``` + +Whether to shuffle links house in most ER modes. + +``` +--experimental +``` + +Enables experimental features + ``` --mixed_travel ``` @@ -290,4 +341,10 @@ How to handle certain glitches in crossed dungeon mode. (default: prevent) --standardize_palettes (mode) ``` -Whether to standardize dungeon palettes in crossed dungeon mode. (default: standardize) \ No newline at end of file +Whether to standardize dungeon palettes in crossed dungeon mode. (default: standardize) + +``` +--reduce_flashing +``` + +Reduces amount of flashing in some animations \ No newline at end of file From a1eb077f285732583b6680213a1631a8ac7dce0d Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 27 Jul 2021 08:58:31 -0600 Subject: [PATCH 02/10] Boss music fix --- RELEASENOTES.md | 138 +----------------------------------------- Rom.py | 2 +- data/base2current.bps | Bin 136267 -> 136271 bytes 3 files changed, 3 insertions(+), 137 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 0ccceb80..163c6087 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,138 +1,4 @@ -# New Features +### Bug Fix -## Maiden Hint for Theives Town Attic +Fix for boss music in non-DR modes (Thanks codemann8) -In crossed dungeon mode, if you bring the maiden to the boss room when the attic is not bombed (and thus no light in the room), she mentions the dungeon where you can find the cracked floor. - -## Shuffle Links House - -Links house can now be shuffled in different ER settings. It will be limited to the Light World (or Dark World in inverted) if Crossed or Insanity shuffle is not one. It it also limited if door shuffle settings allow the Sanctuary to be in the dark world. (This is prevent having no Light World spawn points in Open modes) This setting is ignored by standard mode. THe CLI parameter is --shufflelinks - -## OWG Glitch Logic - -Thanks to qadan, cheuer, & compiling - -## Pseudo Boots - -Thanks to Bonta. You can now start with pseudo boots that let you move fast, but have no other logical uses (bonking open things, hovering, etc) - -## Pendant/Crystal Indicator - -For accessibility, you now get a C or P indicator to the left of the magic bar on the HUD when instead a Crystal or Pendant. Requires ownership of the map of that dungeon for display. Thanks to kan. - -# Bug Fixes and Notes. - -* 0.4.0.12 - * ER Inverted fix for HC Ledge, and Aga Tower choosing Links House incorrectly - * Credits again - hopefully for good - * Incorporated music fixes for now (may revisit later) - * Secure random re-incorporated -* 0.4.0.11 - * Some minor base rom fixes - * Improved distribution of bombable/dashable doors -* 0.4.0.10 - * Renamed to pseudoboots - * Some release note updates -* 0.4.0.9 - * Fixes for stats and P/C indicator (thanks Kara) - * Swamp lobby fixes (thanks Catobat) - * Fix for --hints flag on CLI -* 0.4.0.8 - * Ganon jokes added for when silvers aren't available - * Some text updated (Blind jokes, uncle text) - * Fixed some enemizer Mystery settings - * Added a setting that's random enemy shuffle without Unkillable Thieves possible - * Fixed shop spoiler when money balancing/multiworld balancing - * Fixed a problem with insanity - * Fixed an issue with the credit stats specific to DR (e.g. collection rate total) - * More helpful error message when bps is missing? - * Minor generation issues involving enemizer and the link sprite - * Baserom updates (from Bonta, kan, qwertymodo, ardnaxelark) - * Boss icon on dungeon map (if you have a compass) - * Progressive bow sprite replacement - * Quickswap - consecutive special swaps - * Bonk Counter - * One mind - * MSU fix - * Chest turn tracking (not yet in credits) - * Damaged and magic stats in credits (gt bk removed) - * Fix for infinite bombs - * Pseudo boots option - * Always allowed medallions for swordless (no option yet) -* 0.4.0.7 - * Reduce flashing option added - * Sprite author credit added - * Ranged Crystal switch rules tweaked - * Baserom update: includes Credits Speedup, reduced flashing option, msu resume (but turned off by default) - * Create link sprite's zspr from local ROM and no longer attempts to download it from website - * Some minor bug fixes -* 0.4.0.6 - * Hints now default to off - * The maiden gives you a hint to the attic if you bring her to the unlit boss room - * Beemizer support and fix for shopsanity - * Capacity upgrades removed in hard/expert item difficulties - * Swamp Hub added to lobby shuffle with ugly cave entrance. - * TR Lava Escape added to lobby shuffle. - * Hyrule Main Lobby and Sanctuary can now have a more visible outside exit, and rugs modified to be fully clipped. -* 0.4.0.5 - * Insanity - less restrictions on exiting (all modes) - * Fix for simple bosses shuffle - * Fix for boss shuffle from Mystery.py - * Minor msu fade out bug (thanks codemann8) - * Other bug fixes (thanks Catobat) -* 0.4.0.4 - * Added --shufflelinks option - * Moved spawning as a bunny indoors to experimental - * Baserom bug fixes -* 0.4.0.3 - * Fixed a bug where Sanctuary could be chosen as a lobby for a DW dungeon in non-crossed ER modes -* 0.4.0.2 - * Fixed a bug where Defeat Ganon is not possible - * Fixed the item counter total - * Fixed the bunny state when starting out in Sanc in a dark world dungeon -* 0.4.0.1 - * Moved stonewall pre-opening to not happen in experimental - * Updated baserom - * Boss RNG perseved between files - * Vanilla prize pack fix - * Starting equipment fix - * Post-Aga world state option - * Code optimzation - * Bottle quickswap via double shoulder - * Credits update - * Accessibility option - * Sewer map/compass fix - * Fixed a standard bug where the exits to the ledge would be unavailable if the pyramid was pre-opened - * DR ASM optimization - * Removed Archery Game from Take-Any caves in inverted - * Fixed a problem with new YAML parser -* 0.4.0.0 - * Mystery yaml parser updated to a package maintained version (Thanks StructuralMike) - * Bomb-logic and extend crystal switch logic (Thanks StructuralMike) - * Fixed logic for moved locations in playthrough (Thanks compiling) - * OWG Glitch logic added - -# Known Issues - -* Shopsanity Issues - * Hints for items in shops can be misleading (ER) - * Forfeit in Multiworld not granting all shop items -* Potential keylocks in multi-entrance dungeons -* Incorrect vanilla key logic for Mire - -## Other Notes - -### Triforce Hunt Options - -Thanks to deathFouton! - ---triforce_pool and --triforce_goal added to the CLI. - -Also, to the Mystery.py he added the following options: -* triforce_goal_min -* triforce_goal_max -* triforce_pool_min -* triforce_pool_max -* triforce_min_difference - -See the example yaml file for demonstrated usage. \ No newline at end of file diff --git a/Rom.py b/Rom.py index 40249d56..355509c0 100644 --- a/Rom.py +++ b/Rom.py @@ -30,7 +30,7 @@ from EntranceShuffle import door_addresses, exit_ids JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '9c2878d1035bb3889784906a55a92a26' +RANDOMIZERBASEHASH = '988f1546b14d8f2e6ee30b9de44882da' class JsonRom(object): diff --git a/data/base2current.bps b/data/base2current.bps index a904b07d50f9fadac099414d033b0c27b309563b..5f41171067dccc266f98a3c0b2daee5a07a03f06 100644 GIT binary patch delta 5253 zcmW+(30xD$8qef_a3_F(pb}OQ5Y&KLDIVZ~C?bL<#i*!Y@x`lXttafF0TZ$bVF+7X zFbfP|I9zC|h!+Z42%^SU>)Bex*0!iM6?>?C`WBPl%=h1KuJ4@V$}#cvW8z{g>WBgH zHp_n{I({Y4bevQq;#FUkE4KX?4K#;~aUwjU(4a8kRRh_DDjXyoBo!ToWfKCvK93~% zs@k(c9f=iv#|<=KNAzewe45BHRHnL@Y0$tx+HpcAFEA>?4F85=solsACozIVh{P(6 zGiCE5p2}Oza=wd*2YBaPX1{{-KsbnoIUpkBW;YV!Mr>*cH%n+TZUAB*;OJLkQ2ehN z4csBIU+$t%jrtn|f)xK{McfGPcZtNN>yRtAV0|ew=8zG)0ax>5 zMxC>vOGXcSuFYX!E}@gZu^9~<$RO0%YZxhv03+ZUVJwJ*M}^6bVGw<9z`lor!g%nH_(;0cEn;1+asT+Fg3rK|xE_U{H zdUF#EWN`XIxXpQ3N-G_hZA>G|6|c2JvU?Yj7)GLqLxeK4(ug&E#|#WabQ`ci&e<*0 zR-r*lV%j3KKZqc)$9hyjV*l#VGGxFO6U;!5K`|HJbWQ~ZIMyY?Z3+3o8pS&g85R9- zt&3u8;tjN4O=5k`OcG~^^q!%(7K`ZwVexi?2yZFJ>?H`B4IjDqgvcOr<<@XHkGwSx z@|J5<f#);Q{hs0b!%!NCoAs#|Kq89FHr?$JEKBdB>&@83EQ#i|0A?DI3<5(4T z&Qj|+CKUL?-k5~RJsPxMtA1C;$TUVQcqqU7)O}BwqUlLvq!sT{)sl1@o>SzzZ8X=qI3mPrmSuQRh*z)c6IuX!W_U_67 zV2S1V7xzV=7+Mc(08N(hxpM%hfS=`k4pv)!$r}uGe8Qkk3C4$yOx~jR48oL_H>P%B z@&Q80^6`*{TIM0oCb4?*Y{F`9+|kPC+4$8GC3ekSurju(dwXXOb1w2G~PP zJ6*}gn#!$&T$S`ju>?wwt#_W9SiS|nWs_h!c;Hxo?-A`A<#BC`_ABij?L+O)+TXQy z?O)o{kUbV8k4h>pw_))l%3$dknEsCp{KC*z@#!Q)+J)FB*Tw7_TyEvGS9D#Yc%w_q zao`g;%iQbQq^o_Ymf76!DA^t8+mI!`p-1+?3~C8H)UXER!N(1U{QQIef>UH#I6rn5u@{LepdZw{&E+a0Knj@YADOI(XUQ;sff04M&UA7&bYbnYLsAP45?O|~| zdmtOff%iPSOZ5ht`CcMpjd*DF4%1CyWPLrc%Je1SM3)+SjI36$oeCC{?KYR{BAR*l zp<^Vgr6L?jwM+FulMhaegv|u5Zz1+^0&t^F^cbrl&HN-`XA8E=UQ@3LiT)0$tYDty z(TAz?^kSS4!@t!P%&&APK_R-95a_4ihNs*RDfJZsZNbZH zxDP2C4|SE*jWm-XW#>`5@p22zWJ=itN>_e{X0oO1d=7Qd%w{ROfJ0X}#iVQ^hrZ<` zm$D0o;C-6;Rmv{n&>+n`ld{Piddtn|$)-?q@Nz)~lj+H(Qe?SH1(WT`F5!?@1+&?c zT?*IW?aWIFtKhT}!s4$+(eI3r^A>{$Pb*eLiuJvxYB6i-xRH z^P7rIH&q#61+!bK*T7TMEz@m+Hr-J>RoBo46}!rGj%?(nt4D;zbe7|u8R9BURb&@8 z1b%i1IvgUdm?q%xHu3F;VfX30W-~iqIOIj_bQ5Dr@W?lAl8hq%@iavqIx&Racvg?D zVE&b`%c%(0d}5i&^$@wVg4qwFxM?UwJ~d54$7w371Gr97b_Eq!SRT$bd$KDj4}#cf zlH-PH70d`P$|;|iM9Yg{Z54kgV&+sZvicI}j5r1uVl0FwEwW(&!$_0=Fw!v2%b$#( zG;mX@))Wn&S}ub*u=VS|_&n|mJCQ2+)F&Tq^vPGPkkAV`7zLeA{sjUdX!G-`y@2+< z2_1^@Wb@XMq|S?)2_xF3fqt0X_5_TCi%-o1Phi=paF7N&Pt5?^OWH@dk6)m#zHDOWJ+dYLac$f}OR%&3Z zQd6Em`UH`_K~&-}9bq-i;##~~RTU2PGvSys>%nkXcxGdqXI-bfI!<^r>XVMA#!7nzHNrcx>-txBDgQuhsApSiNCrU}*(XDvTD;tSyTv+Jfu z@6lF@*us~&O0$ewyGPq1W%UH|Ej=~{YJgh^_QCW*y zM-l8jYX{lz%(N=6;h=%ruJNu@HrDs^bSGlskVQXJG?JuGG?N_F%YsgBraavRe4b@j=fQAiM-+_hDd@av&FL&xL)K(Ee<}5Nl$ec&V!kFu0SB{C*mp5it^LQF1 zHEG0y_V^C8x&!U+KnE@$cmcIJ)~UIh2(?>vLMf}bnAwwQ#khdBxxg)iCSTnvdEGF+ z$?V!$`&XF6E|8^lQG23WZ^*J>+x5|L4x@gl6xzLNt$v%Fx@riqzL_4q^=Un}^=V1S zZo?L0QJuBplM6@^ZnfKlPo#Lf^$(j!meqw%w!Y**8uYhK6*_mxI^c5KJny9!dnK=q z&f9tVbKI7a*V`-khwH)G?mCQ))n7DEhPoRgJy&)jzfFfSp6dBhUagfPo49TJNm)DoD4C`xO2q|rypAad)d?ycBpjUj0g8v<#~+~k5S;b_N+f4`<$U~fznq$o zN|)F6_=kC@dQ7`5UH@(5gQalWce2Sokvd{L<>*vgJLgrd&50fVDmR9j;8gsmBd`nL z3D^uZkIg6KVSKx+*&SH-*@n};u zqLs6CR21yI6*>H~OdSzJ5BB%M-RHgRF|-K13b@b*WZYg`SrV_)U^ZJWwnpFCgz%z~kJ z3PBERxf97fl>6_LMvwf|5&uqc)h^<4-|SeK;vsXsiv99xlq~V8Y5Jt+rdf-AB*t}Y zcQ{(IGh2H!)9GlLcLDKFO$I!E*Jo7LM19GE;dE6Q1F-#gb+NlCeZ1aSEW}M%LcC=I~1wBr}5h)<25imnEZxf~1L1eScO!?y_HMMcL-*_{Z7! zNAsz0rrh=_+dKmgrRTz*@0U$Eeck-FQ&Pd3ZSo3`;;j{NLCtYNwzp=2*@ecvw+baT*~d3JZUp0l2iXD^G4t=8|6;Rfyz zU>i%tz_=f700MeG*aFr-{J{a>0N*`W9+-Rc*(6{eH1nPgNp5$8A%;lYG5SSAN*+Ea zbAUNN#{0&GIpJm=4m5h+vOJv&pk|J6!r?x6?Z+VQc5DA}IQKenet0%ab_>}ZdL@5r z@E$Wx1_gD>sddSY`0EB|=S{pa(sNEaBjfeUIwIhUhc%I&08%d+4bh>8rbz{0=Hkjd~^f&xaR4NFPITL z_FFjYv@yo|p4SpfErNF+2Y?9p=i`at6H|+cP%3RX4l{Uq;*;)tBAY9lEcqoKo3&81 z#9%m(-gJi#@72iQ;-3z9WX=4S$o^t?fq(iRi^J6`Ecbu%cXpbZcv$U$9i)};-S3-$ z3sk;HBYjNhFEtavrdSurmi6)2JW!!6&bl0VSOFXQc1!9r#7moqJAhm+hkTb=|p*8Smax z!z>5?d<`a4b)$RK$n&i(i47esTn3rA>diJhewc!tBzL?l#pKJl_{{gz^ai0JH delta 5364 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 q>=Zx2ly7Ye>5S6|F From bbad1d1d8b1020b50453b66b2d88c5fb8712be38 Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 27 Jul 2021 11:33:10 -0600 Subject: [PATCH 03/10] Version bump Mystery can apply reduce_flashing --- Main.py | 2 +- Mystery.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Main.py b/Main.py index e92bbf61..96235926 100644 --- a/Main.py +++ b/Main.py @@ -28,7 +28,7 @@ from Fill import sell_potions, sell_keys, balance_multiworld_progression, balanc from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops from Utils import output_path, parse_player_names -__version__ = '0.4.0-dev' +__version__ = '0.4.1-dev' class EnemizerError(RuntimeError): diff --git a/Mystery.py b/Mystery.py index d3e3bddf..1ab4a90b 100644 --- a/Mystery.py +++ b/Mystery.py @@ -226,6 +226,7 @@ def roll_settings(weights): ret.sprite = get_choice('sprite', romweights) ret.disablemusic = get_choice('disablemusic', romweights) == 'on' ret.quickswap = get_choice('quickswap', romweights) == 'on' + ret.reduce_flashing = get_choice('reduce_flashing', romweights) == 'on' ret.fastmenu = get_choice('menuspeed', romweights) ret.heartcolor = get_choice('heartcolor', romweights) ret.heartbeep = get_choice('heartbeep', romweights) From e0ff23158a38cd0ce34a4f7520a30271a09e24fd Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 13 Jan 2022 17:01:13 -0700 Subject: [PATCH 04/10] Readme updates --- README.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e997d70d..6cb67700 100644 --- a/README.md +++ b/README.md @@ -251,6 +251,10 @@ Set `--logic` to `owglitches` to make overworld glitches required in the logic. In certain ER shuffles, (not dungeonssimple or dungeonsfulls), you can now control whether Links House is shuffled or remains vanilla. Previously, inverted seeds had this behavior and would shuffle links house, but now if will only do so if this is specified. Now, also works for open modes, but links house is never shuffled in standard mode. +## Bomb Logic (--bombbag) + +When enabling this option, you do not start with bomb capacity but rather you must find 1 of 2 bomb bags. (They are represented by the +10 capacity item.) Bomb capacity upgrades are otherwise unavailable. + ## Reduce Flashing Accessibility option to reducing some flashing animations in the game. @@ -259,6 +263,10 @@ Accessibility option to reducing some flashing animations in the game. Option to start with ability to dash, but not able to make any boots required logical checks or traversal. +## SFX Shuffle (--shuffle_sfx) + +Shuffles a large portion of the sounds effects. Can be used with the adjuster. + ## Experimental Features The treasure check counter is turned on. Also, you will start as a bunny if your spawn point is in the dark world. @@ -325,6 +333,12 @@ Start with dash ability, but no way to use boots to accomplish checks Whether to shuffle links house in most ER modes. + +``` +--bombbag +``` +Need to find the bombbag upgrade to used bombs + ``` --experimental ``` @@ -347,4 +361,13 @@ Whether to standardize dungeon palettes in crossed dungeon mode. (default: stand --reduce_flashing ``` -Reduces amount of flashing in some animations \ No newline at end of file +Reduces amount of flashing in some animations + +``` +--shuffle_sfx +``` + +Shuffles a bunch of the sounds effects + + + From 922bc6d2d02e7bac1190a1ee2fa6f464bf6e9145 Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 4 Mar 2022 16:45:51 -0700 Subject: [PATCH 05/10] Fix some interior doors during key door identification as missing one reachable side --- DoorShuffle.py | 15 ++++++++++----- Main.py | 2 +- RELEASENOTES.md | 2 ++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/DoorShuffle.py b/DoorShuffle.py index 3d3a6eda..8c0b8e25 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -14,6 +14,7 @@ from RoomData import DoorKind, PairedDoor, reset_rooms from DungeonGenerator import ExplorationState, convert_regions, generate_dungeon, pre_validate, determine_required_paths, drop_entrances from DungeonGenerator import create_dungeon_builders, split_dungeon_builder, simple_dungeon_builder, default_dungeon_entrances from DungeonGenerator import dungeon_portals, dungeon_drops, GenerationException +from DungeonGenerator import valid_region_to_explore as valid_region_to_explore_lim from KeyDoorShuffle import analyze_dungeon, build_key_layout, validate_key_layout, determine_prize_lock from Utils import ncr, kth_combination @@ -1368,6 +1369,8 @@ def combine_layouts(recombinant_builders, dungeon_builders, entrances_map): dungeon_builders[recombine.name] = recombine +# todo: this allows cross-dungeon exploring via HC Ledge or Inaccessible Regions +# todo: @deprecated def valid_region_to_explore(region, world, player): return region and (region.type == RegionType.Dungeon or region.name in world.inaccessible_regions[player] @@ -1559,7 +1562,7 @@ okay_normals = [DoorKind.Normal, DoorKind.SmallKey, DoorKind.Bombable, DoorKind. def find_key_door_candidates(region, checked, world, player): - dungeon = region.dungeon + dungeon_name = region.dungeon.name candidates = [] checked_doors = list(checked) queue = deque([(region, None, None)]) @@ -1569,14 +1572,16 @@ def find_key_door_candidates(region, checked, world, player): d = ext.door if d and d.controller: d = d.controller - if d and not d.blocked and not d.entranceFlag and d.dest is not last_door and d.dest is not last_region and d not in checked_doors: + if d and not d.blocked and d.dest is not last_door and d.dest is not last_region and d not in checked_doors: valid = False - if 0 <= d.doorListPos < 4 and d.type in [DoorType.Interior, DoorType.Normal, DoorType.SpiralStairs]: + if (0 <= d.doorListPos < 4 and d.type in [DoorType.Interior, DoorType.Normal, DoorType.SpiralStairs] + and not d.entranceFlag): room = world.get_room(d.roomIndex, player) position, kind = room.doorList[d.doorListPos] - if d.type == DoorType.Interior: valid = kind in [DoorKind.Normal, DoorKind.SmallKey, DoorKind.Bombable, DoorKind.Dashable] + if valid and d.dest not in candidates: # interior doors are not separable yet + candidates.append(d.dest) elif d.type == DoorType.SpiralStairs: valid = kind in [DoorKind.StairKey, DoorKind.StairKey2, DoorKind.StairKeyLow] elif d.type == DoorType.Normal: @@ -1595,7 +1600,7 @@ def find_key_door_candidates(region, checked, world, player): if valid and d not in candidates: candidates.append(d) connected = ext.connected_region - if connected and (connected.type != RegionType.Dungeon or connected.dungeon == dungeon): + if valid_region_to_explore_lim(connected, dungeon_name, world, player): queue.append((ext.connected_region, d, current)) if d is not None: checked_doors.append(d) diff --git a/Main.py b/Main.py index 573f935f..7dc012e7 100644 --- a/Main.py +++ b/Main.py @@ -29,7 +29,7 @@ from Fill import sell_potions, sell_keys, balance_multiworld_progression, balanc from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops from Utils import output_path, parse_player_names -__version__ = '1.0.0-dev' +__version__ = '1.0.1-dev' from source.classes.BabelFish import BabelFish diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 7a224c7c..b59df407 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -15,6 +15,8 @@ CLI: ```--bombbag``` # Bug Fixes and Notes. +* 1.0.1 + * Fixed a bug with key doors not detecting one side of an interior door * 0.5.1.7 * Baserom update * Fix for Inverted Mode: Dark Lake Hylia shop defaults to selling a blue potion From e89a1430528858d81a9486bfa26cc4ee11766577 Mon Sep 17 00:00:00 2001 From: aerinon Date: Thu, 24 Feb 2022 14:39:45 -0700 Subject: [PATCH 06/10] SSL not verified for alttpr.com/sprites --- source/classes/SpriteSelector.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/classes/SpriteSelector.py b/source/classes/SpriteSelector.py index cb76e64c..9a18cacd 100644 --- a/source/classes/SpriteSelector.py +++ b/source/classes/SpriteSelector.py @@ -4,6 +4,7 @@ import json import os import random import shutil +import ssl from urllib.parse import urlparse from urllib.request import urlopen import webbrowser @@ -149,7 +150,7 @@ class SpriteSelector(object): try: task.update_status("Downloading official sprites list") - with urlopen('https://alttpr.com/sprites') as response: + with urlopen('https://alttpr.com/sprites', context=ssl._create_unverified_context()) as response: sprites_arr = json.loads(response.read().decode("utf-8")) except Exception as e: resultmessage = "Error getting list of official sprites. Sprites not updated.\n\n%s: %s" % (type(e).__name__, e) From ccdbeb6423ef15ed8bd95ab60303d64d95ca48ec Mon Sep 17 00:00:00 2001 From: aerinon Date: Fri, 4 Mar 2022 16:46:46 -0700 Subject: [PATCH 07/10] Note for fix --- RELEASENOTES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index b59df407..4c3ec552 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -17,6 +17,7 @@ CLI: ```--bombbag``` * 1.0.1 * Fixed a bug with key doors not detecting one side of an interior door + * Sprite selector fix for systems with SSL issues * 0.5.1.7 * Baserom update * Fix for Inverted Mode: Dark Lake Hylia shop defaults to selling a blue potion From 341a4f463112ecdfa2bcfc25d11169449d7fe100 Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 8 Mar 2022 08:13:49 -0700 Subject: [PATCH 08/10] District hint work --- Main.py | 2 +- RELEASENOTES.md | 2 +- Regions.py | 30 ++++++------- Rom.py | 96 +++++++++++++++++++++++++++++++---------- source/item/District.py | 78 ++++++++++++++++----------------- 5 files changed, 128 insertions(+), 80 deletions(-) diff --git a/Main.py b/Main.py index 85fbe97b..22c73a50 100644 --- a/Main.py +++ b/Main.py @@ -31,7 +31,7 @@ from Utils import output_path, parse_player_names from source.item.FillUtil import create_item_pool_config, massage_item_pool, district_item_pool_config -__version__ = '1.0.0.1-u' +__version__ = '1.0.0.2-u' from source.classes.BabelFish import BabelFish diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 8e14a571..467d9901 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -67,7 +67,7 @@ In multiworld, the districts chosen apply to all players. ## New Hints -Based on the district algorithm above (whether it is enabled or not,) new hints can appear stating that a district or dungeon is considered a "foolish" choice. The means there are no advancement items in that district (or that the district was not chosen if the district restriction is used). +Based on the district algorithm above (whether it is enabled or not,) new hints can appear about that district or dungeon. For each district and dungeon, it is evaluated whether it contains vital items and how many. If it has not any vital item, items then it moves onto useful items. Useful items are generally safeties or convenience items: shields, mails, half magic, bottles, medallions that aren't required, etc. If it contains none of those and is an overworld district, then it check for a couple more things. First, if dungeons are shuffled, it looks to see if any are in the district, if so, one of those dungeons is picked for the hint. Then, if connectors are shuffled, it checks to see if you can get to unique region through a connector in that district. If none of the above apply, the district or dungeon is considered completely foolish. At least two "foolish" districts are chosen and the rest are random. ### Overworld Map shows dungeon location diff --git a/Regions.py b/Regions.py index 3c0ff09a..d971c5ad 100644 --- a/Regions.py +++ b/Regions.py @@ -88,17 +88,17 @@ def create_regions(world, player): create_lw_region(player, 'Lake Hylia Island', ['Lake Hylia Island']), create_cave_region(player, 'Capacity Upgrade', 'the queen of fairies', ['Capacity Upgrade - Left', 'Capacity Upgrade - Right']), create_cave_region(player, 'Two Brothers House', 'a connector', None, ['Two Brothers House Exit (East)', 'Two Brothers House Exit (West)']), - create_lw_region(player, 'Maze Race Ledge', ['Maze Race'], ['Two Brothers House (West)']), + create_lw_region(player, 'Maze Race Ledge', ['Maze Race'], ['Two Brothers House (West)'], 'a race against time'), create_cave_region(player, '50 Rupee Cave', 'a cave with some cash'), - create_lw_region(player, 'Desert Ledge', ['Desert Ledge'], ['Desert Palace Entrance (North) Rocks', 'Desert Palace Entrance (West)']), + create_lw_region(player, 'Desert Ledge', ['Desert Ledge'], ['Desert Palace Entrance (North) Rocks', 'Desert Palace Entrance (West)'], 'the desert ledge'), create_lw_region(player, 'Desert Ledge (Northeast)', None, ['Checkerboard Cave']), create_lw_region(player, 'Desert Palace Stairs', None, ['Desert Palace Entrance (South)']), - create_lw_region(player, 'Desert Palace Lone Stairs', None, ['Desert Palace Stairs Drop', 'Desert Palace Entrance (East)']), - create_lw_region(player, 'Desert Palace Entrance (North) Spot', None, ['Desert Palace Entrance (North)', 'Desert Ledge Return Rocks']), + create_lw_region(player, 'Desert Palace Lone Stairs', None, ['Desert Palace Stairs Drop', 'Desert Palace Entrance (East)'], 'a sandy vista'), + create_lw_region(player, 'Desert Palace Entrance (North) Spot', None, ['Desert Palace Entrance (North)', 'Desert Ledge Return Rocks'], 'the desert ledge'), create_lw_region(player, 'Master Sword Meadow', ['Master Sword Pedestal']), create_cave_region(player, 'Lost Woods Gamble', 'a game of chance'), create_lw_region(player, 'Hyrule Castle Courtyard', None, ['Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Entrance (South)']), - create_lw_region(player, 'Hyrule Castle Ledge', None, ['Hyrule Castle Entrance (East)', 'Hyrule Castle Entrance (West)', 'Agahnims Tower', 'Hyrule Castle Ledge Courtyard Drop']), + create_lw_region(player, 'Hyrule Castle Ledge', None, ['Hyrule Castle Entrance (East)', 'Hyrule Castle Entrance (West)', 'Agahnims Tower', 'Hyrule Castle Ledge Courtyard Drop'], 'the castle rampart'), create_dungeon_region(player, 'Sewer Drop', 'a drop\'s exit', None, ['Sewer Drop']), # This exists only to be referenced for access checks create_cave_region(player, 'Old Man Cave', 'a connector', ['Old Man'], ['Old Man Cave Exit (East)']), @@ -107,7 +107,7 @@ def create_regions(world, player): create_cave_region(player, 'Old Man House Back', 'a connector', None, ['Old Man House Exit (Top)', 'Old Man House Back to Front']), create_lw_region(player, 'Death Mountain', None, ['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'Broken Bridge (West)', 'Death Mountain Teleporter']), create_cave_region(player, 'Death Mountain Return Cave', 'a connector', None, ['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave Exit (East)']), - create_lw_region(player, 'Death Mountain Return Ledge', None, ['Death Mountain Return Ledge Drop', 'Death Mountain Return Cave (West)']), + create_lw_region(player, 'Death Mountain Return Ledge', None, ['Death Mountain Return Ledge Drop', 'Death Mountain Return Cave (West)'], 'a ledge in the foothills'), create_cave_region(player, 'Spectacle Rock Cave (Top)', 'a connector', ['Spectacle Rock Cave'], ['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']), create_cave_region(player, 'Spectacle Rock Cave (Bottom)', 'a connector', None, ['Spectacle Rock Cave Exit']), create_cave_region(player, 'Spectacle Rock Cave (Peak)', 'a connector', None, ['Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Exit (Peak)']), @@ -173,10 +173,10 @@ def create_regions(world, player): create_cave_region(player, 'Red Shield Shop', 'the rare shop', ['Red Shield Shop - Left', 'Red Shield Shop - Middle', 'Red Shield Shop - Right']), create_cave_region(player, 'Dark Sanctuary Hint', 'a storyteller'), create_cave_region(player, 'Bumper Cave', 'a connector', None, ['Bumper Cave Exit (Bottom)', 'Bumper Cave Exit (Top)']), - create_dw_region(player, 'Bumper Cave Ledge', ['Bumper Cave Ledge'], ['Bumper Cave Ledge Drop', 'Bumper Cave (Top)', 'Bumper Cave Ledge Mirror Spot']), + create_dw_region(player, 'Bumper Cave Ledge', ['Bumper Cave Ledge'], ['Bumper Cave Ledge Drop', 'Bumper Cave (Top)', 'Bumper Cave Ledge Mirror Spot'], 'a ledge with an item'), create_dw_region(player, 'Skull Woods Forest', None, ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)', 'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)']), - create_dw_region(player, 'Skull Woods Forest (West)', None, ['Skull Woods Second Section Hole', 'Skull Woods Second Section Door (West)', 'Skull Woods Final Section']), + create_dw_region(player, 'Skull Woods Forest (West)', None, ['Skull Woods Second Section Hole', 'Skull Woods Second Section Door (West)', 'Skull Woods Final Section'], 'a deep, dark forest'), create_dw_region(player, 'Dark Desert', None, ['Misery Mire', 'Mire Shed', 'Desert Ledge (Northeast) Mirror Spot', 'Desert Ledge Mirror Spot', 'Desert Palace Stairs Mirror Spot', 'Desert Palace Entrance (North) Mirror Spot', 'Dark Desert Hint', 'Dark Desert Fairy']), create_cave_region(player, 'Mire Shed', 'a cave with two chests', ['Mire Shed - Left', 'Mire Shed - Right']), @@ -184,8 +184,8 @@ def create_regions(world, player): create_dw_region(player, 'Dark Death Mountain (West Bottom)', None, ['Spike Cave', 'Spectacle Rock Mirror Spot', 'Dark Death Mountain Fairy']), create_dw_region(player, 'Dark Death Mountain (Top)', None, ['Dark Death Mountain Drop (East)', 'Dark Death Mountain Drop (West)', 'Ganons Tower', 'Superbunny Cave (Top)', 'Hookshot Cave', 'East Death Mountain (Top) Mirror Spot', 'Turtle Rock']), - create_dw_region(player, 'Dark Death Mountain Ledge', None, ['Dark Death Mountain Ledge (East)', 'Dark Death Mountain Ledge (West)', 'Mimic Cave Mirror Spot', 'Spiral Cave Mirror Spot']), - create_dw_region(player, 'Dark Death Mountain Isolated Ledge', None, ['Isolated Ledge Mirror Spot', 'Turtle Rock Isolated Ledge Entrance']), + create_dw_region(player, 'Dark Death Mountain Ledge', None, ['Dark Death Mountain Ledge (East)', 'Dark Death Mountain Ledge (West)', 'Mimic Cave Mirror Spot', 'Spiral Cave Mirror Spot'], 'a dark ledge'), + create_dw_region(player, 'Dark Death Mountain Isolated Ledge', None, ['Isolated Ledge Mirror Spot', 'Turtle Rock Isolated Ledge Entrance'], 'a dark vista'), create_dw_region(player, 'Dark Death Mountain (East Bottom)', None, ['Superbunny Cave (Bottom)', 'Cave Shop (Dark Death Mountain)', 'Fairy Ascension Mirror Spot']), create_cave_region(player, 'Superbunny Cave (Top)', 'a connector', ['Superbunny Cave - Top', 'Superbunny Cave - Bottom'], ['Superbunny Cave Exit (Top)']), create_cave_region(player, 'Superbunny Cave (Bottom)', 'a connector', None, ['Superbunny Cave Climb', 'Superbunny Cave Exit (Bottom)']), @@ -195,7 +195,7 @@ def create_regions(world, player): create_cave_region(player, 'Hookshot Cave (Back)', 'a connector', None, ['Hookshot Cave Back to Middle', 'Hookshot Cave Back Exit']), create_cave_region(player, 'Hookshot Cave (Middle)', 'a connector', None, ['Hookshot Cave Middle to Back', 'Hookshot Cave Middle to Front']), - create_dw_region(player, 'Death Mountain Floating Island (Dark World)', None, ['Floating Island Drop', 'Hookshot Cave Back Entrance', 'Floating Island Mirror Spot']), + create_dw_region(player, 'Death Mountain Floating Island (Dark World)', None, ['Floating Island Drop', 'Hookshot Cave Back Entrance', 'Floating Island Mirror Spot'], 'a dark island'), create_lw_region(player, 'Death Mountain Floating Island (Light World)', ['Floating Island']), create_dw_region(player, 'Turtle Rock (Top)', None, ['Turtle Rock Drop']), create_lw_region(player, 'Mimic Cave Ledge', None, ['Mimic Cave']), @@ -887,12 +887,12 @@ def create_menu_region(player, name, locations=None, exits=None): return _create_region(player, name, RegionType.Menu, 'Menu', locations, exits) -def create_lw_region(player, name, locations=None, exits=None): - return _create_region(player, name, RegionType.LightWorld, 'Light World', locations, exits) +def create_lw_region(player, name, locations=None, exits=None, hint='Light World'): + return _create_region(player, name, RegionType.LightWorld, hint, locations, exits) -def create_dw_region(player, name, locations=None, exits=None): - return _create_region(player, name, RegionType.DarkWorld, 'Dark World', locations, exits) +def create_dw_region(player, name, locations=None, exits=None, hint='Dark World'): + return _create_region(player, name, RegionType.DarkWorld, hint, locations, exits) def create_cave_region(player, name, hint='Hyrule', locations=None, exits=None): diff --git a/Rom.py b/Rom.py index 02878b0b..ed4c208a 100644 --- a/Rom.py +++ b/Rom.py @@ -2182,29 +2182,60 @@ def write_strings(rom, world, player, team): else: tt[hint_locations.pop(0)] = this_hint - if world.algorithm != 'district': - hint_candidates = [] - for name, district in world.districts[player].items(): - foolish = True - for loc_name in district.locations: - location = world.get_location(loc_name, player) - if location.item.advancement: - foolish = False - break - if foolish: - hint_candidates.append(f'{name} is a foolish choice') - foolish_choice_hints = min(len(hint_candidates), len(hint_locations)) - for i in range(0, foolish_choice_hints): - tt[hint_locations.pop(0)] = hint_candidates.pop(0) - if world.algorithm == 'district': - hint_candidates = [] - for name, district in world.districts[player].items(): - if name not in world.item_pool_config.recorded_choices and not district.sphere_one: - hint_candidates.append(f'{name} is a foolish choice') - random.shuffle(hint_candidates) - foolish_choice_hints = min(len(hint_candidates), len(hint_locations)) - for i in range(0, foolish_choice_hints): - tt[hint_locations.pop(0)] = hint_candidates.pop(0) + hint_candidates = [] + for name, district in world.districts[player].items(): + hint_type = 'foolish' + choice_set = set() + item_count, item_type = 0, 'useful' + for loc_name in district.locations: + location_item = world.get_location(loc_name, player).item + if location_item.advancement: + if 'Heart Container' in location_item.name: + continue + itm_type = 'useful' if useful_item_for_hint(location_item, world) else 'vital' + hint_type = 'path' + if item_type == itm_type: + choice_set.add(location_item) + item_count += 1 + elif itm_type == 'vital': + item_type = 'vital' + item_count = 1 + choice_set.clear() + choice_set.add(location_item) + if hint_type == 'foolish': + if district.dungeons and world.shuffle[player] != 'vanilla': + choice_set.update(district.dungeons) + hint_type = 'dungeon_path' + elif district.access_points and world.shuffle[player] not in ['vanilla', 'dungeonssimple', + 'dungeonsfull']: + choice_set.update([x.hint_text for x in district.access_points]) + hint_type = 'connector' + if hint_type == 'foolish': + hint_candidates.append((hint_type, f'{name} is a foolish choice')) + elif hint_type == 'dungeon_path': + choices = sorted(list(choice_set)) + dungeon_choice = random.choice(choices) # prefer required dungeons... + hint_candidates.append((hint_type, f'{name} is on the path to {dungeon_choice}')) + elif hint_type == 'connector': + choices = sorted(list(choice_set)) + access_point = random.choice(choices) # prefer required access... + hint_candidates.append((hint_type, f'{name} can reach {access_point}')) + elif hint_type == 'path': + if item_count == 1: + the_item = text_for_item(next(iter(choice_set)), world, player, team) + hint_candidates.append((hint_type, f'{name} conceals {the_item}')) + else: + hint_candidates.append((hint_type, f'{name} conceals {item_count} {item_type} items')) + district_hints = min(len(hint_candidates), len(hint_locations)) + random.shuffle(hint_candidates) + hint_candidates.sort(key=lambda x: 1 if x[0] == 'foolish' else 0) + foolish_only = min(2, district_hints) # 2 foolish only + for i in range(0, foolish_only): + tt[hint_locations.pop(0)] = hint_candidates.pop(0)[1] + random.shuffle(hint_candidates) + district_hints -= foolish_only # the rest can be anything + for i in range(0, district_hints): + tt[hint_locations.pop(0)] = hint_candidates.pop(0)[1] if len(hint_locations) > 0: # All remaining hint slots are filled with junk hints. It is done this way to ensure the same junk hint # isn't selected twice. @@ -2358,6 +2389,25 @@ def write_strings(rom, world, player, team): rom.write_bytes(0x181500, data) rom.write_bytes(0x76CC0, [byte for p in pointers for byte in [p & 0xFF, p >> 8 & 0xFF]]) + +useful_item_names = { + 'Mushroom', 'Shovel', 'Magic Powder', 'Progressive Shield', 'Progressive Armor', 'Blue Mail', 'Red Mail', + 'Mirror Shield', 'Blue Boomerang', 'Red Boomerang', 'Bug Catching Net', 'Cane of Byrna', 'Cape', + 'Magic Upgrade (1/2)', 'Magic Upgrade (1/4)', 'Ether', 'Quake'} + + +def useful_item_for_hint(item, world): + return 'Bottle' in item.name or (item.name in useful_item_names + and item.name not in world.required_medallions[item.player]) + + +def text_for_item(item, world, player, team): + if item.player == player: + return item.hint_text + else: + return f'{item.hint_text} for {world.player_names[item.player][team]}' + + def set_inverted_mode(world, player, rom): rom.write_byte(snes_to_pc(0x0283E0), 0xF0) # residual portals rom.write_byte(snes_to_pc(0x02B34D), 0xF0) diff --git a/source/item/District.py b/source/item/District.py index b1dc8c01..309c800c 100644 --- a/source/item/District.py +++ b/source/item/District.py @@ -13,6 +13,9 @@ class District(object): self.entrances = entrances if entrances else [] self.sphere_one = False + self.dungeons = set() + self.access_points = set() + def create_districts(world): world.districts = {} @@ -26,14 +29,14 @@ def create_district_helper(world, player): kak_locations = {'Bottle Merchant', 'Kakariko Tavern', 'Maze Race'} nw_lw_locations = {'Mushroom', 'Master Sword Pedestal'} central_lw_locations = {'Sunken Treasure', 'Flute Spot'} - desert_locations = {'Purple Chest', 'Desert Ledge'} - lake_locations = {'Hobo'} + desert_locations = {'Purple Chest', 'Desert Ledge', 'Bombos Tablet'} + lake_locations = {'Hobo', 'Lake Hylia Island'} east_lw_locations = {"Zora's Ledge", 'King Zora'} - lw_dm_locations = {'Old Man', 'Spectacle Rock', 'Ether Tablet'} + lw_dm_locations = {'Old Man', 'Spectacle Rock', 'Ether Tablet', 'Floating Island'} east_dw_locations = {'Pyramid', 'Catfish'} - south_dw_locations = {'Stumpy', 'Digging Game', 'Bombos Tablet', 'Lake Hylia Island'} + south_dw_locations = {'Stumpy', 'Digging Game'} voo_north_locations = {'Bumper Cave Ledge'} - ddm_locations = {'Floating Island'} + ddm_locations = set() kak_entrances = ['Kakariko Well Cave', 'Bat Cave Cave', 'Elder House (East)', 'Elder House (West)', 'Two Brothers House (East)', 'Two Brothers House (West)', 'Blinds Hideout', 'Chicken House', @@ -43,67 +46,48 @@ def create_district_helper(world, player): nw_lw_entrances = ['North Fairy Cave', 'Lost Woods Hideout Stump', 'Lumberjack Tree Cave', 'Sanctuary', 'Old Man Cave (West)', 'Death Mountain Return Cave (West)', 'Kings Grave', 'Lost Woods Gamble', 'Fortune Teller (Light)', 'Bonk Rock Cave', 'Lumberjack House', 'North Fairy Cave Drop', - 'Lost Woods Hideout Drop', 'Lumberjack Tree Tree', 'Sanctuary Grave'] + 'Lost Woods Hideout Drop', 'Lumberjack Tree Tree', 'Sanctuary Grave', 'Graveyard Cave'] central_lw_entrances = ['Links House', 'Hyrule Castle Entrance (South)', 'Hyrule Castle Entrance (West)', 'Hyrule Castle Entrance (East)', 'Agahnims Tower', 'Hyrule Castle Secret Entrance Stairs', - 'Dam', 'Bonk Fairy (Light)', 'Light Hype Fairy', 'Cave Shop (Lake Hylia)', - 'Lake Hylia Fortune Teller', 'Hyrule Castle Secret Entrance Drop'] + 'Dam', 'Bonk Fairy (Light)', 'Light Hype Fairy', 'Hyrule Castle Secret Entrance Drop', + 'Cave 45'] desert_entrances = ['Desert Palace Entrance (South)', 'Desert Palace Entrance (West)', 'Desert Palace Entrance (North)', 'Desert Palace Entrance (East)', 'Desert Fairy', - 'Aginahs Cave', '50 Rupee Cave'] - lake_entrances = ['Capacity Upgrade', 'Mini Moldorm Cave', 'Good Bee Cave', '20 Rupee Cave', 'Ice Rod Cave'] + 'Aginahs Cave', '50 Rupee Cave', 'Checkerboard Cave'] + lake_entrances = ['Capacity Upgrade', 'Mini Moldorm Cave', 'Good Bee Cave', '20 Rupee Cave', 'Ice Rod Cave', + 'Cave Shop (Lake Hylia)', 'Lake Hylia Fortune Teller'] east_lw_entrances = ['Eastern Palace', 'Waterfall of Wishing', 'Lake Hylia Fairy', 'Sahasrahlas Hut', 'Long Fairy Cave', 'Potion Shop'] lw_dm_entrances = ['Tower of Hera', 'Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave', 'Spectacle Rock Cave (Bottom)', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)', 'Paradox Cave (Top)', 'Fairy Ascension Cave (Bottom)', 'Fairy Ascension Cave (Top)', - 'Spiral Cave', 'Spiral Cave (Bottom)', 'Hookshot Fairy'] + 'Spiral Cave', 'Spiral Cave (Bottom)', 'Hookshot Fairy', 'Mimic Cave'] east_dw_entrances = ['Palace of Darkness', 'Pyramid Entrance', 'Pyramid Fairy', 'East Dark World Hint', 'Palace of Darkness Hint', 'Dark Lake Hylia Fairy', 'Dark World Potion Shop', 'Pyramid Hole'] south_dw_entrances = ['Ice Palace', 'Swamp Palace', 'Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Spike Cave', 'Dark Lake Hylia Ledge Hint', 'Hype Cave', - 'Bonk Fairy (Dark)', 'Archery Game', 'Big Bomb Shop', 'Dark Lake Hylia Shop', 'Cave 45'] + 'Bonk Fairy (Dark)', 'Archery Game', 'Big Bomb Shop', 'Dark Lake Hylia Shop', ] voo_north_entrances = ['Thieves Town', 'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)', 'Skull Woods Final Section', 'Bumper Cave (Bottom)', 'Bumper Cave (Top)', 'Brewery', 'C-Shaped House', 'Chest Game', 'Dark World Hammer Peg Cave', 'Red Shield Shop', 'Dark Sanctuary Hint', - 'Fortune Teller (Dark)', 'Dark World Shop', 'Dark World Lumberjack Shop', 'Graveyard Cave', + 'Fortune Teller (Dark)', 'Dark World Shop', 'Dark World Lumberjack Shop', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole'] - mire_entrances = ['Misery Mire', 'Mire Shed', 'Dark Desert Hint', 'Dark Desert Fairy', 'Checkerboard Cave'] + mire_entrances = ['Misery Mire', 'Mire Shed', 'Dark Desert Hint', 'Dark Desert Fairy'] ddm_entrances = ['Turtle Rock', 'Dark Death Mountain Ledge (West)', 'Dark Death Mountain Ledge (East)', 'Turtle Rock Isolated Ledge Entrance', 'Superbunny Cave (Top)', 'Superbunny Cave (Bottom)', 'Hookshot Cave', 'Hookshot Cave Back Entrance', 'Ganons Tower', 'Spike Cave', - 'Cave Shop (Dark Death Mountain)', 'Dark Death Mountain Fairy', 'Mimic Cave'] + 'Cave Shop (Dark Death Mountain)', 'Dark Death Mountain Fairy'] if inverted: - south_dw_locations.remove('Bombos Tablet') - south_dw_locations.remove('Lake Hylia Island') - voo_north_locations.remove('Bumper Cave Ledge') - ddm_locations.remove('Floating Island') - desert_locations.add('Bombos Tablet') - lake_locations.add('Lake Hylia Island') - nw_lw_locations.add('Bumper Cave Ledge') - lw_dm_locations.add('Floating Island') - - south_dw_entrances.remove('Cave 45') - central_lw_entrances.append('Cave 45') - voo_north_entrances.remove('Graveyard Cave') - nw_lw_entrances.append('Graveyard Cave') - mire_entrances.remove('Checkerboard Cave') - desert_entrances.append('Checkerboard Cave') - ddm_entrances.remove('Mimic Cave') - lw_dm_entrances.append('Mimic Cave') - south_dw_entrances.remove('Big Bomb Shop') central_lw_entrances.append('Inverted Big Bomb Shop') central_lw_entrances.remove('Links House') south_dw_entrances.append('Inverted Links House') voo_north_entrances.remove('Dark Sanctuary') voo_north_entrances.append('Inverted Dark Sanctuary') - voo_north_entrances.remove('Bumper Cave (Top)') - nw_lw_entrances.append('Bumper Cave (Top)') ddm_entrances.remove('Ganons Tower') central_lw_entrances.append('Inverted Ganons Tower') central_lw_entrances.remove('Agahnims Tower') @@ -116,7 +100,7 @@ def create_district_helper(world, player): districts['Kakariko'] = District('Kakariko', kak_locations, entrances=kak_entrances) districts['Northwest Hyrule'] = District('Northwest Hyrule', nw_lw_locations, entrances=nw_lw_entrances) districts['Central Hyrule'] = District('Central Hyrule', central_lw_locations, entrances=central_lw_entrances) - districts['Desert'] = District('Desert', desert_locations, entrances=desert_entrances) + districts['The Desert Area'] = District('Desert', desert_locations, entrances=desert_entrances) districts['Lake Hylia'] = District('Lake Hylia', lake_locations, entrances=lake_entrances) districts['Eastern Hyrule'] = District('Eastern Hyrule', east_lw_locations, entrances=east_lw_entrances) districts['Death Mountain'] = District('Death Mountain', lw_dm_locations, entrances=lw_dm_entrances) @@ -124,7 +108,7 @@ def create_district_helper(world, player): districts['South Dark World'] = District('South Dark World', south_dw_locations, entrances=south_dw_entrances) districts['Northwest Dark World'] = District('Northwest Dark World', voo_north_locations, entrances=voo_north_entrances) - districts['The Mire'] = District('The Mire', set(), entrances=mire_entrances) + districts['The Mire Area'] = District('The Mire', set(), entrances=mire_entrances) districts['Dark Death Mountain'] = District('Dark Death Mountain', ddm_locations, entrances=ddm_entrances) districts.update({x: District(x, set(), dungeon=x) for x in dungeon_table.keys()}) @@ -136,8 +120,9 @@ def resolve_districts(world): state = CollectionState(world) state.sweep_for_events() for player in range(1, world.players + 1): + # these are not static for OWR - but still important + inaccessible = inaccessible_regions_inv if world.mode[player] == 'inverted' else inaccessible_regions_std check_set = find_reachable_locations(state, player) - used_locations = {l for d in world.districts[player].values() for l in d.locations} for name, district in world.districts[player].items(): if district.dungeon: layout = world.dungeon_layouts[player][district.dungeon] @@ -153,12 +138,16 @@ def resolve_districts(world): visited.add(region) if region.type == RegionType.Cave: for location in region.locations: - if location.name not in used_locations and not location.item and location.real: + if not location.item and location.real: district.locations.add(location.name) - used_locations.add(location.name) for ext in region.exits: if ext.connected_region not in visited: queue.appendleft(ext.connected_region) + elif region.type == RegionType.Dungeon and region.dungeon: + district.dungeons.add(region.dungeon.name) + elif region.name in inaccessible: + district.access_points.add(region) + district.sphere_one = len(check_set.intersection(district.locations)) > 0 @@ -169,3 +158,12 @@ def find_reachable_locations(state, player): if location.can_reach(state) and not location.forced_item and location.real: check_set.add(location.name) return check_set + + +inaccessible_regions_std = {'Desert Palace Lone Stairs', 'Bumper Cave Ledge', 'Skull Woods Forest (West)', + 'Dark Death Mountain Ledge', 'Dark Death Mountain Isolated Ledge', + 'Death Mountain Floating Island (Dark World)'} + + +inaccessible_regions_inv = {'Desert Palace Lone Stairs', 'Maze Race Ledge', 'Desert Ledge', + 'Desert Palace Entrance (North) Spot', 'Hyrule Castle Ledge', 'Death Mountain Return Ledge'} From 6fa2cce79d70d0e6f8ae8f40a6990f0998f9de18 Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 8 Mar 2022 08:14:28 -0700 Subject: [PATCH 09/10] Factorial fix --- Utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Utils.py b/Utils.py index 5bd21551..828e466f 100644 --- a/Utils.py +++ b/Utils.py @@ -131,7 +131,7 @@ def kth_combination(k, l, r): def ncr(n, r): - if r == 0: + if r == 0 or r >= n: return 1 return factorial(n) // factorial(r) // factorial(n-r) From 1ebb49fa5918e0ad1843eb5f8ca4abf8e6725d35 Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 8 Mar 2022 08:48:49 -0700 Subject: [PATCH 10/10] Update notes --- RELEASENOTES.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 467d9901..67394d00 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -114,7 +114,9 @@ Same as above but both small keys and bigs keys of the dungeon are not allowed o ## Notes and Bug Fixes - +* 1.0.0.2 + * Include 1.0.1 fixes + * District hint rework * 1.0.0.1 * Add Light Hype Fairy to bombbag mode as needing bombs