Skip to content

LADX: Add "boots controls" option #2085

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Apr 14, 2024
2 changes: 1 addition & 1 deletion worlds/ladx/LADXR/assembler.py
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,7 @@ def getLabels(self) -> ItemsView[str, int]:

def const(name: str, value: int) -> None:
name = name.upper()
assert name not in CONST_MAP
assert name not in CONST_MAP or CONST_MAP[name] == value
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assert name not in CONST_MAP or CONST_MAP[name] == value
assert name not in CONST_MAP or CONST_MAP[name] == value, "I'd recommend putting a message here, so that you get more than a mere AssertionError on webhost."

CONST_MAP[name] = value


Expand Down
7 changes: 5 additions & 2 deletions worlds/ladx/LADXR/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@

from BaseClasses import ItemClassification
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
Expand Down Expand Up @@ -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("wSeashellsCount", 0xDB41)
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)

# We store the link info in unused color dungeon flags, so it gets preserved in the savegame.
Expand Down Expand Up @@ -243,6 +243,9 @@ def generateRom(args, settings, ap_settings, auth, seed_name, logic, rnd=None, m
patches.core.quickswap(rom, 1)
elif settings.quickswap == 'b':
patches.core.quickswap(rom, 0)

patches.core.addBootsControls(rom, ap_settings['boots_controls'])


world_setup = logic.world_setup

Expand Down
1 change: 0 additions & 1 deletion worlds/ladx/LADXR/locations/startItem.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ 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):
Expand Down
9 changes: 8 additions & 1 deletion worlds/ladx/LADXR/patches/bank3e.asm/chest.asm
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ GiveItemFromChest:
dw ChestBow ; CHEST_BOW
dw ChestWithItem ; CHEST_HOOKSHOT
dw ChestWithItem ; CHEST_MAGIC_ROD
dw ChestWithItem ; CHEST_PEGASUS_BOOTS
dw Boots ; CHEST_PEGASUS_BOOTS
dw ChestWithItem ; CHEST_OCARINA
dw ChestWithItem ; CHEST_FEATHER
dw ChestWithItem ; CHEST_SHOVEL
Expand Down Expand Up @@ -273,6 +273,13 @@ ChestMagicPowder:
ld [$DB4C], a
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:
ld a, $01
Expand Down
102 changes: 97 additions & 5 deletions worlds/ladx/LADXR/patches/core.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from .. import assembler
from ..assembler import ASM
from ..entranceInfo import ENTRANCE_INFO
from ..roomEditor import RoomEditor, ObjectWarp, ObjectHorizontal
from ..backgroundEditor import BackgroundEditor
from .. import utils

from ...Options import BootsControls

def bugfixWrittingWrongRoomStatus(rom):
# The normal rom contains a pretty nasty bug where door closing triggers in D7/D8 can effect doors in
Expand Down Expand Up @@ -391,7 +393,7 @@ def addFrameCounter(rom, check_count):
db $20, $20, $20, $00 ;I
db $20, $28, $28, $00 ;M
db $20, $30, $18, $00 ;E

db $20, $70, $16, $00 ;D
db $20, $78, $18, $00 ;E
db $20, $80, $10, $00 ;A
Expand All @@ -408,7 +410,7 @@ def addFrameCounter(rom, check_count):
db $68, $38, $%02x, $00 ;0
db $68, $40, $%02x, $00 ;0
db $68, $48, $%02x, $00 ;0

""" % ((((check_count // 100) % 10) * 2) | 0x40, (((check_count // 10) % 10) * 2) | 0x40, ((check_count % 10) * 2) | 0x40), 0x469D), fill_nop=True)
# Lower line of credits roll into XX XX XX
rom.patch(0x17, 0x0784, 0x082D, ASM("""
Expand All @@ -425,7 +427,7 @@ def addFrameCounter(rom, check_count):
call updateOAM
ld a, [$B001] ; seconds
call updateOAM

ld a, [$DB58] ; death count high
call updateOAM
ld a, [$DB57] ; death count low
Expand Down Expand Up @@ -473,7 +475,7 @@ def addFrameCounter(rom, check_count):
db $68, $18, $40, $00 ;0
db $68, $20, $40, $00 ;0
db $68, $28, $40, $00 ;0

""", 0x4784), fill_nop=True)

# Grab the "mostly" complete A-Z font
Expand Down Expand Up @@ -539,6 +541,97 @@ def addFrameCounter(rom, check_count):
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)

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):
# Patch in a warp icon
tile = utils.createTileData( \
Expand Down Expand Up @@ -739,4 +832,3 @@ def addWarpImprovements(rom, extra_warps):
exit:
ret
"""))

17 changes: 16 additions & 1 deletion worlds/ladx/Options.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,21 @@ class Overworld(Choice, LADXROption):
# [Disable] no music in the whole game""",
# 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):
"""
Sets link's palette
Expand Down Expand Up @@ -485,5 +500,5 @@ class AdditionalWarpPoints(DefaultOffToggle):
'music_change_condition': MusicChangeCondition,
'nag_messages': NagMessages,
'ap_title_screen': APTitleScreen,

'boots_controls': BootsControls,
}
Loading