Skip to content

LADX: Implement remake style warp selection #1587

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 25 commits into from
Oct 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions worlds/ladx/LADXR/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
from . import hints

from .patches import bank34
from .utils import formatText
from ..Options import TrendyGame, Palette
from .roomEditor import RoomEditor, Object
from .patches.aesthetics import rgb_to_bin, bin_to_rgb

from .locations.keyLocation import KeyLocation
Expand Down Expand Up @@ -134,7 +137,7 @@ def generateRom(args, settings, ap_settings, auth, seed_name, logic, rnd=None, m
patches.core.fixWrongWarp(rom)
patches.core.alwaysAllowSecretBook(rom)
patches.core.injectMainLoop(rom)

from ..Options import ShuffleSmallKeys, ShuffleNightmareKeys

if ap_settings["shuffle_small_keys"] != ShuffleSmallKeys.option_original_dungeon or ap_settings["shuffle_nightmare_keys"] != ShuffleNightmareKeys.option_original_dungeon:
Expand Down Expand Up @@ -239,7 +242,7 @@ 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)

world_setup = logic.world_setup

JUNK_HINT = 0.33
Expand All @@ -263,7 +266,7 @@ def gen_hint():
name = "Your"
else:
name = f"{multiworld.player_name[location.item.player]}'s"

if isinstance(location, LinksAwakeningLocation):
location_name = location.ladxr_item.metadata.name
else:
Expand Down Expand Up @@ -323,7 +326,7 @@ def gen_hint():

# TODO: if 0 or 4, 5, remove inaccurate conveyor tiles

from .roomEditor import RoomEditor, Object

room_editor = RoomEditor(rom, 0x2A0)

if ap_settings["trendy_game"] == TrendyGame.option_easy:
Expand Down Expand Up @@ -352,12 +355,12 @@ def gen_hint():
}
def speed():
return rnd.randint(*speeds[ap_settings["trendy_game"]])
rom.banks[0x4][0x76A0-0x4000] = 0xFF - speed()
rom.banks[0x4][0x76A0-0x4000] = 0xFF - speed()
rom.banks[0x4][0x76A2-0x4000] = speed()
rom.banks[0x4][0x76A6-0x4000] = speed()
rom.banks[0x4][0x76A8-0x4000] = 0xFF - speed()
if int(ap_settings["trendy_game"]) >= TrendyGame.option_hardest:
rom.banks[0x4][0x76A1-0x4000] = 0xFF - speed()
rom.banks[0x4][0x76A1-0x4000] = 0xFF - speed()
rom.banks[0x4][0x76A3-0x4000] = speed()
rom.banks[0x4][0x76A5-0x4000] = speed()
rom.banks[0x4][0x76A7-0x4000] = 0xFF - speed()
Expand All @@ -374,12 +377,14 @@ def speed():
[0x0f, 0x38, 0x0f],
[0x30, 0x62, 0x30],
[0x8b, 0xac, 0x0f],
[0x9b, 0xbc, 0x0f],
[0x9b, 0xbc, 0x0f],
]
for color in gb_colors:
for channel in range(3):
color[channel] = color[channel] * 31 // 0xbc


if ap_settings["warp_improvements"]:
patches.core.addWarpImprovements(rom, ap_settings["additional_warp_points"])

palette = ap_settings["palette"]
if palette != Palette.option_normal:
Expand Down Expand Up @@ -410,7 +415,7 @@ def clamp(x, min, max):
for address in range(start, end, 2):
packed = (rom.banks[bank][address + 1] << 8) | rom.banks[bank][address]
r,g,b = bin_to_rgb(packed)

# 1 bit
if palette == Palette.option_1bit:
r &= 0b10000
Expand Down
205 changes: 204 additions & 1 deletion worlds/ladx/LADXR/patches/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,8 +255,9 @@ def warpHome(rom):
""" % (type, map, room, x, y)), fill_nop=True)

# Patch the RAM clear not to delete our custom dialog when we screen transition
# This is kind of horrible as it relies on bank 1 being loaded, lol
rom.patch(0x01, 0x042C, "C629", "6B7E")
rom.patch(0x01, 0x3E6B, 0x3FFF, ASM("""
rom.patch(0x01, 0x3E6B, 0x3E7B, ASM("""
ld bc, $A0
call $29DC
ld bc, $1200
Expand Down Expand Up @@ -537,3 +538,205 @@ def addFrameCounter(rom, check_count):
gfx_low = "\n".join([line.split(" ")[n] for line in tile_graphics.split("\n")[8:]])
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 addWarpImprovements(rom, extra_warps):
# Patch in a warp icon
tile = utils.createTileData( \
"""11111111
10000000
10200320
10323200
10033300
10023230
10230020
10000000""", key="0231")
MINIMAP_BASE = 0x3800

# This is replacing a junk tile never used on the minimap
rom.banks[0x2C][MINIMAP_BASE + len(tile) * 0x65 : MINIMAP_BASE + len(tile) * 0x66] = tile

# Allow using ENTITY_WARP for finding which map sections are warps
# Interesting - 3CA0 should be free, but something has pushed all the code forward a byte
rom.patch(0x02, 0x3CA1, None, ASM("""
ld e, $0F
ld d, $00
warp_search_loop:
; Warp search loop
ld hl, $C3A0
add hl, de ; $5FE1: $19
ld a, [hl] ; $5FE2: $7E
cp $61 ; ENTITY_WARP
jr nz, search_continue ; if it's not a warp, check the next one
ld hl, $C280
add hl, de
ld a, [hl]
and a
jr z, search_continue ; if this is despawned, check the next one
found:
jp $511B ; found
search_continue:
dec e
ld a, e
cp $FF
jr nz, warp_search_loop

not_found:
jp $512B

"""))

# Insert redirect to above code
rom.patch(0x02, 0x1109, ASM("""
ldh a, [$F6]
cp 1

"""), ASM("""
jp $7CA1
nop
"""))
# Leaves some extra bytes behind, if we need more space in 0x02

# On warp hole, open map instead
rom.patch(0x19, 0x1DB9, None, ASM("""
ld a, 7 ; Set GAMEPLAY_MAP
ld [$DB95], a
ld a, 0 ; reset subtype
ld [$DB96], a
ld a, 1 ; Set flag for using teleport
ld [$FFDD], a

ret
"""), fill_nop=True)

# Patch over some instructions that decided if we are in debug mode holding some
# buttons with instead checking for FFDD (why FFDD? It appears to be never used anywhere, so we repurpose it for "is in teleport mode")
rom.banks[0x01][0x17B8] = 0xDD
rom.banks[0x01][0x17B9] = 0xFF
rom.banks[0x01][0x17FD] = 0xDD
rom.banks[0x01][0x17FE] = 0xFF

# If in warp mode, don't allow manual exit
rom.patch(0x01, 0x1800, "20021E60", ASM("jp nz, $5818"), fill_nop=True)

# Allow warp with just B
rom.banks[0x01][0x17C0] = 0x20

# Allow cursor to move over black squares
# This allows warping to undiscovered areas - a fine cheat, but needs a check for wOverworldRoomStatus in the warp code
CHEAT_WARP_ANYWHERE = False
if CHEAT_WARP_ANYWHERE:
rom.patch(0x01, 0x1AE8, None, ASM("jp $5AF5"))

# This disables the arrows around the selection bubble
#rom.patch(0x01, 0x1B6F, None, ASM("ret"), fill_nop=True)

# Fix lag when moving the cursor
# One option - just disable the delay code
#rom.patch(0x01, 0x1A76, 0x1A76+3, ASM("xor a"), fill_nop=True)
#rom.banks[0x01][0x1A7C] = 0
# Another option - just remove the animation
rom.banks[0x01][0x1B20] = 0
rom.banks[0x01][0x1B3B] = 0

# Patch the icon for all teleports
all_warps = [0x01, 0x95, 0x2C, 0xEC]


if extra_warps:
# mamu
all_warps.append(0x45)
# Tweak the flute location
rom.banks[0x14][0x0E95] += 0x10
rom.banks[0x14][0x0EA3] += 0x01

mamu_pond = RoomEditor(rom, 0x45)
# Remove some tall grass so we can add a warp instead
mamu_pond.changeObject(1, 6, 0xE8)
mamu_pond.moveObject(1, 6, 3, 5)
mamu_pond.addEntity(3, 5, 0x61)

mamu_pond.store(rom)

# eagle
all_warps.append(0x0F)
room = RoomEditor(rom, 0x0F)
# Move one cliff edge and change it into a pit
room.changeObject(7, 6, 0xE8)
room.moveObject(7, 6, 6, 4)

# Add the warp
room.addEntity(6, 4, 0x61)
# move the two corners
room.moveObject(6, 7, 7, 7)
room.moveObject(6, 6, 7, 6)
for object in room.objects:
# Extend the lower wall
if ((object.x == 0 and object.y == 7)
# Extend the lower floor
or (object.x == 0 and object.y == 6)):
room.overlay[object.x + object.count + object.y * 10] = object.type_id
object.count += 1
room.store(rom)

for warp in all_warps:
# Set icon
rom.banks[0x20][0x168B + warp] = 0x55
# Set text
if not rom.banks[0x01][0x1959 + warp]:
rom.banks[0x01][0x1959 + warp] = 0x42
# Set palette
# rom.banks[0x20][0x178B + 0x95] = 0x1

# Setup [?!] icon on map and associated text
rom.banks[0x01][0x1909 + 0x42] = 0x2B
rom.texts[0x02B] = utils.formatText('Warp')

# call warp function (why not just jmp?!)
rom.patch(0x01, 0x17C3, None, ASM("""
call $7E7B
ret
"""))

# Build a switch statement by hand
warp_jump = "".join(f"cp ${hex(warp)[2:]}\njr z, success\n" for warp in all_warps)

rom.patch(0x01, 0x3E7B, None, ASM(f"""
TeleportHandler:

ld a, [$DBB4] ; Load the current selected tile
; TODO: check if actually revealed so we can have free movement
; Check cursor against different tiles to see if we are selecting a warp
{warp_jump}
jr exit

success:
ld a, $0B
ld [$DB95], a ; Gameplay type
xor a
ld [$D401], a ; wWarp0MapCategory
ldh [$DD], a ; unset teleport flag(!!!)
ld [$D402], a ; wWarp0Map
ld a, [$DBB4] ; wDBB4
ld [$D403], a ; wWarp0Room

ld a, $68
ld [$D404], a ; wWarp0DestinationX
ldh [$98], a ; LinkPositionY
ld [$D475], a
ld a, $70
ld [$D405], a ; wWarp0DestinationY
ldh [$99], a ; LinkPositionX
ld a, $66
ld [$D416], a ; wWarp0PositionTileIndex
ld a, $07
ld [$DB96], a ; wGameplaySubtype
ldh a, [$A2]
ld [$DBC8], a
call $0C83 ; ApplyMapFadeOutTransition
xor a ; $5DF3: $AF
ld [$C167], a ; $5DF4: $EA $67 $C1

exit:
ret
"""))

17 changes: 16 additions & 1 deletion worlds/ladx/Options.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,20 @@ class Palette(Choice):
option_greyscale = 3
option_pink = 4
option_inverted = 5


class WarpImprovements(DefaultOffToggle):
"""
[On] Adds remake style warp screen to the game. Choose your warp destination on the map after jumping in a portal and press B to select.
[Off] No change
"""

class AdditionalWarpPoints(DefaultOffToggle):
"""
[On] (requires warp improvements) Adds a warp point at Crazy Tracy's house (the Mambo teleport spot) and Eagle's Tower
[Off] No change
"""


links_awakening_options: typing.Dict[str, typing.Type[Option]] = {
'logic': Logic,
# 'heartpiece': DefaultOnToggle, # description='Includes heart pieces in the item pool'),
Expand All @@ -400,6 +413,8 @@ class Palette(Choice):
# 'bowwow': Bowwow,
# 'overworld': Overworld,
'link_palette': LinkPalette,
'warp_improvements': WarpImprovements,
'additional_warp_points': AdditionalWarpPoints,
'trendy_game': TrendyGame,
'gfxmod': GfxMod,
'palette': Palette,
Expand Down