Links Awakening: Implement New Game (#1334)
Adds Link's Awakening: DX. Fully imports and forks LADXR, with permission - https://github.com/daid/LADXR
This commit is contained in:
		
							
								
								
									
										26
									
								
								worlds/ladx/LADXR/locations/all.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								worlds/ladx/LADXR/locations/all.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| from .beachSword import BeachSword | ||||
| from .chest import Chest, DungeonChest | ||||
| from .droppedKey import DroppedKey | ||||
| from .seashell import Seashell, SeashellMansion | ||||
| from .heartContainer import HeartContainer | ||||
| from .owlStatue import OwlStatue | ||||
| from .madBatter import MadBatter | ||||
| from .shop import ShopItem | ||||
| from .startItem import StartItem | ||||
| from .toadstool import Toadstool | ||||
| from .witch import Witch | ||||
| from .goldLeaf import GoldLeaf, SlimeKey | ||||
| from .boomerangGuy import BoomerangGuy | ||||
| from .anglerKey import AnglerKey | ||||
| from .hookshot import HookshotDrop | ||||
| from .faceKey import FaceKey | ||||
| from .birdKey import BirdKey | ||||
| from .heartPiece import HeartPiece | ||||
| from .tunicFairy import TunicFairy | ||||
| from .song import Song | ||||
| from .instrument import Instrument | ||||
| from .fishingMinigame import FishingMinigame | ||||
| from .keyLocation import KeyLocation | ||||
| from .tradeSequence import TradeSequenceItem | ||||
|  | ||||
| from .items import * | ||||
							
								
								
									
										6
									
								
								worlds/ladx/LADXR/locations/anglerKey.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								worlds/ladx/LADXR/locations/anglerKey.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| from .droppedKey import DroppedKey | ||||
|  | ||||
|  | ||||
| class AnglerKey(DroppedKey): | ||||
|     def __init__(self): | ||||
|         super().__init__(0x0CE) | ||||
							
								
								
									
										32
									
								
								worlds/ladx/LADXR/locations/beachSword.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								worlds/ladx/LADXR/locations/beachSword.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| from .droppedKey import DroppedKey | ||||
| from .items import * | ||||
| from ..roomEditor import RoomEditor | ||||
| from ..assembler import ASM | ||||
| from typing import Optional | ||||
| from ..rom import ROM | ||||
|  | ||||
|  | ||||
| class BeachSword(DroppedKey): | ||||
|     def __init__(self) -> None: | ||||
|         super().__init__(0x0F2) | ||||
|  | ||||
|     def patch(self, rom: ROM, option: str, *, multiworld: Optional[int] = None) -> None: | ||||
|         if option != SWORD or multiworld is not None: | ||||
|             # Set the heart piece data | ||||
|             super().patch(rom, option, multiworld=multiworld) | ||||
|  | ||||
|             # Patch the room to contain a heart piece instead of the sword on the beach | ||||
|             re = RoomEditor(rom, 0x0F2) | ||||
|             re.removeEntities(0x31)  # remove sword | ||||
|             re.addEntity(5, 5, 0x35)  # add heart piece | ||||
|             re.store(rom) | ||||
|  | ||||
|             # Prevent shield drops from the like-like from turning into swords. | ||||
|             rom.patch(0x03, 0x1B9C, ASM("ld a, [$DB4E]"), ASM("ld a, $01"), fill_nop=True) | ||||
|             rom.patch(0x03, 0x244D, ASM("ld a, [$DB4E]"), ASM("ld a, $01"), fill_nop=True) | ||||
|  | ||||
|     def read(self, rom: ROM) -> str: | ||||
|         re = RoomEditor(rom, 0x0F2) | ||||
|         if re.hasEntity(0x31): | ||||
|             return SWORD | ||||
|         return super().read(rom) | ||||
							
								
								
									
										23
									
								
								worlds/ladx/LADXR/locations/birdKey.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								worlds/ladx/LADXR/locations/birdKey.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| from .droppedKey import DroppedKey | ||||
| from ..roomEditor import RoomEditor | ||||
| from ..assembler import ASM | ||||
|  | ||||
|  | ||||
| class BirdKey(DroppedKey): | ||||
|     def __init__(self): | ||||
|         super().__init__(0x27A) | ||||
|  | ||||
|     def patch(self, rom, option, *, multiworld=None): | ||||
|         super().patch(rom, option, multiworld=multiworld) | ||||
|  | ||||
|         re = RoomEditor(rom, self.room) | ||||
|  | ||||
|         # Make the bird key accessible without the rooster | ||||
|         re.removeObject(1, 6) | ||||
|         re.removeObject(2, 6) | ||||
|         re.removeObject(3, 5) | ||||
|         re.removeObject(3, 6) | ||||
|         re.moveObject(1, 5, 2, 6) | ||||
|         re.moveObject(2, 5, 3, 6) | ||||
|         re.addEntity(3, 5, 0x9D) | ||||
|         re.store(rom) | ||||
							
								
								
									
										94
									
								
								worlds/ladx/LADXR/locations/boomerangGuy.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								worlds/ladx/LADXR/locations/boomerangGuy.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| from .itemInfo import ItemInfo | ||||
| from .constants import * | ||||
| from ..assembler import ASM | ||||
| from ..utils import formatText | ||||
|  | ||||
|  | ||||
| class BoomerangGuy(ItemInfo): | ||||
|     OPTIONS = [BOOMERANG, HOOKSHOT, MAGIC_ROD, PEGASUS_BOOTS, FEATHER, SHOVEL] | ||||
|  | ||||
|     def __init__(self): | ||||
|         super().__init__(0x1F5) | ||||
|         self.setting = 'trade' | ||||
|  | ||||
|     def configure(self, options): | ||||
|         self.MULTIWORLD = False | ||||
|  | ||||
|         self.setting = options.boomerang | ||||
|         if self.setting == 'gift': | ||||
|             self.MULTIWORLD = True | ||||
|  | ||||
|     # Cannot trade: | ||||
|     # SWORD, BOMB, SHIELD, POWER_BRACELET, OCARINA, MAGIC_POWDER, BOW | ||||
|     # Checks for these are at $46A2, and potentially we could remove those. | ||||
|     # But SHIELD, BOMB and MAGIC_POWDER would most likely break things. | ||||
|     # SWORD and POWER_BRACELET would most likely introduce the lv0 shield/bracelet issue | ||||
|     def patch(self, rom, option, *, multiworld=None): | ||||
|         # Always have the boomerang trade guy enabled (normally you need the magnifier) | ||||
|         rom.patch(0x19, 0x05EC, ASM("ld a, [wTradeSequenceItem]\ncp $0E"), ASM("ld a, $0E\ncp $0E"), fill_nop=True)  # show the guy | ||||
|         rom.patch(0x00, 0x3199, ASM("ld a, [wTradeSequenceItem]\ncp $0E"), ASM("ld a, $0E\ncp $0E"), fill_nop=True)  # load the proper room layout | ||||
|         rom.patch(0x19, 0x05F4, ASM("ld a, [wTradeSequenceItem2]\nand a"), ASM("xor a"), fill_nop=True) | ||||
|  | ||||
|         if self.setting == 'trade': | ||||
|             inv = INVENTORY_MAP[option] | ||||
|             # Patch the check if you traded back the boomerang (so traded twice) | ||||
|             rom.patch(0x19, 0x063F, ASM("cp $0D"), ASM("cp $%s" % (inv))) | ||||
|             # Item to give by "default" (aka, boomerang) | ||||
|             rom.patch(0x19, 0x06C1, ASM("ld a, $0D"), ASM("ld a, $%s" % (inv))) | ||||
|             # Check if inventory slot is boomerang to give back item in this slot | ||||
|             rom.patch(0x19, 0x06FC, ASM("cp $0D"), ASM("cp $%s" % (inv))) | ||||
|             # Put the boomerang ID in the inventory of the boomerang guy (aka, traded back) | ||||
|             rom.patch(0x19, 0x0710, ASM("ld a, $0D"), ASM("ld a, $%s" % (inv))) | ||||
|  | ||||
|             rom.texts[0x222] = formatText("Okay, let's do it!") | ||||
|             rom.texts[0x224] = formatText("You got the {%s} in exchange for the item you had." % (option)) | ||||
|             rom.texts[0x225] = formatText("Give me back my {%s}, I beg you! I'll return the item you gave me" % (option), ask="Okay Not Now") | ||||
|             rom.texts[0x226] = formatText("The item came back to you. You returned the other item.") | ||||
|         else: | ||||
|             # Patch the inventory trade to give an specific item instead | ||||
|             rom.texts[0x221] = formatText("I found a good item washed up on the beach... Want to have it?", ask="Okay No") | ||||
|             rom.patch(0x19, 0x069C, 0x06C6, ASM(""" | ||||
|                 ; Mark trade as done | ||||
|                 ld a, $06 | ||||
|                 ld [$DB7D], a | ||||
|  | ||||
|                 ld a, [$472B] | ||||
|                 ldh [$F1], a | ||||
|                 ld a, $06 | ||||
|                 rst 8 | ||||
|                  | ||||
|                 ld a, $0D | ||||
|             """), fill_nop=True) | ||||
|             # Show the right item above link | ||||
|             rom.patch(0x19, 0x0786, 0x0793, ASM(""" | ||||
|                 ld a, [$472B] | ||||
|                 ldh [$F1], a | ||||
|                 ld a, $01 | ||||
|                 rst 8 | ||||
|             """), fill_nop=True) | ||||
|             # Give the proper message for this item | ||||
|             rom.patch(0x19, 0x075A, 0x076A, ASM(""" | ||||
|                 ld a, [$472B] | ||||
|                 ldh [$F1], a | ||||
|                 ld a, $0A | ||||
|                 rst 8 | ||||
|             """), fill_nop=True) | ||||
|             rom.patch(0x19, 0x072B, "00", "%02X" % (CHEST_ITEMS[option])) | ||||
|  | ||||
|             # Ignore the trade back. | ||||
|             rom.texts[0x225] = formatText("It's a secret to everybody.") | ||||
|             rom.patch(0x19, 0x0668, ASM("ld a, [$DB7D]"), ASM("ret"), fill_nop=True) | ||||
|  | ||||
|             if multiworld is not None: | ||||
|                 rom.banks[0x3E][0x3300 + self.room] = multiworld | ||||
|  | ||||
|     def read(self, rom): | ||||
|         if rom.banks[0x19][0x06C5] == 0x00: | ||||
|             for k, v in CHEST_ITEMS.items(): | ||||
|                 if v == rom.banks[0x19][0x072B]: | ||||
|                     return k | ||||
|         else: | ||||
|             for k, v in INVENTORY_MAP.items(): | ||||
|                 if int(v, 16) == rom.banks[0x19][0x0640]: | ||||
|                     return k | ||||
|         raise ValueError() | ||||
							
								
								
									
										50
									
								
								worlds/ladx/LADXR/locations/chest.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								worlds/ladx/LADXR/locations/chest.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| from .itemInfo import ItemInfo | ||||
| from .constants import * | ||||
| from ..assembler import ASM | ||||
|  | ||||
|  | ||||
| class Chest(ItemInfo): | ||||
|     def __init__(self, room): | ||||
|         super().__init__(room) | ||||
|         self.addr = room + 0x560 | ||||
|  | ||||
|     def patch(self, rom, option, *, multiworld=None): | ||||
|         rom.banks[0x14][self.addr] = CHEST_ITEMS[option] | ||||
|  | ||||
|         if self.room == 0x1B6: | ||||
|             # Patch the code that gives the nightmare key when you throw the pot at the chest in dungeon 6 | ||||
|             # As this is hardcoded for a specific chest type | ||||
|             rom.patch(3, 0x145D, ASM("ld a, $19"), ASM("ld a, $%02x" % (CHEST_ITEMS[option]))) | ||||
|         if multiworld is not None: | ||||
|             rom.banks[0x3E][0x3300 + self.room] = multiworld | ||||
|  | ||||
|     def read(self, rom): | ||||
|         value = rom.banks[0x14][self.addr] | ||||
|         for k, v in CHEST_ITEMS.items(): | ||||
|             if v == value: | ||||
|                 return k | ||||
|         raise ValueError("Could not find chest contents in ROM (0x%02x)" % (value)) | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return "%s:%03x" % (self.__class__.__name__, self.room) | ||||
|  | ||||
|  | ||||
| class DungeonChest(Chest): | ||||
|     def patch(self, rom, option, *, multiworld=None): | ||||
|         if (option.startswith(MAP) and option != MAP) \ | ||||
|                 or (option.startswith(COMPASS)  and option != COMPASS) \ | ||||
|                 or (option.startswith(STONE_BEAK) and option != STONE_BEAK) \ | ||||
|                     or (option.startswith(NIGHTMARE_KEY) and option != NIGHTMARE_KEY) \ | ||||
|                     or (option.startswith(KEY) and option != KEY): | ||||
|             if self._location.dungeon == int(option[-1]) and multiworld is None: | ||||
|                 option = option[:-1] | ||||
|         super().patch(rom, option, multiworld=multiworld) | ||||
|  | ||||
|     def read(self, rom): | ||||
|         result = super().read(rom) | ||||
|         if result in [MAP, COMPASS, STONE_BEAK, NIGHTMARE_KEY, KEY]: | ||||
|             return "%s%d" % (result, self._location.dungeon) | ||||
|         return result | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return "%s:%03x:%d" % (self.__class__.__name__, self.room, self._location.dungeon) | ||||
							
								
								
									
										131
									
								
								worlds/ladx/LADXR/locations/constants.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								worlds/ladx/LADXR/locations/constants.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,131 @@ | ||||
| from .items import * | ||||
|  | ||||
| INVENTORY_MAP = { | ||||
|     SWORD: "01", | ||||
|     BOMB: "02", | ||||
|     POWER_BRACELET: "03", | ||||
|     SHIELD: "04", | ||||
|     BOW: "05", | ||||
|     HOOKSHOT: "06", | ||||
|     MAGIC_ROD: "07", | ||||
|     PEGASUS_BOOTS: "08", | ||||
|     OCARINA: "09", | ||||
|     FEATHER: "0A", | ||||
|     SHOVEL: "0B", | ||||
|     MAGIC_POWDER: "0C", | ||||
|     BOOMERANG: "0D", | ||||
|     TOADSTOOL: "0E", | ||||
| } | ||||
| CHEST_ITEMS = { | ||||
|     POWER_BRACELET: 0x00, | ||||
|     SHIELD: 0x01, | ||||
|     BOW: 0x02, | ||||
|     HOOKSHOT: 0x03, | ||||
|     MAGIC_ROD: 0x04, | ||||
|     PEGASUS_BOOTS: 0x05, | ||||
|     OCARINA: 0x06, | ||||
|     FEATHER: 0x07, SHOVEL: 0x08, MAGIC_POWDER: 0x09, BOMB: 0x0A, SWORD: 0x0B, FLIPPERS: 0x0C, | ||||
|     MAGNIFYING_LENS: 0x0D, MEDICINE: 0x10, | ||||
|     TAIL_KEY: 0x11, ANGLER_KEY: 0x12, FACE_KEY: 0x13, BIRD_KEY: 0x14, GOLD_LEAF: 0x15, | ||||
|     RUPEES_50: 0x1B, RUPEES_20: 0x1C, RUPEES_100: 0x1D, RUPEES_200: 0x1E, RUPEES_500: 0x1F, | ||||
|     SEASHELL: 0x20, MESSAGE: 0x21, GEL: 0x22, | ||||
|     MAP: 0x16, COMPASS: 0x17, STONE_BEAK: 0x18, NIGHTMARE_KEY: 0x19, KEY: 0x1A, | ||||
|     ROOSTER: 0x96, | ||||
|  | ||||
|     BOOMERANG: 0x0E, | ||||
|     SLIME_KEY: 0x0F, | ||||
|  | ||||
|     KEY1: 0x23, | ||||
|     KEY2: 0x24, | ||||
|     KEY3: 0x25, | ||||
|     KEY4: 0x26, | ||||
|     KEY5: 0x27, | ||||
|     KEY6: 0x28, | ||||
|     KEY7: 0x29, | ||||
|     KEY8: 0x2A, | ||||
|     KEY9: 0x2B, | ||||
|  | ||||
|     MAP1: 0x2C, | ||||
|     MAP2: 0x2D, | ||||
|     MAP3: 0x2E, | ||||
|     MAP4: 0x2F, | ||||
|     MAP5: 0x30, | ||||
|     MAP6: 0x31, | ||||
|     MAP7: 0x32, | ||||
|     MAP8: 0x33, | ||||
|     MAP9: 0x34, | ||||
|  | ||||
|     COMPASS1: 0x35, | ||||
|     COMPASS2: 0x36, | ||||
|     COMPASS3: 0x37, | ||||
|     COMPASS4: 0x38, | ||||
|     COMPASS5: 0x39, | ||||
|     COMPASS6: 0x3A, | ||||
|     COMPASS7: 0x3B, | ||||
|     COMPASS8: 0x3C, | ||||
|     COMPASS9: 0x3D, | ||||
|  | ||||
|     STONE_BEAK1: 0x3E, | ||||
|     STONE_BEAK2: 0x3F, | ||||
|     STONE_BEAK3: 0x40, | ||||
|     STONE_BEAK4: 0x41, | ||||
|     STONE_BEAK5: 0x42, | ||||
|     STONE_BEAK6: 0x43, | ||||
|     STONE_BEAK7: 0x44, | ||||
|     STONE_BEAK8: 0x45, | ||||
|     STONE_BEAK9: 0x46, | ||||
|  | ||||
|     NIGHTMARE_KEY1: 0x47, | ||||
|     NIGHTMARE_KEY2: 0x48, | ||||
|     NIGHTMARE_KEY3: 0x49, | ||||
|     NIGHTMARE_KEY4: 0x4A, | ||||
|     NIGHTMARE_KEY5: 0x4B, | ||||
|     NIGHTMARE_KEY6: 0x4C, | ||||
|     NIGHTMARE_KEY7: 0x4D, | ||||
|     NIGHTMARE_KEY8: 0x4E, | ||||
|     NIGHTMARE_KEY9: 0x4F, | ||||
|  | ||||
|     TOADSTOOL: 0x50, | ||||
|  | ||||
|     HEART_PIECE: 0x80, | ||||
|     BOWWOW: 0x81, | ||||
|     ARROWS_10: 0x82, | ||||
|     SINGLE_ARROW: 0x83, | ||||
|  | ||||
|     MAX_POWDER_UPGRADE: 0x84, | ||||
|     MAX_BOMBS_UPGRADE: 0x85, | ||||
|     MAX_ARROWS_UPGRADE: 0x86, | ||||
|  | ||||
|     RED_TUNIC: 0x87, | ||||
|     BLUE_TUNIC: 0x88, | ||||
|     HEART_CONTAINER: 0x89, | ||||
|     BAD_HEART_CONTAINER: 0x8A, | ||||
|  | ||||
|     SONG1: 0x8B, | ||||
|     SONG2: 0x8C, | ||||
|     SONG3: 0x8D, | ||||
|      | ||||
|     INSTRUMENT1: 0x8E, | ||||
|     INSTRUMENT2: 0x8F, | ||||
|     INSTRUMENT3: 0x90, | ||||
|     INSTRUMENT4: 0x91, | ||||
|     INSTRUMENT5: 0x92, | ||||
|     INSTRUMENT6: 0x93, | ||||
|     INSTRUMENT7: 0x94, | ||||
|     INSTRUMENT8: 0x95, | ||||
|  | ||||
|     TRADING_ITEM_YOSHI_DOLL: 0x97, | ||||
|     TRADING_ITEM_RIBBON: 0x98, | ||||
|     TRADING_ITEM_DOG_FOOD: 0x99, | ||||
|     TRADING_ITEM_BANANAS: 0x9A, | ||||
|     TRADING_ITEM_STICK: 0x9B, | ||||
|     TRADING_ITEM_HONEYCOMB: 0x9C, | ||||
|     TRADING_ITEM_PINEAPPLE: 0x9D, | ||||
|     TRADING_ITEM_HIBISCUS: 0x9E, | ||||
|     TRADING_ITEM_LETTER: 0x9F, | ||||
|     TRADING_ITEM_BROOM: 0xA0, | ||||
|     TRADING_ITEM_FISHING_HOOK: 0xA1, | ||||
|     TRADING_ITEM_NECKLACE: 0xA2, | ||||
|     TRADING_ITEM_SCALE: 0xA3, | ||||
|     TRADING_ITEM_MAGNIFYING_GLASS: 0xA4, | ||||
| } | ||||
							
								
								
									
										57
									
								
								worlds/ladx/LADXR/locations/droppedKey.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								worlds/ladx/LADXR/locations/droppedKey.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| from .itemInfo import ItemInfo | ||||
| from .constants import * | ||||
| patched_already = {} | ||||
|  | ||||
| class DroppedKey(ItemInfo): | ||||
|     default_item = None | ||||
|  | ||||
|     def __init__(self, room=None): | ||||
|         extra = None | ||||
|         if room == 0x169:  # Room in D4 where the key drops down the hole into the sidescroller | ||||
|             extra = 0x017C | ||||
|         elif room == 0x166:  # D4 boss, also place the item in out real boss room. | ||||
|             extra = 0x01ff | ||||
|         elif room == 0x223:  # D7 boss, also place the item in our real boss room. | ||||
|             extra = 0x02E8 | ||||
|         elif room == 0x092:  # Marins song | ||||
|             extra = 0x00DC | ||||
|         elif room == 0x0CE: | ||||
|             extra = 0x01F8 | ||||
|         super().__init__(room, extra) | ||||
|     def patch(self, rom, option, *, multiworld=None): | ||||
|         if (option.startswith(MAP) and option != MAP) or (option.startswith(COMPASS) and option != COMPASS) or option.startswith(STONE_BEAK) or (option.startswith(NIGHTMARE_KEY) and option != NIGHTMARE_KEY )or (option.startswith(KEY) and option != KEY): | ||||
|             if option[-1] == 'P': | ||||
|                 print(option) | ||||
|             if self._location.dungeon == int(option[-1]) and multiworld is None and self.room not in {0x166, 0x223}: | ||||
|                 option = option[:-1] | ||||
|         rom.banks[0x3E][self.room + 0x3800] = CHEST_ITEMS[option] | ||||
|         #assert room not in patched_already, f"{self} {patched_already[room]}" | ||||
|         #patched_already[room] = self | ||||
|  | ||||
|  | ||||
|         if self.extra: | ||||
|             assert(not self.default_item) | ||||
|             rom.banks[0x3E][self.extra + 0x3800] = CHEST_ITEMS[option] | ||||
|  | ||||
|         if multiworld is not None: | ||||
|             rom.banks[0x3E][0x3300 + self.room] = multiworld | ||||
|              | ||||
|             if self.extra: | ||||
|                 rom.banks[0x3E][0x3300 + self.extra] = multiworld | ||||
|  | ||||
|     def read(self, rom): | ||||
|         assert self._location is not None, hex(self.room) | ||||
|         value = rom.banks[0x3E][self.room + 0x3800] | ||||
|         for k, v in CHEST_ITEMS.items(): | ||||
|             if v == value: | ||||
|                 if k in [MAP, COMPASS, STONE_BEAK, NIGHTMARE_KEY, KEY]: | ||||
|                     assert self._location.dungeon is not None, "Dungeon item outside of dungeon? %r" % (self) | ||||
|                     return "%s%d" % (k, self._location.dungeon) | ||||
|                 return k | ||||
|         raise ValueError("Could not find chest contents in ROM (0x%02x)" % (value)) | ||||
|  | ||||
|     def __repr__(self): | ||||
|         if self._location and self._location.dungeon: | ||||
|             return "%s:%03x:%d" % (self.__class__.__name__, self.room, self._location.dungeon) | ||||
|         else: | ||||
|             return "%s:%03x" % (self.__class__.__name__, self.room) | ||||
							
								
								
									
										6
									
								
								worlds/ladx/LADXR/locations/faceKey.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								worlds/ladx/LADXR/locations/faceKey.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| from .droppedKey import DroppedKey | ||||
|  | ||||
|  | ||||
| class FaceKey(DroppedKey): | ||||
|     def __init__(self): | ||||
|         super().__init__(0x27F) | ||||
							
								
								
									
										13
									
								
								worlds/ladx/LADXR/locations/fishingMinigame.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								worlds/ladx/LADXR/locations/fishingMinigame.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| from .droppedKey import DroppedKey | ||||
| from .constants import * | ||||
|  | ||||
|  | ||||
| class FishingMinigame(DroppedKey): | ||||
|     def __init__(self): | ||||
|         super().__init__(0x2B1) | ||||
|  | ||||
|     def configure(self, options): | ||||
|         if options.heartpiece: | ||||
|             super().configure(options) | ||||
|         else: | ||||
|             self.OPTIONS = [HEART_PIECE] | ||||
							
								
								
									
										12
									
								
								worlds/ladx/LADXR/locations/goldLeaf.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								worlds/ladx/LADXR/locations/goldLeaf.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| from .droppedKey import DroppedKey | ||||
|  | ||||
|  | ||||
| class GoldLeaf(DroppedKey): | ||||
|     pass  # Golden leaves are patched to work exactly like dropped keys | ||||
|  | ||||
|  | ||||
| class SlimeKey(DroppedKey): | ||||
|     # The slime key is secretly a golden leaf and just normally uses logic depended on the room number. | ||||
|     # As we patched it to act like a dropped key, we can just be a dropped key in the right room | ||||
|     def __init__(self): | ||||
|         super().__init__(0x0C6) | ||||
							
								
								
									
										15
									
								
								worlds/ladx/LADXR/locations/heartContainer.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								worlds/ladx/LADXR/locations/heartContainer.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| from .droppedKey import DroppedKey | ||||
| from .items import * | ||||
|  | ||||
|  | ||||
| class HeartContainer(DroppedKey): | ||||
|     # Due to the patches a heartContainers acts like a dropped key. | ||||
|     def configure(self, options): | ||||
|         if options.heartcontainers or options.hpmode == 'extralow': | ||||
|             super().configure(options) | ||||
|         elif options.hpmode == 'inverted': | ||||
|             self.OPTIONS = [BAD_HEART_CONTAINER] | ||||
|         elif options.hpmode == 'low': | ||||
|             self.OPTIONS = [HEART_PIECE] | ||||
|         else: | ||||
|             self.OPTIONS = [HEART_CONTAINER] | ||||
							
								
								
									
										12
									
								
								worlds/ladx/LADXR/locations/heartPiece.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								worlds/ladx/LADXR/locations/heartPiece.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| from .droppedKey import DroppedKey | ||||
| from .items import * | ||||
|  | ||||
|  | ||||
| class HeartPiece(DroppedKey): | ||||
|     # Due to the patches a heartPiece acts like a dropped key. | ||||
|  | ||||
|     def configure(self, options): | ||||
|         if options.heartpiece: | ||||
|             super().configure(options) | ||||
|         else: | ||||
|             self.OPTIONS = [HEART_PIECE] | ||||
							
								
								
									
										18
									
								
								worlds/ladx/LADXR/locations/hookshot.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								worlds/ladx/LADXR/locations/hookshot.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| from .droppedKey import DroppedKey | ||||
|  | ||||
|  | ||||
| """ | ||||
| The hookshot is dropped by the master stalfos. | ||||
| The master stalfos drops a "key" with, and modifies a bunch of properties: | ||||
|  | ||||
|     ld   a, $30                                   ; $7EE1: $3E $30 | ||||
|     call SpawnNewEntity_trampoline                ; $7EE3: $CD $86 $3B | ||||
|  | ||||
| And then the dropped key handles the rest with room number specific code. | ||||
| As we patched the dropped key, this requires no extra handling. | ||||
| """ | ||||
|  | ||||
|  | ||||
| class HookshotDrop(DroppedKey): | ||||
|     def __init__(self): | ||||
|         super().__init__(0x180) | ||||
							
								
								
									
										9
									
								
								worlds/ladx/LADXR/locations/instrument.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								worlds/ladx/LADXR/locations/instrument.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| from .droppedKey import DroppedKey | ||||
|  | ||||
|  | ||||
| class Instrument(DroppedKey): | ||||
|     # Thanks to patches, an instrument is just a dropped key as far as the randomizer is concerned. | ||||
|  | ||||
|     def configure(self, options): | ||||
|         if not options.instruments and not options.goal == "seashells": | ||||
|             self.OPTIONS = ["INSTRUMENT%d" % (self._location.dungeon)] | ||||
							
								
								
									
										43
									
								
								worlds/ladx/LADXR/locations/itemInfo.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								worlds/ladx/LADXR/locations/itemInfo.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| import typing | ||||
| from ..checkMetadata import checkMetadataTable | ||||
| from .constants import * | ||||
|  | ||||
|  | ||||
| class ItemInfo: | ||||
|     MULTIWORLD = True | ||||
|  | ||||
|     def __init__(self, room=None, extra=None): | ||||
|         self.item = None | ||||
|         self._location = None | ||||
|         self.room = room | ||||
|         self.extra = extra | ||||
|         self.metadata = checkMetadataTable.get(self.nameId, checkMetadataTable["None"]) | ||||
|         self.forced_item = None | ||||
|         self.custom_item_name = None | ||||
|          | ||||
|         self.event = None | ||||
|     @property | ||||
|     def location(self): | ||||
|         return self._location | ||||
|  | ||||
|     def setLocation(self, location): | ||||
|         self._location = location | ||||
|  | ||||
|     def getOptions(self): | ||||
|         return self.OPTIONS | ||||
|  | ||||
|     def configure(self, options): | ||||
|         pass | ||||
|  | ||||
|     def read(self, rom): | ||||
|         raise NotImplementedError() | ||||
|  | ||||
|     def patch(self, rom, option, *, multiworld=None): | ||||
|         raise NotImplementedError() | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return self.__class__.__name__ | ||||
|      | ||||
|     @property | ||||
|     def nameId(self): | ||||
|         return "0x%03X" % self.room if self.room is not None else "None" | ||||
							
								
								
									
										127
									
								
								worlds/ladx/LADXR/locations/items.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								worlds/ladx/LADXR/locations/items.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,127 @@ | ||||
| POWER_BRACELET = "POWER_BRACELET" | ||||
| SHIELD = "SHIELD" | ||||
| BOW = "BOW" | ||||
| HOOKSHOT = "HOOKSHOT" | ||||
| MAGIC_ROD = "MAGIC_ROD" | ||||
| PEGASUS_BOOTS = "PEGASUS_BOOTS" | ||||
| OCARINA = "OCARINA" | ||||
| FEATHER = "FEATHER" | ||||
| SHOVEL = "SHOVEL" | ||||
| MAGIC_POWDER = "MAGIC_POWDER" | ||||
| BOMB = "BOMB" | ||||
| SWORD = "SWORD" | ||||
| FLIPPERS = "FLIPPERS" | ||||
| MAGNIFYING_LENS = "MAGNIFYING_LENS" | ||||
| MEDICINE = "MEDICINE" | ||||
| TAIL_KEY = "TAIL_KEY" | ||||
| ANGLER_KEY = "ANGLER_KEY" | ||||
| FACE_KEY = "FACE_KEY" | ||||
| BIRD_KEY = "BIRD_KEY" | ||||
| SLIME_KEY = "SLIME_KEY" | ||||
| GOLD_LEAF = "GOLD_LEAF" | ||||
| RUPEES_50 = "RUPEES_50" | ||||
| RUPEES_20 = "RUPEES_20" | ||||
| RUPEES_100 = "RUPEES_100" | ||||
| RUPEES_200 = "RUPEES_200" | ||||
| RUPEES_500 = "RUPEES_500" | ||||
| SEASHELL = "SEASHELL" | ||||
| MESSAGE = "MESSAGE" | ||||
| GEL = "GEL" | ||||
| BOOMERANG = "BOOMERANG" | ||||
| HEART_PIECE = "HEART_PIECE" | ||||
| BOWWOW = "BOWWOW" | ||||
| ARROWS_10 = "ARROWS_10" | ||||
| SINGLE_ARROW = "SINGLE_ARROW" | ||||
| ROOSTER = "ROOSTER" | ||||
|  | ||||
| MAX_POWDER_UPGRADE = "MAX_POWDER_UPGRADE" | ||||
| MAX_BOMBS_UPGRADE = "MAX_BOMBS_UPGRADE" | ||||
| MAX_ARROWS_UPGRADE = "MAX_ARROWS_UPGRADE" | ||||
|  | ||||
| RED_TUNIC = "RED_TUNIC" | ||||
| BLUE_TUNIC = "BLUE_TUNIC" | ||||
| HEART_CONTAINER = "HEART_CONTAINER" | ||||
| BAD_HEART_CONTAINER = "BAD_HEART_CONTAINER" | ||||
|  | ||||
| TOADSTOOL = "TOADSTOOL" | ||||
|  | ||||
| KEY = "KEY" | ||||
| KEY1 = "KEY1" | ||||
| KEY2 = "KEY2" | ||||
| KEY3 = "KEY3" | ||||
| KEY4 = "KEY4" | ||||
| KEY5 = "KEY5" | ||||
| KEY6 = "KEY6" | ||||
| KEY7 = "KEY7" | ||||
| KEY8 = "KEY8" | ||||
| KEY9 = "KEY9" | ||||
|  | ||||
| NIGHTMARE_KEY = "NIGHTMARE_KEY" | ||||
| NIGHTMARE_KEY1 = "NIGHTMARE_KEY1" | ||||
| NIGHTMARE_KEY2 = "NIGHTMARE_KEY2" | ||||
| NIGHTMARE_KEY3 = "NIGHTMARE_KEY3" | ||||
| NIGHTMARE_KEY4 = "NIGHTMARE_KEY4" | ||||
| NIGHTMARE_KEY5 = "NIGHTMARE_KEY5" | ||||
| NIGHTMARE_KEY6 = "NIGHTMARE_KEY6" | ||||
| NIGHTMARE_KEY7 = "NIGHTMARE_KEY7" | ||||
| NIGHTMARE_KEY8 = "NIGHTMARE_KEY8" | ||||
| NIGHTMARE_KEY9 = "NIGHTMARE_KEY9" | ||||
|  | ||||
| MAP = "MAP" | ||||
| MAP1 = "MAP1" | ||||
| MAP2 = "MAP2" | ||||
| MAP3 = "MAP3" | ||||
| MAP4 = "MAP4" | ||||
| MAP5 = "MAP5" | ||||
| MAP6 = "MAP6" | ||||
| MAP7 = "MAP7" | ||||
| MAP8 = "MAP8" | ||||
| MAP9 = "MAP9" | ||||
| COMPASS = "COMPASS" | ||||
| COMPASS1 = "COMPASS1" | ||||
| COMPASS2 = "COMPASS2" | ||||
| COMPASS3 = "COMPASS3" | ||||
| COMPASS4 = "COMPASS4" | ||||
| COMPASS5 = "COMPASS5" | ||||
| COMPASS6 = "COMPASS6" | ||||
| COMPASS7 = "COMPASS7" | ||||
| COMPASS8 = "COMPASS8" | ||||
| COMPASS9 = "COMPASS9" | ||||
| STONE_BEAK = "STONE_BEAK" | ||||
| STONE_BEAK1 = "STONE_BEAK1" | ||||
| STONE_BEAK2 = "STONE_BEAK2" | ||||
| STONE_BEAK3 = "STONE_BEAK3" | ||||
| STONE_BEAK4 = "STONE_BEAK4" | ||||
| STONE_BEAK5 = "STONE_BEAK5" | ||||
| STONE_BEAK6 = "STONE_BEAK6" | ||||
| STONE_BEAK7 = "STONE_BEAK7" | ||||
| STONE_BEAK8 = "STONE_BEAK8" | ||||
| STONE_BEAK9 = "STONE_BEAK9" | ||||
|  | ||||
| SONG1 = "SONG1" | ||||
| SONG2 = "SONG2" | ||||
| SONG3 = "SONG3" | ||||
|  | ||||
| INSTRUMENT1 = "INSTRUMENT1" | ||||
| INSTRUMENT2 = "INSTRUMENT2" | ||||
| INSTRUMENT3 = "INSTRUMENT3" | ||||
| INSTRUMENT4 = "INSTRUMENT4" | ||||
| INSTRUMENT5 = "INSTRUMENT5" | ||||
| INSTRUMENT6 = "INSTRUMENT6" | ||||
| INSTRUMENT7 = "INSTRUMENT7" | ||||
| INSTRUMENT8 = "INSTRUMENT8" | ||||
|  | ||||
| TRADING_ITEM_YOSHI_DOLL = "TRADING_ITEM_YOSHI_DOLL" | ||||
| TRADING_ITEM_RIBBON = "TRADING_ITEM_RIBBON" | ||||
| TRADING_ITEM_DOG_FOOD = "TRADING_ITEM_DOG_FOOD" | ||||
| TRADING_ITEM_BANANAS = "TRADING_ITEM_BANANAS" | ||||
| TRADING_ITEM_STICK = "TRADING_ITEM_STICK" | ||||
| TRADING_ITEM_HONEYCOMB = "TRADING_ITEM_HONEYCOMB" | ||||
| TRADING_ITEM_PINEAPPLE = "TRADING_ITEM_PINEAPPLE" | ||||
| TRADING_ITEM_HIBISCUS = "TRADING_ITEM_HIBISCUS" | ||||
| TRADING_ITEM_LETTER = "TRADING_ITEM_LETTER" | ||||
| TRADING_ITEM_BROOM = "TRADING_ITEM_BROOM" | ||||
| TRADING_ITEM_FISHING_HOOK = "TRADING_ITEM_FISHING_HOOK" | ||||
| TRADING_ITEM_NECKLACE = "TRADING_ITEM_NECKLACE" | ||||
| TRADING_ITEM_SCALE = "TRADING_ITEM_SCALE" | ||||
| TRADING_ITEM_MAGNIFYING_GLASS = "TRADING_ITEM_MAGNIFYING_GLASS" | ||||
							
								
								
									
										18
									
								
								worlds/ladx/LADXR/locations/keyLocation.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								worlds/ladx/LADXR/locations/keyLocation.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| from .itemInfo import ItemInfo | ||||
|  | ||||
|  | ||||
| class KeyLocation(ItemInfo): | ||||
|     OPTIONS = [] | ||||
|  | ||||
|     def __init__(self, key): | ||||
|         super().__init__() | ||||
|         self.event = key | ||||
|  | ||||
|     def patch(self, rom, option, *, multiworld=None): | ||||
|         pass | ||||
|  | ||||
|     def read(self, rom): | ||||
|         return self.OPTIONS[0] | ||||
|  | ||||
|     def configure(self, options): | ||||
|         pass | ||||
							
								
								
									
										23
									
								
								worlds/ladx/LADXR/locations/madBatter.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								worlds/ladx/LADXR/locations/madBatter.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| from .itemInfo import ItemInfo | ||||
| from .constants import * | ||||
|  | ||||
|  | ||||
| class MadBatter(ItemInfo): | ||||
|     def configure(self, options): | ||||
|         return | ||||
|  | ||||
|     def patch(self, rom, option, *, multiworld=None): | ||||
|         rom.banks[0x18][0x0F90 + (self.room & 0x0F)] = CHEST_ITEMS[option] | ||||
|         if multiworld is not None: | ||||
|             rom.banks[0x3E][0x3300 + self.room] = multiworld | ||||
|  | ||||
|     def read(self, rom): | ||||
|         assert self._location is not None, hex(self.room) | ||||
|         value = rom.banks[0x18][0x0F90 + (self.room & 0x0F)] | ||||
|         for k, v in CHEST_ITEMS.items(): | ||||
|             if v == value: | ||||
|                 return k | ||||
|         raise ValueError("Could not find mad batter contents in ROM (0x%02x)" % (value)) | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return "%s:%03x" % (self.__class__.__name__, self.room) | ||||
							
								
								
									
										41
									
								
								worlds/ladx/LADXR/locations/owlStatue.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								worlds/ladx/LADXR/locations/owlStatue.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| from .itemInfo import ItemInfo | ||||
| from .constants import * | ||||
|  | ||||
|  | ||||
| class OwlStatue(ItemInfo): | ||||
|     def configure(self, options): | ||||
|         if options.owlstatues == "both": | ||||
|             return | ||||
|         if options.owlstatues == "dungeon" and self.room >= 0x100: | ||||
|             return | ||||
|         if options.owlstatues == "overworld" and self.room < 0x100: | ||||
|             return | ||||
|         raise RuntimeError("Tried to configure an owlstatue that was not enabled") | ||||
|         self.OPTIONS = [RUPEES_20] | ||||
|  | ||||
|     def patch(self, rom, option, *, multiworld=None): | ||||
|         if option.startswith(MAP) or option.startswith(COMPASS) or option.startswith(STONE_BEAK) or option.startswith(NIGHTMARE_KEY) or option.startswith(KEY): | ||||
|             if self._location.dungeon == int(option[-1]) and multiworld is not None: | ||||
|                 option = option[:-1] | ||||
|         rom.banks[0x3E][self.room + 0x3B16] = CHEST_ITEMS[option] | ||||
|  | ||||
|     def read(self, rom): | ||||
|         assert self._location is not None, hex(self.room) | ||||
|         value = rom.banks[0x3E][self.room + 0x3B16] | ||||
|         for k, v in CHEST_ITEMS.items(): | ||||
|             if v == value: | ||||
|                 if k in [MAP, COMPASS, STONE_BEAK, NIGHTMARE_KEY, KEY]: | ||||
|                     assert self._location.dungeon is not None, "Dungeon item outside of dungeon? %r" % (self) | ||||
|                     return "%s%d" % (k, self._location.dungeon) | ||||
|                 return k | ||||
|         raise ValueError("Could not find owl statue contents in ROM (0x%02x)" % (value)) | ||||
|  | ||||
|     def __repr__(self): | ||||
|         if self._location and self._location.dungeon: | ||||
|             return "%s:%03x:%d" % (self.__class__.__name__, self.room, self._location.dungeon) | ||||
|         else: | ||||
|             return "%s:%03x" % (self.__class__.__name__, self.room) | ||||
|      | ||||
|     @property | ||||
|     def nameId(self): | ||||
|         return "0x%03X-Owl" % self.room | ||||
							
								
								
									
										14
									
								
								worlds/ladx/LADXR/locations/seashell.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								worlds/ladx/LADXR/locations/seashell.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| from .droppedKey import DroppedKey | ||||
| from .items import * | ||||
|  | ||||
|  | ||||
| class Seashell(DroppedKey): | ||||
|     # Thanks to patches, a seashell is just a dropped key as far as the randomizer is concerned. | ||||
|  | ||||
|     def configure(self, options): | ||||
|         if not options.seashells: | ||||
|             self.OPTIONS = [SEASHELL] | ||||
|  | ||||
|  | ||||
| class SeashellMansion(DroppedKey): | ||||
|     pass | ||||
							
								
								
									
										42
									
								
								worlds/ladx/LADXR/locations/shop.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								worlds/ladx/LADXR/locations/shop.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| from .itemInfo import ItemInfo | ||||
| from .constants import * | ||||
| from ..utils import formatText | ||||
| from ..assembler import ASM | ||||
|  | ||||
|  | ||||
| class ShopItem(ItemInfo): | ||||
|     def __init__(self, index): | ||||
|         self.__index = index | ||||
|         # pass in the alternate index for shop 2 | ||||
|         # The "real" room is at 0x2A1, but we store the second item data as if link were in 0x2A7 | ||||
|         room = 0x2A1 | ||||
|         if index == 1: | ||||
|             room = 0x2A7 | ||||
|         super().__init__(room) | ||||
|  | ||||
|     def patch(self, rom, option, *, multiworld=None): | ||||
|         mw_text = "" | ||||
|         if multiworld: | ||||
|             mw_text = f" for player {rom.player_names[multiworld - 1]}" | ||||
|  | ||||
|         if self.__index == 0: | ||||
|             # Old index, maybe not needed any more | ||||
|             rom.patch(0x04, 0x37C5, "08", "%02X" % (CHEST_ITEMS[option])) | ||||
|             rom.texts[0x030] = formatText(f"Deluxe {{%s}} 200 {{RUPEES}}{mw_text}!" % (option), ask="Buy  No Way") | ||||
|             rom.banks[0x3E][0x3800 + 0x2A1] = CHEST_ITEMS[option] | ||||
|             if multiworld: | ||||
|                 rom.banks[0x3E][0x3300 + 0x2A1] = multiworld | ||||
|         elif self.__index == 1: | ||||
|             rom.patch(0x04, 0x37C6, "02", "%02X" % (CHEST_ITEMS[option])) | ||||
|             rom.texts[0x02C] = formatText(f"{{%s}} Only 980 {{RUPEES}}{mw_text}!" % (option), ask="Buy  No Way") | ||||
|              | ||||
|             rom.banks[0x3E][0x3800 + 0x2A7] = CHEST_ITEMS[option] | ||||
|             if multiworld: | ||||
|                 rom.banks[0x3E][0x3300 + 0x2A7] = multiworld | ||||
|  | ||||
|     def read(self, rom): | ||||
|         value = rom.banks[0x04][0x37C5 + self.__index] | ||||
|         for k, v in CHEST_ITEMS.items(): | ||||
|             if v == value: | ||||
|                 return k | ||||
|         raise ValueError("Could not find shop item contents in ROM (0x%02x)" % (value)) | ||||
							
								
								
									
										5
									
								
								worlds/ladx/LADXR/locations/song.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								worlds/ladx/LADXR/locations/song.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| from .droppedKey import DroppedKey | ||||
|  | ||||
|  | ||||
| class Song(DroppedKey): | ||||
|     pass | ||||
							
								
								
									
										38
									
								
								worlds/ladx/LADXR/locations/startItem.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								worlds/ladx/LADXR/locations/startItem.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| from .itemInfo import ItemInfo | ||||
| from .constants import * | ||||
| from .droppedKey import DroppedKey | ||||
| from ..assembler import ASM | ||||
| from ..utils import formatText | ||||
| from ..roomEditor import RoomEditor | ||||
|  | ||||
|  | ||||
| class StartItem(DroppedKey): | ||||
|     # We need to give something here that we can use to progress. | ||||
|     # FEATHER | ||||
|     OPTIONS = [SWORD, SHIELD, POWER_BRACELET, OCARINA,  BOOMERANG, MAGIC_ROD, TAIL_KEY, SHOVEL, HOOKSHOT, PEGASUS_BOOTS, MAGIC_POWDER, BOMB] | ||||
|  | ||||
|     MULTIWORLD = False | ||||
|  | ||||
|     def __init__(self): | ||||
|         super().__init__(0x2A3) | ||||
|         self.give_bowwow = False | ||||
|  | ||||
|     def configure(self, options): | ||||
|         if options.bowwow != 'normal': | ||||
|             # When we have bowwow mode, we pretend to be a sword for logic reasons | ||||
|             self.OPTIONS = [SWORD] | ||||
|             self.give_bowwow = True | ||||
|         if options.randomstartlocation and options.entranceshuffle != 'none': | ||||
|             self.OPTIONS.append(FLIPPERS) | ||||
|  | ||||
|     def patch(self, rom, option, *, multiworld=None): | ||||
|         assert multiworld is None | ||||
|  | ||||
|         if self.give_bowwow: | ||||
|             option = BOWWOW | ||||
|             rom.texts[0xC8] = formatText("Got BowWow!") | ||||
|  | ||||
|         if option != SHIELD: | ||||
|             rom.patch(5, 0x0CDA, ASM("ld a, $22"), ASM("ld a, $00"))  # do not change links sprite into the one with a shield | ||||
|  | ||||
|         super().patch(rom, option) | ||||
							
								
								
									
										18
									
								
								worlds/ladx/LADXR/locations/toadstool.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								worlds/ladx/LADXR/locations/toadstool.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| from .droppedKey import DroppedKey | ||||
| from .items import * | ||||
|  | ||||
|  | ||||
| class Toadstool(DroppedKey): | ||||
|     def __init__(self): | ||||
|         super().__init__(0x050) | ||||
|  | ||||
|     def configure(self, options): | ||||
|         if not options.witch: | ||||
|             self.OPTIONS = [TOADSTOOL] | ||||
|         else: | ||||
|             super().configure(options) | ||||
|  | ||||
|     def read(self, rom): | ||||
|         if len(self.OPTIONS) == 1: | ||||
|             return TOADSTOOL | ||||
|         return super().read(rom) | ||||
							
								
								
									
										55
									
								
								worlds/ladx/LADXR/locations/tradeSequence.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								worlds/ladx/LADXR/locations/tradeSequence.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| from .itemInfo import ItemInfo | ||||
| from .constants import * | ||||
| from .droppedKey import DroppedKey | ||||
|  | ||||
| TradeRequirements = { | ||||
|     TRADING_ITEM_YOSHI_DOLL: None, | ||||
|     TRADING_ITEM_RIBBON: TRADING_ITEM_YOSHI_DOLL, | ||||
|     TRADING_ITEM_DOG_FOOD: TRADING_ITEM_RIBBON, | ||||
|     TRADING_ITEM_BANANAS: TRADING_ITEM_DOG_FOOD, | ||||
|     TRADING_ITEM_STICK: TRADING_ITEM_BANANAS, | ||||
|     TRADING_ITEM_HONEYCOMB: TRADING_ITEM_STICK, | ||||
|     TRADING_ITEM_PINEAPPLE: TRADING_ITEM_HONEYCOMB, | ||||
|     TRADING_ITEM_HIBISCUS: TRADING_ITEM_PINEAPPLE, | ||||
|     TRADING_ITEM_LETTER: TRADING_ITEM_HIBISCUS, | ||||
|     TRADING_ITEM_BROOM: TRADING_ITEM_LETTER, | ||||
|     TRADING_ITEM_FISHING_HOOK: TRADING_ITEM_BROOM, | ||||
|     TRADING_ITEM_NECKLACE: TRADING_ITEM_FISHING_HOOK, | ||||
|     TRADING_ITEM_SCALE: TRADING_ITEM_NECKLACE, | ||||
|     TRADING_ITEM_MAGNIFYING_GLASS: TRADING_ITEM_SCALE, | ||||
| } | ||||
| class TradeSequenceItem(DroppedKey): | ||||
|     def __init__(self, room, default_item): | ||||
|         self.unadjusted_room = room | ||||
|         if room == 0x2B2: | ||||
|             # Offset room for trade items to avoid collisions  | ||||
|             roomLo = room & 0xFF | ||||
|             roomHi = room ^ roomLo | ||||
|             roomLo = (roomLo + 2) & 0xFF | ||||
|             room = roomHi | roomLo | ||||
|         super().__init__(room) | ||||
|         self.default_item = default_item | ||||
|  | ||||
|     def configure(self, options): | ||||
|         if not options.tradequest: | ||||
|             self.OPTIONS = [self.default_item] | ||||
|         super().configure(options) | ||||
|  | ||||
|     #def patch(self, rom, option, *, multiworld=None): | ||||
|     #    rom.banks[0x3E][self.room + 0x3B16] = CHEST_ITEMS[option] | ||||
|  | ||||
|     def read(self, rom): | ||||
|         assert(False) | ||||
|         assert self._location is not None, hex(self.room) | ||||
|         value = rom.banks[0x3E][self.room + 0x3B16] | ||||
|         for k, v in CHEST_ITEMS.items(): | ||||
|             if v == value: | ||||
|                 return k | ||||
|         raise ValueError("Could not find owl statue contents in ROM (0x%02x)" % (value)) | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return "%s:%03x" % (self.__class__.__name__, self.room) | ||||
|  | ||||
|     @property | ||||
|     def nameId(self): | ||||
|         return "0x%03X-Trade" % self.unadjusted_room | ||||
							
								
								
									
										27
									
								
								worlds/ladx/LADXR/locations/tunicFairy.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								worlds/ladx/LADXR/locations/tunicFairy.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| from .itemInfo import ItemInfo | ||||
| from .constants import * | ||||
|  | ||||
|  | ||||
| class TunicFairy(ItemInfo): | ||||
|  | ||||
|     def __init__(self, index): | ||||
|         self.index = index | ||||
|         super().__init__(0x301) | ||||
|          | ||||
|     def patch(self, rom, option, *, multiworld=None): | ||||
|         # Old index, maybe not needed anymore | ||||
|         rom.banks[0x36][0x11BF + self.index] = CHEST_ITEMS[option] | ||||
|         rom.banks[0x3e][0x3800 + 0x301 + self.index*3] = CHEST_ITEMS[option] | ||||
|         if multiworld: | ||||
|             rom.banks[0x3e][0x3300 + 0x301 + self.index*3] = multiworld | ||||
|          | ||||
|     def read(self, rom): | ||||
|         value = rom.banks[0x36][0x11BF + self.index] | ||||
|         for k, v in CHEST_ITEMS.items(): | ||||
|             if v == value: | ||||
|                 return k | ||||
|         raise ValueError("Could not find tunic fairy contents in ROM (0x%02x)" % (value)) | ||||
|  | ||||
|     @property | ||||
|     def nameId(self): | ||||
|         return "0x%03X-%s" % (self.room, self.index) | ||||
							
								
								
									
										31
									
								
								worlds/ladx/LADXR/locations/witch.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								worlds/ladx/LADXR/locations/witch.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| from .constants import * | ||||
| from .itemInfo import ItemInfo | ||||
|  | ||||
|  | ||||
| class Witch(ItemInfo): | ||||
|     def __init__(self): | ||||
|         super().__init__(0x2A2) | ||||
|  | ||||
|     def configure(self, options): | ||||
|         if not options.witch: | ||||
|             self.OPTIONS = [MAGIC_POWDER] | ||||
|  | ||||
|     def patch(self, rom, option, *, multiworld=None): | ||||
|         if multiworld or option != MAGIC_POWDER: | ||||
|              | ||||
|             rom.banks[0x3E][self.room + 0x3800] = CHEST_ITEMS[option] | ||||
|             if multiworld is not None: | ||||
|                 rom.banks[0x3E][0x3300 + self.room] = multiworld | ||||
|             else: | ||||
|                 rom.banks[0x3E][0x3300 + self.room] = 0 | ||||
|              | ||||
|             #rom.patch(0x05, 0x08D5, "09", "%02x" % (CHEST_ITEMS[option])) | ||||
|  | ||||
|     def read(self, rom): | ||||
|         if rom.banks[0x05][0x08EF] != 0x00: | ||||
|             return MAGIC_POWDER | ||||
|         value = rom.banks[0x05][0x08D5] | ||||
|         for k, v in CHEST_ITEMS.items(): | ||||
|             if v == value: | ||||
|                 return k | ||||
|         raise ValueError("Could not find witch contents in ROM (0x%02x)" % (value)) | ||||
		Reference in New Issue
	
	Block a user
	 zig-for
					zig-for