Skip to content

Commit

Permalink
LADX: Implement various upstream adjustments (#3829)
Browse files Browse the repository at this point in the history
* magnifying lens changes

daid/LADXR#156

* restore enemy visibility in mermaid statue cave

daid/LADXR#155

* mermaid statue scale bugfix

daid/LADXR#163

* restore vanilla map when rooster is an item

daid/LADXR#132

* fix

* fixes to magnifying lens changes

* load marin singing even if you have marin date
daid/LADXR@4feb309

* Revert "load marin singing even if you have marin date"

This reverts commit a7a546e.

* always patch tradequest
not upstream, but included in this PR because it touches the same parts of the code. https://discord.com/channels/731205301247803413/1227373762412937347

* marin date fix

* fix logic
  • Loading branch information
threeandthreee authored Dec 1, 2024
1 parent 1a5d22c commit ed721dd
Show file tree
Hide file tree
Showing 9 changed files with 110 additions and 57 deletions.
10 changes: 4 additions & 6 deletions worlds/ladx/LADXR/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,9 @@ def generateRom(args, world: "LinksAwakeningWorld"):
if world.ladxr_settings.witch:
patches.witch.updateWitch(rom)
patches.softlock.fixAll(rom)
patches.maptweaks.tweakMap(rom)
if not world.ladxr_settings.rooster:
patches.maptweaks.tweakMap(rom)
patches.maptweaks.tweakBirdKeyRoom(rom)
patches.chest.fixChests(rom)
patches.shop.fixShop(rom)
patches.rooster.patchRooster(rom)
Expand All @@ -176,11 +178,7 @@ def generateRom(args, world: "LinksAwakeningWorld"):
patches.songs.upgradeMarin(rom)
patches.songs.upgradeManbo(rom)
patches.songs.upgradeMamu(rom)
if world.ladxr_settings.tradequest:
patches.tradeSequence.patchTradeSequence(rom, world.ladxr_settings.boomerang)
else:
# Monkey bridge patch, always have the bridge there.
rom.patch(0x00, 0x333D, assembler.ASM("bit 4, e\njr Z, $05"), b"", fill_nop=True)
patches.tradeSequence.patchTradeSequence(rom, world.ladxr_settings)
patches.bowwow.fixBowwow(rom, everywhere=world.ladxr_settings.bowwow != 'normal')
if world.ladxr_settings.bowwow != 'normal':
patches.bowwow.bowwowMapPatches(rom)
Expand Down
17 changes: 0 additions & 17 deletions worlds/ladx/LADXR/locations/birdKey.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,6 @@
from .droppedKey import DroppedKey
from ..roomEditor import RoomEditor
from ..assembler import ASM


class BirdKey(DroppedKey):
def __init__(self):
super().__init__(0x27A)

def patch(self, rom, option, *, multiworld=None):
super().patch(rom, option, multiworld=multiworld)

re = RoomEditor(rom, self.room)

# Make the bird key accessible without the rooster
re.removeObject(1, 6)
re.removeObject(2, 6)
re.removeObject(3, 5)
re.removeObject(3, 6)
re.moveObject(1, 5, 2, 6)
re.moveObject(2, 5, 3, 6)
re.addEntity(3, 5, 0x9D)
re.store(rom)
5 changes: 0 additions & 5 deletions worlds/ladx/LADXR/locations/boomerangGuy.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,6 @@ def configure(self, options):
# But SHIELD, BOMB and MAGIC_POWDER would most likely break things.
# SWORD and POWER_BRACELET would most likely introduce the lv0 shield/bracelet issue
def patch(self, rom, option, *, multiworld=None):
# Always have the boomerang trade guy enabled (normally you need the magnifier)
rom.patch(0x19, 0x05EC, ASM("ld a, [wTradeSequenceItem]\ncp $0E"), ASM("ld a, $0E\ncp $0E"), fill_nop=True) # show the guy
rom.patch(0x00, 0x3199, ASM("ld a, [wTradeSequenceItem]\ncp $0E"), ASM("ld a, $0E\ncp $0E"), fill_nop=True) # load the proper room layout
rom.patch(0x19, 0x05F4, ASM("ld a, [wTradeSequenceItem2]\nand a"), ASM("xor a"), fill_nop=True)

if self.setting == 'trade':
inv = INVENTORY_MAP[option]
# Patch the check if you traded back the boomerang (so traded twice)
Expand Down
45 changes: 25 additions & 20 deletions worlds/ladx/LADXR/logic/overworld.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ def __init__(self, options, world_setup, r):
self._addEntrance("banana_seller", sword_beach, banana_seller, r.bush)
boomerang_cave = Location("Boomerang Cave")
if options.boomerang == 'trade':
Location().add(BoomerangGuy()).connect(boomerang_cave, OR(BOOMERANG, HOOKSHOT, MAGIC_ROD, PEGASUS_BOOTS, FEATHER, SHOVEL))
Location().add(BoomerangGuy()).connect(boomerang_cave, AND(r.shuffled_magnifier, OR(BOOMERANG, HOOKSHOT, MAGIC_ROD, PEGASUS_BOOTS, FEATHER, SHOVEL)))
elif options.boomerang == 'gift':
Location().add(BoomerangGuy()).connect(boomerang_cave, None)
Location().add(BoomerangGuy()).connect(boomerang_cave, r.shuffled_magnifier)
self._addEntrance("boomerang_cave", sword_beach, boomerang_cave, BOMB)
self._addEntranceRequirementExit("boomerang_cave", None) # if exiting, you do not need bombs

Expand Down Expand Up @@ -167,7 +167,9 @@ def __init__(self, options, world_setup, r):
prairie_island_seashell = Location().add(Seashell(0x0A6)).connect(ukuku_prairie, AND(FLIPPERS, r.bush)) # next to lv3
Location().add(Seashell(0x08B)).connect(ukuku_prairie, r.bush) # next to seashell house
Location().add(Seashell(0x0A4)).connect(ukuku_prairie, PEGASUS_BOOTS) # smash into tree next to phonehouse
self._addEntrance("castle_jump_cave", ukuku_prairie, Location().add(Chest(0x1FD)), OR(AND(FEATHER, PEGASUS_BOOTS), ROOSTER)) # left of the castle, 5 holes turned into 3
self._addEntrance("castle_jump_cave", ukuku_prairie, Location().add(Chest(0x1FD)), ROOSTER)
if not options.rooster:
self._addEntranceRequirement("castle_jump_cave", AND(FEATHER, PEGASUS_BOOTS)) # left of the castle, 5 holes turned into 3
Location().add(Seashell(0x0B9)).connect(ukuku_prairie, POWER_BRACELET) # under the rock

left_bay_area = Location()
Expand Down Expand Up @@ -353,7 +355,7 @@ def __init__(self, options, world_setup, r):
self._addEntrance("d4", d4_entrance, None, ANGLER_KEY)
self._addEntranceRequirementExit("d4", FLIPPERS) # if exiting, you can leave with flippers without opening the dungeon
mambo = Location().connect(Location().add(Song(0x2FD)), AND(OCARINA, FLIPPERS)) # Manbo's Mambo
self._addEntrance("mambo", d4_entrance, mambo, FLIPPERS)
self._addEntrance("mambo", d4_entrance, mambo, FLIPPERS)

# Raft game.
raft_house = Location("Raft House")
Expand All @@ -379,7 +381,9 @@ def __init__(self, options, world_setup, r):
self._addEntrance("rooster_house", outside_rooster_house, None, None)
bird_cave = Location()
bird_key = Location().add(BirdKey())
bird_cave.connect(bird_key, OR(AND(FEATHER, COUNT(POWER_BRACELET, 2)), ROOSTER))
bird_cave.connect(bird_key, ROOSTER)
if not options.rooster:
bird_cave.connect(bird_key, AND(FEATHER, COUNT(POWER_BRACELET, 2))) # elephant statue added
if options.logic != "casual":
bird_cave.connect(lower_right_taltal, None, one_way=True) # Drop in a hole at bird cave
self._addEntrance("bird_cave", outside_rooster_house, bird_cave, None)
Expand Down Expand Up @@ -468,17 +472,18 @@ def __init__(self, options, world_setup, r):
swamp.connect(writes_hut_outside, HOOKSHOT, one_way=True) # hookshot the sign in front of writes hut
graveyard_heartpiece.connect(graveyard_cave_right, FEATHER) # jump to the bottom right tile around the blocks
graveyard_heartpiece.connect(graveyard_cave_right, OR(HOOKSHOT, BOOMERANG)) # push bottom block, wall clip and hookshot/boomerang corner to grab item

self._addEntranceRequirement("mamu", AND(FEATHER, POWER_BRACELET)) # can clear the gaps at the start with just feather, can reach bottom left sign with a well timed jump while wall clipped
self._addEntranceRequirement("prairie_madbatter_connector_entrance", AND(OR(FEATHER, ROOSTER), OR(MAGIC_POWDER, BOMB))) # use bombs or powder to get rid of a bush on the other side by jumping across and placing the bomb/powder before you fall into the pit
fisher_under_bridge.connect(bay_water, AND(TRADING_ITEM_FISHING_HOOK, FLIPPERS)) # can talk to the fisherman from the water when the boat is low (requires swimming up out of the water a bit)
crow_gold_leaf.connect(castle_courtyard, POWER_BRACELET) # bird on tree at left side kanalet, can use both rocks to kill the crow removing the kill requirement
castle_inside.connect(kanalet_chain_trooper, BOOMERANG, one_way=True) # kill the ball and chain trooper from the left side, then use boomerang to grab the dropped item
animal_village_bombcave_heartpiece.connect(animal_village_bombcave, AND(PEGASUS_BOOTS, FEATHER)) # jump across horizontal 4 gap to heart piece
desert_lanmola.connect(desert, BOMB) # use bombs to kill lanmola

d6_connector_left.connect(d6_connector_right, AND(OR(FLIPPERS, PEGASUS_BOOTS), FEATHER)) # jump the gap in underground passage to d6 left side to skip hookshot
bird_key.connect(bird_cave, COUNT(POWER_BRACELET, 2)) # corner walk past the one pit on the left side to get to the elephant statue
if not options.rooster:
bird_key.connect(bird_cave, COUNT(POWER_BRACELET, 2)) # corner walk past the one pit on the left side to get to the elephant statue
fire_cave_bottom.connect(fire_cave_top, PEGASUS_BOOTS, one_way=True) # flame skip

if options.logic == 'glitched' or options.logic == 'hell':
Expand All @@ -502,9 +507,9 @@ def __init__(self, options, world_setup, r):
tiny_island.connect(left_bay_area, AND(FEATHER, r.bush)) # jesus jump around
bay_madbatter_connector_exit.connect(bay_madbatter_connector_entrance, FEATHER, one_way=True) # jesus jump (3 screen) through the underground passage leading to martha's bay mad batter
self._addEntranceRequirement("prairie_madbatter_connector_entrance", AND(FEATHER, POWER_BRACELET)) # villa buffer into the top side of the bush, then pick it up

ukuku_prairie.connect(richard_maze, OR(BOMB, BOOMERANG, MAGIC_POWDER, MAGIC_ROD, SWORD), one_way=True) # break bushes on north side of the maze, and 1 pit buffer into the maze
fisher_under_bridge.connect(bay_water, AND(BOMB, FLIPPERS)) # can bomb trigger the item without having the hook
fisher_under_bridge.connect(bay_water, AND(BOMB, FLIPPERS)) # can bomb trigger the item without having the hook
animal_village.connect(ukuku_prairie, FEATHER) # jesus jump
below_right_taltal.connect(next_to_castle, FEATHER) # jesus jump (north of kanalet castle phonebooth)
animal_village_connector_right.connect(animal_village_connector_left, FEATHER) # text clip past the obstacles (can go both ways), feather to wall clip the obstacle without triggering text or shaq jump in bottom right corner if text is off
Expand All @@ -519,12 +524,12 @@ def __init__(self, options, world_setup, r):
obstacle_cave_inside_chest.connect(obstacle_cave_inside, FEATHER) # jump to the rightmost pits + 1 pit buffer to jump across
obstacle_cave_exit.connect(obstacle_cave_inside, FEATHER) # 1 pit buffer above boots crystals to get past
lower_right_taltal.connect(hibiscus_item, AND(TRADING_ITEM_PINEAPPLE, BOMB), one_way=True) # bomb trigger papahl from below ledge, requires pineapple

self._addEntranceRequirement("heartpiece_swim_cave", FEATHER) # jesus jump into the cave entrance after jumping down the ledge, can jesus jump back to the ladder 1 screen below
self._addEntranceRequirement("mambo", FEATHER) # jesus jump from (unlocked) d4 entrance to mambo's cave entrance
outside_raft_house.connect(below_right_taltal, FEATHER, one_way=True) # jesus jump from the ledge at raft to the staircase 1 screen south

self._addEntranceRequirement("multichest_left", FEATHER) # jesus jump past staircase leading up the mountain
self._addEntranceRequirement("multichest_left", FEATHER) # jesus jump past staircase leading up the mountain
outside_rooster_house.connect(lower_right_taltal, FEATHER) # jesus jump (1 or 2 screen depending if angler key is used) to staircase leading up the mountain
d7_platau.connect(water_cave_hole, None, one_way=True) # use save and quit menu to gain control while falling to dodge the water cave hole
mountain_bridge_staircase.connect(outside_rooster_house, AND(PEGASUS_BOOTS, FEATHER)) # cross bridge to staircase with pit buffer to clip bottom wall and jump across
Expand All @@ -547,7 +552,7 @@ def __init__(self, options, world_setup, r):
graveyard.connect(forest, OR(PEGASUS_BOOTS, HOOKSHOT)) # boots bonk witches hut, or hookshot spam across the pit
graveyard_cave_left.connect(graveyard_cave_right, HOOKSHOT) # hookshot spam over the pit
graveyard_cave_right.connect(graveyard_cave_left, PEGASUS_BOOTS, one_way=True) # boots bonk off the cracked block

self._addEntranceRequirementEnter("mamu", AND(PEGASUS_BOOTS, POWER_BRACELET)) # can clear the gaps at the start with multiple pit buffers, can reach bottom left sign with bonking along the bottom wall
self._addEntranceRequirement("castle_jump_cave", PEGASUS_BOOTS) # pit buffer to clip bottom wall and boots bonk across
prairie_cave_secret_exit.connect(prairie_cave, AND(BOMB, OR(PEGASUS_BOOTS, HOOKSHOT))) # hookshot spam or boots bonk across pits can go from left to right by pit buffering on top of the bottom wall then boots bonk across
Expand All @@ -563,15 +568,15 @@ def __init__(self, options, world_setup, r):
animal_village.connect(bay_water, FEATHER) # jesus jump (can always reach bay_water with jesus jumping from every way to enter bay_water, so no one_way)
ukuku_prairie.connect(bay_water, FEATHER, one_way=True) # jesus jump
bay_water.connect(d5_entrance, FEATHER) # jesus jump into d5 entrance (wall clip), wall clip + jesus jump to get out

crow_gold_leaf.connect(castle_courtyard, BOMB) # bird on tree at left side kanalet, place a bomb against the tree and the crow flies off. With well placed second bomb the crow can be killed
mermaid_statue.connect(animal_village, AND(TRADING_ITEM_SCALE, FEATHER)) # early mermaid statue by buffering on top of the right ledge, then superjumping to the left (horizontal pixel perfect)
animal_village_bombcave_heartpiece.connect(animal_village_bombcave, PEGASUS_BOOTS) # boots bonk across bottom wall (both at entrance and in item room)

d6_armos_island.connect(ukuku_prairie, FEATHER) # jesus jump (3 screen) from seashell mansion to armos island
armos_fairy_entrance.connect(d6_armos_island, PEGASUS_BOOTS, one_way=True) # jesus jump from top (fairy bomb cave) to armos island with fast falling
armos_fairy_entrance.connect(d6_armos_island, PEGASUS_BOOTS, one_way=True) # jesus jump from top (fairy bomb cave) to armos island with fast falling
d6_connector_right.connect(d6_connector_left, PEGASUS_BOOTS) # boots bonk across bottom wall at water and pits (can do both ways)

obstacle_cave_entrance.connect(obstacle_cave_inside, OR(HOOKSHOT, AND(FEATHER, PEGASUS_BOOTS, OR(SWORD, MAGIC_ROD, BOW)))) # get past crystal rocks by hookshotting into top pushable block, or boots dashing into top wall where the pushable block is to superjump down
obstacle_cave_entrance.connect(obstacle_cave_inside, AND(PEGASUS_BOOTS, ROOSTER)) # get past crystal rocks pushing the top pushable block, then boots dashing up picking up the rooster before bonking. Pause buffer until rooster is fully picked up then throw it down before bonking into wall
d4_entrance.connect(below_right_taltal, FEATHER) # jesus jump a long way
Expand All @@ -583,7 +588,7 @@ def __init__(self, options, world_setup, r):
mountain_bridge_staircase.connect(outside_rooster_house, OR(PEGASUS_BOOTS, FEATHER)) # cross bridge to staircase with pit buffer to clip bottom wall and jump or boots bonk across
left_right_connector_cave_entrance.connect(left_right_connector_cave_exit, AND(PEGASUS_BOOTS, FEATHER), one_way=True) # boots jump to bottom left corner of pits, pit buffer and jump to left
left_right_connector_cave_exit.connect(left_right_connector_cave_entrance, AND(ROOSTER, OR(PEGASUS_BOOTS, SWORD, BOW, MAGIC_ROD)), one_way=True) # pass through the passage in reverse using a boots rooster hop or rooster superjump in the one way passage area

self.start = start_house
self.egg = windfish_egg
self.nightmare = nightmare
Expand Down Expand Up @@ -659,7 +664,7 @@ def __init__(self, outside, requirement, one_way_enter_requirement="UNSET", one_
self.requirement = requirement
self.one_way_enter_requirement = one_way_enter_requirement
self.one_way_exit_requirement = one_way_exit_requirement

def addRequirement(self, new_requirement):
self.requirement = OR(self.requirement, new_requirement)

Expand All @@ -674,9 +679,9 @@ def addEnterRequirement(self, new_requirement):
self.one_way_enter_requirement = new_requirement
else:
self.one_way_enter_requirement = OR(self.one_way_enter_requirement, new_requirement)

def enterIsSet(self):
return self.one_way_enter_requirement != "UNSET"

def exitIsSet(self):
return self.one_way_exit_requirement != "UNSET"
3 changes: 3 additions & 0 deletions worlds/ladx/LADXR/logic/requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ def __init__(self, options):
self.rear_attack_range = OR(MAGIC_ROD, BOW) # mimic
self.fire = OR(MAGIC_POWDER, MAGIC_ROD) # torches
self.push_hardhat = OR(SHIELD, SWORD, HOOKSHOT, BOOMERANG)
self.shuffled_magnifier = TRADING_ITEM_MAGNIFYING_GLASS

self.boss_requirements = [
SWORD, # D1 boss
Expand Down Expand Up @@ -293,6 +294,8 @@ def __init__(self, options):
}

# Adjust for options
if not options.tradequest:
self.shuffled_magnifier = True
if options.bowwow != 'normal':
# We cheat in bowwow mode, we pretend we have the sword, as bowwow can pretty much do all what the sword ca$ # Except for taking out bushes (and crystal pillars are removed)
self.bush.remove(SWORD)
Expand Down
13 changes: 13 additions & 0 deletions worlds/ladx/LADXR/patches/maptweaks.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,16 @@ def addBetaRoom(rom):
re.store(rom)

rom.room_sprite_data_indoor[0x0FC] = rom.room_sprite_data_indoor[0x1A1]


def tweakBirdKeyRoom(rom):
# Make the bird key accessible without the rooster
re = RoomEditor(rom, 0x27A)
re.removeObject(1, 6)
re.removeObject(2, 6)
re.removeObject(3, 5)
re.removeObject(3, 6)
re.moveObject(1, 5, 2, 6)
re.moveObject(2, 5, 3, 6)
re.addEntity(3, 5, 0x9D)
re.store(rom)
4 changes: 4 additions & 0 deletions worlds/ladx/LADXR/patches/songs.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ def upgradeMarin(rom):
rst 8
"""), fill_nop=True)

# Load marin singing even if you have the marin date
rom.patch(0x03, 0x0A91, ASM("jp nz, $3F8D"), "", fill_nop=True)
rom.patch(0x05, 0x0E6E, ASM("jp nz, $7B4B"), "", fill_nop=True)


def upgradeManbo(rom):
# Instead of checking if we have the song, check if we have a specific room flag set
Expand Down
Loading

0 comments on commit ed721dd

Please sign in to comment.