LADX: Add "boots controls" option (#2085)
This commit is contained in:
parent
98e2d89a1c
commit
ca5c0d9eb8
|
@ -757,7 +757,7 @@ class Assembler:
|
||||||
|
|
||||||
def const(name: str, value: int) -> None:
|
def const(name: str, value: int) -> None:
|
||||||
name = name.upper()
|
name = name.upper()
|
||||||
assert name not in CONST_MAP
|
assert name not in CONST_MAP or CONST_MAP[name] == value
|
||||||
CONST_MAP[name] = value
|
CONST_MAP[name] = value
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@ from .locations.keyLocation import KeyLocation
|
||||||
|
|
||||||
from BaseClasses import ItemClassification
|
from BaseClasses import ItemClassification
|
||||||
from ..Locations import LinksAwakeningLocation
|
from ..Locations import LinksAwakeningLocation
|
||||||
from ..Options import TrendyGame, Palette, MusicChangeCondition
|
from ..Options import TrendyGame, Palette, MusicChangeCondition, BootsControls
|
||||||
|
|
||||||
|
|
||||||
# Function to generate a final rom, this patches the rom with all required patches
|
# Function to generate a final rom, this patches the rom with all required patches
|
||||||
|
@ -97,7 +97,7 @@ def generateRom(args, settings, ap_settings, auth, seed_name, logic, rnd=None, m
|
||||||
assembler.const("wTradeSequenceItem2", 0xDB7F) # Normally used to store that we have exchanged the trade item, we use it to store flags of which trade items we have
|
assembler.const("wTradeSequenceItem2", 0xDB7F) # Normally used to store that we have exchanged the trade item, we use it to store flags of which trade items we have
|
||||||
assembler.const("wSeashellsCount", 0xDB41)
|
assembler.const("wSeashellsCount", 0xDB41)
|
||||||
assembler.const("wGoldenLeaves", 0xDB42) # New memory location where to store the golden leaf counter
|
assembler.const("wGoldenLeaves", 0xDB42) # New memory location where to store the golden leaf counter
|
||||||
assembler.const("wCollectedTunics", 0xDB6D) # Memory location where to store which tunic options are available
|
assembler.const("wCollectedTunics", 0xDB6D) # Memory location where to store which tunic options are available (and boots)
|
||||||
assembler.const("wCustomMessage", 0xC0A0)
|
assembler.const("wCustomMessage", 0xC0A0)
|
||||||
|
|
||||||
# We store the link info in unused color dungeon flags, so it gets preserved in the savegame.
|
# We store the link info in unused color dungeon flags, so it gets preserved in the savegame.
|
||||||
|
@ -244,6 +244,9 @@ def generateRom(args, settings, ap_settings, auth, seed_name, logic, rnd=None, m
|
||||||
elif settings.quickswap == 'b':
|
elif settings.quickswap == 'b':
|
||||||
patches.core.quickswap(rom, 0)
|
patches.core.quickswap(rom, 0)
|
||||||
|
|
||||||
|
patches.core.addBootsControls(rom, ap_settings['boots_controls'])
|
||||||
|
|
||||||
|
|
||||||
world_setup = logic.world_setup
|
world_setup = logic.world_setup
|
||||||
|
|
||||||
JUNK_HINT = 0.33
|
JUNK_HINT = 0.33
|
||||||
|
|
|
@ -10,7 +10,6 @@ class StartItem(DroppedKey):
|
||||||
# We need to give something here that we can use to progress.
|
# We need to give something here that we can use to progress.
|
||||||
# FEATHER
|
# FEATHER
|
||||||
OPTIONS = [SWORD, SHIELD, POWER_BRACELET, OCARINA, BOOMERANG, MAGIC_ROD, TAIL_KEY, SHOVEL, HOOKSHOT, PEGASUS_BOOTS, MAGIC_POWDER, BOMB]
|
OPTIONS = [SWORD, SHIELD, POWER_BRACELET, OCARINA, BOOMERANG, MAGIC_ROD, TAIL_KEY, SHOVEL, HOOKSHOT, PEGASUS_BOOTS, MAGIC_POWDER, BOMB]
|
||||||
|
|
||||||
MULTIWORLD = False
|
MULTIWORLD = False
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
|
@ -51,7 +51,7 @@ GiveItemFromChest:
|
||||||
dw ChestBow ; CHEST_BOW
|
dw ChestBow ; CHEST_BOW
|
||||||
dw ChestWithItem ; CHEST_HOOKSHOT
|
dw ChestWithItem ; CHEST_HOOKSHOT
|
||||||
dw ChestWithItem ; CHEST_MAGIC_ROD
|
dw ChestWithItem ; CHEST_MAGIC_ROD
|
||||||
dw ChestWithItem ; CHEST_PEGASUS_BOOTS
|
dw Boots ; CHEST_PEGASUS_BOOTS
|
||||||
dw ChestWithItem ; CHEST_OCARINA
|
dw ChestWithItem ; CHEST_OCARINA
|
||||||
dw ChestWithItem ; CHEST_FEATHER
|
dw ChestWithItem ; CHEST_FEATHER
|
||||||
dw ChestWithItem ; CHEST_SHOVEL
|
dw ChestWithItem ; CHEST_SHOVEL
|
||||||
|
@ -273,6 +273,13 @@ ChestMagicPowder:
|
||||||
ld [$DB4C], a
|
ld [$DB4C], a
|
||||||
jp ChestWithItem
|
jp ChestWithItem
|
||||||
|
|
||||||
|
Boots:
|
||||||
|
; We use DB6D to store which tunics we have available
|
||||||
|
; ...and the boots
|
||||||
|
ld a, [wCollectedTunics]
|
||||||
|
or $04
|
||||||
|
ld [wCollectedTunics], a
|
||||||
|
jp ChestWithItem
|
||||||
|
|
||||||
Flippers:
|
Flippers:
|
||||||
ld a, $01
|
ld a, $01
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
|
from .. import assembler
|
||||||
from ..assembler import ASM
|
from ..assembler import ASM
|
||||||
from ..entranceInfo import ENTRANCE_INFO
|
from ..entranceInfo import ENTRANCE_INFO
|
||||||
from ..roomEditor import RoomEditor, ObjectWarp, ObjectHorizontal
|
from ..roomEditor import RoomEditor, ObjectWarp, ObjectHorizontal
|
||||||
from ..backgroundEditor import BackgroundEditor
|
from ..backgroundEditor import BackgroundEditor
|
||||||
from .. import utils
|
from .. import utils
|
||||||
|
|
||||||
|
from ...Options import BootsControls
|
||||||
|
|
||||||
def bugfixWrittingWrongRoomStatus(rom):
|
def bugfixWrittingWrongRoomStatus(rom):
|
||||||
# The normal rom contains a pretty nasty bug where door closing triggers in D7/D8 can effect doors in
|
# The normal rom contains a pretty nasty bug where door closing triggers in D7/D8 can effect doors in
|
||||||
|
@ -539,6 +541,97 @@ OAMData:
|
||||||
rom.banks[0x38][0x1400+n*0x20:0x1410+n*0x20] = utils.createTileData(gfx_high)
|
rom.banks[0x38][0x1400+n*0x20:0x1410+n*0x20] = utils.createTileData(gfx_high)
|
||||||
rom.banks[0x38][0x1410+n*0x20:0x1420+n*0x20] = utils.createTileData(gfx_low)
|
rom.banks[0x38][0x1410+n*0x20:0x1420+n*0x20] = utils.createTileData(gfx_low)
|
||||||
|
|
||||||
|
def addBootsControls(rom, boots_controls: BootsControls):
|
||||||
|
if boots_controls == BootsControls.option_vanilla:
|
||||||
|
return
|
||||||
|
consts = {
|
||||||
|
"INVENTORY_PEGASUS_BOOTS": 0x8,
|
||||||
|
"INVENTORY_POWER_BRACELET": 0x3,
|
||||||
|
"UsePegasusBoots": 0x1705,
|
||||||
|
"J_A": (1 << 4),
|
||||||
|
"J_B": (1 << 5),
|
||||||
|
"wAButtonSlot": 0xDB01,
|
||||||
|
"wBButtonSlot": 0xDB00,
|
||||||
|
"wPegasusBootsChargeMeter": 0xC14B,
|
||||||
|
"hPressedButtonsMask": 0xCB
|
||||||
|
}
|
||||||
|
for c,v in consts.items():
|
||||||
|
assembler.const(c, v)
|
||||||
|
|
||||||
|
BOOTS_START_ADDR = 0x11E8
|
||||||
|
condition = {
|
||||||
|
BootsControls.option_bracelet: """
|
||||||
|
ld a, [hl]
|
||||||
|
; Check if we are using the bracelet
|
||||||
|
cp INVENTORY_POWER_BRACELET
|
||||||
|
jr z, .yesBoots
|
||||||
|
""",
|
||||||
|
BootsControls.option_press_a: """
|
||||||
|
; Check if we are using the A slot
|
||||||
|
cp J_A
|
||||||
|
jr z, .yesBoots
|
||||||
|
ld a, [hl]
|
||||||
|
""",
|
||||||
|
BootsControls.option_press_b: """
|
||||||
|
; Check if we are using the B slot
|
||||||
|
cp J_B
|
||||||
|
jr z, .yesBoots
|
||||||
|
ld a, [hl]
|
||||||
|
"""
|
||||||
|
}[boots_controls.value]
|
||||||
|
|
||||||
|
# The new code fits exactly within Nintendo's poorly space optimzied code while having more features
|
||||||
|
boots_code = assembler.ASM("""
|
||||||
|
CheckBoots:
|
||||||
|
; check if we own boots
|
||||||
|
ld a, [wCollectedTunics]
|
||||||
|
and $04
|
||||||
|
; if not, move on to the next inventory item (shield)
|
||||||
|
jr z, .out
|
||||||
|
|
||||||
|
; Check the B button
|
||||||
|
ld hl, wBButtonSlot
|
||||||
|
ld d, J_B
|
||||||
|
call .maybeBoots
|
||||||
|
|
||||||
|
; Check the A button
|
||||||
|
inc l ; l = wAButtonSlot - done this way to save a byte or two
|
||||||
|
ld d, J_A
|
||||||
|
call .maybeBoots
|
||||||
|
|
||||||
|
; If neither, reset charge meter and bail
|
||||||
|
xor a
|
||||||
|
ld [wPegasusBootsChargeMeter], a
|
||||||
|
jr .out
|
||||||
|
|
||||||
|
.maybeBoots:
|
||||||
|
; Check if we are holding this button even
|
||||||
|
ldh a, [hPressedButtonsMask]
|
||||||
|
and d
|
||||||
|
ret z
|
||||||
|
"""
|
||||||
|
# Check the special condition (also loads the current item for button into a)
|
||||||
|
+ condition +
|
||||||
|
"""
|
||||||
|
; Check if we are just using boots regularly
|
||||||
|
cp INVENTORY_PEGASUS_BOOTS
|
||||||
|
ret nz
|
||||||
|
.yesBoots:
|
||||||
|
; We're using boots! Do so.
|
||||||
|
call UsePegasusBoots
|
||||||
|
; If we return now we will go back into CheckBoots, we don't want that
|
||||||
|
; We instead want to move onto the next item
|
||||||
|
; but if we don't cleanup, the next "ret" will take us back there again
|
||||||
|
; So we pop the return address off of the stack
|
||||||
|
pop af
|
||||||
|
.out:
|
||||||
|
""", BOOTS_START_ADDR)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
original_code = 'fa00dbfe08200ff0cbe6202805cd05171804afea4bc1fa01dbfe08200ff0cbe6102805cd05171804afea4bc1'
|
||||||
|
rom.patch(0, BOOTS_START_ADDR, original_code, boots_code, fill_nop=True)
|
||||||
|
|
||||||
def addWarpImprovements(rom, extra_warps):
|
def addWarpImprovements(rom, extra_warps):
|
||||||
# Patch in a warp icon
|
# Patch in a warp icon
|
||||||
tile = utils.createTileData( \
|
tile = utils.createTileData( \
|
||||||
|
@ -739,4 +832,3 @@ success:
|
||||||
exit:
|
exit:
|
||||||
ret
|
ret
|
||||||
"""))
|
"""))
|
||||||
|
|
||||||
|
|
|
@ -316,6 +316,21 @@ class Overworld(Choice, LADXROption):
|
||||||
# [Disable] no music in the whole game""",
|
# [Disable] no music in the whole game""",
|
||||||
# aesthetic=True),
|
# aesthetic=True),
|
||||||
|
|
||||||
|
class BootsControls(Choice):
|
||||||
|
"""
|
||||||
|
Adds additional button to activate Pegasus Boots (does nothing if you haven't picked up your boots!)
|
||||||
|
[Vanilla] Nothing changes, you have to equip the boots to use them
|
||||||
|
[Bracelet] Holding down the button for the bracelet also activates boots (somewhat like Link to the Past)
|
||||||
|
[Press A] Holding down A activates boots
|
||||||
|
[Press B] Holding down B activates boots
|
||||||
|
"""
|
||||||
|
display_name = "Boots Controls"
|
||||||
|
option_vanilla = 0
|
||||||
|
option_bracelet = 1
|
||||||
|
option_press_a = 2
|
||||||
|
option_press_b = 3
|
||||||
|
|
||||||
|
|
||||||
class LinkPalette(Choice, LADXROption):
|
class LinkPalette(Choice, LADXROption):
|
||||||
"""
|
"""
|
||||||
Sets link's palette
|
Sets link's palette
|
||||||
|
@ -485,5 +500,5 @@ links_awakening_options: typing.Dict[str, typing.Type[Option]] = {
|
||||||
'music_change_condition': MusicChangeCondition,
|
'music_change_condition': MusicChangeCondition,
|
||||||
'nag_messages': NagMessages,
|
'nag_messages': NagMessages,
|
||||||
'ap_title_screen': APTitleScreen,
|
'ap_title_screen': APTitleScreen,
|
||||||
|
'boots_controls': BootsControls,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue