LADX: Add "boots controls" option (#2085)
This commit is contained in:
parent
98e2d89a1c
commit
ca5c0d9eb8
worlds/ladx
|
@ -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
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
"""))
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue