From d756960a0b81c3e042ac690d0c03acbc89e09b4d Mon Sep 17 00:00:00 2001 From: Held_der_Zeit <76132257+ZeitHeld@users.noreply.github.com> Date: Tue, 16 Jan 2024 06:54:48 +0100 Subject: [PATCH 01/21] Worlds Docs: Translations German (Clique, BK Sudoku, OoT) (#2581) * Sudoku German * German OOT (+ Room Image) * German Clique * german translation * translation flexibility - ff1 * german setup - oot * Transaltion Flexibilty - SM64 * translation flexibilty - factorio * translation flexibilty - kh2 * translation flexibility - Super Metroid * translation flexibility - Stardew Valley * german translation added - clique * translation flexibility - terraria * translation flexibilty - checksfinder * Sudoku Setup - Grammar Fix * Sudoku Main - Fix Grammar * Revert "translation flexibility - ff1" This reverts commit 6df434c682ef31dbedb88a90137bdc5103b12062. * Revert "Transaltion Flexibilty - SM64" This reverts commit 754bf95d2f9fa75bb5681bb2f6ad37faf1393b14. * Revert "translation flexibilty - factorio" This reverts commit db1226a9dec901e3a5f107ffa53612fe5cf001f0. * Revert "translation flexibility - Super Metroid" This reverts commit ca5bd9a64aa81b70bfb7e35b4e4bd137d93b4f90. * Revert "translation flexibilty - kh2" This reverts commit 076534ee32573f61c64861e2d2f940da95696272. * Revert "translation flexibility - Stardew Valley" This reverts commit 4b137013942262f63e1fbafae6248883b7956f51. * Revert "translation flexibility - terraria" This reverts commit a0abfc8a038d0519dfc55af6155aa62a74399def. * Revert "translation flexibilty - checksfinder" This reverts commit a4de49961d799e0301694b1629d8942780f4a325. * Sugesstion - Fixes in Grammar (and Typos) One or two suggesstions need to be changed a bit further (such as an incomplete sentence) Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> * Update guide_de.md * Update setup_de.md * Update de_Sudoku.md * Update __init__.py * Update worlds/oot/docs/setup_de.md Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> --------- Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> --- worlds/bk_sudoku/__init__.py | 29 ++++--- worlds/bk_sudoku/docs/de_Sudoku.md | 21 +++++ worlds/bk_sudoku/docs/setup_de.md | 27 ++++++ worlds/clique/__init__.py | 30 ++++--- worlds/clique/docs/de_Clique.md | 18 ++++ worlds/clique/docs/guide_de.md | 25 ++++++ worlds/oot/__init__.py | 11 ++- worlds/oot/docs/MultiWorld-Room_oot.png | Bin 0 -> 56829 bytes worlds/oot/docs/de_Ocarina of Time.md | 41 +++++++++ worlds/oot/docs/setup_de.md | 108 ++++++++++++++++++++++++ 10 files changed, 289 insertions(+), 21 deletions(-) create mode 100644 worlds/bk_sudoku/docs/de_Sudoku.md create mode 100644 worlds/bk_sudoku/docs/setup_de.md create mode 100644 worlds/clique/docs/de_Clique.md create mode 100644 worlds/clique/docs/guide_de.md create mode 100644 worlds/oot/docs/MultiWorld-Room_oot.png create mode 100644 worlds/oot/docs/de_Ocarina of Time.md create mode 100644 worlds/oot/docs/setup_de.md diff --git a/worlds/bk_sudoku/__init__.py b/worlds/bk_sudoku/__init__.py index 36d863bb44..195339c380 100644 --- a/worlds/bk_sudoku/__init__.py +++ b/worlds/bk_sudoku/__init__.py @@ -7,16 +7,25 @@ from ..AutoWorld import WebWorld, World class Bk_SudokuWebWorld(WebWorld): options_page = "games/Sudoku/info/en" theme = 'partyTime' - tutorials = [ - Tutorial( - tutorial_name='Setup Guide', - description='A guide to playing BK Sudoku', - language='English', - file_name='setup_en.md', - link='setup/en', - authors=['Jarno'] - ) - ] + + setup_en = Tutorial( + tutorial_name='Setup Guide', + description='A guide to playing BK Sudoku', + language='English', + file_name='setup_en.md', + link='setup/en', + authors=['Jarno'] + ) + setup_de = Tutorial( + tutorial_name='Setup Anleitung', + description='Eine Anleitung um BK-Sudoku zu spielen', + language='Deutsch', + file_name='setup_de.md', + link='setup/de', + authors=['Held_der_Zeit'] + ) + + tutorials = [setup_en, setup_de] class Bk_SudokuWorld(World): diff --git a/worlds/bk_sudoku/docs/de_Sudoku.md b/worlds/bk_sudoku/docs/de_Sudoku.md new file mode 100644 index 0000000000..abb50c5498 --- /dev/null +++ b/worlds/bk_sudoku/docs/de_Sudoku.md @@ -0,0 +1,21 @@ +# BK-Sudoku + +## Was ist das für ein Spiel? + +BK-Sudoku ist kein typisches Archipelago-Spiel; stattdessen ist es ein gewöhnlicher Sudoku-Client der sich zu jeder +beliebigen Multiworld verbinden kann. Einmal verbunden kannst du ein 9x9 Sudoku spielen um einen zufälligen Hinweis +für dein Spiel zu erhalten. Es ist zwar langsam, aber es gibt dir etwas zu tun, solltest du mal nicht in der Lage sein +weitere „Checks” zu erreichen. +(Wer mag kann auch einfach so Sudoku spielen. Man muss nicht mit einer Multiworld verbunden sein, um ein Sudoku zu +spielen/generieren.) + +## Wie werden Hinweise freigeschalten? + +Nach dem Lösen eines Sudokus wird für den verbundenen Slot ein zufällig ausgewählter Hinweis freigegeben, für einen +Gegenstand der noch nicht gefunden wurde. + +## Wo ist die Seite für die Einstellungen? + +Es gibt keine Seite für die Einstellungen. Dieses Spiel kann nicht in deinen YAML-Dateien benutzt werden. Stattdessen +kann sich der Client mit einem beliebigen Slot einer Multiworld verbinden. In dem Client selbst kann aber der +Schwierigkeitsgrad des Sudoku ausgewählt werden. diff --git a/worlds/bk_sudoku/docs/setup_de.md b/worlds/bk_sudoku/docs/setup_de.md new file mode 100644 index 0000000000..71a8e5f624 --- /dev/null +++ b/worlds/bk_sudoku/docs/setup_de.md @@ -0,0 +1,27 @@ +# BK-Sudoku Setup Anleitung + +## Benötigte Software +- [Bk-Sudoku](https://github.com/Jarno458/sudoku) +- Windows 8 oder höher + +## Generelles Konzept + +Dies ist ein Client, der sich mit jedem beliebigen Slot einer Multiworld verbinden kann. Er lässt dich ein (9x9) Sudoku +spielen, um zufällige Hinweise für den verbundenen Slot freizuschalten. + +Aufgrund des Fakts, dass der Sudoku-Client sich zu jedem beliebigen Slot verbinden kann, ist es daher nicht notwendig +eine YAML für dieses Spiel zu generieren, da es keinen neuen Slot zur Multiworld-Session hinzufügt. + +## Installationsprozess + +Gehe zu der aktuellsten (latest) Veröffentlichung der [BK-Sudoku Releases](https://github.com/Jarno458/sudoku/releases). +Downloade und extrahiere/entpacke die `Bk_Sudoku.zip`-Datei. + +## Verbinden mit einer Multiworld + +1. Starte `Bk_Sudoku.exe` +2. Trage den Namen des Slots ein, mit dem du dich verbinden möchtest +3. Trage die Server-URL und den Port ein +4. Drücke auf Verbinden (connect) +5. Wähle deinen Schwierigkeitsgrad +6. Versuche das Sudoku zu Lösen diff --git a/worlds/clique/__init__.py b/worlds/clique/__init__.py index 5838389047..30c0e47f81 100644 --- a/worlds/clique/__init__.py +++ b/worlds/clique/__init__.py @@ -11,16 +11,26 @@ from .Rules import get_button_rule class CliqueWebWorld(WebWorld): theme = "partyTime" - tutorials = [ - Tutorial( - tutorial_name="Start Guide", - description="A guide to playing Clique.", - language="English", - file_name="guide_en.md", - link="guide/en", - authors=["Phar"] - ) - ] + + setup_en = Tutorial( + tutorial_name="Start Guide", + description="A guide to playing Clique.", + language="English", + file_name="guide_en.md", + link="guide/en", + authors=["Phar"] + ) + + setup_de = Tutorial( + tutorial_name="Anleitung zum Anfangen", + description="Eine Anleitung um Clique zu spielen.", + language="Deutsch", + file_name="guide_de.md", + link="guide/de", + authors=["Held_der_Zeit"] + ) + + tutorials = [setup_en, setup_de] class CliqueWorld(World): diff --git a/worlds/clique/docs/de_Clique.md b/worlds/clique/docs/de_Clique.md new file mode 100644 index 0000000000..cde0a23cf6 --- /dev/null +++ b/worlds/clique/docs/de_Clique.md @@ -0,0 +1,18 @@ +# Clique + +## Was ist das für ein Spiel? + +~~Clique ist ein psychologisches Überlebens-Horror Spiel, in dem der Spieler der Versuchung wiederstehen muss große~~ +~~(rote) Knöpfe zu drücken.~~ + +Clique ist ein scherzhaftes Spiel, welches für Archipelago im März 2023 entwickelt wurde, um zu zeigen, wie einfach +es sein kann eine Welt für Archipelago zu entwicklen. Das Ziel des Spiels ist es den großen (standardmäßig) roten +Knopf zu drücken. Wenn ein Spieler auf dem `hard_mode` (schwieriger Modus) spielt, muss dieser warten bis jemand +anderes in der Multiworld den Knopf aktiviert, damit er gedrückt werden kann. + +Clique kann auf den meisten modernen, HTML5-fähigen Browsern gespielt werden. + +## Wo ist die Seite für die Einstellungen? + +Die [Seite für die Spielereinstellungen dieses Spiels](../player-options) enthält alle Optionen die man benötigt um +eine YAML-Datei zu konfigurieren und zu exportieren. diff --git a/worlds/clique/docs/guide_de.md b/worlds/clique/docs/guide_de.md new file mode 100644 index 0000000000..26e08dbbdd --- /dev/null +++ b/worlds/clique/docs/guide_de.md @@ -0,0 +1,25 @@ +# Clique Anleitung + +Nachdem dein Seed generiert wurde, gehe auf die Website von [Clique dem Spiel](http://clique.pharware.com/) und gib +Server-Daten, deinen Slot-Namen und ein Passwort (falls vorhanden) ein. Klicke dann auf "Connect" (Verbinden). + +Wenn du auf "Einfach" spielst, kannst du unbedenklich den Knopf drücken und deine "Befriedigung" erhalten. + +Wenn du auf "Schwer" spielst, ist es sehr wahrscheinlich, dass du warten musst bevor du dein Ziel erreichen kannst. +Glücklicherweise läuft Click auf den meißten großen Browsern, die HTML5 unterstützen. Das heißt du kannst Clique auf +deinem Handy starten und produktiv sein während du wartest! + +Falls du einige Ideen brauchst was du tun kannst, während du wartest bis der Knopf aktiviert wurde, versuche +(mindestens) eins der Folgenden: + +- Dein Zimmer aufräumen. +- Die Wäsche machen. +- Etwas Essen von einem X-Belieben Fast Food Restaruant holen. +- Das tägliche Wordle machen. +- ~~Deine Seele an **Phar** verkaufen.~~ +- Deine Hausaufgaben erledigen. +- Deine Post abholen. + + +~~Solltest du auf irgendwelche Probleme in diesem Spiel stoßen, solltest du keinesfalls nicht **thephar** auf~~ +~~Discord kontaktieren. *zwinker* *zwinker*~~ diff --git a/worlds/oot/__init__.py b/worlds/oot/__init__.py index e9c889d6f6..eb9c41f0b0 100644 --- a/worlds/oot/__init__.py +++ b/worlds/oot/__init__.py @@ -118,7 +118,16 @@ class OOTWeb(WebWorld): ["TheLynk"] ) - tutorials = [setup, setup_es, setup_fr] + setup_de = Tutorial( + setup.tutorial_name, + setup.description, + "Deutsch", + "setup_de.md", + "setup/de", + ["Held_der_Zeit"] + ) + + tutorials = [setup, setup_es, setup_fr, setup_de] class OOTWorld(World): diff --git a/worlds/oot/docs/MultiWorld-Room_oot.png b/worlds/oot/docs/MultiWorld-Room_oot.png new file mode 100644 index 0000000000000000000000000000000000000000..f0f224e5e1af13016d371fbdc87455b0110ee4ae GIT binary patch literal 56829 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

HgOEE#j zN{+q+ZIMIyjGK1rg|XyW$|{#woS)Bm-eIA9yg~fJ`?=@`WBxS3$!Nm>bw_@GTUpPlU&> zmajQE4N(SbGL2#( z2gNS>g*>;f1UdpCw(elRf&7!EQF-%TyQN2}v-M0uW$%92;^jG)ynzc|s~*i42PI=W zUCchH9Y?WPk~G);S9UoV8dYrvm}fGAJ$t)&!cH}3MDF_(q0aoJxZ9R=$8qXLT{lKY zCb6B!>(8*V#Go|_oE_(eKh6X3^m^>yfxq0mXdmrqymi+dW$2hJ-zj2{vL%q19)iy;HyB7-$h=UP<$A(BrN%K;W@AzS}<)b{4>XDk3 z;5f@uIPPb&Wp((?r+r&sQenk89MeceZcCAL$S)FK9_i$kUwl%;#-3s1VA)LGSSV~F zuZS_U4l3<^{5StXBV3Ow?gO)P?ZzUy4y2LW<~H;%F@khIhEoF$3AhFkXGU|UFdG*+ zR%Rj#`81TV*#W?IA%0N`et6yMsnVJy8`TbyuxoVd_L^c&c|N;b5aOErxRm9m2~Cu} zKRUg|s`k2WlvXAB{M=E7xAON8y+1ayg5T(Fq7_%I_3iOyES?c&7^YOsn<`gQ)ZY{6 z9K>HcyrL(o$MPE;c4&?19x%vi2=?{Q<11*JR+*H|xbDU#jc+y6NNJ@~Z0(I6IIR~Gr6u|}(6d$by{-zABcIoqOV~0=b|( z*NW|(=Wsil62tDIum|yxj`zfMb(xaut&aU^c(wS%=&X+S< z&R0ZhA02Aj$~wkaY=CuGYbR!^#bGWR9TUj{V!OXSMj{R5+X^54TVD5j>XxWMBzs8S zWq~@54&KHU(2V+zG4D#3>G0UsC6!8dnBolft?KhAH zl&ieFY<(QOy)_RJqmA>mF)-^BRS8}zym2v5$m3&+opidG{Jk|75qz!`oW*^kgGl`O zD^r5)3Zt@HsM^PAU;cDd3f{Xyxwkc)9YZMv>^($>Zjq4ODmazr4jM9gPg4T(1;$N?s^|4z|A*LZ}d5T~#f zv{{|8P)>ncvf;+NrStU1Ap`b7HXaL<-y|@jV?GjtU^RrIfHFf8+1K&x#*%^kzo#dz zcvZ^*o*#e=w2L4&Hrc$FAebjk&!QTB&Jfo86sr2_5Q~dgjV_&&h57DZE5hA?JQfo} zd?de_p&v@@Vmxw=zqAl(mc4@#NB57IptNMY@a>dn|KHJferh?z&X0fp+BTaRMUap^ZZx)w7b6Ks5KLgmTKA3`iQx`^mWhZVIbRXyOzgp`Ezi*F?Ogz-% z0q_f&vdzJ(E=|c}R1H)J>8@b(agM>8c`#qV>vi46JCLYHvWRPx*le=0g4@#N*YD15 zIa8$ae)nOv4m6-K0{5W&-yVp}b8c+5nM5V!#n2q8MqwfbRz(mF^ zzxRob61<8Y!v~L{4@SZS6Y8klsm?@+4-#R`_@% zip%{4CB*F&{n9+u&~JBy@h|GDoK!lBXB&&!>mCnCr~Paef&s~rTOJ?6S*q#P5X`+t!zjf znr8nh5y`+^DyOyhP$uwe9Mw6} zBC&H-c5Kcgnz8O3^H!%3KN1@^lQOV5U?Q3F{9KV1_+G|&_MHd!Sv`~_0|5Q4?e2_k zRM}j@{aBwWoBo_9jOvPNwP%+|`R&q!z0%cDio`h4MLK$8yAaBjVCQ_}lS_i9DNMv~ zH-@XTK7nSJb73NV8S8Ta-@OJ)#y_3*uB0tk3Y9ZJAT@;DltNN74qwYzQX?QP5|$tm9%N2b!{aCvOtQab=Zu5irLbiQ zRHwC~pfyn$;n_$FR!LWlR3s-m^|h_acjQVANG5%Iqr>Cun?MRA?6rgD12JsXCK=Dm z?jPoaLcH3Ah4w%FyYROfyfj%_#f^;jM@MR@@KY`(WQv&l4jWh8j{HcjSAMk!W@>&_ zx^C*Xw#O@v`hftF7XW%j5mHxBZ@vrk`mYPddw8TUOQ0rrKy&6=>*3}77KP?I_wY)+ zN1v&Q%xa>#CojqGP1X>EripE<^6pLqt!kd>ug?7qqd)LP!njwIx#l|_+;9!Ec|~Ah z>BU|A#(uRkPV9TtLhO4d4ujT8q!RR?)7fDHy~JD>!T~?Iy-jrv3F_39xH4M><8>s&Yq`!3jApWDH*_}Rn-II)t}#D0+N53y(=NH87nrQyY( z*Dl41_x3YwRfcq<%#S~uj{515Su_)Scy;)BNoYs>wt;aeSd7F+}wQ3_eeR*z$L&+8BcMQ2)KhzpZWYT$zDo4g%JNa+;WX^R1f?+gRP%Z~0{%(dv zK)&-9<+Cop;6SBHIBvRs{<_NjR;kW3H3S46nVa@Aa<3bzoM&HhW^Dz)(q6xD> z{Cv03d0KO$WLnLO4np%-I3~T&d<1JMT^~qf~+b zgFY29rJub3i|gC$pi}z|(f~qd_$x89EgsfjJJ~pV4sG*0O?1HcWj!Ba55@I=+8%1xe$2Jkb4qHW$Z3b z(flQtpR;`ZYTM>#Cw&#B3}|P|Z<4L+j*(d_beIOX$c}k>w2=@w&%ve)lF_SY&v(5$ zE8pkFe#g3R%($WIziINHdDF!Yp>c-4mC~}JX5_aAFad7)Z{)xIa{og^<430BKwf2; zQ`f_d{6Km))bH}c5@yE~NV~y(NT_)K5PI=lfc;>HOkZeunts$G&4D#*B6Y!@o&wYn zQhmn76i|n;_SL51r&(+o18YU?_iFTq5$xeoZla#=2kZPT?2G7Icl%i*aBKk+>;We+ ziJZSHd4eDZVw-8*XT_=<@Vt+5`_r6;Hq#o(%7Te90|oUH#v0o$;i@HjO)v%JU$5la zdrq8LBMBIzyJFFzXwRNF(OxoKXjd%mZclCLVu7(LGT{A{7P5*uArU)awG)Ea7QKv4 zmN|TxSV`Y{5QACE%mwUqYXj|#H~2%kH&HxK_K>6L$5$hg5;|L#Lk-WR!`QIfN?DH9#ygG=W&vVHVdmAKGsKO()HpvsD-mJEWe#_ ziYgB6;~ykoPRDWHU>(>{Ninw-MfK?=xIB4KHd~BfRJ@^+y8WY|>!HB!J>AMCEYOM& zfJ;1<`y)^?0LN_kkzbrgECh7*>Cy_yeg3PcL}Gk=0hv&!t|d^M`6X7~0y!SAyBR(`3bmsR(7>|9(O! z*AU2ny}XsGxtE>=3B`uvJOta&7?*oc`*n-%Va7r8h(vAE2u1ud-~Rs1_v!wilkW@G zPH3|(e+cYWb~7Ae2@S0cP=o}rf^n>LvS8@sk17aCQscQ4M($Q{imR6kcT7<~F=2dm zM~1Al$PIBMUJi9<Z!5b2*Ai0u(YUzFc`E3p|tW zy?k@hC8U1f_@;h2P1{>uN^{~(tY9hjWn#4tQH7lDkzc&3S2^Dmqmg+M$Wh$dVw_a1 zW|NF%!FVdsI&BSX%SxIYxvoNhQe~Jb_&$!Lro$*;rEXne0Tj2$p=+vSY^f}0l7{0l zuH=8A;1I#fDf_Ob6&cZ(7Kzm&TN)>N}I2Y3S4>)75 zcAXI{C0XgN)OLkW%f^>=lTY0;g~M;wx4c9HuY`Ts?2vMqYIK`*V|BB^MS*f{uA5KV zJ?mn+BXmjo)DPFwE~M6vmhe5}L+Hf+WFP#z|J73`-yWn`bbEO{>{%DfC@xwKd#`4! zb^XRv*(BH*ef{P|wf)!77@(zoQv-y$X|@wS_+ZPS{F&>fzc~{B$vHopfSEUoTn_7^ z!aA@z=Ta{%St=tOhmHo5)n(gr>SfHaOp}YkyKY8)WHFZM3Ku=nmgrHZ_v-PPB>n)U zVX)$lV-Y<`(tylLu|qvCejJ7jrWQvz+G&`BYBC2d1BmVK^yp!}YUi`n9t|}jw{HM9 z`OU6i-Kqy7$p9sBk7VyL*#JSHTdxE|F72&&VpU_`NmFcay0809vApK!;uh2y^PPko z71g9IeVI67dcdz&R5z9@S!GfQXV+G{8wa@kv+d_ zSxA5rLWEbBCOqZ`{XXCdH}b|mKEYFNax;ExC-}O~-aM*y=kEN8xmL()x^5g8Z6S3o z^Gro3V?f6HR~S3snsKO4HBE&r6YPx-5WpeKfS>`smTB`Jb-TZY^mO@iTDn z->dRm&h@__bnDnWFwxNX?~Zfrbh3{I`UN*zKF7N_MGv5>d=}R?Z@Rk-tb4Tia{nvs zBBNaezs3t*Hq-C?YI2^|mJ^97gI4?m14HgNDIaESg+h)ELEQ01AK+9)yi|rdwIUbJ zIg8ylYs}(J2CKmW*l3f|j2u^ev8x&`O1~kYn9yTKRW??qW+jS<4N9{L6>dapo|%b7 z%d`45Ew*b>lSQ&rKC}wnPSx3qpYAI5EWbd<&`Lyu+8xFw%};iQ?kY`Iw{{F{K%kEV zyc>cKE$M0VidFq-i5p>XO}|zTj1|;0JWFv#49?Y&C!V&Qe$E_KYZz)kDGM3-Nh{{~$rj z2W;Nb6w(px#{&2AdOHQlwto;sfRkMdVydsg#;<3esHKu3P44twFM7ub*CrrWJ*KY{ z&bi8hytSRzze-L&Y3}(&YH?M$-9d7})<==~z$Rj?6Fm1~alGC=o?MeA(;GoW9*r9d zA*7AoZ4cu{ zcC^=xICTtj8zaUlbKVqdH3K){LQNGzLqzK8q=aKxkX4S*U4&LxE!Ky^qzv6nqH-my zDupJo-JBUooeqEd6kkKG-Epf3gv>(gDw7YQIQ2=B6uOs2(^J)WvVvHmNU#TYD8Xb4 zwp*HOJd^|C6IXlL;t~v31CCcqln@J5kDko~q`B8Pc%vYFgif*RE_E8gDMI4-nD;=y z-?3z-gJzS#sqoPD>}9GxsPiJ`W~j69Y_wN5?!c!kB^g0HB6YRf%@>k@%}(ftm)dH>nrU&i%x@CkB6^Df@(3DaF^Qw~L(%m;4}*B9tW5JRt!m zu6a`__(wI8D)nA2dI^e6RYOaa;w`1%enlT;cT9&X32j@pg_aXWChZD6{il4UhV#U* z3A}yHJnE}CW(@-wrT`idm!nb8V)$+AEkOl=jivy@THo#2=eyObji!*1S zPvh8#{i47`QK#2LOF3bTwJ!4>MR9SM^nkVmrWluMI(Lm_AI0PK8(pX_y46u$_e*Z5 z?NZ0=(09p*Ap)-H_SdV3I9YdM=}*V1tD+E7L*y(EWY5-d<7~upVlK9?Jzay5*JT4C zoaP`j(QvnFu>xP|c%vzVx(arFG%8ASr;KKqDn-m2SBO$sOUoCvy)y!Hi`-^rcEa3i zI$m`N@n}gIbxAsiwXkI8c?k>L_LJB^p7QsrDp>N)ioaz0oH%!dbDXRpn0&O12+#7Hc6v)f;lkb z>oXBuR3p_y6AIBxZaM;5ULB?ZC~Lrec{geNl6_B%3~i7(uHn}ox>}hu-Ln%5B0*LbBM3!-kVGC zN+9xNgh#<3<7Eh4%M;D&+hxx*j27i)+zxm;D6N~v+!D^*X3~vt+hVS&{?J~7%{C;? zfN6U?3?tgLv46jZB}$1(fU@71%F@59Z}DJOv_rkQoo!iGU z?s}8;r%GO=BnwNK$;}W0^v+ikZW2NxIL-Lsi-O*^b~T*{Mb+)xte?E=TA9BzR6NT* zPfD%k4a=IULN1urgk@X{&wEy-6)oY@@xkn^ZebQJ#M&Ij_^F#m`$V`&0_Z5)xu;kM ztfLXN@TLg}OG*4CcuvoPC6VqH1qg8it{a^JEqagz%>bceW4o75O^cfBbzAYe<4V+A zi>AMpg33JFrpuaqsHv(q@g!7InaAN%1CQklK=T^XA_OK80{;$1M_p}z7bd9l3dT{- z8y>cv8Sx@Ao!mbC$!cfGfH0lyP>#cE6(f`8!6)29pC0NqI$?YiF)eby-%;Ne8nHLt zWmV(*=$FcJv>1+?%VEyG-R)eymCNx5PvULn`>$ahWW1WkpIy%R&fbha8y9GVGI_{1 zod(#g7S(?(p#Qy+K{aV5=x1EgSv^SLw!Jvfk-$FPiOFhv9lhp^mQz1LlFXS@fuo0?CLW{zW3u z;LEQSG~r`DK|-5HJQq&wt+e=G4ZnB`a>+JfvL_zgdVgTH9K9mcN+1oZY^<|fKEl1A zxpqlClnx{;IrIfd`cWz38yf{e7jF2&G|y-@SVQeGxrqkg=S6;Nk@DO8YJ+2C)>TNd zq?tHSVkm`j=dmglejjeWY4>UgJ%y!>TpQ+Q}3tTIDt^*pFrD&DGlcI z^$TRyR#F^$m$F*rY=L26>4~O{-CHOT?QJlwKt1lB!66M3@eU#Io?+dVc7#XS3PE@| zy5D@L$yEfU50?K@-VsS{rr4}?Ds<3gr9m~2>@HM|!p$tVSaG5neTs1+z}oLNd&|=J z>6gqi>8%zO*x0^!-^hxW)3j&@TP^&_CuROsL9;}~VUGIU6T28PfLL<$mD8Y4^Qxo~65 z+xn{FqP?&uqm{=rNC~4n@%HGDPeDN{Gvd+01X2CP5KW`V1}fVTQ_JjAxnG8J0MIkV zvh!ZLC^ZioV4nk5EyJ_xMn+GbdNnM6SK8;oqB3fj<#P4DlQ8-u*R_6mi+emLDFWyq z4aS8f2}V{K@!y@uklyZ7_T=}v@-#UI!W(Wv zt_|WyNp%U3H9DC;x;n%bs-STO7PnpJeEU4AWgSI`Un|$yejhYK!B*#Oci|MD#0dV}#ro;|F_Du%P;kGP@p;7?Xtx52vYp zJZ+Tx+97i&3_i57#ekMZZcwIz4>* zJui?GM@O@)Ze*d-&C?p>N~{33vYc3Ga9Q$idr3jp$==sNm%h)GnmxwD00DVfRlUOi z`$k1HzggIv37@I#ltkbwqz@g%*{OgXujgpBmrTm#^Z5cd=kaVnG_sSlY#K-6YRg2m z4g-#AkoKIJuH*yyZVK?_gxSxx54IlRVmY%QMcjeqCNr4<Lk2cy(hjp^LjtTeIg2 zQZ;^v0MXKO)H-Hq-KSf5FJV$38F+b{`!cv&8rgLV#kJSk^)xOF5)RBpwhym*JpUf0 z)m+V8DT8~Liz_2#!_!d(( zl1RB^bA6zbpuO1V2H1!Mgj=rxQTW6b5gY5oOq68 z^o&wsz*t}r-3@n03(M9kY{t6&bAcHQ4B4|%q-I;sViUSL5=Xun8LYh5@wqmU)uw(soZ}Yr$=o0@gHx+}Mw>h5mW+y< zZ#oIR2I#fAQkWK$&5olvm0)&fQ~nReYR8dEydS%gm5ca3l+<@nsDJPFG={|EPgx=9 zA?k!th3ANpp0c*4uPHO3RUhl{vgN%eE4}IP$}wk6!NCrmGYo&a-oog?_MF*K>O2_n z^bq5^_opZb{_7v*$AGk$MQOQgx=z{%2AbWE&G$?5g061E%{nhcd~pl@s9J+j@Pk5I zPj~Q`BF}X;@|Fq9?b_W%m{zo0;k^7+dqGl%YJw$i?;;IlGaB zq|98NXntQO;V0h8D(X!_DU(mOs!7uqzwz^z4251N{t!&BmprVYYK-Xd#i!^mT0Nxw zUg*vY#%onWCkJ-I{}l5lv&ye&ip%F+?!QFJvuHU6hkIJ#y`bOxb?(tEt;2bGS-H84 zT5)f_`YY9$vBB6ybO}qSn-}mMIsC*OOOQEps_LRFg zCTc_`{QcHa_<3gLAGAG`eCKZcK>NWJ_3kVt8zU-?2kCZQyh3S}_%qEj_Gb4|{h-!& z9=;-`3TPB#Q3rTplRr>0ZfC37t$yu8#To4e#Pf$=Mpyags&{j*yAjOuz)veH*#r&q z2Y^ChMtdhEoZs&BMm$|z!WW~WxhMS^p;E|j0<2qCD zggK@JqM9(+qF5_#RTXnwLlrr!2%bGIizgq|tgoG6CTKaZ)8J`3)GR$cp{!_OP1f!! z&lY6YrPB56j}6&mGf$|-bSLtd>(e7aHLRZhsKFF4IbbFu8AAfbqaosEbmVYP^Qpz| zsNid-Sb4xSCRrV8UU1+6bZ+>MOwH7sFN;$l6UgKzTn*}=Up(@IuuY=6$Au*Dm_P8O zi=)it<2}4Y5v?F911AKv(l1XnJGdvXPr7xK8wJdiUb>|`=*JF1?ihiO`t?DTd- zl39%m_#y0K`^q^jN`4e%TZ(YJ8ergkwdi`{Q&tGDzyQo~DPhg(8R#~NN#bZot?DKz z5=&|1@GLbfy75vEuHCFY_<=+KDxoORVK3=KR|9i&SZoLX$WH(_zc|D*%#8W|HjfZs zFA>BN<9hv1uSlhHwPwLiU^hW4!P}}1V_P!qLKpsX9O+9x4{mW{q*g+AJ&^`kFU6kp z#olosENm-(+=Y?EIK{n(G}uL54j*j)7;dbP!e8_9ux7FjS_~(+Uwi-wNBAqTB#KHmi!Js3> zB$^|RwcD>5p#e~D9|@lDo%o)!QpJuYF+XB=(aIv-nU-;#s4J{dWKB1t2zFc2V7GM~ zZPn;PF2?nwPhsA`L%WSezzl~@9ifE?&nxArrtYW>!YNmKcEY)sX!oyM)s`g6!5L9n zJN9Wp)13c#ZcJ?u{iJMXA(V{1lZZG^BL5oC2#x`G3Sf^7>i8VLr$3?!osxzG=fkA| z2Xd3QCI>Y()jGp~GVOI5X}3^fnEsuJFt`mPzSd%Vw0YmcBzpOxguzn$c-P$=Ct)Ec z$1R%27Ra!(4vi;}V>Bb!NNtDfdlu`_b&?tX0$A68_#*QkB!DSP73(JN{Jx`q!T=?B zfxdBce}@;#n3h+`{DTkA>@bYChEp=x())NoFQ@hdeN@BAY2RmZLs z`5zlE%KA5Onq^a2&&V=K+2{y?CdKUgS&q8Wd*hTcuTW4@BwE25dYGY0A^Sbofr)De5vXhxZP;$ z;t}x06>B4^OiMW~cnN63Hvt$_Ux{pR00k3avLWBjL;|EK>CP4LsFa)XcyDXn&w=oW;VVGIs` znswO|yAmCecGfC6{ybWtNBKvff3900vJ|gX&I5XDKV0!WQ52@3AM=rWDmH@2>ZK$c zrc?BT%~k&S{b=E11DVb|UzOq(n>J+h744A55>emOgdPyuBUZj}c<`srey+ta2Eo+x zpq#}FYWw{W-2o?~LjMq<{$0E62*safD@@h0x67s7{5UiXW8{@;NmXpstL~L3wY~eH zkdYc3oHNKZKYBuK^BIcV7K+NaKS5Vi%-}Dziu}v`s1mnl-NkBifAugg?dq0uGyTt! z;#IKN4Ni6x%J>>^sw*oEN>uhGr=CUz<%m5_ETSVqwy;jJ;JH`{Ko0pk#@3E<;_|FD z3{@Ok=@LNsuGn2__4VdcdWWr=g#F7|0AlCx%Se4yog_Iu)HDmtf+^p+TlLgHPc1b- z|9Jvf%$WF03||Y<7ZE~0XyetT->hp2)Q9FyE)~0sdj-ja zNTZ?Xxywu%KA67?#v_RHN7qG!vUBmQE7^d*&{QLJB|$kURuOHjYWF`&3n@1DXbKf5 z0%feW1hE>09vLpCEFY#WqS*RnuJ1SDg_1|C+$%11 zrY(}dI3{3`wvkvogzVe3A+8||`Xg+5(>=R!$CufoGR*MTq1m82#|)i$(kxh7gpNdC ze|(ny&uPW(-Rx^~Phphu{Wz-#S($O*A#`h7TeBGufSmW4vxE`HiXCiW9G$ucG~MA4 zDIS~pi|goP43M9rBC$WP_PY4;_d@&E3RCR)jDPGr2cwxRr%u{5Wmy zPfAQ#HRZGOr#>8w5*houlcj}GusV^jN*E8-Ai#PJ$Clyb{-J~@Zy>-uj(9C`Dc|3^ zAMpG56>hK|3sKgx?mr048^YChtS=&04rP#&XxL>svt%`sZ&%tm-A1lkh|qHsgNvQp z!33-hSVmR!SOzTeG`saI5-AjKNHO#>aPXsuI)U+VcKZQ48K-6o3-BmhaJP(7vOU#O zMba0i@jvLV7ZF`qhH&sv4an_7xe=1K6ZGawSN_M-t)DgE02>+5FiO^#^nO!OllYYu z1>tSbrt41)(#`eEg$Qvrh^9MW!^7CXJz0W?p)=TiR9}?%ZI>>Ga6pW|oWLZo=Qn;M zK$g#?-Dmcv?1nG@6RIBmDLvmAT!bjBLbkVG<(3gYK8OKNFZ3X!h;q^V2-khHg9n@Zhv2dELoe?E?KGvYFqz-cSI)jl1MUN$TaG;vJ8 z#yE%0mj!hF7&|;H*0i~c5FSB>F3ac*vY#6Gi)9pUyC7fg<-Dpx2W5d@LS5B^RCm+d zwk!2l?d@l2S(Sz0#s>?}T~Y9V-b4R&qBLB_zCtVn{woTrO^ld5hdN;TB0_I3FJ^#^ zVVI8sCyG_HnJ>7QdR9|NFmYiL^@0U*+)5U zF~J{{SoxB-DQdQ0u{g|FhmgF7^a2@P$fmbi{V2DSTMc~2_VcCIBq|!4+!lz`ct|&k z`Q^<}pqB~Ic)&IdcP}njL^YR>Qu$x4U1vC(?c0B<12t>!G^$q27FF{xLu;fcIwG}O z6sZ_BTO`z;r8R<@MeI@33R2YGdqjnj)JSdd*XMnX|A+U}`{BKh`?$Yc4TkLcEH)V+NcBV#g-d=PeWCir zq#QwLJZdPr;+BB2(dXjT>_NI%bR^iw`5K3Tntl#Jaa`y9r-mLnFNys@TK4b+Yv()h z?93ZB=|<75G3oBDBCO610=WF{58+PH9nI1V#TT8*DZT58(22j7SJ{y3LJ22O_=#`m z0m zoF5A@b^AHsV?y3`A%zVJF{3t}es$+mtWQhML@#*zZ-xX2JBzdV<87P9w_jUUc#E6+ zuL3!PvI6{8@i7Ai1cpZ*x8HVHQ4xPVGk7ntu&={Kn)IU^2cRPh)Eh6;9&43LT9DXQ z{GOeLjf~0gfAZMZS=4jBO1iDgSq((|O`8IL^m`6%2yASSXb4^=mX0LyR8VAG& z8#RbgL-0gXmxavOyxc4Ut(=i}pbm}uI5CQVd76)tWtNp&7t0!BfoOC~{fY>)?{j67 zQ2UQGVdF8j-@w}r&~*~z-Tgq?Zum0s1gM1aGWChx*GMDcOjW{9EuC&NvacNwk@vT? zw}48q7kDS1q1jgG&7=9-P931czkV<*5`1x6bAFq^ofW_WG? zJc!{5Q#%m;63*D+0m18kje%6(!WgvQYZlv{CN;|ea@$JhQ;2eGu{V)o!~uHEmrYadYWhj+7+@=-z85W<0l`%VcIiC<8C=Ub8npQRgU^`Yy^^vTlp`8+ET-aPbH<0SbSrGrrs0pKyvd${@{mDR; zp%;%UqriTrd|wn56&F|4zo#giYpf36fGxKlepChQqZ3@UkZV^r`8LAwFh5{ky6epx zD&G9u8@UPs8%#%w)haIagCYwbDuqZFoYtOZ>Bb~md*-w`Z;IVR?QKcn%K=XH{cNg_ z)Eh3t8$9$ZW_1Q=gvMd}QNq>qnQpVxej zV_~QuhSebSwJb$lZk2NrkQO->(I4TBxh*-!*Gn$PrwGp|9%1>W9$5l|Cb%}7hS^qeqCar50eVlZ6qJJ19w6;BYLu6Raca0kl z&ioA7hqRHe7)rNbmfw+&EN~s2L&=?K9#`G`20}WRYxiUvXfEhlL#RG=j&h@lCxbgK z$nTBeFL!xp{V3`=jsI&3(@`@Gdd7yh5(;@Jos+>~96f7~#cC3Fq8>P6W zR~R9aYy9a7yv~==Wj3Wx&d!HX1A*e$9$~wG{Ke~2Mud-~{5TR2#KF_?v@i`gT-d*u z;@cvgQSgX8o}@PzaThf+BAeWS%$YC%ZUYL)*SSd(db1{CN_cIh@oYV-#!rm_hXuXl zLi(}9rMwN%^~X&rYpzE`^T#aKce>pCl!#uLe~i3Tpk_XY_a=AnR}<}{!^J)8H)$=9 zFslAzh_fdb-aQ*;u{$ykoEJ`OG33NnCe`7uw0VMYx^d6rB2X=B&|o&noVMKNZiQ4M zo_Z>vOG$W$t7PGztsUNi-jrp(zCM=MHwQQ+bxB!+9_ee0s^rF-ACOqT!tp7BXB zzP;hjWFpEpj3Y9sTG%(wY)P05-d|Vh>r%~Xh_n&|`YY!e>ckaenUlBl2&9p|6I)h% zFUuHN_#BD&vuS{hV4k}7Vs6Kfy%W!ww+S})u3h6d-x(GR=W$)`t0Qdz551rPyKXh{ z;`4Neca*S!HS(Gy7@)P=_wx4gFR*CdQY|-6$*d&HME6Iii z=L!Byn&-Mf3=|_d37D4f&o+Jta>)bd;iNBy*(tGTNwLFc`^Yn2t8-CbwUme$^uO!j zZ**jljxpE5YO$__c2@IO4u+pAFJ2w6$**>(&}kg_ zV0e%%*HZ*{lLg;@S@Hyar;Q?KhxCdcy*#a>mkIAz)J`Bh+}665wo*6MOXaD?oRl49 zyzJulxjMt)VK!*B{2f6m2ON!tBJan!a2IIGaiN?unQmH(gtf9vw`=W8+mPa9)e(qa zu9S*of$mdU{8w=g#^lR_F8pr6>s5INHR_T@)|?(Z#NQ*=7tg_w^SOSMrUvVaq>uG z`4NDnx4H$ngym~$*6fdERaLX=kTKaEqoh*W29?G9>Yn9U{F9k0AH9@$?lG2XeQDp| z9P5|)9-VFMn;+aojnD9&kN;;o%3`*!5Iw)McV77yS#pEp=l^Z>zGXJKu)y@zBB>7X zrvY5~EzGK>=eE@UjQ#od27}d`;D3_-q=x<90GUBYI*44de0vcJV>;5hknzWp?9b~k z2yDi!k$#{!q+2@|9yaVnsXgf{D=nmbE6`lOu{XoaE^rkLCT;yUzx&YibVPdbp`s7@ z3}RwI8+M)ZOTjU@+bJ*ye2=CGjj=*j#4kzY{#!qaJBg-LP%d&%N)t;XIl}8(>fbS_ zHgrz8`L8^;&6s&~pMRJibe6rjs`oBC;J9#OPM!F;*)+%ayNLmwpAew%93wP4uf^k-T}ehs8>54#_&X>E$8 zG?c{pMLasSw(4T2z4bz7$k;}l8gRN;cYu@&Qh6#icgz%E4@Ju@H#8Y}$eNsO$d{xY z{Y7H*_AQT=4HE*NqZQ#c>FTmSj09B)7K+ue;}Ycn zFS7lHC5Cu_Z?pM=`$fUVUM`}JrnZ0ky32?f+)Xl+ZYoX_s-=- zg(i`cdOMApNwBo6(r;a7^Ds~xb8q8~<{_oy{eIl%L@(u?a7nVGXvUM=+onpOi#_V6 z%6lZ~wI&s=I3G}JJX$q&Apmln7+~x@GBL(9-yt37H7g+?(d+GuL+BEVTJW5J)#CR( zkq3)y3riu=n_{Mov0KgqMU*eK^4hPTH)2R<1ZSqbq$!8VLeA3FiVO~WH)X^6h6Tt% zDXsx}c{TG?}2< zhN&!$JgUc~jsrOCpTd&#_iFV^SATngXYjc*_8RJVnX^`CPs-iVYgHTd zr|Ipi-3BdEt;kS=B&pNLtzXxJ`&CghTDhjNYd@7d^#Xo!Z|=jQ4S5|~nUeKR2NtZU zNmz8CEH_WjriGVE>;%Glsk^W0+KO2i$Zx)@jKYG10h;J!mstcqshhpIsT>q~9!7oC zQaxhAJhO{@(fAo$j#yTzRjQHxgAZ3rUFm+@28R39BdUTkSq^T*{&==IX%=;Sh=!jT zz5-hWKFZth6II{hIluGI#%t9?HzJl9N;AE^>n}lBd?55VvM$)n<(NVUfBpM`|Cun; z8ePBbfRql057BYAVmA*s82iM2?*h-4n4B%|8eH~7tAQ=;(mVu z<5j6GvS;>!@bsBbEi^*&;H~IMd1AH1)w>}GasOZPCqMlKDZBgcXjmBcQ0qTwT>@kP z`vUb$tg3lw+^XHCor(sUcly++H9#=Uou_b^D!9X9+CPyzw!j8S@8HDG^P0y@1`iXA z>wqtZgjH&7zm zZq5{4Xt~H_w}1>facM%VtB1nMmNZnV+ASK$(s!m?G6!FOcp1~@8~>B|$$0@qAvnu= zMOBs;eG!F}cux<+DOJV4;E|;L!aqJP4CVq8e?pVWtg?Dg_ zQZC7d8gS)=rX{c1_Iq6pY36NHSRisGbUt|io59P2hK}P{{CalPvTdiv+oaWfBLwgWvaSZAE{THqLAI-{~xK;aap;A6;r^W_{?b#zykakm)zaLUs% zEg3m#Tm^CUd4fTJGDYgj+`k0SAnz`xR~xUV;_r6t(hQA#&~-&3PMbz-8WWc!``A#o zhtV!eu`g0L>-)!c6`%S66jXIIdy+RX&8eq&W!d$)5-;{3AA*n2Sul^KjXHb3ER#w& zSmfj)E;e#iL4J^_U}@FD0z|97$nC96dcCMJJp6-K!&rPUp!+%i@UmNTnc7*I-Gs_; zHpAx!EcTI*xbjJ*Zp!}O(O=X#7fA)p_ciYcJ?1>4jw&82LUMjHR{mP zaN4)Rc2^Q$4ZJDy6c(L`-sj%~;h@a38n)}gYylKwzP@s4QUkd_fg0=`c=NPGd#cWq zS;f4|yHVCooAaEL0Yo`ig{BHYU9#f|%RB1Z=ZE)hLR1efWy$P4= zXuEvYL!-?6a}Bk3dbIfhe>Lg&l7!UFIq4KQD= zCl&O^&6RLkf zW{J!bGjNASET>n+CgkyF<~y0V&Ax?XMuoL%`mCnP-$04}Bf7oxpUa17Cgy__gHpy3 zNRO_((Ws*lJjuao+{-iSkxE+bXq|snVfv;MkrdU6{j?4{LGyRTZS>;d$r#mMS3j zT^UcVE+G`WN3qVx!oK$I_=b+g-H2wc=&a|F8}N`n;L znO?6p^!c4FEu>({aeFPrY;TsLm83(7 zTVKem?0r+ZQ^vY^PyOnuy}z>X%NHB=w)_!xP-}^_hAClB`IX+sxy=W%PitMBI8Ejx e>5c%4XBP$3(05Zwcl7@{fAn>Xv@x2`g8u_$H#DvQ literal 0 HcmV?d00001 diff --git a/worlds/oot/docs/de_Ocarina of Time.md b/worlds/oot/docs/de_Ocarina of Time.md new file mode 100644 index 0000000000..4d9fd2ea14 --- /dev/null +++ b/worlds/oot/docs/de_Ocarina of Time.md @@ -0,0 +1,41 @@ +# The Legend of Zelda: Ocarina of Time + +## Wo ist die Seite für die Einstellungen? + +Die [Seite für die Spielereinstellungen dieses Spiels](../player-options) enthält alle Optionen die man benötigt um +eine YAML-Datei zu konfigurieren und zu exportieren. + +## Was macht der Randomizer in diesem Spiel? + +Items, welche der Spieler für gewöhnlich im Verlauf des Spiels erhalten würde, wurden umhergemischt. Die Logik bleit +bestehen, damit ist das Spiel immer durchspielbar. Doch weil die Items durch das ganze Spiel gemischt wurden, müssen + manche Bereiche früher bescuht werden, als man es in Vanilla tun würde. +Eine Liste von implementierter Logik, die unoffensichtlich erscheinen kann, kann +[hier (Englisch)](https://wiki.ootrandomizer.com/index.php?title=Logic) gefunden werden. + +## Welche Items und Bereiche werden gemischt? + +Alle ausrüstbare und sammelbare Gegenstände, sowie Munition können gemischt werden. Und alle Bereiche, die einen +dieser Items enthalten könnten, haben (sehr wahrscheinlich) ihren Inhalt verändert. Goldene Skulltulas können ebenfalls +dazugezählt werden, je nach Wunsch des Spielers. + +## Welche Items können in sich in der Welt eines anderen Spielers befinden? + +Jedes dieser Items, die gemicht werden können, können in einer Multiworld auch in der Welt eines anderen Spielers +fallen. Es ist jedoch möglich ausgewählte Items auf deine eigene Welt zu beschränken. + +## Wie sieht ein Item einer anderen Welt in OoT aus? + +Items, die zu einer anderen Welt gehören, werden repräsentiert durch Zelda's Brief. + +## Was passiert, wenn der Spieler ein Item erhält? + +Sobald der Spieler ein Item erhält, wird Link das Item über seinen Kopf halten und der ganzen Welt präsentieren. +Gut für's Geschäft! + +## Einzigartige Lokale Befehle + +Die folgenden Befehle stehen nur im OoTClient, um mit Archipelago zu spielen, zur Verfügung: + +- `/n64` Überprüffe den Verbindungsstatus deiner N64 +- `/deathlink` Schalte den "Deathlink" des Clients um. Überschreibt die zuvor konfigurierten Einstellungen. diff --git a/worlds/oot/docs/setup_de.md b/worlds/oot/docs/setup_de.md new file mode 100644 index 0000000000..92c3150a7d --- /dev/null +++ b/worlds/oot/docs/setup_de.md @@ -0,0 +1,108 @@ +# Setup Anleitung für Ocarina of Time: Archipelago Edition + +## WICHTIG + +Da wir BizHawk benutzen, gilt diese Anleitung nur für Windows und Linux. + +## Benötigte Software + +- BizHawk: [BizHawk Veröffentlichungen von TASVideos](https://tasvideos.org/BizHawk/ReleaseHistory) + - Version 2.3.1 und später werden unterstützt. Version 2.9 ist empfohlen. + - Detailierte Installtionsanweisungen für BizHawk können über den obrigen Link gefunden werden. + - Windows-Benutzer müssen die Prerequisiten installiert haben. Diese können ebenfalls über + den obrigen Link gefunden werden. +- Der integrierte Archipelago-Client, welcher [hier](https://github.com/ArchipelagoMW/Archipelago/releases) installiert + werden kann. +- Eine `Ocarina of Time v1.0 US(?) ROM`. (Nicht aus Europa und keine Master-Quest oder Debug-Rom!) + +## Konfigurieren von BizHawk + +Sobald Bizhawk einmal installiert wurde, öffne **EmuHawk** und ändere die folgenen Einsteluungen: + +- (≤ 2.8) Gehe zu `Config > Customize`. Wechlse zu dem `Advanced`-Reiter, wechsle dann den `Lua Core` von "NLua+KopiLua" zu + `"Lua+LuaInterface"`. Starte danach EmuHawk neu. Dies ist zwingend notwendig, damit die Lua-Scripts, mit denen man sich mit dem Client verbindet, ordnungsgemäß funktionieren. + **ANMERKUNG: Selbst wenn "Lua+LuaInterface" bereits ausgewählt ist, wechsle zwischen den beiden Optionen umher und** + **wähle es erneut aus. Neue Installationen oder Versionen von EmuHawk neigen dazu "Lua+LuaInterface" als die** + **Standard-Option anzuzeigen, aber laden dennoch "NLua+KopiLua", bis dieser Schritt getan ist.** +- Unter `Config > Customize > Advanced`, gehe sicher dass der Haken bei `AutoSaveRAM` ausgeählt ist, und klicke dann + den 5s-Knopf. Dies verringert die Wahrscheinlichkeit den Speicherfrotschritt zu verlieren, sollte der Emulator mal + abstürzen. +- **(Optional)** Unter `Config > Customize` kannst du die Haken in den "Run in background" + (Laufe weiter im Hintergrund) und "Accept background input" (akzeptiere Tastendruck im Hintergrund) Kästchen setzen. + Dies erlaubt dir das Spiel im Hintergrund weiter zu spielen, selbst wenn ein anderes Fenster aktiv ist. (Nützlich bei + mehreren oder eher großen Bildschrimen/Monitoren.) +- Unter `Config > Hotkeys` sind viele Hotkeys, die mit oft genuten Tasten belegt worden sind. Es wird empfohlen die + meisten (oder alle) Hotkeys zu deaktivieren. Dies kann schnell mit `Esc` erledigt werden. +- Wird mit einem Kontroller gespielt, bei der Tastenbelegung (bei einem Laufendem Spiel, unter + `Config > Controllers...`), deaktiviere "P1 A Up", "P1 A Down", "P1 A Left", and "P1 A Right" und gehe stattdessen in + den Reiter `Analog Controls` um den Stick zu belegen, da sonst Probleme beim Zielen auftreten (mit dem Bogen oder + ähnliches). Y-Axis ist für Oben und Unten, und die X-Axis ist für Links und Rechts. +- Unter `N64` setze einen Haken bei "Use Expansion Slot" (Benutze Erweiterungs-Slot). Dies wird benötigt damit + savestates/schnellspeichern funktioniert. (Das N64-Menü taucht nur **nach** dem laden einer N64-ROM auf.) + +Es wird sehr empfohlen N64 Rom-Erweiterungen (\*.n64, \*.z64) mit dem Emuhawk - welcher zuvor installiert wurde - zu +verknüpfen. +Um dies zu tun, muss eine beliebige N64 Rom aufgefunden werden, welche in deinem Besitz ist, diese Rechtsklicken und +dann auf "Öffnen mit..." gehen. Gehe dann auf "Andere App auswählen" und suche nach deinen BizHawk-Ordner, in der +sich der Emulator befindet, und wähle dann `EmuHawk.exe` **(NICHT "DiscoHawk.exe"!)** aus. + +Eine Alternative BizHawk Setup Anleitung (auf Englisch), sowie weitere Hilfe bei Problemen kann +[hier](https://wiki.ootrandomizer.com/index.php?title=Bizhawk) gefunden werden. + +## Erstelle eine YAML-Datei + +### Was ist eine YAML-Datei und Warum brauch ich eine? + +Eine YAML-Datie enthält einen Satz an einstellbaren Optionen, die dem Generator mitteilen, wie +dein Spiel generiert werden soll. In einer Multiworld stellt jeder Spieler eine eigene YAML-Datei zur Verfügung. Dies +erlaubt jeden Spieler eine personalisierte Erfahrung nach derem Geschmack. Damit kann auch jeder Spieler in einer +Multiworld (des gleichen Spiels) völlig unterschiedliche Einstellungen haben. + +Für weitere Informationen, besuche die allgemeine Anleitung zum Erstellen einer +YAML-Datei: [Archipelago Setup Anleitung](/tutorial/Archipelago/setup/en) + +### Woher bekomme ich eine YAML-Datei? + +Die Seite für die Spielereinstellungen auf dieser Website erlaubt es dir deine persönlichen Einstellungen nach +vorlieben zu konfigurieren und eine YAML-Datei zu exportieren. +Seite für die Spielereinstellungen: +[Seite für die Spielereinstellungen von Ocarina of Time](/games/Ocarina%20of%20Time/player-options) + +### Überprüfen deiner YAML-Datei + +Wenn du deine YAML-Datei überprüfen möchtest, um sicher zu gehen, dass sie funktioniert, kannst du dies auf der +YAML-Überprüfungsseite tun. +YAML-Überprüfungsseite: [YAML-Überprüfungsseite](/check) + +## Beitreten einer Multiworld + +### Erhalte deinen OoT-Patch + +(Der folgende Prozess ist bei den meisten ROM-basierenden Spielen sehr ähnlich.) + +Wenn du einer Multiworld beitrittst, wirst du gefordert eine YAML-Datei bei dem Host abzugeben. Ist dies getan, +erhälst du (in der Regel) einen Link vom Host der Multiworld. Dieser führt dich zu einem Raum, in dem alle +teilnehmenden Spieler (bzw. Welten) aufgelistet sind. Du solltest dich dann auf **deine** Welt konzentrieren +und klicke dann auf `Download APZ5 File...`. +![Screenshot of a Multiworld Room with an Ocarina of Time Player](/static/generated/docs/Ocarina%20of%20Time/MultiWorld-room_oot.png) + +Führe die `.apz5`-Datei mit einem Doppelklick aus, um deinen Ocarina Of Time-Client zu starten, sowie das patchen +deiner ROM. Ist dieser Prozess fertig (kann etwas dauern), startet sich der Client und der Emulator automatisch +(sofern das "Öffnen mit..." ausgewählt wurde). + +### Verbinde zum Multiserver + +Sind einmal der Client und der Emulator gestartet, müssen sie nur noch miteinander verbunden werden. Gehe dazu in +deinen Archipelago-Ordner, dann zu `data/lua`, und füge das `connector_oot.lua` Script per Drag&Drop (ziehen und +fallen lassen) auf das EmuHawk-Fenster. (Alternativ kannst du die Lua-Konsole manuell öffnen, gehe dazu auf +`Script > Open Script` und durchsuche die Ordner nach `data/lua/connector_oot.lua`.) + +Um den Client mit dem Multiserver zu verbinden, füge einfach `:` in das Textfeld ganz oben im +Client ein und drücke Enter oder "Connect" (verbinden). Wird ein Passwort benötigt, musst du es danach unten in das +Textfeld (für den Chat und Befehle) eingeben. +Alternativ kannst du auch in dem unterem Textfeld den folgenden Befehl schreiben: +`/connect : [Passwort]` (wie die Adresse und der Port lautet steht in dem Raum, oder wird von deinem +Host an dich weitergegeben.) +Beispiel: `/connect archipelago.gg:12345 Passw123` + +Du bist nun bereit für dein Zeitreise-Abenteuer in Hyrule. From adad7b532de9c94fe8d6e2217b1edf83ba183cb7 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Tue, 16 Jan 2024 07:09:54 -0500 Subject: [PATCH 02/21] Lingo: Turn The Colorful into a countdown achievement (#2710) The Colorful currently, in logic, does not expect you to solve the achievement panel until all of the doors are opened. This is not enforced by the client in complex door shuffle. It is also not typical of how achievements in Lingo usually work, and it ended up this way because of the fact that The Colorful is, uniquely, not a countdown panel. This change modifies logic so that solving each panel within The Colorful is required in order to access the achievement, rather than opening all of the doors. This will be accompanied by a change to the client that will turn the achievement panel into a countdown. --- worlds/lingo/data/LL1.yaml | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/worlds/lingo/data/LL1.yaml b/worlds/lingo/data/LL1.yaml index ea5886fea0..cc46677990 100644 --- a/worlds/lingo/data/LL1.yaml +++ b/worlds/lingo/data/LL1.yaml @@ -2635,12 +2635,6 @@ panels: - OBSTACLE The Colorful: - # The set of required_doors in the achievement panel should prevent - # generation from asking you to solve The Colorful before opening all of the - # doors. Access from the roof is included so that the painting here could be - # an entrance. The client will have to be hardcoded to not open the door to - # the achievement until all of the doors are open, whether by solving the - # panels or through receiving items. entrances: The Colorful (Gray): room: The Colorful (Gray) @@ -2651,27 +2645,27 @@ id: Countdown Panels/Panel_colorful_colorful check: True tag: forbid - required_door: + required_panel: - room: The Colorful (White) - door: Progress Door + panel: BEGIN - room: The Colorful (Black) - door: Progress Door + panel: FOUND - room: The Colorful (Red) - door: Progress Door + panel: LOAF - room: The Colorful (Yellow) - door: Progress Door + panel: CREAM - room: The Colorful (Blue) - door: Progress Door + panel: SUN - room: The Colorful (Purple) - door: Progress Door + panel: SPOON - room: The Colorful (Orange) - door: Progress Door + panel: LETTERS - room: The Colorful (Green) - door: Progress Door + panel: WALLS - room: The Colorful (Brown) - door: Progress Door + panel: IRON - room: The Colorful (Gray) - door: Progress Door + panel: OBSTACLE achievement: The Colorful paintings: - id: arrows_painting_12 From c6896c6af9dd408b6827f031db06afcb7eeebaf7 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Tue, 16 Jan 2024 07:11:20 -0500 Subject: [PATCH 03/21] Lingo: Make The Colorful optionally progressive (#2711) --- worlds/lingo/data/LL1.yaml | 22 ++++++++++++++++++++++ worlds/lingo/data/ids.yaml | 1 + worlds/lingo/items.py | 6 ++++++ worlds/lingo/options.py | 8 ++++++++ worlds/lingo/player_logic.py | 3 ++- 5 files changed, 39 insertions(+), 1 deletion(-) diff --git a/worlds/lingo/data/LL1.yaml b/worlds/lingo/data/LL1.yaml index cc46677990..d7d4630e86 100644 --- a/worlds/lingo/data/LL1.yaml +++ b/worlds/lingo/data/LL1.yaml @@ -2670,6 +2670,28 @@ paintings: - id: arrows_painting_12 orientation: north + progression: + Progressive Colorful: + - room: The Colorful (White) + door: Progress Door + - room: The Colorful (Black) + door: Progress Door + - room: The Colorful (Red) + door: Progress Door + - room: The Colorful (Yellow) + door: Progress Door + - room: The Colorful (Blue) + door: Progress Door + - room: The Colorful (Purple) + door: Progress Door + - room: The Colorful (Orange) + door: Progress Door + - room: The Colorful (Green) + door: Progress Door + - room: The Colorful (Brown) + door: Progress Door + - room: The Colorful (Gray) + door: Progress Door Welcome Back Area: entrances: Starting Room: diff --git a/worlds/lingo/data/ids.yaml b/worlds/lingo/data/ids.yaml index 3239f21854..2b9e7f3d8c 100644 --- a/worlds/lingo/data/ids.yaml +++ b/worlds/lingo/data/ids.yaml @@ -1452,3 +1452,4 @@ progression: Progressive Fearless: 444470 Progressive Orange Tower: 444482 Progressive Art Gallery: 444563 + Progressive Colorful: 444580 diff --git a/worlds/lingo/items.py b/worlds/lingo/items.py index af24570f27..7b1a650561 100644 --- a/worlds/lingo/items.py +++ b/worlds/lingo/items.py @@ -28,6 +28,10 @@ class ItemData(NamedTuple): # door shuffle is on and tower isn't progressive return world.options.shuffle_doors != ShuffleDoors.option_none \ and not world.options.progressive_orange_tower + elif self.mode == "the colorful": + # complex door shuffle is on and colorful isn't progressive + return world.options.shuffle_doors == ShuffleDoors.option_complex \ + and not world.options.progressive_colorful elif self.mode == "complex door": return world.options.shuffle_doors == ShuffleDoors.option_complex elif self.mode == "door group": @@ -70,6 +74,8 @@ def load_item_data(): if room_name in PROGRESSION_BY_ROOM and door_name in PROGRESSION_BY_ROOM[room_name]: if room_name == "Orange Tower": door_mode = "orange tower" + elif room_name == "The Colorful": + door_mode = "the colorful" else: door_mode = "special" diff --git a/worlds/lingo/options.py b/worlds/lingo/options.py index c00208621f..ec6158fab5 100644 --- a/worlds/lingo/options.py +++ b/worlds/lingo/options.py @@ -21,6 +21,13 @@ class ProgressiveOrangeTower(DefaultOnToggle): display_name = "Progressive Orange Tower" +class ProgressiveColorful(DefaultOnToggle): + """When "Shuffle Doors" is on "complex", this setting governs the manner in which The Colorful opens up. + If off, there is an item for each room of The Colorful, meaning that random rooms in the middle of the sequence can open up without giving you access to them. + If on, there are ten progressive items, which open up the sequence from White forward.""" + display_name = "Progressive Colorful" + + class LocationChecks(Choice): """On "normal", there will be a location check for each panel set that would ordinarily open a door, as well as for achievement panels and a small handful of other panels. @@ -117,6 +124,7 @@ class DeathLink(Toggle): class LingoOptions(PerGameCommonOptions): shuffle_doors: ShuffleDoors progressive_orange_tower: ProgressiveOrangeTower + progressive_colorful: ProgressiveColorful location_checks: LocationChecks shuffle_colors: ShuffleColors shuffle_panels: ShufflePanels diff --git a/worlds/lingo/player_logic.py b/worlds/lingo/player_logic.py index fa497c59bd..57bcc4bfd5 100644 --- a/worlds/lingo/player_logic.py +++ b/worlds/lingo/player_logic.py @@ -83,7 +83,8 @@ class LingoPlayerLogic: def handle_non_grouped_door(self, room_name: str, door_data: Door, world: "LingoWorld"): if room_name in PROGRESSION_BY_ROOM and door_data.name in PROGRESSION_BY_ROOM[room_name]: - if room_name == "Orange Tower" and not world.options.progressive_orange_tower: + if (room_name == "Orange Tower" and not world.options.progressive_orange_tower)\ + or (room_name == "The Colorful" and not world.options.progressive_colorful): self.set_door_item(room_name, door_data.name, door_data.item_name) else: progressive_item_name = PROGRESSION_BY_ROOM[room_name][door_data.name].item_name From 0efc13fc8a2b8b8a3bfd391a9f8ef5be804337ab Mon Sep 17 00:00:00 2001 From: JaredWeakStrike <96694163+JaredWeakStrike@users.noreply.github.com> Date: Tue, 16 Jan 2024 07:12:33 -0500 Subject: [PATCH 04/21] KH2: Location Groups and Subclasses (#2700) --- worlds/kh2/Client.py | 3 +- worlds/kh2/Items.py | 21 +----- worlds/kh2/Locations.py | 145 +++++++++++++++++++-------------------- worlds/kh2/Regions.py | 11 +-- worlds/kh2/Rules.py | 2 +- worlds/kh2/Subclasses.py | 29 ++++++++ worlds/kh2/__init__.py | 10 +-- 7 files changed, 118 insertions(+), 103 deletions(-) create mode 100644 worlds/kh2/Subclasses.py diff --git a/worlds/kh2/Client.py b/worlds/kh2/Client.py index 544e710741..513d85257b 100644 --- a/worlds/kh2/Client.py +++ b/worlds/kh2/Client.py @@ -821,7 +821,8 @@ class KH2Context(CommonContext): def finishedGame(ctx: KH2Context, message): if ctx.kh2slotdata['FinalXemnas'] == 1: - if not ctx.final_xemnas and ctx.kh2_loc_name_to_id[LocationName.FinalXemnas] in ctx.locations_checked: + if not ctx.final_xemnas and ctx.kh2_read_byte(ctx.Save + all_world_locations[LocationName.FinalXemnas].addrObtained) \ + & 0x1 << all_world_locations[LocationName.FinalXemnas].bitIndex > 0: ctx.final_xemnas = True # three proofs if ctx.kh2slotdata['Goal'] == 0: diff --git a/worlds/kh2/Items.py b/worlds/kh2/Items.py index 3e656b418b..cb3d7c8d85 100644 --- a/worlds/kh2/Items.py +++ b/worlds/kh2/Items.py @@ -2,22 +2,7 @@ import typing from BaseClasses import Item from .Names import ItemName - - -class KH2Item(Item): - game: str = "Kingdom Hearts 2" - - -class ItemData(typing.NamedTuple): - quantity: int = 0 - kh2id: int = 0 - # Save+ mem addr - memaddr: int = 0 - # some items have bitmasks. if bitmask>0 bitor to give item else - bitmask: int = 0 - # if ability then - ability: bool = False - +from .Subclasses import ItemData # 0x130000 Reports_Table = { @@ -209,7 +194,7 @@ Armor_Table = { ItemName.GrandRibbon: ItemData(1, 157, 0x35D4), } Usefull_Table = { - ItemName.MickeyMunnyPouch: ItemData(1, 535, 0x3695), # 5000 munny per + ItemName.MickeyMunnyPouch: ItemData(1, 535, 0x3695), # 5000 munny per ItemName.OletteMunnyPouch: ItemData(2, 362, 0x363C), # 2500 munny per ItemName.HadesCupTrophy: ItemData(1, 537, 0x3696), ItemName.UnknownDisk: ItemData(1, 462, 0x365F), @@ -349,7 +334,7 @@ GoofyAbility_Table = { Wincon_Table = { ItemName.LuckyEmblem: ItemData(kh2id=367, memaddr=0x3641), # letter item - ItemName.Victory: ItemData(kh2id=263, memaddr=0x111), + # ItemName.Victory: ItemData(kh2id=263, memaddr=0x111), ItemName.Bounty: ItemData(kh2id=461, memaddr=0x365E), # Dummy 14 # ItemName.UniversalKey:ItemData(,365,0x363F,0)#Tournament Poster } diff --git a/worlds/kh2/Locations.py b/worlds/kh2/Locations.py index 9d7d948443..61fafe9094 100644 --- a/worlds/kh2/Locations.py +++ b/worlds/kh2/Locations.py @@ -1,19 +1,9 @@ import typing from BaseClasses import Location -from .Names import LocationName, ItemName - - -class KH2Location(Location): - game: str = "Kingdom Hearts 2" - - -class LocationData(typing.NamedTuple): - locid: int - yml: str - charName: str = "Sora" - charNumber: int = 1 - +from .Names import LocationName, ItemName, RegionName +from .Subclasses import LocationData +from .Regions import KH2REGIONS # data's addrcheck sys3 addr obtained roomid bit index is eventid LoD_Checks = { @@ -541,7 +531,7 @@ TWTNW_Checks = { LocationName.Xemnas1: LocationData(26, "Double Get Bonus"), LocationName.Xemnas1GetBonus: LocationData(26, "Second Get Bonus"), LocationName.Xemnas1SecretAnsemReport13: LocationData(537, "Chest"), - LocationName.FinalXemnas: LocationData(71, "Get Bonus"), + # LocationName.FinalXemnas: LocationData(71, "Get Bonus"), LocationName.XemnasDataPowerBoost: LocationData(554, "Chest"), } @@ -806,74 +796,75 @@ Atlantica_Checks = { } event_location_to_item = { - LocationName.HostileProgramEventLocation: ItemName.HostileProgramEvent, - LocationName.McpEventLocation: ItemName.McpEvent, + LocationName.HostileProgramEventLocation: ItemName.HostileProgramEvent, + LocationName.McpEventLocation: ItemName.McpEvent, # LocationName.ASLarxeneEventLocation: ItemName.ASLarxeneEvent, - LocationName.DataLarxeneEventLocation: ItemName.DataLarxeneEvent, - LocationName.BarbosaEventLocation: ItemName.BarbosaEvent, - LocationName.GrimReaper1EventLocation: ItemName.GrimReaper1Event, - LocationName.GrimReaper2EventLocation: ItemName.GrimReaper2Event, - LocationName.DataLuxordEventLocation: ItemName.DataLuxordEvent, - LocationName.DataAxelEventLocation: ItemName.DataAxelEvent, - LocationName.CerberusEventLocation: ItemName.CerberusEvent, - LocationName.OlympusPeteEventLocation: ItemName.OlympusPeteEvent, - LocationName.HydraEventLocation: ItemName.HydraEvent, + LocationName.DataLarxeneEventLocation: ItemName.DataLarxeneEvent, + LocationName.BarbosaEventLocation: ItemName.BarbosaEvent, + LocationName.GrimReaper1EventLocation: ItemName.GrimReaper1Event, + LocationName.GrimReaper2EventLocation: ItemName.GrimReaper2Event, + LocationName.DataLuxordEventLocation: ItemName.DataLuxordEvent, + LocationName.DataAxelEventLocation: ItemName.DataAxelEvent, + LocationName.CerberusEventLocation: ItemName.CerberusEvent, + LocationName.OlympusPeteEventLocation: ItemName.OlympusPeteEvent, + LocationName.HydraEventLocation: ItemName.HydraEvent, LocationName.OcPainAndPanicCupEventLocation: ItemName.OcPainAndPanicCupEvent, - LocationName.OcCerberusCupEventLocation: ItemName.OcCerberusCupEvent, - LocationName.HadesEventLocation: ItemName.HadesEvent, + LocationName.OcCerberusCupEventLocation: ItemName.OcCerberusCupEvent, + LocationName.HadesEventLocation: ItemName.HadesEvent, # LocationName.ASZexionEventLocation: ItemName.ASZexionEvent, - LocationName.DataZexionEventLocation: ItemName.DataZexionEvent, - LocationName.Oc2TitanCupEventLocation: ItemName.Oc2TitanCupEvent, - LocationName.Oc2GofCupEventLocation: ItemName.Oc2GofCupEvent, + LocationName.DataZexionEventLocation: ItemName.DataZexionEvent, + LocationName.Oc2TitanCupEventLocation: ItemName.Oc2TitanCupEvent, + LocationName.Oc2GofCupEventLocation: ItemName.Oc2GofCupEvent, # LocationName.Oc2CupsEventLocation: ItemName.Oc2CupsEventLocation, - LocationName.HadesCupEventLocations: ItemName.HadesCupEvents, - LocationName.PrisonKeeperEventLocation: ItemName.PrisonKeeperEvent, - LocationName.OogieBoogieEventLocation: ItemName.OogieBoogieEvent, - LocationName.ExperimentEventLocation: ItemName.ExperimentEvent, + LocationName.HadesCupEventLocations: ItemName.HadesCupEvents, + LocationName.PrisonKeeperEventLocation: ItemName.PrisonKeeperEvent, + LocationName.OogieBoogieEventLocation: ItemName.OogieBoogieEvent, + LocationName.ExperimentEventLocation: ItemName.ExperimentEvent, # LocationName.ASVexenEventLocation: ItemName.ASVexenEvent, - LocationName.DataVexenEventLocation: ItemName.DataVexenEvent, - LocationName.ShanYuEventLocation: ItemName.ShanYuEvent, - LocationName.AnsemRikuEventLocation: ItemName.AnsemRikuEvent, - LocationName.StormRiderEventLocation: ItemName.StormRiderEvent, - LocationName.DataXigbarEventLocation: ItemName.DataXigbarEvent, - LocationName.RoxasEventLocation: ItemName.RoxasEvent, - LocationName.XigbarEventLocation: ItemName.XigbarEvent, - LocationName.LuxordEventLocation: ItemName.LuxordEvent, - LocationName.SaixEventLocation: ItemName.SaixEvent, - LocationName.XemnasEventLocation: ItemName.XemnasEvent, - LocationName.ArmoredXemnasEventLocation: ItemName.ArmoredXemnasEvent, - LocationName.ArmoredXemnas2EventLocation: ItemName.ArmoredXemnas2Event, + LocationName.DataVexenEventLocation: ItemName.DataVexenEvent, + LocationName.ShanYuEventLocation: ItemName.ShanYuEvent, + LocationName.AnsemRikuEventLocation: ItemName.AnsemRikuEvent, + LocationName.StormRiderEventLocation: ItemName.StormRiderEvent, + LocationName.DataXigbarEventLocation: ItemName.DataXigbarEvent, + LocationName.RoxasEventLocation: ItemName.RoxasEvent, + LocationName.XigbarEventLocation: ItemName.XigbarEvent, + LocationName.LuxordEventLocation: ItemName.LuxordEvent, + LocationName.SaixEventLocation: ItemName.SaixEvent, + LocationName.XemnasEventLocation: ItemName.XemnasEvent, + LocationName.ArmoredXemnasEventLocation: ItemName.ArmoredXemnasEvent, + LocationName.ArmoredXemnas2EventLocation: ItemName.ArmoredXemnas2Event, # LocationName.FinalXemnasEventLocation: ItemName.FinalXemnasEvent, - LocationName.DataXemnasEventLocation: ItemName.DataXemnasEvent, - LocationName.ThresholderEventLocation: ItemName.ThresholderEvent, - LocationName.BeastEventLocation: ItemName.BeastEvent, - LocationName.DarkThornEventLocation: ItemName.DarkThornEvent, - LocationName.XaldinEventLocation: ItemName.XaldinEvent, - LocationName.DataXaldinEventLocation: ItemName.DataXaldinEvent, - LocationName.TwinLordsEventLocation: ItemName.TwinLordsEvent, - LocationName.GenieJafarEventLocation: ItemName.GenieJafarEvent, + LocationName.DataXemnasEventLocation: ItemName.DataXemnasEvent, + LocationName.ThresholderEventLocation: ItemName.ThresholderEvent, + LocationName.BeastEventLocation: ItemName.BeastEvent, + LocationName.DarkThornEventLocation: ItemName.DarkThornEvent, + LocationName.XaldinEventLocation: ItemName.XaldinEvent, + LocationName.DataXaldinEventLocation: ItemName.DataXaldinEvent, + LocationName.TwinLordsEventLocation: ItemName.TwinLordsEvent, + LocationName.GenieJafarEventLocation: ItemName.GenieJafarEvent, # LocationName.ASLexaeusEventLocation: ItemName.ASLexaeusEvent, - LocationName.DataLexaeusEventLocation: ItemName.DataLexaeusEvent, - LocationName.ScarEventLocation: ItemName.ScarEvent, - LocationName.GroundShakerEventLocation: ItemName.GroundShakerEvent, - LocationName.DataSaixEventLocation: ItemName.DataSaixEvent, - LocationName.HBDemyxEventLocation: ItemName.HBDemyxEvent, - LocationName.ThousandHeartlessEventLocation: ItemName.ThousandHeartlessEvent, - LocationName.Mushroom13EventLocation: ItemName.Mushroom13Event, - LocationName.SephiEventLocation: ItemName.SephiEvent, - LocationName.DataDemyxEventLocation: ItemName.DataDemyxEvent, - LocationName.CorFirstFightEventLocation: ItemName.CorFirstFightEvent, - LocationName.CorSecondFightEventLocation: ItemName.CorSecondFightEvent, - LocationName.TransportEventLocation: ItemName.TransportEvent, - LocationName.OldPeteEventLocation: ItemName.OldPeteEvent, - LocationName.FuturePeteEventLocation: ItemName.FuturePeteEvent, + LocationName.DataLexaeusEventLocation: ItemName.DataLexaeusEvent, + LocationName.ScarEventLocation: ItemName.ScarEvent, + LocationName.GroundShakerEventLocation: ItemName.GroundShakerEvent, + LocationName.DataSaixEventLocation: ItemName.DataSaixEvent, + LocationName.HBDemyxEventLocation: ItemName.HBDemyxEvent, + LocationName.ThousandHeartlessEventLocation: ItemName.ThousandHeartlessEvent, + LocationName.Mushroom13EventLocation: ItemName.Mushroom13Event, + LocationName.SephiEventLocation: ItemName.SephiEvent, + LocationName.DataDemyxEventLocation: ItemName.DataDemyxEvent, + LocationName.CorFirstFightEventLocation: ItemName.CorFirstFightEvent, + LocationName.CorSecondFightEventLocation: ItemName.CorSecondFightEvent, + LocationName.TransportEventLocation: ItemName.TransportEvent, + LocationName.OldPeteEventLocation: ItemName.OldPeteEvent, + LocationName.FuturePeteEventLocation: ItemName.FuturePeteEvent, # LocationName.ASMarluxiaEventLocation: ItemName.ASMarluxiaEvent, - LocationName.DataMarluxiaEventLocation: ItemName.DataMarluxiaEvent, - LocationName.TerraEventLocation: ItemName.TerraEvent, - LocationName.TwilightThornEventLocation: ItemName.TwilightThornEvent, - LocationName.Axel1EventLocation: ItemName.Axel1Event, - LocationName.Axel2EventLocation: ItemName.Axel2Event, - LocationName.DataRoxasEventLocation: ItemName.DataRoxasEvent, + LocationName.DataMarluxiaEventLocation: ItemName.DataMarluxiaEvent, + LocationName.TerraEventLocation: ItemName.TerraEvent, + LocationName.TwilightThornEventLocation: ItemName.TwilightThornEvent, + LocationName.Axel1EventLocation: ItemName.Axel1Event, + LocationName.Axel2EventLocation: ItemName.Axel2Event, + LocationName.DataRoxasEventLocation: ItemName.DataRoxasEvent, + LocationName.FinalXemnasEventLocation: ItemName.Victory, } all_weapon_slot = { LocationName.FAKESlot, @@ -1361,3 +1352,9 @@ exclusion_table = { location for location, data in all_locations.items() if location not in event_location_to_item.keys() and location not in popups_set and location != LocationName.StationofSerenityPotion and data.yml == "Chest" } } + +location_groups: typing.Dict[str, list] +location_groups = { + Region_Name: [loc for loc in Region_Locs if "Event" not in loc] + for Region_Name, Region_Locs in KH2REGIONS.items() if Region_Locs +} diff --git a/worlds/kh2/Regions.py b/worlds/kh2/Regions.py index 6dd8313107..235500ec89 100644 --- a/worlds/kh2/Regions.py +++ b/worlds/kh2/Regions.py @@ -1,9 +1,11 @@ import typing from BaseClasses import MultiWorld, Region +from . import Locations -from .Locations import KH2Location, event_location_to_item -from . import LocationName, RegionName, Events_Table +from .Subclasses import KH2Location +from .Names import LocationName, RegionName +from .Items import Events_Table KH2REGIONS: typing.Dict[str, typing.List[str]] = { "Menu": [], @@ -788,7 +790,7 @@ KH2REGIONS: typing.Dict[str, typing.List[str]] = { LocationName.ArmoredXemnas2EventLocation ], RegionName.FinalXemnas: [ - LocationName.FinalXemnas + LocationName.FinalXemnasEventLocation ], RegionName.DataXemnas: [ LocationName.XemnasDataPowerBoost, @@ -1020,7 +1022,8 @@ def create_regions(self): multiworld.regions += [create_region(multiworld, player, active_locations, region, locations) for region, locations in KH2REGIONS.items()] # fill the event locations with events - for location, item in event_location_to_item.items(): + + for location, item in Locations.event_location_to_item.items(): multiworld.get_location(location, player).place_locked_item( multiworld.worlds[player].create_event_item(item)) diff --git a/worlds/kh2/Rules.py b/worlds/kh2/Rules.py index 111d12d0d6..65f690fdde 100644 --- a/worlds/kh2/Rules.py +++ b/worlds/kh2/Rules.py @@ -270,7 +270,7 @@ class KH2WorldRules(KH2Rules): add_item_rule(location, lambda item: item.player == self.player and item.name in SupportAbility_Table.keys()) def set_kh2_goal(self): - final_xemnas_location = self.multiworld.get_location(LocationName.FinalXemnas, self.player) + final_xemnas_location = self.multiworld.get_location(LocationName.FinalXemnasEventLocation, self.player) if self.multiworld.Goal[self.player] == "three_proofs": final_xemnas_location.access_rule = lambda state: self.kh2_has_all(three_proofs, state) if self.multiworld.FinalXemnas[self.player]: diff --git a/worlds/kh2/Subclasses.py b/worlds/kh2/Subclasses.py new file mode 100644 index 0000000000..79f52c41c0 --- /dev/null +++ b/worlds/kh2/Subclasses.py @@ -0,0 +1,29 @@ +import typing + +from BaseClasses import Location, Item + + +class KH2Location(Location): + game: str = "Kingdom Hearts 2" + + +class LocationData(typing.NamedTuple): + locid: int + yml: str + charName: str = "Sora" + charNumber: int = 1 + + +class KH2Item(Item): + game: str = "Kingdom Hearts 2" + + +class ItemData(typing.NamedTuple): + quantity: int = 0 + kh2id: int = 0 + # Save+ mem addr + memaddr: int = 0 + # some items have bitmasks. if bitmask>0 bitor to give item else + bitmask: int = 0 + # if ability then + ability: bool = False diff --git a/worlds/kh2/__init__.py b/worlds/kh2/__init__.py index 2bddbd5ec3..d02614d380 100644 --- a/worlds/kh2/__init__.py +++ b/worlds/kh2/__init__.py @@ -12,6 +12,7 @@ from .OpenKH import patch_kh2 from .Options import KingdomHearts2Options from .Regions import create_regions, connect_regions from .Rules import * +from .Subclasses import KH2Item def launch_client(): @@ -49,7 +50,9 @@ class KH2World(World): for item_id, item in enumerate(item_dictionary_table.keys(), 0x130000)} location_name_to_id = {item: location for location, item in enumerate(all_locations.keys(), 0x130000)} + item_name_groups = item_groups + location_name_groups = location_groups visitlocking_dict: Dict[str, int] plando_locations: Dict[str, str] @@ -253,11 +256,8 @@ class KH2World(World): self.goofy_gen_early() self.keyblade_gen_early() - if self.multiworld.FinalXemnas[self.player]: - self.plando_locations[LocationName.FinalXemnas] = ItemName.Victory - else: - self.plando_locations[LocationName.FinalXemnas] = self.create_filler().name - self.total_locations -= 1 + # final xemnas isn't a location anymore + # self.total_locations -= 1 if self.options.WeaponSlotStartHint: for location in all_weapon_slot: From d390d2eff801ebd8d2d920ff8536439cdf1bfbd6 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Tue, 16 Jan 2024 07:13:02 -0500 Subject: [PATCH 05/21] Lingo: Remove colors from Bearer SIXes (#2677) --- worlds/lingo/data/LL1.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/worlds/lingo/data/LL1.yaml b/worlds/lingo/data/LL1.yaml index d7d4630e86..32a7659b82 100644 --- a/worlds/lingo/data/LL1.yaml +++ b/worlds/lingo/data/LL1.yaml @@ -4218,9 +4218,6 @@ SIX: id: Backside Room/Panel_six_six_5 tag: midwhite - colors: - - red - - yellow hunt: True required_door: room: Number Hunt @@ -4296,9 +4293,6 @@ SIX: id: Backside Room/Panel_six_six_6 tag: midwhite - colors: - - red - - yellow hunt: True required_door: room: Number Hunt From 7affb885ba533e4abfc74f276eaad3bbb0431e9e Mon Sep 17 00:00:00 2001 From: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com> Date: Tue, 16 Jan 2024 13:13:44 +0100 Subject: [PATCH 06/21] The Witness: Add "Town Desert Laser Redirect Control (Panel)" as an item (#2669) --- worlds/witness/Options.py | 10 +++++++--- worlds/witness/WitnessItems.txt | 4 +++- worlds/witness/WitnessLogic.txt | 6 +++--- worlds/witness/WitnessLogicExpert.txt | 6 +++--- worlds/witness/WitnessLogicVanilla.txt | 6 +++--- worlds/witness/player_logic.py | 3 ++- worlds/witness/rules.py | 19 +++++++++++++------ .../Complex_Additional_Panels.txt | 1 + .../settings/Door_Shuffle/Simple_Panels.txt | 2 +- worlds/witness/static_logic.py | 1 - 10 files changed, 36 insertions(+), 22 deletions(-) diff --git a/worlds/witness/Options.py b/worlds/witness/Options.py index 4c4b4f7626..84855bf867 100644 --- a/worlds/witness/Options.py +++ b/worlds/witness/Options.py @@ -114,9 +114,13 @@ class ShufflePostgame(Toggle): class VictoryCondition(Choice): - """Change the victory condition from the original game's ending (elevator) to beating the Challenge - or solving the mountaintop box, either using the short solution - (7 lasers or whatever you've changed it to) or the long solution (11 lasers or whatever you've changed it to).""" + """Set the victory condition for this world. + Elevator: Start the elevator at the bottom of the mountain (requires Mountain Lasers). + Challenge: Beat the secret Challenge (requires Challenge Lasers). + Mountain Box Short: Input the short solution to the Mountaintop Box (requires Mountain Lasers). + Mountain Box Long: Input the long solution to the Mountaintop Box (requires Challenge Lasers). + It is important to note that while the Mountain Box requires Desert Laser to be redirected in Town for that laser + to count, the laser locks on the Elevator and Challenge Timer panels do not.""" display_name = "Victory Condition" option_elevator = 0 option_challenge = 1 diff --git a/worlds/witness/WitnessItems.txt b/worlds/witness/WitnessItems.txt index 750d6bd4eb..6457117909 100644 --- a/worlds/witness/WitnessItems.txt +++ b/worlds/witness/WitnessItems.txt @@ -69,6 +69,7 @@ Doors: 1167 - Town Maze Rooftop Bridge (Panel) - 0x2896A 1169 - Town Windmill Entry (Panel) - 0x17F5F 1172 - Town Cargo Box Entry (Panel) - 0x0A0C8 +1173 - Town Desert Laser Redirect Control (Panel) - 0x09F98 1182 - Windmill Turn Control (Panel) - 0x17D02 1184 - Theater Entry (Panel) - 0x17F89 1185 - Theater Video Input (Panel) - 0x00815 @@ -234,7 +235,7 @@ Doors: 2000 - Desert Control Panels - 0x09FAA,0x1C2DF,0x1831E,0x1C260,0x1831C,0x1C2F3,0x1831D,0x1C2B1,0x1831B 2005 - Quarry Stoneworks Control Panels - 0x03678,0x03676,0x03679,0x03675 2010 - Quarry Boathouse Control Panels - 0x03852,0x03858,0x275FA -2015 - Town Control Panels - 0x2896A,0x334D8 +2015 - Town Control Panels - 0x2896A,0x334D8,0x09F98 2020 - Windmill & Theater Control Panels - 0x17D02,0x00815 2025 - Bunker Control Panels - 0x34BC5,0x34BC6,0x0A079 2030 - Swamp Control Panels - 0x00609,0x18488,0x181F5,0x17E2B,0x17C0A,0x17E07 @@ -250,6 +251,7 @@ Doors: 2125 - Monastery Panels - 0x09D9B,0x00C92,0x00B10 2130 - Town Church & RGB House Panels - 0x28998,0x28A0D,0x334D8 2135 - Town Maze Panels - 0x2896A,0x28A79 +2137 - Town Dockside House Panels - 0x0A0C8,0x09F98 2140 - Windmill & Theater Panels - 0x17D02,0x00815,0x17F5F,0x17F89,0x0A168,0x33AB2 2145 - Treehouse Panels - 0x0A182,0x0288C,0x02886,0x2700B,0x17CBC,0x037FF 2150 - Bunker Panels - 0x34BC5,0x34BC6,0x0A079,0x0A099,0x17C2E diff --git a/worlds/witness/WitnessLogic.txt b/worlds/witness/WitnessLogic.txt index acfbe8c14e..424f990c33 100644 --- a/worlds/witness/WitnessLogic.txt +++ b/worlds/witness/WitnessLogic.txt @@ -474,7 +474,7 @@ Town (Town) - Main Island - True - The Ocean - 0x0A054 - Town Maze Rooftop - 0x2 158218 - 0x0A054 (Boat Spawn) - 0x17CA6 | 0x17CDF | 0x09DB8 | 0x17C95 - Boat 158219 - 0x0A0C8 (Cargo Box Entry Panel) - True - Black/White Squares & Shapers Door - 0x0A0C9 (Cargo Box Entry) - 0x0A0C8 -158707 - 0x09F98 (Desert Laser Redirect) - True - True +158707 - 0x09F98 (Desert Laser Redirect Control) - True - True 158220 - 0x18590 (Transparent) - True - Symmetry 158221 - 0x28AE3 (Vines) - 0x18590 - True 158222 - 0x28938 (Apple Tree) - 0x28AE3 - True @@ -895,9 +895,9 @@ Mountainside Vault (Mountainside): Mountaintop (Mountaintop) - Mountain Top Layer - 0x17C34: 158405 - 0x0042D (River Shape) - True - True -158406 - 0x09F7F (Box Short) - 7 Lasers - True +158406 - 0x09F7F (Box Short) - 7 Lasers + Redirect - True 158407 - 0x17C34 (Mountain Entry Panel) - 0x09F7F - Stars & Black/White Squares & Stars + Same Colored Symbol -158800 - 0xFFF00 (Box Long) - 11 Lasers & 0x17C34 - True +158800 - 0xFFF00 (Box Long) - 11 Lasers + Redirect & 0x17C34 - True 159300 - 0x001A3 (River Shape EP) - True - True 159320 - 0x3370E (Arch Black EP) - True - True 159324 - 0x336C8 (Arch White Right EP) - True - True diff --git a/worlds/witness/WitnessLogicExpert.txt b/worlds/witness/WitnessLogicExpert.txt index b1d9b8e30e..accb640e34 100644 --- a/worlds/witness/WitnessLogicExpert.txt +++ b/worlds/witness/WitnessLogicExpert.txt @@ -474,7 +474,7 @@ Town (Town) - Main Island - True - The Ocean - 0x0A054 - Town Maze Rooftop - 0x2 158218 - 0x0A054 (Boat Spawn) - 0x17CA6 | 0x17CDF | 0x09DB8 | 0x17C95 - Boat 158219 - 0x0A0C8 (Cargo Box Entry Panel) - True - Squares & Black/White Squares & Shapers & Triangles Door - 0x0A0C9 (Cargo Box Entry) - 0x0A0C8 -158707 - 0x09F98 (Desert Laser Redirect) - True - True +158707 - 0x09F98 (Desert Laser Redirect Control) - True - True 158220 - 0x18590 (Transparent) - True - Symmetry 158221 - 0x28AE3 (Vines) - 0x18590 - True 158222 - 0x28938 (Apple Tree) - 0x28AE3 - True @@ -895,9 +895,9 @@ Mountainside Vault (Mountainside): Mountaintop (Mountaintop) - Mountain Top Layer - 0x17C34: 158405 - 0x0042D (River Shape) - True - True -158406 - 0x09F7F (Box Short) - 7 Lasers - True +158406 - 0x09F7F (Box Short) - 7 Lasers + Redirect - True 158407 - 0x17C34 (Mountain Entry Panel) - 0x09F7F - Stars & Black/White Squares & Stars + Same Colored Symbol & Triangles -158800 - 0xFFF00 (Box Long) - 11 Lasers & 0x17C34 - True +158800 - 0xFFF00 (Box Long) - 11 Lasers + Redirect & 0x17C34 - True 159300 - 0x001A3 (River Shape EP) - True - True 159320 - 0x3370E (Arch Black EP) - True - True 159324 - 0x336C8 (Arch White Right EP) - True - True diff --git a/worlds/witness/WitnessLogicVanilla.txt b/worlds/witness/WitnessLogicVanilla.txt index 779ead6bde..4c5e52c5cb 100644 --- a/worlds/witness/WitnessLogicVanilla.txt +++ b/worlds/witness/WitnessLogicVanilla.txt @@ -474,7 +474,7 @@ Town (Town) - Main Island - True - The Ocean - 0x0A054 - Town Maze Rooftop - 0x2 158218 - 0x0A054 (Boat Spawn) - 0x17CA6 | 0x17CDF | 0x09DB8 | 0x17C95 - Boat 158219 - 0x0A0C8 (Cargo Box Entry Panel) - True - Black/White Squares & Shapers Door - 0x0A0C9 (Cargo Box Entry) - 0x0A0C8 -158707 - 0x09F98 (Desert Laser Redirect) - True - True +158707 - 0x09F98 (Desert Laser Redirect Control) - True - True 158220 - 0x18590 (Transparent) - True - Symmetry 158221 - 0x28AE3 (Vines) - 0x18590 - True 158222 - 0x28938 (Apple Tree) - 0x28AE3 - True @@ -895,9 +895,9 @@ Mountainside Vault (Mountainside): Mountaintop (Mountaintop) - Mountain Top Layer - 0x17C34: 158405 - 0x0042D (River Shape) - True - True -158406 - 0x09F7F (Box Short) - 7 Lasers - True +158406 - 0x09F7F (Box Short) - 7 Lasers + Redirect - True 158407 - 0x17C34 (Mountain Entry Panel) - 0x09F7F - Black/White Squares -158800 - 0xFFF00 (Box Long) - 11 Lasers & 0x17C34 - True +158800 - 0xFFF00 (Box Long) - 11 Lasers + Redirect & 0x17C34 - True 159300 - 0x001A3 (River Shape EP) - True - True 159320 - 0x3370E (Arch Black EP) - True - True 159324 - 0x336C8 (Arch White Right EP) - True - True diff --git a/worlds/witness/player_logic.py b/worlds/witness/player_logic.py index e1ef1ae431..e05199c2b3 100644 --- a/worlds/witness/player_logic.py +++ b/worlds/witness/player_logic.py @@ -103,7 +103,8 @@ class WitnessPlayerLogic: if option_entity in self.EVENT_NAMES_BY_HEX: new_items = frozenset({frozenset([option_entity])}) - elif option_entity in {"7 Lasers", "11 Lasers", "PP2 Weirdness", "Theater to Tunnels"}: + elif option_entity in {"7 Lasers", "11 Lasers", "7 Lasers + Redirect", "11 Lasers + Redirect", + "PP2 Weirdness", "Theater to Tunnels"}: new_items = frozenset({frozenset([option_entity])}) else: new_items = self.reduce_req_within_region(option_entity) diff --git a/worlds/witness/rules.py b/worlds/witness/rules.py index 75c662ac0f..5eded11ad4 100644 --- a/worlds/witness/rules.py +++ b/worlds/witness/rules.py @@ -29,8 +29,9 @@ laser_hexes = [ ] -def _has_laser(laser_hex: str, world: "WitnessWorld", player: int) -> Callable[[CollectionState], bool]: - if laser_hex == "0x012FB": +def _has_laser(laser_hex: str, world: "WitnessWorld", player: int, + redirect_required: bool) -> Callable[[CollectionState], bool]: + if laser_hex == "0x012FB" and redirect_required: return lambda state: ( _can_solve_panel(laser_hex, world, world.player, world.player_logic, world.locat)(state) and state.has("Desert Laser Redirection", player) @@ -39,11 +40,11 @@ def _has_laser(laser_hex: str, world: "WitnessWorld", player: int) -> Callable[[ return _can_solve_panel(laser_hex, world, world.player, world.player_logic, world.locat) -def _has_lasers(amount: int, world: "WitnessWorld") -> Callable[[CollectionState], bool]: +def _has_lasers(amount: int, world: "WitnessWorld", redirect_required: bool) -> Callable[[CollectionState], bool]: laser_lambdas = [] for laser_hex in laser_hexes: - has_laser_lambda = _has_laser(laser_hex, world, world.player) + has_laser_lambda = _has_laser(laser_hex, world, world.player, redirect_required) laser_lambdas.append(has_laser_lambda) @@ -155,10 +156,16 @@ def _has_item(item: str, world: "WitnessWorld", player: int, return lambda state: state.can_reach(item, "Region", player) if item == "7 Lasers": laser_req = world.options.mountain_lasers.value - return _has_lasers(laser_req, world) + return _has_lasers(laser_req, world, False) + if item == "7 Lasers + Redirect": + laser_req = world.options.mountain_lasers.value + return _has_lasers(laser_req, world, True) if item == "11 Lasers": laser_req = world.options.challenge_lasers.value - return _has_lasers(laser_req, world) + return _has_lasers(laser_req, world, False) + if item == "11 Lasers + Redirect": + laser_req = world.options.challenge_lasers.value + return _has_lasers(laser_req, world, True) elif item == "PP2 Weirdness": return lambda state: _can_do_expert_pp2(state, world) elif item == "Theater to Tunnels": diff --git a/worlds/witness/settings/Door_Shuffle/Complex_Additional_Panels.txt b/worlds/witness/settings/Door_Shuffle/Complex_Additional_Panels.txt index 79bda7ea22..f7acbb2e55 100644 --- a/worlds/witness/settings/Door_Shuffle/Complex_Additional_Panels.txt +++ b/worlds/witness/settings/Door_Shuffle/Complex_Additional_Panels.txt @@ -10,6 +10,7 @@ Quarry Boathouse Hook Control (Panel) Monastery Shutters Control (Panel) Town Maze Rooftop Bridge (Panel) Town RGB Control (Panel) +Town Desert Laser Redirect Control (Panel) Windmill Turn Control (Panel) Theater Video Input (Panel) Bunker Drop-Down Door Controls (Panel) diff --git a/worlds/witness/settings/Door_Shuffle/Simple_Panels.txt b/worlds/witness/settings/Door_Shuffle/Simple_Panels.txt index 79da154491..42258bca1a 100644 --- a/worlds/witness/settings/Door_Shuffle/Simple_Panels.txt +++ b/worlds/witness/settings/Door_Shuffle/Simple_Panels.txt @@ -10,7 +10,7 @@ Monastery Panels Town Church & RGB House Panels Town Maze Panels Windmill & Theater Panels -Town Cargo Box Entry (Panel) +Town Dockside House Panels Treehouse Panels Bunker Panels Swamp Panels diff --git a/worlds/witness/static_logic.py b/worlds/witness/static_logic.py index 29c171d45c..0e8d649af6 100644 --- a/worlds/witness/static_logic.py +++ b/worlds/witness/static_logic.py @@ -109,7 +109,6 @@ class StaticWitnessLogicObj: "Laser", "Laser Hedges", "Laser Pressure Plates", - "Desert Laser Redirect" } is_vault_or_video = "Vault" in entity_name or "Video" in entity_name From fe3bc8d6be2d260f83910f8539f912b974775d4f Mon Sep 17 00:00:00 2001 From: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com> Date: Tue, 16 Jan 2024 13:14:38 +0100 Subject: [PATCH 07/21] The Witness: Add Obelisk Side locations to always and priority hints (#2665) --- worlds/witness/hints.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/worlds/witness/hints.py b/worlds/witness/hints.py index d238aa4adf..68fc68946b 100644 --- a/worlds/witness/hints.py +++ b/worlds/witness/hints.py @@ -187,8 +187,8 @@ def get_always_hint_items(world: "WitnessWorld") -> List[str]: return always -def get_always_hint_locations(_: "WitnessWorld") -> List[str]: - return [ +def get_always_hint_locations(world: "WitnessWorld") -> List[str]: + always = [ "Challenge Vault Box", "Mountain Bottom Floor Discard", "Theater Eclipse EP", @@ -196,6 +196,16 @@ def get_always_hint_locations(_: "WitnessWorld") -> List[str]: "Mountainside Cloud Cycle EP", ] + # Add Obelisk Sides that contain EPs that are meant to be hinted, if they are necessary to complete the Obelisk Side + if world.options.EP_difficulty == "eclipse": + always.append("Town Obelisk Side 6") # Eclipse EP + + if world.options.EP_difficulty != "normal": + always.append("Treehouse Obelisk Side 4") # Couch EP + always.append("River Obelisk Side 1") # Cloud Cycle EP. Needs to be changed to "Mountainside Obelisk" soon + + return always + def get_priority_hint_items(world: "WitnessWorld") -> List[str]: priority = { @@ -249,8 +259,8 @@ def get_priority_hint_items(world: "WitnessWorld") -> List[str]: return sorted(priority) -def get_priority_hint_locations(_: "WitnessWorld") -> List[str]: - return [ +def get_priority_hint_locations(world: "WitnessWorld") -> List[str]: + priority = [ "Swamp Purple Underwater", "Shipwreck Vault Box", "Town RGB Room Left", @@ -265,6 +275,13 @@ def get_priority_hint_locations(_: "WitnessWorld") -> List[str]: "Boat Shipwreck Green EP", "Quarry Stoneworks Control Room Left", ] + + # Add Obelisk Sides that contain EPs that are meant to be hinted, if they are necessary to complete the Obelisk Side + if world.options.EP_difficulty != "normal": + priority.append("Town Obelisk Side 6") # Theater Flowers EP + priority.append("Treehouse Obelisk Side 4") # Shipwreck Green EP + + return priority def make_hint_from_item(world: "WitnessWorld", item_name: str, own_itempool: List[Item]): From d000b52ae0cf346b4ff730c5c9d0eff3fc9b44c5 Mon Sep 17 00:00:00 2001 From: Yussur Mustafa Oraji Date: Tue, 16 Jan 2024 13:38:19 +0100 Subject: [PATCH 08/21] V6: Use new options api (#2668) * v6: Use new options API * v6: Add display names for some options --- worlds/v6/Options.py | 21 ++++++++++++--------- worlds/v6/Regions.py | 11 ----------- worlds/v6/Rules.py | 28 +++++++++++++++------------- worlds/v6/__init__.py | 14 +++++++------- 4 files changed, 34 insertions(+), 40 deletions(-) diff --git a/worlds/v6/Options.py b/worlds/v6/Options.py index 107fbab465..1950d1bcbd 100644 --- a/worlds/v6/Options.py +++ b/worlds/v6/Options.py @@ -1,8 +1,10 @@ import typing -from Options import Option, DeathLink, Range, Toggle +from dataclasses import dataclass +from Options import Option, DeathLink, Range, Toggle, PerGameCommonOptions class DoorCost(Range): """Amount of Trinkets required to enter Areas. Set to 0 to disable artificial locks.""" + display_name = "Door Cost" range_start = 0 range_end = 3 default = 3 @@ -13,6 +15,7 @@ class AreaCostRandomizer(Toggle): class DeathLinkAmnesty(Range): """Amount of Deaths to take before sending a DeathLink signal, for balancing difficulty""" + display_name = "Death Link Amnesty" range_start = 0 range_end = 30 default = 15 @@ -25,11 +28,11 @@ class MusicRandomizer(Toggle): """Randomize Music""" display_name = "Music Randomizer" -v6_options: typing.Dict[str,type(Option)] = { - "MusicRandomizer": MusicRandomizer, - "AreaRandomizer": AreaRandomizer, - "DoorCost": DoorCost, - "AreaCostRandomizer": AreaCostRandomizer, - "death_link": DeathLink, - "DeathLinkAmnesty": DeathLinkAmnesty -} \ No newline at end of file +@dataclass +class V6Options(PerGameCommonOptions): + music_rando: MusicRandomizer + area_rando: AreaRandomizer + door_cost: DoorCost + area_cost: AreaCostRandomizer + death_link: DeathLink + death_link_amnesty: DeathLinkAmnesty diff --git a/worlds/v6/Regions.py b/worlds/v6/Regions.py index 5a8f0315f4..f6e9ee7538 100644 --- a/worlds/v6/Regions.py +++ b/worlds/v6/Regions.py @@ -31,14 +31,3 @@ def create_regions(world: MultiWorld, player: int): locWrp_names = ["Edge Games"] regWrp.locations += [V6Location(player, loc_name, location_table[loc_name], regWrp) for loc_name in locWrp_names] world.regions.append(regWrp) - - -def connect_regions(world: MultiWorld, player: int, source: str, target: str, rule): - sourceRegion = world.get_region(source, player) - targetRegion = world.get_region(target, player) - - connection = Entrance(player,'', sourceRegion) - connection.access_rule = rule - - sourceRegion.exits.append(connection) - connection.connect(targetRegion) \ No newline at end of file diff --git a/worlds/v6/Rules.py b/worlds/v6/Rules.py index ecb34f2f32..bf0d60499e 100644 --- a/worlds/v6/Rules.py +++ b/worlds/v6/Rules.py @@ -1,6 +1,6 @@ import typing from ..generic.Rules import add_rule -from .Regions import connect_regions, v6areas +from .Regions import v6areas def _has_trinket_range(state, player, start, end) -> bool: @@ -10,34 +10,36 @@ def _has_trinket_range(state, player, start, end) -> bool: return True -def set_rules(world, player, area_connections: typing.Dict[int, int], area_cost_map: typing.Dict[int, int]): +def set_rules(multiworld, options, player, area_connections: typing.Dict[int, int], area_cost_map: typing.Dict[int, int]): areashuffle = list(range(len(v6areas))) - if world.AreaRandomizer[player].value: - world.random.shuffle(areashuffle) + if options.area_rando: + multiworld.random.shuffle(areashuffle) area_connections.update({(index + 1): (value + 1) for index, value in enumerate(areashuffle)}) area_connections.update({0: 0}) - if world.AreaCostRandomizer[player].value: - world.random.shuffle(areashuffle) + if options.area_cost: + multiworld.random.shuffle(areashuffle) area_cost_map.update({(index + 1): (value + 1) for index, value in enumerate(areashuffle)}) area_cost_map.update({0: 0}) + menu_region = multiworld.get_region("Menu", player) for i in range(1, 5): - connect_regions(world, player, "Menu", v6areas[area_connections[i] - 1], - lambda state, i=i: _has_trinket_range(state, player, - world.DoorCost[player].value * (area_cost_map[i] - 1), - world.DoorCost[player].value * area_cost_map[i])) + target_region = multiworld.get_region(v6areas[area_connections[i] - 1], player) + menu_region.connect(connecting_region=target_region, + rule=lambda state, i=i: _has_trinket_range(state, player, + options.door_cost * (area_cost_map[i] - 1), + options.door_cost * area_cost_map[i])) # Special Rule for V - add_rule(world.get_location("V", player), lambda state: state.can_reach("Laboratory", 'Region', player) and + add_rule(multiworld.get_location("V", player), lambda state: state.can_reach("Laboratory", 'Region', player) and state.can_reach("The Tower", 'Region', player) and state.can_reach("Space Station 2", 'Region', player) and state.can_reach("Warp Zone", 'Region', player)) # Special Rule for NPC Trinket - add_rule(world.get_location("NPC Trinket", player), + add_rule(multiworld.get_location("NPC Trinket", player), lambda state: state.can_reach("Laboratory", 'Region', player) or (state.can_reach("The Tower", 'Region', player) and state.can_reach("Space Station 2", 'Region', player) and state.can_reach("Warp Zone", 'Region', player))) - world.completion_condition[player] = lambda state: state.can_reach("V", 'Location', player) + multiworld.completion_condition[player] = lambda state: state.can_reach("V", 'Location', player) diff --git a/worlds/v6/__init__.py b/worlds/v6/__init__.py index 6ff7fba60c..30a76f82cc 100644 --- a/worlds/v6/__init__.py +++ b/worlds/v6/__init__.py @@ -2,7 +2,7 @@ import typing import os, json from .Items import item_table, V6Item from .Locations import location_table, V6Location -from .Options import v6_options +from .Options import V6Options from .Rules import set_rules from .Regions import create_regions from BaseClasses import Item, ItemClassification, Tutorial @@ -41,7 +41,7 @@ class V6World(World): music_map: typing.Dict[int,int] - option_definitions = v6_options + options_dataclass = V6Options def create_regions(self): create_regions(self.multiworld, self.player) @@ -49,7 +49,7 @@ class V6World(World): def set_rules(self): self.area_connections = {} self.area_cost_map = {} - set_rules(self.multiworld, self.player, self.area_connections, self.area_cost_map) + set_rules(self.multiworld, self.options, self.player, self.area_connections, self.area_cost_map) def create_item(self, name: str) -> Item: return V6Item(name, ItemClassification.progression, item_table[name], self.player) @@ -61,7 +61,7 @@ class V6World(World): def generate_basic(self): musiclist_o = [1,2,3,4,9,12] musiclist_s = musiclist_o.copy() - if self.multiworld.MusicRandomizer[self.player].value: + if self.options.music_rando: self.multiworld.random.shuffle(musiclist_s) self.music_map = dict(zip(musiclist_o, musiclist_s)) @@ -69,10 +69,10 @@ class V6World(World): return { "MusicRando": self.music_map, "AreaRando": self.area_connections, - "DoorCost": self.multiworld.DoorCost[self.player].value, + "DoorCost": self.options.door_cost.value, "AreaCostRando": self.area_cost_map, - "DeathLink": self.multiworld.death_link[self.player].value, - "DeathLink_Amnesty": self.multiworld.DeathLinkAmnesty[self.player].value + "DeathLink": self.options.death_link.value, + "DeathLink_Amnesty": self.options.death_link_amnesty.value } def generate_output(self, output_directory: str): From 3a588099bd46833697a5407490b39348e1a89a00 Mon Sep 17 00:00:00 2001 From: Bryce Wilson Date: Tue, 16 Jan 2024 07:09:47 -0700 Subject: [PATCH 09/21] Pokemon Emerald: Automatically exclude locations based on goal (#2655) --- worlds/pokemon_emerald/__init__.py | 58 ++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/worlds/pokemon_emerald/__init__.py b/worlds/pokemon_emerald/__init__.py index b7730fbdf7..5d50e0db96 100644 --- a/worlds/pokemon_emerald/__init__.py +++ b/worlds/pokemon_emerald/__init__.py @@ -7,7 +7,7 @@ import logging import os from typing import Any, Set, List, Dict, Optional, Tuple, ClassVar -from BaseClasses import ItemClassification, MultiWorld, Tutorial +from BaseClasses import ItemClassification, MultiWorld, Tutorial, LocationProgressType from Fill import FillError, fill_restrictive from Options import Toggle import settings @@ -20,7 +20,7 @@ from .items import (ITEM_GROUPS, PokemonEmeraldItem, create_item_label_to_code_m offset_item_value) from .locations import (LOCATION_GROUPS, PokemonEmeraldLocation, create_location_label_to_id_map, create_locations_with_tags) -from .options import (ItemPoolType, RandomizeWildPokemon, RandomizeBadges, RandomizeTrainerParties, RandomizeHms, +from .options import (Goal, ItemPoolType, RandomizeWildPokemon, RandomizeBadges, RandomizeTrainerParties, RandomizeHms, RandomizeStarters, LevelUpMoves, RandomizeAbilities, RandomizeTypes, TmCompatibility, HmCompatibility, RandomizeStaticEncounters, NormanRequirement, PokemonEmeraldOptions) from .pokemon import get_random_species, get_random_move, get_random_damaging_move, get_random_type @@ -146,6 +146,60 @@ class PokemonEmeraldWorld(World): self.multiworld.regions.extend(regions.values()) + # Exclude locations which are always locked behind the player's goal + def exclude_locations(location_names: List[str]): + for location_name in location_names: + try: + self.multiworld.get_location(location_name, + self.player).progress_type = LocationProgressType.EXCLUDED + except KeyError: + continue # Location not in multiworld + + if self.options.goal == Goal.option_champion: + # Always required to beat champion before receiving this + exclude_locations([ + "Littleroot Town - S.S. Ticket from Norman" + ]) + + # S.S. Ticket requires beating champion, so ferry is not accessible until after goal + if not self.options.enable_ferry: + exclude_locations([ + "SS Tidal - Hidden Item in Lower Deck Trash Can", + "SS Tidal - TM49 from Thief" + ]) + + # Construction workers don't move until champion is defeated + if "Safari Zone Construction Workers" not in self.options.remove_roadblocks.value: + exclude_locations([ + "Safari Zone NE - Hidden Item North", + "Safari Zone NE - Hidden Item East", + "Safari Zone NE - Item on Ledge", + "Safari Zone SE - Hidden Item in South Grass 1", + "Safari Zone SE - Hidden Item in South Grass 2", + "Safari Zone SE - Item in Grass" + ]) + elif self.options.goal == Goal.option_norman: + # If the player sets their options such that Surf or the Balance + # Badge is vanilla, a very large number of locations become + # "post-Norman". Similarly, access to the E4 may require you to + # defeat Norman as an event or to get his badge, making postgame + # locations inaccessible. Detecting these situations isn't trivial + # and excluding all locations requiring Surf would be a bad idea. + # So for now we just won't touch it and blame the user for + # constructing their options in this way. Players usually expect + # to only partially complete their world when playing this goal + # anyway. + + # Locations which are directly unlocked by defeating Norman. + exclude_locations([ + "Petalburg Gym - Balance Badge", + "Petalburg Gym - TM42 from Norman", + "Petalburg City - HM03 from Wally's Uncle", + "Dewford Town - TM36 from Sludge Bomb Man", + "Mauville City - Basement Key from Wattson", + "Mauville City - TM24 from Wattson" + ]) + def create_items(self) -> None: item_locations: List[PokemonEmeraldLocation] = [ location From 5df7a8f686251dd017d9d2088902ee40e535a96a Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Tue, 16 Jan 2024 09:10:59 -0500 Subject: [PATCH 10/21] Lingo: Disable forced good item when early color hallways is on (#2729) --- worlds/lingo/player_logic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/worlds/lingo/player_logic.py b/worlds/lingo/player_logic.py index 57bcc4bfd5..f3efc2914c 100644 --- a/worlds/lingo/player_logic.py +++ b/worlds/lingo/player_logic.py @@ -224,7 +224,7 @@ class LingoPlayerLogic: "kind of logic error.") if door_shuffle != ShuffleDoors.option_none and location_classification != LocationClassification.insanity \ - and not early_color_hallways is False: + and not early_color_hallways: # If shuffle doors is on, force a useful item onto the HI panel. This may not necessarily get you out of BK, # but the goal is to allow you to reach at least one more check. The non-painting ones are hardcoded right # now. We only allow the entrance to the Pilgrim Room if color shuffle is off, because otherwise there are From 1c2dcb7b01286cfea53f4c07b52ea37a495ac157 Mon Sep 17 00:00:00 2001 From: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:11:52 +0100 Subject: [PATCH 11/21] The Witness: Add Desert Control Panels (#2643) --- worlds/witness/WitnessItems.txt | 7 +++++-- worlds/witness/WitnessLogic.txt | 12 ++++++------ worlds/witness/WitnessLogicExpert.txt | 12 ++++++------ worlds/witness/WitnessLogicVanilla.txt | 12 ++++++------ worlds/witness/__init__.py | 3 ++- worlds/witness/locations.py | 4 ++-- .../Door_Shuffle/Complex_Additional_Panels.txt | 3 +++ 7 files changed, 30 insertions(+), 23 deletions(-) diff --git a/worlds/witness/WitnessItems.txt b/worlds/witness/WitnessItems.txt index 6457117909..758ed45465 100644 --- a/worlds/witness/WitnessItems.txt +++ b/worlds/witness/WitnessItems.txt @@ -41,10 +41,13 @@ Doors: 1102 - Tutorial Outpost Exit (Panel) - 0x04CA4 1105 - Symmetry Island Lower (Panel) - 0x000B0 1107 - Symmetry Island Upper (Panel) - 0x1C349 +1108 - Desert Surface 3 Control (Panel) - 0x09FA0 +1109 - Desert Surface 8 Control (Panel) - 0x09F86 1110 - Desert Light Room Entry (Panel) - 0x0C339 1111 - Desert Flood Controls (Panel) - 0x1C2DF,0x1831E,0x1C260,0x1831C,0x1C2F3,0x1831D,0x1C2B1,0x1831B 1112 - Desert Light Control (Panel) - 0x09FAA 1113 - Desert Flood Room Entry (Panel) - 0x0A249 +1114 - Desert Elevator Room Hexagonal Control (Panel) - 0x0A015 1115 - Quarry Elevator Control (Panel) - 0x17CC4 1117 - Quarry Entry 1 (Panel) - 0x09E57 1118 - Quarry Entry 2 (Panel) - 0x17C09 @@ -232,7 +235,7 @@ Doors: 1984 - Caves Shortcuts - 0x2D859,0x2D73F 1987 - Tunnels Doors - 0x27739,0x27263,0x09E87,0x0348A -2000 - Desert Control Panels - 0x09FAA,0x1C2DF,0x1831E,0x1C260,0x1831C,0x1C2F3,0x1831D,0x1C2B1,0x1831B +2000 - Desert Control Panels - 0x09FAA,0x1C2DF,0x1831E,0x1C260,0x1831C,0x1C2F3,0x1831D,0x1C2B1,0x1831B,0x0A015,0x09FA0,0x09F86 2005 - Quarry Stoneworks Control Panels - 0x03678,0x03676,0x03679,0x03675 2010 - Quarry Boathouse Control Panels - 0x03852,0x03858,0x275FA 2015 - Town Control Panels - 0x2896A,0x334D8,0x09F98 @@ -243,7 +246,7 @@ Doors: 2100 - Symmetry Island Panels - 0x1C349,0x000B0 2101 - Tutorial Outpost Panels - 0x0A171,0x04CA4 -2105 - Desert Panels - 0x09FAA,0x1C2DF,0x1831E,0x1C260,0x1831C,0x1C2F3,0x1831D,0x1C2B1,0x1831B,0x0C339,0x0A249 +2105 - Desert Panels - 0x09FAA,0x1C2DF,0x1831E,0x1C260,0x1831C,0x1C2F3,0x1831D,0x1C2B1,0x1831B,0x0C339,0x0A249,0x0A015,0x09FA0,0x09F86 2110 - Quarry Outside Panels - 0x17C09,0x09E57,0x17CC4 2115 - Quarry Stoneworks Panels - 0x01E5A,0x01E59,0x03678,0x03676,0x03679,0x03675 2120 - Quarry Boathouse Panels - 0x03852,0x03858,0x275FA diff --git a/worlds/witness/WitnessLogic.txt b/worlds/witness/WitnessLogic.txt index 424f990c33..ec0922bec6 100644 --- a/worlds/witness/WitnessLogic.txt +++ b/worlds/witness/WitnessLogic.txt @@ -209,12 +209,12 @@ Door - 0x0C316 (Elevator Room Entry) - 0x18076 159034 - 0x337F8 (Flood Room EP) - 0x1C2DF - True Desert Elevator Room (Desert) - Desert Lowest Level Inbetween Shortcuts - 0x01317: -158111 - 0x17C31 (Final Transparent) - True - True -158113 - 0x012D7 (Final Hexagonal) - 0x17C31 & 0x0A015 - True -158114 - 0x0A015 (Final Hexagonal Control) - 0x17C31 - True -158115 - 0x0A15C (Final Bent 1) - True - True -158116 - 0x09FFF (Final Bent 2) - 0x0A15C - True -158117 - 0x0A15F (Final Bent 3) - 0x09FFF - True +158111 - 0x17C31 (Elevator Room Transparent) - True - True +158113 - 0x012D7 (Elevator Room Hexagonal) - 0x17C31 & 0x0A015 - True +158114 - 0x0A015 (Elevator Room Hexagonal Control) - 0x17C31 - True +158115 - 0x0A15C (Elevator Room Bent 1) - True - True +158116 - 0x09FFF (Elevator Room Bent 2) - 0x0A15C - True +158117 - 0x0A15F (Elevator Room Bent 3) - 0x09FFF - True 159035 - 0x037BB (Elevator EP) - 0x01317 - True Door - 0x01317 (Elevator) - 0x03608 diff --git a/worlds/witness/WitnessLogicExpert.txt b/worlds/witness/WitnessLogicExpert.txt index accb640e34..056ae145c4 100644 --- a/worlds/witness/WitnessLogicExpert.txt +++ b/worlds/witness/WitnessLogicExpert.txt @@ -209,12 +209,12 @@ Door - 0x0C316 (Elevator Room Entry) - 0x18076 159034 - 0x337F8 (Flood Room EP) - 0x1C2DF - True Desert Elevator Room (Desert) - Desert Lowest Level Inbetween Shortcuts - 0x01317: -158111 - 0x17C31 (Final Transparent) - True - True -158113 - 0x012D7 (Final Hexagonal) - 0x17C31 & 0x0A015 - True -158114 - 0x0A015 (Final Hexagonal Control) - 0x17C31 - True -158115 - 0x0A15C (Final Bent 1) - True - True -158116 - 0x09FFF (Final Bent 2) - 0x0A15C - True -158117 - 0x0A15F (Final Bent 3) - 0x09FFF - True +158111 - 0x17C31 (Elevator Room Transparent) - True - True +158113 - 0x012D7 (Elevator Room Hexagonal) - 0x17C31 & 0x0A015 - True +158114 - 0x0A015 (Elevator Room Hexagonal Control) - 0x17C31 - True +158115 - 0x0A15C (Elevator Room Bent 1) - True - True +158116 - 0x09FFF (Elevator Room Bent 2) - 0x0A15C - True +158117 - 0x0A15F (Elevator Room Bent 3) - 0x09FFF - True 159035 - 0x037BB (Elevator EP) - 0x01317 - True Door - 0x01317 (Elevator) - 0x03608 diff --git a/worlds/witness/WitnessLogicVanilla.txt b/worlds/witness/WitnessLogicVanilla.txt index 4c5e52c5cb..71af12f76d 100644 --- a/worlds/witness/WitnessLogicVanilla.txt +++ b/worlds/witness/WitnessLogicVanilla.txt @@ -209,12 +209,12 @@ Door - 0x0C316 (Elevator Room Entry) - 0x18076 159034 - 0x337F8 (Flood Room EP) - 0x1C2DF - True Desert Elevator Room (Desert) - Desert Lowest Level Inbetween Shortcuts - 0x01317: -158111 - 0x17C31 (Final Transparent) - True - True -158113 - 0x012D7 (Final Hexagonal) - 0x17C31 & 0x0A015 - True -158114 - 0x0A015 (Final Hexagonal Control) - 0x17C31 - True -158115 - 0x0A15C (Final Bent 1) - True - True -158116 - 0x09FFF (Final Bent 2) - 0x0A15C - True -158117 - 0x0A15F (Final Bent 3) - 0x09FFF - True +158111 - 0x17C31 (Elevator Room Transparent) - True - True +158113 - 0x012D7 (Elevator Room Hexagonal) - 0x17C31 & 0x0A015 - True +158114 - 0x0A015 (Elevator Room Hexagonal Control) - 0x17C31 - True +158115 - 0x0A15C (Elevator Room Bent 1) - True - True +158116 - 0x09FFF (Elevator Room Bent 2) - 0x0A15C - True +158117 - 0x0A15F (Elevator Room Bent 3) - 0x09FFF - True 159035 - 0x037BB (Elevator EP) - 0x01317 - True Door - 0x01317 (Elevator) - 0x03608 diff --git a/worlds/witness/__init__.py b/worlds/witness/__init__.py index 6360c33aef..aeee7009cc 100644 --- a/worlds/witness/__init__.py +++ b/worlds/witness/__init__.py @@ -176,7 +176,8 @@ class WitnessWorld(World): extra_checks = [ ("First Hallway Room", "First Hallway Bend"), ("First Hallway", "First Hallway Straight"), - ("Desert Outside", "Desert Surface 3"), + ("Desert Outside", "Desert Surface 1"), + ("Desert Outside", "Desert Surface 2"), ] for i in range(num_early_locs, needed_size): diff --git a/worlds/witness/locations.py b/worlds/witness/locations.py index d20be27940..026977701a 100644 --- a/worlds/witness/locations.py +++ b/worlds/witness/locations.py @@ -55,8 +55,8 @@ class StaticWitnessLocations: "Desert Light Room 3", "Desert Pond Room 5", "Desert Flood Room 6", - "Desert Final Hexagonal", - "Desert Final Bent 3", + "Desert Elevator Room Hexagonal", + "Desert Elevator Room Bent 3", "Desert Laser Panel", "Quarry Entry 1 Panel", diff --git a/worlds/witness/settings/Door_Shuffle/Complex_Additional_Panels.txt b/worlds/witness/settings/Door_Shuffle/Complex_Additional_Panels.txt index f7acbb2e55..b843709085 100644 --- a/worlds/witness/settings/Door_Shuffle/Complex_Additional_Panels.txt +++ b/worlds/witness/settings/Door_Shuffle/Complex_Additional_Panels.txt @@ -1,4 +1,7 @@ Items: +Desert Surface 3 Control (Panel) +Desert Surface 8 Control (Panel) +Desert Elevator Room Hexagonal Control (Panel) Desert Flood Controls (Panel) Desert Light Control (Panel) Quarry Elevator Control (Panel) From e6f7ed50608f4939707db2dfdb86cf161b110c54 Mon Sep 17 00:00:00 2001 From: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:13:04 +0100 Subject: [PATCH 12/21] The Witness: Progressive Symmetry (#2644) --- worlds/witness/WitnessItems.txt | 1 + worlds/witness/hints.py | 3 +-- worlds/witness/settings/Symbol_Shuffle.txt | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/worlds/witness/WitnessItems.txt b/worlds/witness/WitnessItems.txt index 758ed45465..8e50e328a4 100644 --- a/worlds/witness/WitnessItems.txt +++ b/worlds/witness/WitnessItems.txt @@ -16,6 +16,7 @@ Symbols: 72 - Colored Squares 80 - Arrows 200 - Progressive Dots - Dots,Full Dots +210 - Progressive Symmetry - Symmetry,Colored Dots 260 - Progressive Stars - Stars,Stars + Same Colored Symbol Useful: diff --git a/worlds/witness/hints.py b/worlds/witness/hints.py index 68fc68946b..c00827feee 100644 --- a/worlds/witness/hints.py +++ b/worlds/witness/hints.py @@ -227,9 +227,8 @@ def get_priority_hint_items(world: "WitnessWorld") -> List[str]: "Eraser", "Black/White Squares", "Colored Squares", - "Colored Dots", "Sound Dots", - "Symmetry" + "Progressive Symmetry" ] priority.update(world.random.sample(symbols, 5)) diff --git a/worlds/witness/settings/Symbol_Shuffle.txt b/worlds/witness/settings/Symbol_Shuffle.txt index 3d0342f5e2..253fe98bad 100644 --- a/worlds/witness/settings/Symbol_Shuffle.txt +++ b/worlds/witness/settings/Symbol_Shuffle.txt @@ -1,9 +1,8 @@ Items: Arrows Progressive Dots -Colored Dots Sound Dots -Symmetry +Progressive Symmetry Triangles Eraser Shapers From 5c7bae7940a8ce5d330360b155da1151507589e6 Mon Sep 17 00:00:00 2001 From: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:14:06 +0100 Subject: [PATCH 13/21] The Witness: Local Laser Shuffle + Option Presets (#2590) Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> --- worlds/witness/Options.py | 5 +- worlds/witness/__init__.py | 34 ++++++++++--- worlds/witness/presets.py | 101 +++++++++++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+), 9 deletions(-) create mode 100644 worlds/witness/presets.py diff --git a/worlds/witness/Options.py b/worlds/witness/Options.py index 84855bf867..a15485c485 100644 --- a/worlds/witness/Options.py +++ b/worlds/witness/Options.py @@ -28,11 +28,14 @@ class ShuffleSymbols(DefaultOnToggle): display_name = "Shuffle Symbols" -class ShuffleLasers(Toggle): +class ShuffleLasers(Choice): """If on, the 11 lasers are turned into items and will activate on their own upon receiving them. Note: There is a visual bug that can occur with the Desert Laser. It does not affect gameplay - The Laser can still be redirected as normal, for both applications of redirection.""" display_name = "Shuffle Lasers" + option_off = 0 + option_local = 1 + option_anywhere = 2 class ShuffleDoors(Choice): diff --git a/worlds/witness/__init__.py b/worlds/witness/__init__.py index aeee7009cc..b2890768c6 100644 --- a/worlds/witness/__init__.py +++ b/worlds/witness/__init__.py @@ -6,6 +6,7 @@ from typing import Dict, Optional from BaseClasses import Region, Location, MultiWorld, Item, Entrance, Tutorial, CollectionState from Options import PerGameCommonOptions, Toggle +from .presets import witness_option_presets from .hints import get_always_hint_locations, get_always_hint_items, get_priority_hint_locations, \ get_priority_hint_items, make_hints, generate_joke_hints from worlds.AutoWorld import World, WebWorld @@ -31,6 +32,8 @@ class WitnessWebWorld(WebWorld): ["NewSoupVi", "Jarno"] )] + options_presets = witness_option_presets + class WitnessWorld(World): """ @@ -102,14 +105,29 @@ class WitnessWorld(World): self.log_ids_to_hints = dict() - if not (self.options.shuffle_symbols or self.options.shuffle_doors or self.options.shuffle_lasers): - if self.multiworld.players == 1: - warning(f"{self.multiworld.get_player_name(self.player)}'s Witness world doesn't have any progression" - f" items. Please turn on Symbol Shuffle, Door Shuffle or Laser Shuffle if that doesn't" - f" seem right.") - else: - raise Exception(f"{self.multiworld.get_player_name(self.player)}'s Witness world doesn't have any" - f" progression items. Please turn on Symbol Shuffle, Door Shuffle or Laser Shuffle.") + interacts_with_multiworld = ( + self.options.shuffle_symbols or + self.options.shuffle_doors or + self.options.shuffle_lasers == "anywhere" + ) + + has_progression = ( + interacts_with_multiworld + or self.options.shuffle_lasers == "local" + or self.options.shuffle_boat + or self.options.early_caves == "add_to_pool" + ) + + if not has_progression and self.multiworld.players == 1: + warning(f"{self.multiworld.get_player_name(self.player)}'s Witness world doesn't have any progression" + f" items. Please turn on Symbol Shuffle, Door Shuffle or Laser Shuffle if that doesn't seem right.") + elif not interacts_with_multiworld and self.multiworld.players > 1: + raise Exception(f"{self.multiworld.get_player_name(self.player)}'s Witness world doesn't have enough" + f" progression items that can be placed in other players' worlds. Please turn on Symbol" + f" Shuffle, Door Shuffle or non-local Laser Shuffle.") + + if self.options.shuffle_lasers == "local": + self.options.local_items.value |= self.item_name_groups["Lasers"] def create_regions(self): self.regio.create_regions(self, self.player_logic) diff --git a/worlds/witness/presets.py b/worlds/witness/presets.py new file mode 100644 index 0000000000..1fee1a7968 --- /dev/null +++ b/worlds/witness/presets.py @@ -0,0 +1,101 @@ +from typing import Any, Dict + +from .options import * + +witness_option_presets: Dict[str, Dict[str, Any]] = { + # Great for short syncs & scratching that "speedrun with light routing elements" itch. + "Short & Dense": { + "progression_balancing": 30, + + "puzzle_randomization": PuzzleRandomization.option_sigma_normal, + + "shuffle_symbols": False, + "shuffle_doors": ShuffleDoors.option_panels, + "door_groupings": DoorGroupings.option_off, + "shuffle_boat": True, + "shuffle_lasers": ShuffleLasers.option_local, + + "disable_non_randomized_puzzles": True, + "shuffle_discarded_panels": False, + "shuffle_vault_boxes": False, + "shuffle_EPs": ShuffleEnvironmentalPuzzles.option_off, + "EP_difficulty": EnvironmentalPuzzlesDifficulty.option_normal, + "shuffle_postgame": False, + + "victory_condition": VictoryCondition.option_mountain_box_short, + "mountain_lasers": 7, + "challenge_lasers": 11, + + "early_caves": EarlyCaves.option_off, + "elevators_come_to_you": False, + + "trap_percentage": TrapPercentage.default, + "puzzle_skip_amount": PuzzleSkipAmount.default, + "hint_amount": HintAmount.default, + "death_link": DeathLink.default, + }, + + # For relative beginners who want to move to the next step. + "Advanced, But Chill": { + "progression_balancing": 30, + + "puzzle_randomization": PuzzleRandomization.option_sigma_normal, + + "shuffle_symbols": True, + "shuffle_doors": ShuffleDoors.option_doors, + "door_groupings": DoorGroupings.option_regional, + "shuffle_boat": True, + "shuffle_lasers": ShuffleLasers.option_off, + + "disable_non_randomized_puzzles": False, + "shuffle_discarded_panels": True, + "shuffle_vault_boxes": True, + "shuffle_EPs": ShuffleEnvironmentalPuzzles.option_obelisk_sides, + "EP_difficulty": EnvironmentalPuzzlesDifficulty.option_normal, + "shuffle_postgame": False, + + "victory_condition": VictoryCondition.option_mountain_box_long, + "mountain_lasers": 6, + "challenge_lasers": 9, + + "early_caves": EarlyCaves.option_off, + "elevators_come_to_you": False, + + "trap_percentage": TrapPercentage.default, + "puzzle_skip_amount": 15, + "hint_amount": HintAmount.default, + "death_link": DeathLink.default, + }, + + # Allsanity but without the BS (no expert, no tedious EPs). + "Nice Allsanity": { + "progression_balancing": 50, + + "puzzle_randomization": PuzzleRandomization.option_sigma_normal, + + "shuffle_symbols": True, + "shuffle_doors": ShuffleDoors.option_mixed, + "door_groupings": DoorGroupings.option_off, + "shuffle_boat": True, + "shuffle_lasers": ShuffleLasers.option_anywhere, + + "disable_non_randomized_puzzles": False, + "shuffle_discarded_panels": True, + "shuffle_vault_boxes": True, + "shuffle_EPs": ShuffleEnvironmentalPuzzles.option_individual, + "EP_difficulty": EnvironmentalPuzzlesDifficulty.option_normal, + "shuffle_postgame": False, + + "victory_condition": VictoryCondition.option_challenge, + "mountain_lasers": 6, + "challenge_lasers": 9, + + "early_caves": EarlyCaves.option_off, + "elevators_come_to_you": True, + + "trap_percentage": TrapPercentage.default, + "puzzle_skip_amount": 15, + "hint_amount": HintAmount.default, + "death_link": DeathLink.default, + }, +} From e15873e8611f7fcf1b4918ed79faf23524ddda97 Mon Sep 17 00:00:00 2001 From: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:23:30 +0100 Subject: [PATCH 14/21] The Witness: Bonk trap support (#2645) --- worlds/witness/Options.py | 2 +- worlds/witness/WitnessItems.txt | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/worlds/witness/Options.py b/worlds/witness/Options.py index a15485c485..3912d1de11 100644 --- a/worlds/witness/Options.py +++ b/worlds/witness/Options.py @@ -189,7 +189,7 @@ class HintAmount(Range): class DeathLink(Toggle): """If on: Whenever you fail a puzzle (with some exceptions), everyone who is also on Death Link dies. - The effect of a "death" in The Witness is a Power Surge.""" + The effect of a "death" in The Witness is a Bonk Trap.""" display_name = "Death Link" diff --git a/worlds/witness/WitnessItems.txt b/worlds/witness/WitnessItems.txt index 8e50e328a4..e17464a092 100644 --- a/worlds/witness/WitnessItems.txt +++ b/worlds/witness/WitnessItems.txt @@ -30,8 +30,9 @@ Filler: #503 - Energy Fill (Max) - 1 Traps: -600 - Slowness - 8 +600 - Slowness - 6 610 - Power Surge - 2 +615 - Bonk - 1 Jokes: 650 - Functioning Brain From 5dcaa6ca20991cfc1a1f6e7db559c564f4f83f1e Mon Sep 17 00:00:00 2001 From: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:24:10 +0100 Subject: [PATCH 15/21] The Witness: Death Link Amnesty (#2646) --- worlds/witness/Options.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/worlds/witness/Options.py b/worlds/witness/Options.py index 3912d1de11..27aa8b9d95 100644 --- a/worlds/witness/Options.py +++ b/worlds/witness/Options.py @@ -193,6 +193,15 @@ class DeathLink(Toggle): display_name = "Death Link" +class DeathLinkAmnesty(Range): + """Number of panel fails to allow before sending a death through Death Link. + 0 means every panel fail will send a death, 1 means every other panel fail will send a death, etc.""" + display_name = "Death Link Amnesty" + range_start = 0 + range_end = 5 + default = 1 + + @dataclass class TheWitnessOptions(PerGameCommonOptions): puzzle_randomization: PuzzleRandomization @@ -216,3 +225,4 @@ class TheWitnessOptions(PerGameCommonOptions): puzzle_skip_amount: PuzzleSkipAmount hint_amount: HintAmount death_link: DeathLink + death_link_amnesty: DeathLinkAmnesty From 325a510ba73321ef25e9e2739412f1d69e65b4d8 Mon Sep 17 00:00:00 2001 From: JaredWeakStrike <96694163+JaredWeakStrike@users.noreply.github.com> Date: Tue, 16 Jan 2024 09:26:18 -0500 Subject: [PATCH 16/21] KH2: Promise charm logic (#2635) --- worlds/kh2/Rules.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/worlds/kh2/Rules.py b/worlds/kh2/Rules.py index 65f690fdde..1124f8109c 100644 --- a/worlds/kh2/Rules.py +++ b/worlds/kh2/Rules.py @@ -83,6 +83,8 @@ class KH2Rules: return state.has(ItemName.TornPages, self.player, amount) def level_locking_unlock(self, state: CollectionState, amount): + if self.world.options.Promise_Charm and state.has(ItemName.PromiseCharm, self.player): + return True return amount <= sum([state.count(item_name, self.player) for item_name in visit_locking_dict["2VisitLocking"]]) def summon_levels_unlocked(self, state: CollectionState, amount) -> bool: From 71a3e2230d92dd59cc3c092addb7c8c62060de7a Mon Sep 17 00:00:00 2001 From: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:27:09 +0100 Subject: [PATCH 17/21] The Witness: Allow Mountain Lasers to go up to 11 instead of 7. (#2618) --- worlds/witness/Options.py | 7 +++++-- worlds/witness/player_logic.py | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/worlds/witness/Options.py b/worlds/witness/Options.py index 27aa8b9d95..ac1f2bc828 100644 --- a/worlds/witness/Options.py +++ b/worlds/witness/Options.py @@ -140,10 +140,13 @@ class PuzzleRandomization(Choice): class MountainLasers(Range): - """Sets the amount of beams required to enter the final area.""" + """Sets the amount of lasers required to enter the Mountain. + If set to a higher amount than 7, the mountaintop box will be slightly rotated to make it possible to solve without + the hatch being opened. + This change will also be applied logically to the long solution ("Challenge Lasers" setting).""" display_name = "Required Lasers for Mountain Entry" range_start = 1 - range_end = 7 + range_end = 11 default = 7 diff --git a/worlds/witness/player_logic.py b/worlds/witness/player_logic.py index e05199c2b3..5d538e62b7 100644 --- a/worlds/witness/player_logic.py +++ b/worlds/witness/player_logic.py @@ -323,7 +323,10 @@ class WitnessPlayerLogic: elif victory == 3: self.VICTORY_LOCATION = "0xFFF00" - if chal_lasers <= 7: + # Long box can usually only be solved by opening Mountain Entry. However, if it requires 7 lasers or less + # (challenge_lasers <= 7), you can now solve it without opening Mountain Entry first. + # Furthermore, if the user sets mountain_lasers > 7, the box is rotated to not require Mountain Entry either. + if chal_lasers <= 7 or mnt_lasers > 7: adjustment_linesets_in_order.append([ "Requirement Changes:", "0xFFF00 - 11 Lasers - True", From 4fdeec4f70f82bf439b335fb28d4c0af9d6f296c Mon Sep 17 00:00:00 2001 From: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:33:34 +0100 Subject: [PATCH 18/21] The Witness: Cleanup - Options Access, data version, snake_case for file name (#2631) --- worlds/witness/__init__.py | 9 ++++----- worlds/witness/{Options.py => options.py} | 0 2 files changed, 4 insertions(+), 5 deletions(-) rename worlds/witness/{Options.py => options.py} (100%) diff --git a/worlds/witness/__init__.py b/worlds/witness/__init__.py index b2890768c6..a645abc081 100644 --- a/worlds/witness/__init__.py +++ b/worlds/witness/__init__.py @@ -16,7 +16,7 @@ from .locations import WitnessPlayerLocations, StaticWitnessLocations from .items import WitnessItem, StaticWitnessItems, WitnessPlayerItems, ItemData from .regions import WitnessRegions from .rules import set_rules -from .Options import TheWitnessOptions +from .options import TheWitnessOptions from .utils import get_audio_logs from logging import warning, error @@ -43,7 +43,6 @@ class WitnessWorld(World): """ game = "The Witness" topology_present = False - data_version = 14 StaticWitnessLogic() StaticWitnessLocations() @@ -91,10 +90,10 @@ class WitnessWorld(World): } def generate_early(self): - disabled_locations = self.multiworld.exclude_locations[self.player].value + disabled_locations = self.options.exclude_locations.value self.player_logic = WitnessPlayerLogic( - self, disabled_locations, self.multiworld.start_inventory[self.player].value + self, disabled_locations, self.options.start_inventory.value ) self.locat: WitnessPlayerLocations = WitnessPlayerLocations(self, self.player_logic) @@ -272,7 +271,7 @@ class WitnessWorld(World): self.own_itempool += new_items self.multiworld.itempool += new_items if self.items.item_data[item_name].local_only: - self.multiworld.local_items[self.player].value.add(item_name) + self.options.local_items.value.add(item_name) def fill_slot_data(self) -> dict: hint_amount = self.options.hint_amount.value diff --git a/worlds/witness/Options.py b/worlds/witness/options.py similarity index 100% rename from worlds/witness/Options.py rename to worlds/witness/options.py From de8fe21d4a13cdc6500850550d19230808e14f88 Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Tue, 16 Jan 2024 17:10:19 +0100 Subject: [PATCH 19/21] Tests: create sane cov defaults (#2728) --- .coveragerc | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .coveragerc diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000000..17a60ad125 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,5 @@ +[report] +exclude_lines = + pragma: no cover + if TYPE_CHECKING: + if typing.TYPE_CHECKING: From 49ecd4b9c113e8d9b4f8e928326733deee589c1c Mon Sep 17 00:00:00 2001 From: black-sliver <59490463+black-sliver@users.noreply.github.com> Date: Tue, 16 Jan 2024 17:10:58 +0100 Subject: [PATCH 20/21] CI: flake8: max-complexity=14 (#2731) The value of 10 does not really fit some of our world patterns and values up to 15 may be acceptable. Looking at some worlds, 14 seems to be achievable without too much work and reduces the noise in test output, making it more usable. --- .github/workflows/analyze-modified-files.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/analyze-modified-files.yml b/.github/workflows/analyze-modified-files.yml index ba2660809a..d01365745c 100644 --- a/.github/workflows/analyze-modified-files.yml +++ b/.github/workflows/analyze-modified-files.yml @@ -71,7 +71,7 @@ jobs: continue-on-error: true if: env.diff != '' && matrix.task == 'flake8' run: | - flake8 --count --max-complexity=10 --max-doc-length=120 --max-line-length=120 --statistics ${{ env.diff }} + flake8 --count --max-complexity=14 --max-doc-length=120 --max-line-length=120 --statistics ${{ env.diff }} - name: "mypy: Type check modified files" continue-on-error: true From 602c2966fc6574a5b455a59cce50b67dbaed8123 Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Tue, 16 Jan 2024 17:23:18 +0100 Subject: [PATCH 21/21] LttP: move _hint_text to SubClasses (#2532) --- BaseClasses.py | 3 --- worlds/alttp/SubClasses.py | 7 +++++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/BaseClasses.py b/BaseClasses.py index 855e69c5d4..38598d42d9 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1056,9 +1056,6 @@ class Location: @property def hint_text(self) -> str: - hint_text = getattr(self, "_hint_text", None) - if hint_text: - return hint_text return "at " + self.name.replace("_", " ").replace("-", " ") diff --git a/worlds/alttp/SubClasses.py b/worlds/alttp/SubClasses.py index 64e4adaec9..22eeebe181 100644 --- a/worlds/alttp/SubClasses.py +++ b/worlds/alttp/SubClasses.py @@ -26,6 +26,13 @@ class ALttPLocation(Location): self.player_address = player_address self._hint_text = hint_text + @property + def hint_text(self) -> str: + hint_text = getattr(self, "_hint_text", None) + if hint_text: + return hint_text + return "at " + self.name.replace("_", " ").replace("-", " ") + class ALttPItem(Item): game: str = "A Link to the Past"