From e274a53344c8945e99675f9554bc8874c947ca09 Mon Sep 17 00:00:00 2001 From: Ehseezed <97066152+Ehseezed@users.noreply.github.com> Date: Sat, 28 Oct 2023 19:04:35 -0500 Subject: [PATCH] fixed uppercase folder issue --- worlds/am2r/__init__.py | 52 ++++ .../am2r/__pycache__/__init__.cpython-310.pyc | Bin 0 -> 3080 bytes worlds/am2r/__pycache__/items.cpython-310.pyc | Bin 0 -> 5282 bytes .../__pycache__/locations.cpython-310.pyc | Bin 0 -> 14995 bytes .../am2r/__pycache__/options.cpython-310.pyc | Bin 0 -> 2212 bytes .../am2r/__pycache__/regions.cpython-310.pyc | Bin 0 -> 9027 bytes worlds/am2r/__pycache__/rules.cpython-310.pyc | Bin 0 -> 2873 bytes worlds/am2r/docs/en_AM2R.md | 20 ++ worlds/am2r/docs/setup_en.md | 12 + worlds/am2r/items.py | 144 ++++++++++ worlds/am2r/locations.py | 201 ++++++++++++++ worlds/am2r/options.py | 109 ++++++++ worlds/am2r/regions.py | 260 ++++++++++++++++++ worlds/am2r/rules.py | 60 ++++ worlds/am2r/todo.py | 3 + 15 files changed, 861 insertions(+) create mode 100644 worlds/am2r/__init__.py create mode 100644 worlds/am2r/__pycache__/__init__.cpython-310.pyc create mode 100644 worlds/am2r/__pycache__/items.cpython-310.pyc create mode 100644 worlds/am2r/__pycache__/locations.cpython-310.pyc create mode 100644 worlds/am2r/__pycache__/options.cpython-310.pyc create mode 100644 worlds/am2r/__pycache__/regions.cpython-310.pyc create mode 100644 worlds/am2r/__pycache__/rules.cpython-310.pyc create mode 100644 worlds/am2r/docs/en_AM2R.md create mode 100644 worlds/am2r/docs/setup_en.md create mode 100644 worlds/am2r/items.py create mode 100644 worlds/am2r/locations.py create mode 100644 worlds/am2r/options.py create mode 100644 worlds/am2r/regions.py create mode 100644 worlds/am2r/rules.py create mode 100644 worlds/am2r/todo.py diff --git a/worlds/am2r/__init__.py b/worlds/am2r/__init__.py new file mode 100644 index 000000000000..021fafd1abdb --- /dev/null +++ b/worlds/am2r/__init__.py @@ -0,0 +1,52 @@ +from typing import Dict +from .items import item_table +from .locations import get_location_datas, EventId +from .regions import create_regions_and_locations +from BaseClasses import Tutorial, Item +from .options import AM2R_options +from worlds.AutoWorld import World, WebWorld + + +class AM2RWeb(WebWorld): + theme = "partyTime" + tutorials = [Tutorial( + "Multiworld Setup Guide", + "A guide to setting up the Archipelago AM2R software on your computer. This guide covers single-player, multiworld, and related software.", + "English", + "setup_en.md", + "setup/en", + ["Zed"] + )] + + +class AM2RWorld(World): + """ + AM2R is a remake of the classic Metroid 2 game for the Game Boy that tries its best to keep the feel + of the original as well as filling in some gaps to more closely tie into Metroid Fusion and brings some + items from there as well. + """ + game = "AM2R" + option_definitions = options.AM2R_options + web = AM2RWeb() + + item_name_to_id = items.item_name_to_id + location_name_to_id = {location.name: location.code for location in get_location_datas(None, None)} + + item_name_groups = items.item_name_groups + data_version = 0 + + def fill_slot_data(self) -> Dict[str, object]: + return {name: getattr(self.multiworld, name)[self.player].value for name in self.option_definitions} + + def create_regions(self) -> None: + create_regions_and_locations(self.multiworld, self.player) + + def create_item(self, name: str) -> Item: + return items.create_item(self.player, name) + + def create_items(self) -> None: + self.multiworld.get_location("The Last Metroid is in Captivity", self.player).place_locked_item(self.create_item("The Galaxy is at Peace")) + items.create_all_items(self.multiworld, self.player) + + def set_rules(self) -> None: + self.multiworld.completion_condition[self.player] = lambda state: state.has("The Galaxy is at Peace", self.player) diff --git a/worlds/am2r/__pycache__/__init__.cpython-310.pyc b/worlds/am2r/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cd05aef01c757605ef6d5ec8309fb8aa329ec9c6 GIT binary patch literal 3080 zcmZ`*TW=dT7Uo=ZwIs`zxJ{A8PP4rUNY$e4Q)>k4CS9Z0+9);w3Jg*V=piMKGow*S zDyfC+lMQUKul)n@WB*N``MOWR{((iF_B+(oNjkzDkwfxazjF@DcDu>&{Qlpst;?^G4B( zo5e=F;nr$-t7ykgM2^h20w-^!N^&C; zC5Kqo2_K2&ZjIyhhZ9q(jORA|7Hn~%b#V0AKaz4x{Afqzhf?Lj)_;&EZj_Qv>koW6 z^VZLVQVA?JmbcgD|7xZE97+VXp)=k zDanf-NHZBnhm%am=`RP-kOq+{BQ1@|M#Bh;=2S)pDm~4{GUvlGB94(R2j(ePGAc*W zc{x#0S{CDpk*Xg(Jk9i+QCdEcN=F);a{2K%=jT%0iHfE8I}sceDeyH?EP3|h+BZ0m z>C@>3^q7njIqDZ;T6dFA5rxVdi1bzcICqFOIzY`@Jt8eXq));>CSL_R*TPj}DFwzB@cPTL1PjdEk-rpT;`I zNs^5+lO+A|xoU$gWq{rVsk6}Y*M6bbeCc+)7T#rA{2RyC!z=tal$En;T$A++7B^(` z46vtwUozQ3&?^`IeRko+ZLD|t$FbTN7hqb;$G(c7W|2<_U2gAk!*`oCs`#{mE_=7?Ou9|Hpe2a+`B zT4!l=B#kOF5q%a75u9jHDz|({ldsEjOt^`R%A}4mqoWh4F)gFVQqI6OkTQ3I=6sPj z!)(O!i0kO7%-#PW%X7kKHUezGcgV+@m=q;2ndYUIIRs@A8}N0Z(cdN->E=LpqHrA0 z#9=3kvRy|5RTiWyn9POs-D(_?(J7UN#p7c@LA!@n?}23O8T+yJPw$y`#_)Q-`ak&( zW(Y%mhXh9(rlp{A;`cwb^+h>ZAyMssk8SP?)FDc!yYCWnlC;OByURHAR~wEM>13-4 zd7?(w6r_b<{yH%Jiw!*C3IDSG3p-J{`ZkEOs!diIhGY1@@bM2| zodxR_hPH+h$&FFAxk_+boh7}kJmL8SmaDhGQ{4dh4fPgL8qBOGdFm#}hj^XEx7qa0 z%3_LlqVv+Y+S}hJ-{t!5nkP_Gw`b)v5pqEJNR?InnHalNcO&QE&UJ27P@TwKZ~FEh ziM-~eg=<~xiMc`S;T!nn!ly3u0OlR{0!2kz-6HZQA}eK_Ml)ruw~ik6Ic2u5DRX_3 zb6`rbT0AsfqOpUCVb7gD&%7%_;a~8n7ubq}x!stvqnXnTfr+rg@qGo3irjMtuhXjI zO>eARw8rK&|5R|q`R7v%;2G#6oJLpv^WYhKj;?j(U7_!r%JV9%llQLX-UBSBXe2WF zj-xm%(ib%c`F@Y1X+Fu!`O7`pamaK2^La$|$VA^up2}X$xjb&I_iSfoyahO>l3Md) zDH3NsnWx?diMw-iIH2bI^u~C#){NGl;G8DE-|_&Sy*GMBzvh}{R=$Ii*R%4>qvYCm z5PCH?33VikV52F<@FMO{l$8|Yo`0+;dr)42Q?6%m^=w5WE9IIg4t4eYoEIm8-~ED^ z5qdgo`u<9PwAw_Wo$9qzo1Xxz3DNN`Rj6FL3sG8*giE+ralT5r#**dd3z6@5)15bn z1Q^LN$Gw}1tW=vsx*)ceB%(~w;zQ~zwl=>$DeAZCT_V(dZSYi{xRBaSisS5#V9F#D zico2H7L|8x#dcR)hpL>6wQbQgFQFR@Dv2T=*+8SdRm+nznHqJQ1inY4LBs*+BN|b* ztYy(M*A-m}s8wikAl(hZO4Y;9-^Ckxdww(64w^wHbUTlGJGN@h$7obex-&@;0&HVp zfp!+DDt)ZYZ=bH}*p08bmiMS7OYLqyGc5_R&R@1WyTSAia3^wCPPIX6Eh6Mbr^B1f au=V}fUF$9(Qj>Wt*7CaE;_U{zt^Wf!PWWj6 literal 0 HcmV?d00001 diff --git a/worlds/am2r/__pycache__/items.cpython-310.pyc b/worlds/am2r/__pycache__/items.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8060c54b6972932e92ca7ef1c897756d0679da67 GIT binary patch literal 5282 zcmaJ^>vJ5%5#OENd+nW0tA}OD2Kn#{&VVo^U>?F)mLC`_VJw@+Vp!HUBWb0*hiCV& zrJO>@nc$?7RHdlOCqCGzN>%(9Xva)Y<;aqjLgJ_&Y<>E^A!ldZ0P zRntzonsG7;H-l_7=j5uEV^#A`9$1Q}gF>~>>8loYFt#L$>F&$!J8fb8OfqR59t-zF!`hxsmW zM)-)xt66qqrtY{bALV1EZ6>kAOS6$?fAhR)3U_10C}JUdauCR&4suu_j{|uk8Pky|13AK<#4O!WPXT$l2blo!Ob>Dt$g@4j zb3ne=gM1&z^F7E5K#uhwF9LZ|JnDf!Lf%fa$+UK^l-`LiElb?tDxRRgYvt+Th!Qc8S^IP57CrmLU z?0Gh(i4wmssqwe@MfIQi%5)x6+`$g_61b_(|1!Ayd${j_`>yh}p?Q`@Qdp9+3 zpSq)+(lGYv9-Xd2qM{_U+Y@4+IB{^L`k`)~r_*{8FpM zt;>L~>zEh912ZC$b)Sb*QcN$3%5oUnUFf2?*7T*|vArN7x89&R+)uoqDJrDZ_8)Re zl3iXzLuii9HHTf+gdt3>!)htK8Q$rojDzfeo`D^jPEJXr&`x8wnK(;YpYkFvHY;^5 z;?#nyHyd%W(z6js%EtxS(a)_ltFvV*&bn?5)^Od}a@}g3Hv?)HT=!bj3pyObuSIdc z>w2|XJyKQ)A!CG*lv$cI-;nhMDPXmFO%4E&gCKV`xrf?B?!7+!@|6oD8Y9LE?&7#Mi0S9+s7WmiN=a7V!9ljkW#m$W%aCH)NNhi z4>vI>v1MYeJ20|Hk$JfRBbsrRR#8r1QY$tWV3pZ&S`J~P944}h$T$()6`I^lWOIi} zYcFAsfY^9K6U5S>8zFJJ5qK*?#wJ-cPD>FrWv#-%>i)~!_QaP&+>hd6ZJ?13$AqACk`_bS!XE&kZZ9R$GH7#Nhsi)sIkp(CcjoAtf>;7ms z*<=pS2HDWpTH1%%D{M_)V`_)mm+TswOUzo<6E`wqHgQyKBsO>2gEh$!+Ld~>ahzx9biBg$A+-CaCL1oh!=p;$M zRtcJ1xKl60DU~ocIpW3^M|!md0q@>4B8sWbA{9#Z4=OmklO`0zAsbr^lnqE z8Bw~W-9jX`jWx4{+jy_GmTEDKH`@BBww8`EtyC*LjFVl$1*{fWz;qn5hu><9Pq3Rvo*E$XH*th4*kPUrQH-YdQOE)#goT^sq_xk3V4IxWi&NXoESr_Wpft{>Zulf?eI$7~v{&Y>q zg_Y7JueR)mG((CcLjO9obEi=;Ht3dF&CfM_PUB&1{|1<=nb-Z&8_jCNU!flO(yuKf z{8d7%xrSE}iO3HK=$mT@!J$DtMB(eV2rJBC(n;Y}{T~5wOlq#?-t|7|pq~+%h7PM(>&I%)1yr3?FB#MZ z;@>3lISHEN(f&`U{{@j-M1D$~+>BQ_+nk^GYyQuupLqvYdR?xRGppHi*dMKXuefQb z5>0e3e?|(iDw2*hw^)}^BA7irb3vi&Kbj9 zjpAlMFa+TNyd>Se2*UTMy;VypZ!J!It*4`{NT*E1<>cU-N|U5z9@ zrx`vX@-awk5td}1xR7X*^m=rl^iEeYnOq(sLIy<@N%gQi+yxRdlZ9m&yRIvC+qR&fJJ*l&Hl9HVk2p>3brNA#S zYnTQ}YehYMKeo5@QC<+1k1Cgw5wc{6`n`@zwxVj3q+(4+D-EOwN!E~L4oMnInljFx z^g^LNZ?SlWU~&RUUsG`=slq~4m?mjjW#}SF(DzG{tJRm!LF!HrQ8`^1oNir}E9Dhx zQQ7OHo1vI*2GXU@o^2nAZn*3>+^Y!5T7{H7Ktt5`6os1#Eal^|d6g;`ayR7(Ny$S_ zi`@54*&awa47W=oIXaBMb?4NcY+pvr5A9g?c zyKS?-*#qpa_8|LWYuuQi#m>o0+&2e*F-_NK5 H8^8ZQKgQ}( literal 0 HcmV?d00001 diff --git a/worlds/am2r/__pycache__/locations.cpython-310.pyc b/worlds/am2r/__pycache__/locations.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..25bfda3f34d362c56358417463e926080ed0eeba GIT binary patch literal 14995 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 JY6>l{{(lda+9Ut~ literal 0 HcmV?d00001 diff --git a/worlds/am2r/__pycache__/options.cpython-310.pyc b/worlds/am2r/__pycache__/options.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a0dfa9d346a7e86a877d88f284862dcb55d4d260 GIT binary patch literal 2212 zcmZ`*TWcFf6yBTevL)GxQ}>}vdX1n7G%a~3p+vRYG$pn}oKRQ{%XsHV+In|KnVEGW z<31%nqkka(rqAV?z-drNsG%* z2Nw@;=s%D=cZ4I{)XCh?&Aiad{Lpua=cPeb3v1=vPwQDDY-G)_nYF@J)(+cQC+uY1 zuvdc=MGM!hQr?3#Scjfy|L%u<@L>RrvA6BO zhUnaPL>Fpe4K~F(3`7sEjhmt`25*A!`W1-_o4D5-``ZrcY=}+N*%H?do#FL&cq?b< z+uHLyk+}^XC)(IxH;K*Awe7tkHOVhhrD9mOy^+?C9j9#8Oe@; zog`_B3#phqHay8I!9+4MRkD~&NzF`w$qXhOH)?aXva>vijdA}$sj-h5ilDz6BH4f2 zw9P2Wc?MBr+fkHBQKU5QM$wCcrwd6ViiC`bEfPIT`Mlg>JBqv&X>yirRSU?4ZHUUP zZyj|5PbtaYKks~d^hC*t;@K`Y{Ai@&X)=S9Pvp^PZ}j-#Xzw!b_Gs_!)1#LZOpS7! z%PNj{W^>g=t92ZjCQT=Bt6$SCCI26)Pf`87q4JlZ3Y(&i;cD2yG8!WVJ5z{H^x6HN z_$e^p@s!DN>Gf~!v0UaX<;N?hW6+Gk48urq%~BcjGX2grmj)SEz$%kfB+77j2yK7Z zQa$`veIf%Q8$>pVY#|AIl@lTi1IN@BcCcW;t2iwLM5B8btR!EuR#B*CACSFmB-lY5 zO=RW6z(p^T?Ek&UQ2JgkQo{Qe3BU17@!5}KC@Iz!WNveKfFqXdd9$!J#R=jdSjtyXW3( zU$`g1q4U<;FY#g@?@z!iPKZuonLXLK0~#kXB&u(8>4KkLKa*> zX6uz}t{_yO(!|=rdoZ`}_nZBeH}YK1-TLRlwOhfz><2lu literal 0 HcmV?d00001 diff --git a/worlds/am2r/__pycache__/regions.cpython-310.pyc b/worlds/am2r/__pycache__/regions.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..be61f2071c9004c7b13b29c52bb34b9d2efff33a GIT binary patch literal 9027 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 (Change this reference before release) +- [AM2R Multiworld Mod](https://github.com/Ehseezed/Amalgam) +- [AM2R Launcher](https://github.com/AM2R-Community-Developers/AM2RLauncher/releases) +- Your copy of AM2R 1.1 for the launcher + +## Installation Process + +Download the latest release of [AM2R Launcher](https://github.com/AM2R-Community-Developers/AM2RLauncher/releases) and +then using your AM2R 1.1 set up the launcher once the launcher is set up you can install the mod \ No newline at end of file diff --git a/worlds/am2r/items.py b/worlds/am2r/items.py new file mode 100644 index 000000000000..cd728d68bf2a --- /dev/null +++ b/worlds/am2r/items.py @@ -0,0 +1,144 @@ +import itertools +from collections import Counter +from typing import Dict, List, NamedTuple, Set + +from BaseClasses import Item, ItemClassification, MultiWorld +from .options import MetroidsAreChecks, MetroidsRequired, get_option_value + + +class ItemData(NamedTuple): + code: int + group: str + classification: ItemClassification = ItemClassification.progression + required_num: int = 0 + + +class AM2RItem(Item): + game: str = "AM2R" + + +def create_item(player: int, name: str) -> Item: + item_data = item_table[name] + return AM2RItem(name, item_data.classification, item_data.code, player) + + +def create_fixed_item_pool() -> List[str]: + required_items: Dict[str, int] = {name: data.required_num for name, data in item_table.items()} + return list(Counter(required_items).elements()) + + +def create_metroid_items(MetroidsRequired: MetroidsRequired, MetroidsAreChecks: MetroidsAreChecks) -> List[str]: + metroid_count = 0 + if MetroidsAreChecks == MetroidsAreChecks.option_include_A6: + metroid_count = MetroidsRequired.value + 5 + elif MetroidsAreChecks == MetroidsAreChecks.option_exclude_A6: + metroid_count += MetroidsRequired.value + return ["Metroid" for _ in range(metroid_count)] + + +def create_trap_items(multiworld: MultiWorld, player: int, locations_to_trap: int) -> List[str]: + trap_pool = trap_weights.copy() + + return multiworld.random.choices( + population=list(trap_pool.keys()), + weights=list(trap_pool.values()), + k=locations_to_trap + ) + + +def create_random_items(multiworld: MultiWorld, player: int, random_count: int) -> List[str]: + filler_pool = filler_weights.copy() + + return multiworld.random.choices( + population=list(filler_pool.keys()), + weights=list(filler_pool.values()), + k=random_count + ) + + +def create_all_items(multiworld: MultiWorld, player: int) -> None: + sum_locations = len(multiworld.get_unfilled_locations(player)) + + itempool = ( + create_fixed_item_pool() + + create_metroid_items(multiworld.MetroidsRequired[player], multiworld.MetroidsAreChecks[player]) + ) + + trap_percentage = get_option_value(multiworld, player, "TrapFillPercentage") + trap_fill = trap_percentage / 100 + + random_count = sum_locations - len(itempool) + locations_to_trap = int(trap_fill * random_count) + itempool += create_trap_items(multiworld, player, locations_to_trap) + + random_count = sum_locations - len(itempool) + itempool += create_random_items(multiworld, player, random_count) + + multiworld.itempool += [create_item(player, name) for name in itempool] + + +item_table: Dict[str, ItemData] = { + "Missile": ItemData(8678000, "Ammo", ItemClassification.filler), + "Super Missile": ItemData(8678001, "Ammo", ItemClassification.progression, 1), + "Power Bomb": ItemData(8678002, "Ammo", ItemClassification.progression, 2), + "Energy Tank": ItemData(8678003, "Ammo", ItemClassification.filler, 1), + # "Arm Cannon": ItemData8678004, ("Equipment", ItemClassification.progression, 1), + # "Morph Ball": ItemData8678005, ("Equipment", ItemClassification.progression, 1), + # "Power Grip": ItemData8678006, ("Equipment", ItemClassification.progression, 1), + "Bombs": ItemData(8678007, "Equipment", ItemClassification.progression, 1), + "Spider Ball": ItemData(8678008, "Equipment", ItemClassification.progression, 1), + "Hi Jump": ItemData(8678009, "Equipment", ItemClassification.progression, 1), + "Spring Ball": ItemData(8678010, "Equipment", ItemClassification.progression, 1), + "Space Jump": ItemData(8678011, "Equipment", ItemClassification.progression, 1), + "Speed Booster": ItemData(8678012, "Equipment", ItemClassification.progression, 1), + "Screw Attack": ItemData(8678013, "Equipment", ItemClassification.progression, 1), + "Varia Suit": ItemData(8678014, "Equipment", ItemClassification.useful, 1), + "Gravity Suit": ItemData(8678015, "Equipment", ItemClassification.progression, 1), + "Charge Beam": ItemData(8678016, "Beam", ItemClassification.useful, 1), + "Wave Beam": ItemData(8678017, "Beam", ItemClassification.useful, 1), + "Spazer": ItemData(8678018, "Beam", ItemClassification.useful, 1), + "Plasma Beam": ItemData(8678019, "Beam", ItemClassification.useful, 1), + "Ice Beam": ItemData(8678020, "Beam", ItemClassification.progression, 1), + "Equipment Trap": ItemData(8678021, "Trap", ItemClassification.trap), + "Freeze Trap": ItemData(8678022, "Trap", ItemClassification.trap), + "Short Beam": ItemData(8678023, "Trap", ItemClassification.trap), + "EMP Trap": ItemData(8678024, "Trap", ItemClassification.trap), + "Metroid": ItemData(8678025, "MacGuffin", ItemClassification.progression_skip_balancing), + "The Galaxy is at Peace": ItemData(8678026, "Victory", ItemClassification.progression) + +} + +filler_weights: Dict[str, int] = { + "Missile": 44, + "Super Missile": 10, + "Power Bomb": 10, + "Energy Tank": 10 +} +trap_weights: Dict[str, int] = { + "Equipment Trap": 1, + "Freeze Trap": 1, + "Short Beam": 1, + "EMP Trap": 1 +} + + +def get_item_group(item_name: str) -> str: + return item_table[item_name].group + + +def item_is_filler(item_name: str) -> bool: + return item_table[item_name].classification == ItemClassification.filler + + +def item_is_trap(item_name: str) -> bool: + return item_table[item_name].classification == ItemClassification.trap + + +trap_items: List[str] = list(filter(item_is_trap, item_table.keys())) +filler_items: List[str] = list(filter(item_is_filler, item_table.keys())) + +item_name_to_id: Dict[str, int] = {name: data.code for name, data in item_table.items()} + +item_name_groups: Dict[str, Set[str]] = { + group: set(item_names) for group, item_names in itertools.groupby(item_table, get_item_group) +} diff --git a/worlds/am2r/locations.py b/worlds/am2r/locations.py new file mode 100644 index 000000000000..1041518a40b7 --- /dev/null +++ b/worlds/am2r/locations.py @@ -0,0 +1,201 @@ +from typing import List, Tuple, Optional, Callable, NamedTuple +from BaseClasses import MultiWorld, CollectionState +from .rules import AM2RLogic +from .options import MetroidsAreChecks, is_option_enabled + + +EventId: Optional[int] = None + + +class LocationData(NamedTuple): + region: str + name: str + code: Optional[int] + rule: Callable[[CollectionState], bool] = lambda state: True + +def get_location_datas(world: Optional[MultiWorld], player: Optional[int]): + location_table = [LocationData, ...] + logic = AM2RLogic(world, player) + + location_table: List[LocationData] = [ + LocationData("Main Caves", "Main Caves: Vertical Spike Room Upper", 8680000, lambda state: logic.AM2R_can_fly(state) or state.has("Spider Ball", player) and state.has("Bombs", player)), # spider + bomb + LocationData("Main Caves", "Main Caves: Vertical Spike Room Lower", 8680001, logic.AM2R_can_bomb), # bomb + LocationData("Main Caves", "Main Caves: Crumble Spike Room", 8680002, logic.AM2R_can_jump), # jump + LocationData("Main Caves", "Main Caves: Maze", 8680003), + LocationData("Main Caves", "Main Caves: Shinespark Before the drop", 8680004, lambda state: state.has("Speed Booster", player)), # speed + LocationData("Main Caves", "Main Caves: Shinespark After the drop", 8680005, lambda state: state.has("Speed Booster", player)), # speed + + LocationData("Golden Temple", "Golden Temple: Bombs", 8680006), + LocationData("Golden Temple", "Golden Temple: Below Bombs", 8680007, logic.AM2R_can_bomb), # bomb + LocationData("Golden Temple", "Golden Temple: Hidden Energy Tank", 8680008, logic.AM2R_can_bomb), # bomb + LocationData("Golden Temple", "Golden Temple: Charge Beam", 8680009), + LocationData("Golden Temple", "Golden Temple: Armory Left", 8680010), + LocationData("Golden Temple", "Golden Temple: Armory Upper", 8680011), + LocationData("Golden Temple", "Golden Temple: Armory Lower", 8680012), + LocationData("Golden Temple", "Golden Temple: Armory Behind The False Wall ", 8680013, logic.AM2R_can_bomb), # bomb + LocationData("Golden Temple", "Golden Temple: Puzzle Room 1", 8680014), + LocationData("Golden Temple", "Golden Temple: Puzzle Room 2", 8680015), + LocationData("Golden Temple", "Golden Temple: Puzzle Room 3", 8680016), + LocationData("Golden Temple", "Golden Temple: Spider Ball", 8680017), + LocationData("Golden Temple", "Golden Temple: Celling Missile", 8680018, lambda state: state.has("Speed Booster", player) or logic.AM2R_can_spider(state)), # canspider + LocationData("Golden Temple", "Golden Temple: EMP room", 8680019, lambda state: state.has("Super Missile", player) and logic.AM2R_has_ballspark(state) and logic.AM2R_can_bomb(state) and state.has("Screw Attack", player)), # super + ballspark + + LocationData("Guardian", "Guardian: Up Above", 8680020, lambda state: logic.AM2R_can_bomb(state) and ((logic.AM2R_can_schmove(state) and state.has("Bombs", player)) or logic.AM2R_can_fly(state))), # bomb + schmove + LocationData("Guardian", "Guardian: Behind The Door", 8680021, lambda state: state.has("Power Bomb", player) and ((logic.AM2R_can_schmove(state) and state.has("Bombs", player)) or logic.AM2R_can_fly(state))), # PB + schmove + + LocationData("Hydro Station", "Hydro Station: Cliff", 8680022, logic.AM2R_can_fly), + LocationData("Hydro Station", "Hydro Station: Morph Tunnel", 8680023), + LocationData("Hydro Station", "Hydro Station: Turbine Room", 8680024, logic.AM2R_can_bomb), # bomb + LocationData("Hydro Station", "Hydro Station: Not so Secret Tunel", 8680025, logic.AM2R_can_schmove), # schmove + LocationData("Hydro Station", "Hydro Station: Water puzzle Beside Varia", 8680026, logic.AM2R_can_bomb), # bomb + LocationData("Hydro Station", "Hydro Station: Varia Suit", 8680027, logic.AM2R_can_bomb), # bomb + LocationData("Hydro Station", "Hydro Station: EMP room", 8680028, lambda state: state.has("Super Missile", player) and state.has("Speed Booster", player)), # super + speed + + LocationData("Arachnus", "Arachnus: Boss", 8680029), + + LocationData("Inner Hydro Station", "Hydro Station: Wave Beam", 8680030, logic.AM2R_can_bomb), + LocationData("Inner Hydro Station", "Hydro Station: Below Tower Pipe Upper", 8680031, lambda state: logic.AM2R_can_schmove(state) and logic.AM2R_can_bomb(state)), # schmove + LocationData("Inner Hydro Station", "Hydro Station: Below Tower Pipe Lower", 8680032, logic.AM2R_can_bomb), + LocationData("Inner Hydro Station", "Hydro Station: Dead End Missile ", 8680033, logic.AM2R_can_bomb), + LocationData("Inner Hydro Station", "Hydro Station: Hi Jump", 8680034), + LocationData("Inner Hydro Station", "Hydro Station: Behind Hi Jump Upper", 8680035, lambda state: logic.AM2R_can_schmove(state) and logic.AM2R_can_bomb(state)), + LocationData("Inner Hydro Station", "Hydro Station: Behind Hi Jump", 8680036, logic.AM2R_can_bomb), + + LocationData("Hydro Nest", "Hydro Nest: Below the Walkway", 8680037, logic.AM2R_can_bomb), # Bomb + LocationData("Hydro Nest", "Hydro Nest: Speed Celling", 8680038, lambda state: state.has("Speed Booster", player) and state.has("Speed Booster", player)), # speed + LocationData("Hydro Nest", "Hydro Nest: Behind the Wall", 8680039, lambda state: state.has("Power Bomb", player) and state.has("Screw Attack", player) and state.has("Speed Booster", player)), # PB + screw/speed + + LocationData("Industrial Complex Nest", "Industrial Complex: Above Save", 8680040), + LocationData("Industrial Complex Nest", "Industrial Complex: EMP Room", 8680041, lambda state: state.has("Power Bomb", player) and state.has("Super Missile", player) and state.can_reach("EMP", "Region", player)), # PB + super + LocationData("Industrial Complex Nest", "Industrial Complex Nest: Nest Shinespark", 8680042, lambda state: state.has("Super Missile", player) and state.has("Speed Booster", player) and logic.AM2R_can_schmove(state) and logic.AM2R_can_bomb(state)), # super + schmove + + LocationData("Pre Industrial Complex", "Industrial Complex: In the Sand", 8680043), + LocationData("Pre Industrial Complex", "Industrial Complex: Complex Side After Tunnel", 8680044, lambda state: (state.has("Speed Booster", player) or logic.AM2R_can_spider(state)) and logic.AM2R_can_bomb(state)), + LocationData("Pre Industrial Complex", "Industrial Complex: Complex Side Tunnel", 8680045, lambda state: state.has("Speed Booster", player) or logic.AM2R_can_spider(state)), + LocationData("Pre Industrial Complex", "Industrial Complex: Save Room", 8680046, lambda state: state.has("Speed Booster", player) or logic.AM2R_can_spider(state)), + LocationData("Pre Industrial Complex", "Industrial Complex: Spazer", 8680047, lambda state: state.has("Speed Booster", player) or logic.AM2R_can_spider(state)), + LocationData("Pre Industrial Complex", "Industrial Complex: Gamma Spark", 8680048, lambda state: state.has("Speed Booster", player)), + LocationData("Pre Industrial Complex", "Industrial Complex: Speed Booster", 8680049, lambda state: state.has("Speed Booster", player) or logic.AM2R_can_bomb(state)), # bomb + + LocationData("Torizo Ascended", "Torizo Ascended: Boss", 8680050, logic.AM2R_can_schmove), + + LocationData("Industrial Complex", "Industrial Complex: Conveyor Belt Room", 8680051, lambda state: state.has("Speed Booster", player)), + LocationData("Industrial Complex", "Industrial Complex: Doom Treadmill", 8680052, lambda state: state.has("Speed Booster", player) and logic.AM2R_can_bomb(state)), + LocationData("Industrial Complex", "Industrial Complex: Robot room in the Wall", 8680053, lambda state: state.has("Speed Booster", player)), + LocationData("Industrial Complex", "Industrial Complex: Robot room in the Floor", 8680054, lambda state: state.has("Super Missile", player) and state.has("Speed Booster", player)), + LocationData("Industrial Complex", "Industrial Complex: First Supers", 8680055, lambda state: state.has("Super Missile", player) and state.has("Speed Booster", player)), + + LocationData("GFS Thoth", "GFS Thoth: Research Camp", 8680056), + LocationData("GFS Thoth", "GFS Thoth: Hornoad room", 8680057, lambda state: state.has("Power Bomb", player)), + LocationData("GFS Thoth", "GFS Thoth: Outside the Front of the Ship", 8680058, lambda state: state.has("Power Bomb", player)), + LocationData("GFS Thoth", "GFS Thoth: Genesis", 8680059, lambda state: state.has("Power Bomb", player)), + + LocationData("The Tower", "The Tower: Beside Hydro Pipe", 8680060, lambda state: state.has("Screw Attack", player)), + LocationData("The Tower", "The Tower: Right Side of Tower", 8680061), + LocationData("The Tower", "The Tower: In the Ceiling", 8680062, lambda state: logic.AM2R_can_bomb(state) and (state.has("Space Jump", player) or state.has("Spider Ball", player))), # spider + bomb + LocationData("The Tower", "The Tower: Dark Maze", 8680063, logic.AM2R_can_bomb), # bomb + LocationData("The Tower", "The Tower: Outside the Dark Maze", 8680064, logic.AM2R_can_bomb), + LocationData("The Tower", "The Tower: Plasma Beam", 8680065, lambda state: logic.AM2R_can_bomb(state) and state.can_reach("Tester", "Region", player)), + LocationData("The Tower", "The Tower: Beside Tester", 8680066, lambda state: state.has("Power Bomb", player)), # pb + LocationData("The Tower", "The Tower: Left side of tower", 8680067, lambda state: state.has("Power Bomb", player)), # pb + + LocationData("Geothermal", "The Tower: Geothermal Reactor", 8680068), + LocationData("Geothermal", "The Tower: Post Geothermal Chozo", 8680069, lambda state: state.has("Power Bomb", player)), # pb + LocationData("Geothermal", "The Tower: Post Geothermal Shinespark", 8680070, lambda state: state.has("Power Bomb", player) and state.has("Speed Booster", player) and state.has("Super Missile", player)), # Pb + spped + super + + LocationData("Underwater Distribution Center", "Distribution Center: Main Room Shinespark", 8680071, lambda state: state.has("Gravity Suit", player) and state.has("Speed Booster", player)), # grav + screw + LocationData("Underwater Distribution Center", "Distribution Center: Speed Hallway", 8680072, lambda state: state.has("Speed Booster", player) and state.has("Gravity Suit", player)), # speed + grav + + LocationData("EMP", "Distribution Center: After EMP Activation", 8680073, lambda state: state.has("Screw Attack", player)), # screw + + LocationData("Underwater Distro Connection", "Distribution Center: Spiderball Spike \"Maze\"", 8680074, lambda state: state.has("Spider_Ball", player)), # spiderball + LocationData("Underwater Distro Connection", "Distribution Center: Before Spikey Tunnel", 8680075), + LocationData("Underwater Distro Connection", "Distribution Center: Spikey Tunnel Shinespark", 8680076, lambda state: state.has("Gravity Suit", player) and state.has("Speed Booster", player)), # grav + speed + LocationData("Underwater Distro Connection", "Distribution Center: After Spikey Tunnel", 8680078, lambda state: state.has("Power Bomb", player) and state.has("Speed Booster", player) and state.has("Gravity Suit", player) and state.has("Space_Jump", player)), # speed + grav + space + pb + + LocationData("Pipe Hell R", "Distribution Center: Screw Attack", 8680080), + LocationData("Pipe Hell Outside", "Distribution Center: Outside after Gravity", 8680081, lambda state: state.has("Power Bomb", player) and state.has("Space_Jump", player) and state.has("Gravity Suit", player)), # pb + space + grav + LocationData("Pipe Hell R", "Distribution Center: Before Underwater Pipe", 8680082, lambda state: state.has("Power Bomb", player) and state.has("Speed Booster", player)), # pb + speed + + LocationData("Gravity", "Distribution Center: Before Gravity", 8680083, lambda state: (state.has("Bombs", player) and (state.has("ChargeBeam", player) or state.has("Gravity Suit", player))) or state.has("Power Bomb", player)), # bomb + charge/gravity / PB + LocationData("Gravity", "Distribution Center: Gravity", 8680084, logic.AM2R_can_bomb), # can bomb + + LocationData("Ice Beam", "Serris: Ice Beam", 8680085, lambda state: state.has("Ice Beam", player) and (state.has("Super Missile", player) or state.has("Speed Booster", player))), # speed / Supers + + LocationData("Deep Caves", "Deep Caves: Ball Spark", 8680086, logic.AM2R_has_ballspark), + LocationData("Deep Caves", "Deep Caves: Behind the Bomb Block", 8680087, logic.AM2R_can_bomb), + + LocationData("Deep Caves", "Deep Caves: After Omega", 8680088), + + LocationData("Research Station", "The Last Metroid is in Captivity", EventId), + ] + + if not world or is_option_enabled(world, player, "MetroidsAreChecks"): + location_table += ( + #metroids + # todo remove or place locked items below when option not enabled + LocationData("First Alpha", "The Forgotten Alpha", 8680100), + + LocationData("Golden Temple", "Golden Temple: Metroid above Spider Ball", 8680101, logic.AM2R_can_spider), + LocationData("Golden Temple Nest", "Golden Temple Nest: Moe", 8680102, logic.AM2R_can_bomb), # Loj + LocationData("Golden Temple Nest", "Golden Temple Nest: Larry", 8680103, logic.AM2R_can_bomb), # Loj + LocationData("Golden Temple Nest", "Golden Temple Nest: Curly", 8680104, logic.AM2R_can_bomb), # Loj + + LocationData("Main Caves", "Main Caves: Freddy Fazbear", 8680105), # Epsilon + LocationData("Hydro Station", "Hydro Station: Turbine Terror", 8680106), # Xander + LocationData("Hydro Station", "Hydro Station: The Lookout", 8680107, logic.AM2R_can_schmove), # Xander + LocationData("Hydro Station", "Hydro Station: Recent Guardian", 8680108), # ANX + + LocationData("Hydro Nest", "Hydro Nest: Spiderman Decent", 8680109), + LocationData("Hydro Nest", "Hydro Nest: Carnage Awful", 8680110), + LocationData("Hydro Nest", "Hydro Nest: Venom Awesome", 8680111), + LocationData("Hydro Nest", "Hydro Nest: Something More Something Awesome", 8680112), + + LocationData("Industrial Complex Nest", "Industrial Nest: Mimolette", 8680113, lambda state: state.has("Speed Booster", player) or state.has("Super Missile", player)), + LocationData("Industrial Complex Nest", "Industrial Nest: The Big Cheese", 8680114, lambda state: state.has("Speed Booster", player) or state.has("Super Missile", player)), + LocationData("Industrial Complex Nest", "Industrial Nest: Mohwir", 8680115, lambda state: logic.AM2R_can_bomb(state) and (state.has("Speed Booster", player) or state.has("Super Missile", player))), + LocationData("Industrial Complex Nest", "Industrial Nest: Chirn", 8680116, lambda state: logic.AM2R_can_bomb(state) and (state.has("Speed Booster", player) or state.has("Super Missile", player))), + LocationData("Industrial Complex Nest", "Industrial Nest: BHHarbinger", 8680117, lambda state: logic.AM2R_can_bomb(state) and (state.has("Speed Booster", player) or state.has("Super Missile", player))), + LocationData("Industrial Complex Nest", "Industrial Nest: The Abyssal Creature", 8680118, lambda state: logic.AM2R_can_bomb(state) and state.has("Spider Ball", player) and (state.has("Speed Booster", player) or state.has("Super Missile", player))), + + LocationData("Pre Industrial Complex", "Industrial Complex: Sisyphus", 8680119, logic.AM2R_can_spider), # Mimo + LocationData("Pre Industrial Complex", "Industrial Complex: And then there\'s this Asshole", 8680120, logic.AM2R_can_spider), # ANX + + LocationData("Industrial Complex", "Inside Industrial: Guardian of Doom Treadmill", 8680121, lambda state: state.has("Speed Booster", player) and logic.AM2R_can_bomb(state)), + LocationData("Industrial Complex", "Inside Industrial: Rawsome1234 by the Lava", 8680122, lambda state: state.has("Speed Booster", player) and logic.AM2R_can_bomb(state)), + + LocationData("GFS Thoth", "Dual Alphas: Marco", 8680123), # Epsilon + LocationData("GFS Thoth", "Dual Alphas: Polo", 8680124), # Epsilon + + LocationData("Mines", "Mines: Unga", 8680125, lambda state: state.has("Super Missile", player) and (state.has("Space Jump", player) or state.has("Spider Ball", player))), + LocationData("Mines", "Mines: Gunga", 8680126, lambda state: state.has("Super Missile", player) and (state.has("Space Jump", player) or state.has("Spider Ball", player))), + + LocationData("The Tower", "The Tower: Patricia", 8680127, logic.AM2R_can_fly), # Mahan + LocationData("The Tower", "The Tower: Variable \"GUH\"", 8680128, logic.AM2R_can_fly), # ANX + LocationData("The Tower", "Ruler of The Tower: Slagathor", 8680129, logic.AM2R_can_schmove), # Rawsome + LocationData("The Tower", "The Tower: Anakin", 8680130, logic.AM2R_can_bomb), # Xander + LocationData("The Tower", "The Tower: Mr.Sandman", 8680131, logic.AM2R_can_fly), # Xander + LocationData("The Tower", "The Tower: Xander", 8680132, lambda state: state.has("Space Jump", player)), + + LocationData("EMP", "EMP: Sir Zeta Commander of the Alpha Squadron", 8680133, logic.AM2R_can_bomb), # Lucina + + LocationData("Pipe Hell R", "Alpha Squadron: Timmy", 8680134), # Lucina + LocationData("Pipe Hell R", "Alpha Squadron: Tommy", 8680135), # Lucina + LocationData("Pipe Hell R", "Alpha Squadron: Terry", 8680136), # Lucina + LocationData("Pipe Hell R", "Alpha Squadron: Telly", 8680137), # Lucina + LocationData("Pipe Hell R", "Alpha Squadron: Martin", 8680138), + + LocationData("Underwater Distro Connection", "Underwater: Gamma Bros Mario", 8680139), # Lucina + LocationData("Underwater Distro Connection", "Underwater: Gamma Bros Luigi", 8680140), # Lucina + ) + + if MetroidsAreChecks == MetroidsAreChecks.option_include_A6: + location_table += ( + LocationData("Deep Caves", "Deep Caves: Little Bro", 8680141), + LocationData("Deep Caves", "Deep Caves: Big Sis", 8680142), + LocationData("Omega Nest", "Omega Nest: SA-X Queen Lucina", 8680143), + LocationData("Omega Nest", "Omega Nest: Epsilon", 8680144), + LocationData("Omega Nest", "Omega Nest: Druid", 8680145), + ) + + return tuple(location_table) diff --git a/worlds/am2r/options.py b/worlds/am2r/options.py new file mode 100644 index 000000000000..e659505f224a --- /dev/null +++ b/worlds/am2r/options.py @@ -0,0 +1,109 @@ +from typing import Union, List, Dict +from BaseClasses import MultiWorld +from Options import AssembleOptions, Choice, DeathLink, DefaultOnToggle, Range, StartInventoryPool + + +class MetroidsRequired(Range): + """Chose how many Metroids need to be killed or obtained to go through to the omega nest""" + display_name = "Metroids Required for Omega Nest" + range_start = 0 + range_end = 41 + default = 41 + + +class MetroidsAreChecks(Choice): + """Have each of the 46 non lab Metroids be treated as locations""" + display_name = "Metroids are Checks" + default = 0 + option_disabled = 0 + option_exclude_A6 = 1 + option_include_A6 = 2 + + +class TrapFillPercentage(Range): + """Adds in Slightly inconvenient Traps into the item pool Equipment Traps disable 1 random item for up to 3 minutes + depending on the disabled item (more critical items will be disabled for less time). Ice Traps seem rather + self-explanatory, but they will freeze you upon receiving them with a full fanfare and an actual player freeze""" + display_name = "Trap Fill Percentage" + range_start = 0 + range_end = 100 + default = 0 + + +#class ItemSprites(OptionList): +# """Changes Item Sprites. Does not affect gameplay +# Sprite Authors appear in the item description""" +# display_name = "Item Sprites" +# default = 0 +# option_default = 0 +# option_themed = 1 +# option_chiny = 2 +# option_ungrouped = 3 +# option_lies = 4 + + +#class Visuals(Toggle): +# """"""Re-colours all the visual elements with new fresh palettes. Does not affect gameplay. +# Courtesy of Abyssal"""""" + + +#class StartingWeapons(Choice): +# """Removes your Arm Cannon and makes it a findable item""" +# display_name = "Starting Weapons" +# default = 0 +# option_normal = 0 +# option_missiles_only = 1 +# option_beam_only = 2 +# option_none = 3 + + +#class RandomizeBaby(Toggle): +# """Randomizes the baby metroid as a cosmetic find""" +# display_name = "Randomize Baby" + + +#class AreaRando(Choice): +# """Activates Area Randomization and or Boss Randomization, also activates rolling saves as softlock prevention +# Area Randomizer will shuffle various Areas arround in order to create a new expierence +# Boss Randomization randomizes Arachnus, Torizo Ascended, and Genesis with each other also then randomizes +# Temple Guardian, Tester and Serris +# Both activates Both independently on their own""" +# display_name = "Area Randomizer" +# +# default = 0 +# option_disabled = 0 +# option_area = 1 +# option_boss = 2 +# option_both = 3 + + +# class IceMissiles(Toggle): +# """Changes missiles to have Ice properties +# Does not account for jumping off enemies +# only counts as being able to freeze meboids and metroid larva""" +# display_name = "Ice Missiles" + + +AM2R_options: Dict[str, AssembleOptions] = { + "MetroidsRequired": MetroidsRequired, + "MetroidsAreChecks": MetroidsAreChecks, + "TrapFillPercentage": TrapFillPercentage, + # "Item Sprites": ItemSprites, + # "Starting Weapons": StartingWeapons, + # "Randomize Baby", RandomizeBaby + # "Area Rando": AreaRando, + # "Ice Missiles": IceMissiles, + # "DeathLink": DeathLink, +} + + +def is_option_enabled(world: MultiWorld, player: int, name: str) -> bool: + return get_option_value(world, player, name) > 0 + + +def get_option_value(world: MultiWorld, player: int, name: str) -> Union[int, Dict, List]: + option = getattr(world, name, None) + if option is None: + return 0 + + return option[player].value diff --git a/worlds/am2r/regions.py b/worlds/am2r/regions.py new file mode 100644 index 000000000000..b04e543d20bf --- /dev/null +++ b/worlds/am2r/regions.py @@ -0,0 +1,260 @@ +from typing import List, Set, Dict, Tuple, Optional, Callable, NamedTuple +from BaseClasses import CollectionState, MultiWorld, Region, Entrance, Location +from .locations import LocationData, get_location_datas +from .rules import AM2RLogic + +EventId: Optional[int] = None + + +class LocationData(NamedTuple): + region: str + name: str + code: Optional[int] + rule: Callable[[CollectionState], bool] = lambda state: True + + +def create_regions_and_locations(world: MultiWorld, player: int): + location_datas: Tuple[LocationData] = get_location_datas(world, player) + + locations_per_region: Dict[str, List[LocationData]] = split_location_datas_per_region(location_datas) + + regions = [ + create_region(world, player, locations_per_region, "Menu"), + create_region(world, player, locations_per_region, "Main Caves"), + create_region(world, player, locations_per_region, "First Alpha"), + create_region(world, player, locations_per_region, "Guardian"), + create_region(world, player, locations_per_region, "Golden Temple"), + create_region(world, player, locations_per_region, "Golden Temple Nest"), + create_region(world, player, locations_per_region, "Hydro Station"), + create_region(world, player, locations_per_region, "Inner Hydro Station"), + create_region(world, player, locations_per_region, "Hydro Nest"), + create_region(world, player, locations_per_region, "Arachnus"), + create_region(world, player, locations_per_region, "Mines"), + create_region(world, player, locations_per_region, "Industrial Complex Nest"), + create_region(world, player, locations_per_region, "Pre Industrial Complex"), + create_region(world, player, locations_per_region, "Industrial Complex"), + create_region(world, player, locations_per_region, "Torizo Ascended"), + create_region(world, player, locations_per_region, "The Tower"), + create_region(world, player, locations_per_region, "Geothermal"), + create_region(world, player, locations_per_region, "Tester"), + create_region(world, player, locations_per_region, "Tester Upper"), + create_region(world, player, locations_per_region, "Tester Lower"), + create_region(world, player, locations_per_region, "Underwater Distribution Center"), + create_region(world, player, locations_per_region, "Underwater Distro Connection"), + create_region(world, player, locations_per_region, "Serris"), + create_region(world, player, locations_per_region, "Ice Beam"), + create_region(world, player, locations_per_region, "Pipe Hell BL"), + create_region(world, player, locations_per_region, "Pipe Hell BR"), + create_region(world, player, locations_per_region, "Pipe Hell L"), + create_region(world, player, locations_per_region, "Pipe Hell R"), + create_region(world, player, locations_per_region, "Pipe Hell Outside"), + create_region(world, player, locations_per_region, "Fast Travel"), + create_region(world, player, locations_per_region, "Gravity"), + create_region(world, player, locations_per_region, "EMP"), + create_region(world, player, locations_per_region, "GFS Thoth"), + create_region(world, player, locations_per_region, "Genesis"), + create_region(world, player, locations_per_region, "Deep Caves"), + create_region(world, player, locations_per_region, "Omega Nest"), + create_region(world, player, locations_per_region, "The Lab"), + create_region(world, player, locations_per_region, "Research Station") + ] + + if __debug__: + throwIfAnyLocationIsNotAssignedToARegion(regions, locations_per_region.keys()) + + world.regions += regions + + logic = AM2RLogic(world, player) + + connect(world, player, "Menu", "Main Caves"), + + connect(world, player, "Main Caves", "Guardian"), + connect(world, player, "Guardian", "Main Caves"), + + connect(world, player, "Main Caves", "First Alpha"), + connect(world, player, "First Alpha", "Main Caves"), + + connect(world, player, "Main Caves", "Hydro Station"), + connect(world, player, "Hydro Station", "Main Caves"), + + connect(world, player, "Main Caves", "Mines", lambda state: state.has("Super Missile", player)), + connect(world, player, "Mines", "Main Caves"), + + connect(world, player, "Main Caves", "Industrial Complex Nest"), + connect(world, player, "Industrial Complex Nest", "Main Caves"), + + connect(world, player, "Main Caves", "The Tower"), + connect(world, player, "The Tower", "Main Caves"), + + connect(world, player, "Main Caves", "Underwater Distribution Center", lambda state: state.has("Power Bomb", player) or state.has("Super Missile", player)), + connect(world, player, "Underwater Distribution Center", "Main Caves", lambda state: state.has("Ice Beam", player)), + + connect(world, player, "Main Caves", "Deep Caves", logic.AM2R_can_down), + connect(world, player, "Deep Caves", "Main Caves"), + + connect(world, player, "Main Caves", "GFS Thoth"), + connect(world, player, "GFS Thoth", "Main Caves"), + + connect(world, player, "Guardian", "Golden Temple"), + connect(world, player, "Golden Temple", "Guardian"), + + connect(world, player, "Guardian", "Golden Temple Nest"), + connect(world, player, "Golden Temple Nest", "Guardian"), + + connect(world, player, "Golden Temple", "Fast Travel", lambda state: state.has("Screw Attack", player)), + connect(world, player, "Fast Travel", "Golden Temple", lambda state: state.has("Screw Attack", player)), + + connect(world, player, "Hydro Station", "Hydro Nest", logic.AM2R_can_jump), + connect(world, player, "Hydro Nest", "Hydro Station"), + + connect(world, player, "Hydro Station", "The Tower", lambda state: state.has("Screw Attack", player)), + connect(world, player, "The Tower", "Hydro Station", lambda state: state.has("Screw Attack", player)), + + connect(world, player, "Hydro Station", "The Lab", logic.AM2R_can_lab), + connect(world, player, "The Lab", "Hydro Station", lambda state: state.has("Screw Attack", player)), + + connect(world, player, "Hydro Station", "Arachnus", logic.AM2R_can_bomb), + connect(world, player, "Arachnus", "Hydro Station"), + + connect(world, player, "Hydro Station", "Inner Hydro Station", lambda state: state.has("Screw Attack", player) or logic.AM2R_can_bomb(state)) + connect(world, player, "Inner Hydro Station", "Hydro Station", lambda state: state.has("Screw Attack", player) or logic.AM2R_can_bomb(state)) + + connect(world, player, "Industrial Complex Nest", "Pre Industrial Complex", lambda state: state.has("Speed Booster", player) or logic.AM2R_can_bomb(state)), + connect(world, player, "Pre Industrial Complex", "Industrial Complex Nest", lambda state: state.has("Speed Booster", player) or logic.AM2R_can_bomb(state)), + + connect(world, player, "Pre Industrial Complex", "Industrial Complex"), + connect(world, player, "Industrial Complex", "Pre Industrial Complex", lambda state: state.has("Speed Booster", player)), + + connect(world, player, "Pre Industrial Complex", "Fast Travel", lambda state: state.has("Screw Attack", player)), + connect(world, player, "Fast Travel", "Pre Industrial Complex", lambda state: state.has("Screw Attack", player)), + + connect(world, player, "Pre Industrial Complex", "Torizo Ascended"), + connect(world, player, "Torizo Ascended", "Pre Industrial Complex"), + # A4 to Geothermal + connect(world, player, "The Tower", "Geothermal", lambda state: state.has("Speed Booster", player) and state.has("Power Bomb", player)), + connect(world, player, "Geothermal", "The Tower", lambda state: state.has("Speed Booster", player) and state.has("Power Bomb", player)), + # tower to A5 + connect(world, player, "The Tower", "Fast Travel", lambda state: state.has("Screw Attack", player)), + connect(world, player, "Fast Travel", "The Tower", lambda state: state.has("Screw Attack", player)), + # tester + connect(world, player, "The Tower", "Tester Lower", logic.AM2R_can_bomb), + connect(world, player, "The Tower", "Tester Upper", logic.AM2R_can_bomb), + connect(world, player, "Tester Lower", "Tester"), + connect(world, player, "Tester", "Tester Lower"), + connect(world, player, "Tester", "Tester Upper"), + connect(world, player, "Tester Upper", "Tester"), + connect(world, player, "Tester Lower", "The Tower", logic.AM2R_can_bomb), + connect(world, player, "Tester Upper", "The Tower", logic.AM2R_can_bomb), + # A5 + connect(world, player, "Underwater Distribution Center", "EMP"), + connect(world, player, "EMP", "Underwater Distribution Center"), + + connect(world, player, "Underwater Distribution Center", "Serris"), + connect(world, player, "Serris", "Underwater Distribution Center", lambda state: state.has("Gravity Suit", player)), + + connect(world, player, "Ice Beam", "Serris"), + connect(world, player, "Serris", "Ice Beam", lambda state: state.has("Gravity Suit", player)), + + # Pipe Hell Fuckery + connect(world, player, "EMP", "Pipe Hell BL"), + connect(world, player, "Pipe Hell BL", "EMP"), + + connect(world, player, "Pipe Hell BL", "Pipe Hell BR"), + connect(world, player, "Pipe Hell BR", "Pipe Hell BL"), + + connect(world, player, "Pipe Hell L", "Pipe Hell BL", lambda state: state.has("Screw Attack", player)), + connect(world, player, "Pipe Hell BL", "Pipe Hell L", lambda state: state.has("Screw Attack", player)), + + connect(world, player, "Pipe Hell BR", "Pipe Hell L"), + connect(world, player, "Pipe Hell L", "Pipe Hell BR"), + + connect(world, player, "Pipe Hell BR", "Pipe Hell R", lambda state: state.has("Screw Attack", player)), + connect(world, player, "Pipe Hell R", "Pipe Hell BR", lambda state: state.has("Screw Attack", player)), + + connect(world, player, "Pipe Hell R", "Pipe Hell L", logic.AM2R_can_bomb), + connect(world, player, "Pipe Hell L", "Pipe Hell R", logic.AM2R_can_bomb), + + connect(world, player, "Pipe Hell L", "Fast Travel", lambda state: state.has("Screw Attack", player)), + connect(world, player, "Fast Travel", "Pipe Hell L", lambda state: state.has("Screw Attack", player)), + + connect(world, player, "Fast Travel", "Gravity", lambda state: state.has("Gravity Suit", player)), # one way transition due to crumbles + + connect(world, player, "Fast Travel", "Underwater Distribution Center"), + connect(world, player, "Underwater Distribution Center", "Fast Travel", lambda state: state.can_reach("Fast Travel", "Region", player)), + + connect(world, player, "Gravity", "Pipe Hell Outside", lambda state: state.has("Gravity Suit", player) and state.has("Space Jump", player)), + connect(world, player, "Pipe Hell Outside", "Gravity"), + + connect(world, player, "Pipe Hell Outside", "Pipe Hell R", logic.AM2R_can_bomb), + connect(world, player, "Pipe Hell R", "Pipe Hell Outside", lambda state: state.can_reach("Pipe Hell Outside", "Region", player)), + + connect(world, player, "Underwater Distribution Center", "Underwater Distro Connection", lambda state: state.has("Ice Beam", player) or (state.has("Gravity Suit", player) and state.has("Speed Booster", player))), + connect(world, player, "Underwater Distro Connection", "Underwater Distribution Center", lambda state: state.has("Ice Beam", player) or (state.has("Gravity Suit", player) and state.has("Speed Booster", player))), + + connect(world, player, "Underwater Distro Connection", "Pipe Hell R"), + connect(world, player, "Pipe Hell R", "Underwater Distro Connection", lambda state: state.has("Super Missile", player) or (state.has("Gravity Suit", player) and state.has("Speed Booster", player))) + + connect(world, player, "Deep Caves", "Omega Nest") + connect(world, player, "Omega Nest", "Deep Caves") + + connect(world, player, "Omega Nest", "The Lab", logic.AM2R_can_lab) # , logic.AM2R_can_lab + connect(world, player, "The Lab", "Omega Nest") + + connect(world, player, "The Lab", "Research Station") + + +def throwIfAnyLocationIsNotAssignedToARegion(regions: List[Region], regionNames: Set[str]): + existingRegions: Set[str] = set() + + for region in regions: + existingRegions.add(region.name) + + if regionNames - existingRegions: + raise Exception( + "AM2R: the following regions are used in locations: {}, but no such region exists".format( + regionNames - existingRegions)) + + +def create_location(player: int, location_data: LocationData, region: Region) -> Location: + location = Location(player, location_data.name, location_data.code, region) + location.access_rule = location_data.rule + + if id is None: + location.event = True + location.locked = True + + return location + + +def create_region(world: MultiWorld, player: int, locations_per_region: Dict[str, List[LocationData]],name: str) -> Region: + region = Region(name, player, world) + + if name in locations_per_region: + for location_data in locations_per_region[name]: + location = create_location(player, location_data, region) + region.locations.append(location) + + return region + + +def connect(world: MultiWorld, player: int, source: str, target: str, + rule: Optional[Callable[[CollectionState], bool]] = None): + sourceRegion = world.get_region(source, player) + targetRegion = world.get_region(target, player) + + connection = Entrance(player, "", sourceRegion) + + if rule: + connection.access_rule = rule + + sourceRegion.exits.append(connection) + connection.connect(targetRegion) + + +def split_location_datas_per_region(locations: Tuple[LocationData, ...]) -> Dict[str, List[LocationData]]: + per_region: Dict[str, List[LocationData]] = {} + + for location in locations: + per_region.setdefault(location.region, []).append(location) + + return per_region diff --git a/worlds/am2r/rules.py b/worlds/am2r/rules.py new file mode 100644 index 000000000000..fdb56053ba91 --- /dev/null +++ b/worlds/am2r/rules.py @@ -0,0 +1,60 @@ +import itertools +from typing import Union +from BaseClasses import MultiWorld, CollectionState +from .options import MetroidsRequired, MetroidsAreChecks, get_option_value + + +class AM2RLogic: + player: int + + def __init__(self, world: MultiWorld, player: int): + self.player = player + + def AM2R_can_bomb(self, state: CollectionState) -> bool: + return state.has_any({'Bombs', 'Power Bombs'}, self.player) + + def AM2R_can_jump(self, state: CollectionState) -> bool: + return state.has_any({'Hi Jump', 'Space Jump', 'Bombs'}, self.player) + + def AM2R_can_fly(self, state: CollectionState) -> bool: + return state.has_any({'Bombs', 'Space Jump'}, self.player) + + def AM2R_can_spider(self, state: CollectionState) -> bool: + return state.has('Spiderball', self.player) \ + or self.AM2R_can_fly(state) + + def AM2R_can_schmove(self, state: CollectionState) -> bool: + return self.AM2R_can_spider(state) \ + or state.has('Hi Jump', self.player) + + def AM2R_has_ballspark(self, state: CollectionState) -> bool: + return state.has_all({'Speed Booster', 'Spring Ball'}, self.player) + + def AM2R_can_down(self, state: CollectionState) -> bool: + amount = get_option_value(MultiWorld, self.player, "MetroidsRequired") + check_state = get_option_value(MultiWorld, self.player, "MetroidsAreChecks") + + if check_state >= 1: + return state.has("Metroid", self.player, amount) \ + and state.has("Ice Beam", self.player) and self.AM2R_can_spider(state) and self.AM2R_can_bomb(state) \ + and (state.has("Screw Attack", self.player) or state.has("Power Bomb", self.player)) + else: + return state.has_all({"Speed Booster", "Ice Beam", "Super Missile"}, self.player) \ + and self.AM2R_can_fly(state) and self.AM2R_can_bomb(state) and (state.has("Screw Attack", self.player) or state.has("Power Bomb", self.player)) + + def AM2R_can_lab(self, state: CollectionState) -> bool: + amount = get_option_value(MultiWorld, self.player, "MetroidsRequired") + check_state = get_option_value(MultiWorld, self.player, "MetroidsAreChecks") + + if check_state == 2: + amount += 5 + return state.has("Metroid", self.player, amount) \ + and state.has("Ice Beam", self.player) and self.AM2R_can_spider(state) and self.AM2R_can_bomb(state) \ + and (state.has("Screw Attack", self.player) or state.has("Power Bomb", self.player)) + elif check_state == 1: + return state.has("Metroid", self.player, amount) \ + and state.has("Ice Beam", self.player) and self.AM2R_can_spider(state) and self.AM2R_can_bomb(state) \ + and (state.has("Screw Attack", self.player) or state.has("Power Bomb", self.player)) + else: + return state.has_all({"Speed Booster", "Ice Beam", "Super Missile"}, self.player) \ + and self.AM2R_can_fly(state) and self.AM2R_can_bomb(state) and (state.has("Screw Attack", self.player) or state.has("Power Bomb", self.player)) diff --git a/worlds/am2r/todo.py b/worlds/am2r/todo.py new file mode 100644 index 000000000000..c8522326b4a0 --- /dev/null +++ b/worlds/am2r/todo.py @@ -0,0 +1,3 @@ +# todo AM2R client mod +# todo Can you add the door lock indicators on the map that Varia added For the door rando patch +# todo Upon item pickup display sprite author when using the custom sprite options