LADX: Add "boots controls" option ()

This commit is contained in:
zig-for 2024-04-13 18:21:55 -07:00 committed by GitHub
parent 98e2d89a1c
commit ca5c0d9eb8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 127 additions and 11 deletions
worlds/ladx

View File

@ -757,7 +757,7 @@ class Assembler:
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
CONST_MAP[name] = value

View File

@ -65,7 +65,7 @@ from .locations.keyLocation import KeyLocation
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
@ -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.
@ -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

View File

@ -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):

View File

@ -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
@ -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

View File

@ -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
@ -391,7 +393,7 @@ OAMData:
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
@ -408,7 +410,7 @@ OAMData:
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("""
@ -425,7 +427,7 @@ OAMData:
call updateOAM
ld a, [$B001] ; seconds
call updateOAM
ld a, [$DB58] ; death count high
call updateOAM
ld a, [$DB57] ; death count low
@ -473,7 +475,7 @@ OAMData:
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
@ -539,6 +541,97 @@ OAMData:
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( \
@ -739,4 +832,3 @@ success:
exit:
ret
"""))

View File

@ -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
@ -485,5 +500,5 @@ links_awakening_options: typing.Dict[str, typing.Type[Option]] = {
'music_change_condition': MusicChangeCondition,
'nag_messages': NagMessages,
'ap_title_screen': APTitleScreen,
'boots_controls': BootsControls,
}