forked from mirror/Archipelago
* Add the world * doc update * docs * Fix Blast/Missile not clearing Reflect * Update worlds/earthbound/__init__.py Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> * Update worlds/earthbound/__init__.py remove unused import Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> * Update worlds/earthbound/__init__.py Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> * Update worlds/earthbound/modules/dungeon_er.py make bool optional Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> * Update worlds/earthbound/modules/boss_shuffle.py typing update Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> * Update worlds/earthbound/modules/boss_shuffle.py Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> * Filter events out of item name to id * we call it a glorp * Update worlds/earthbound/Regions.py Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> * Update worlds/earthbound/__init__.py Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> * Update worlds/earthbound/Items.py Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> * Update worlds/earthbound/Regions.py Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> * Fix missing optional import * hint stuff * -Fix Apple Kid text being wrong -Fix Slimy Pile text being wrong * -Fix some sprite corruption if PSI was used when an enemy loaded another enemy -Fixed a visible artifact tile during some cutscenes * Update ver * Update docs * Fix some money scripting issues * Add argument to PSI fakeout attack * Updated monkey caves shop description * Remove closing markdown from doc * Add new flavors * Make flavors actually work * Update platforms * Fix common gear getting duplicated * Split region initialization * Condense checks for start inventory + some other junk * Fix some item groups - change receiver phone to warp pad * wow that one was really bad :glorp: * blah * Fix cutoff option text * switch start inventory concatenation to itertools * Fix sky runner scripting bug - added some new comm suggestions * Fix crash when generating with spoiler_only * Fix happy-happy teleport not unlocking after beating carpainter * Hint man hints can now use CreateHint packets to create hints in other games * Adjust some filler rarity * Update world to use CreateHints and deprecate old method * Fix epilogue skip being offset * Rearrange a couple regions * Fix tendapants getting deleted in battle * update doc * i got scared and forgot i had multiple none checks and am worried about this triggering but tested and it works * Fix mostly typing errors from silvris * More type checks * More typing * Typema * Type * Fix enemy levels overwriting music * Fix gihugic blunder * Fix Lumine Hall enabling OSS * del world * Rel 4.2.7 * Remove some debug logs * Fix vanilla bug with weird ambush detection * Fix Starman Junior having an unscaled Freeze * Change shop scaling * Fix shops using the wrong thankful script * Update some bosses in boss shuffle * Loc group adjustment * Update some boss shuffle stuff | Fix Enemizer attacks getting overwritten by Shuffle data | Fix flunkies not updating and still being used with enemizer * Get rid of some debug stuff * Get boss shuffle running, dont merge * Fix json and get boss shuffle no plando back up * Fix Magicant Boost not initializing to Ness if party count = 4 * Fix belch shop using wrong logic * Don't re-send goal status * EBitem * remove : * idk if this is whatvi wanted * All client messagesnow only send when relevant instead of constantly * Patch up the rest of boss plando * Fix Giygas being not excluded from enemizer * Fix epilogue again * adjust the sphere scaling name * add the things * Fix Ness being placed onto monotoli when monotoli was in sea of eden * Fix prefill properly * Fix boss shuffle on vanilla slots. * rename this, apparently * Update archipelago.json --------- Co-authored-by: Duck <31627079+duckboycool@users.noreply.github.com> Co-authored-by: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com>
213 lines
10 KiB
Python
213 lines
10 KiB
Python
import struct
|
|
from typing import Optional, TYPE_CHECKING
|
|
from dataclasses import dataclass
|
|
from ..game_data.local_data import item_id_table
|
|
from typing import Optional, TYPE_CHECKING
|
|
from dataclasses import dataclass
|
|
from ..game_data.local_data import item_id_table
|
|
if TYPE_CHECKING:
|
|
from .. import EarthBoundWorld
|
|
from ..Rom import LocalRom
|
|
|
|
|
|
@dataclass
|
|
class EBDungeonDoor:
|
|
address: int
|
|
copyaddress: int
|
|
direction: int
|
|
is_script: bool = False # Script warps invert the x and y coordinate
|
|
|
|
|
|
def shuffle_dungeons(world: "EarthBoundWorld") -> None:
|
|
# Is the dept. store a dungeon
|
|
single_exit_dungeons = [
|
|
"Giant Step",
|
|
"Happy-Happy HQ",
|
|
"Lilliput Steps",
|
|
"Milky Well",
|
|
"Gold Mine",
|
|
"Moonside",
|
|
"Monotoli Building",
|
|
"Magnet Hill",
|
|
"Pink Cloud",
|
|
"Dungeon Man",
|
|
"Stonehenge Base",
|
|
"Lumine Hall",
|
|
"Fire Spring",
|
|
"Sea of Eden"
|
|
]
|
|
|
|
double_exit_dungeons = [
|
|
"Arcade",
|
|
"Brickroad Maze",
|
|
"Rainy Circle",
|
|
"Belch's Factory",
|
|
"Pyramid"
|
|
]
|
|
|
|
world.dungeon_connections = {
|
|
"Arcade": "Arcade",
|
|
"Giant Step": "Giant Step",
|
|
"Happy-Happy HQ": "Happy-Happy HQ",
|
|
"Lilliput Steps": "Lilliput Steps",
|
|
"Belch's Factory": "Belch's Factory",
|
|
"Milky Well": "Milky Well",
|
|
"Gold Mine": "Gold Mine",
|
|
"Moonside": "Moonside",
|
|
"Monotoli Building": "Monotoli Building",
|
|
"Magnet Hill": "Magnet Hill",
|
|
"Pink Cloud": "Pink Cloud",
|
|
"Dungeon Man": "Dungeon Man",
|
|
"Stonehenge Base": "Stonehenge Base",
|
|
"Brickroad Maze": "Brickroad Maze",
|
|
"Rainy Circle": "Rainy Circle",
|
|
"Pyramid": "Pyramid",
|
|
"Lumine Hall": "Lumine Hall",
|
|
"Fire Spring": "Fire Spring",
|
|
"Sea of Eden": "Sea of Eden"
|
|
}
|
|
if world.options.magicant_mode:
|
|
# Don't shuffle Magicant when it's important
|
|
single_exit_dungeons.remove("Sea of Eden")
|
|
|
|
shuffled_single_dungeons = single_exit_dungeons.copy()
|
|
shuffled_double_dungeons = double_exit_dungeons.copy()
|
|
|
|
if world.options.dungeon_shuffle:
|
|
world.random.shuffle(shuffled_single_dungeons)
|
|
world.random.shuffle(shuffled_double_dungeons)
|
|
|
|
for index, entrance in enumerate(single_exit_dungeons):
|
|
world.dungeon_connections[entrance] = shuffled_single_dungeons[index]
|
|
|
|
for index, entrance in enumerate(double_exit_dungeons):
|
|
world.dungeon_connections[entrance] = shuffled_double_dungeons[index]
|
|
|
|
|
|
def write_dungeon_entrances(world: "EarthBoundWorld", rom: "LocalRom") -> None:
|
|
dungeon_entrances = {
|
|
"Arcade": ["Arcade Entrance", "Arcade Exit", "Arcade Back Exit", "Arcade Back Entrance"],
|
|
"Giant Step": ["Giant Step Entrance", "Giant Step Exit"],
|
|
"Happy-Happy HQ": ["Happy-Happy HQ Entrance", "Happy-Happy HQ Exit"],
|
|
"Lilliput Steps": ["Lilliput Steps Entrance", "Lilliput Steps Exit"],
|
|
"Belch's Factory": ["Factory Script Warp", "Factory Exit", "Factory Back Exit", "Factory Back Entrance"],
|
|
"Milky Well": ["Milky Well Entrance", "Milky Well Exit"],
|
|
"Gold Mine": ["Mine Entrance", "Mine Exit"],
|
|
"Monotoli Building": ["Monotoli Entrance", "Monotoli Exit"],
|
|
"Moonside": ["Cafe Entrance", "Cafe Exit"],
|
|
"Brickroad Maze": ["Maze Entrance", "Maze Exit", "Maze Back Exit", "Maze Back Entrance"],
|
|
"Rainy Circle": ["Rainy Entrance", "Rainy Exit", "Rainy Back Exit", "Rainy Back Entrance"],
|
|
"Magnet Hill": ["Sewer Entrance", "Sewer Exit"],
|
|
"Pink Cloud": ["Pink Cloud Entrance", "Pink Cloud Exit"],
|
|
"Pyramid": ["Pyramid Entrance", "Pyramid Exit", "Pyramid Back Exit", "Pyramid Back Entrance"],
|
|
"Dungeon Man": ["D.M. Entrance Script", "D.M. Exit Script"],
|
|
"Stonehenge Base": ["Stonehenge Entrance", "Stonehenge Exit"],
|
|
"Lumine Hall": ["Lumine Entrance", "Lumine Exit"],
|
|
"Fire Spring": ["Fire Spring Entrance", "Fire Spring Exit"],
|
|
"Sea of Eden": ["Sea Entrance Script", "Sea Exit Script"]
|
|
}
|
|
|
|
all_dungeon_doors = {
|
|
"Arcade Entrance": EBDungeonDoor(0x0F00CC, 0x321000, 7),
|
|
"Arcade Exit": EBDungeonDoor(0x0F029A, 0x321010, 5),
|
|
"Arcade Back Exit": EBDungeonDoor(0x0F026E, 0x321020, 1),
|
|
"Arcade Back Entrance": EBDungeonDoor(0x0F00C1, 0x321030, 5),
|
|
"Giant Step Entrance": EBDungeonDoor(0x0F0032, 0x321040, 7),
|
|
"Giant Step Exit": EBDungeonDoor(0x0F04B5, 0x321050, 5),
|
|
"Happy-Happy HQ Entrance": EBDungeonDoor(0x0F09E9, 0x321060, 7),
|
|
"Happy-Happy HQ Exit": EBDungeonDoor(0x0F0A99, 0x321070, 5),
|
|
"Lilliput Steps Entrance": EBDungeonDoor(0x0F09F4, 0x321080, 3),
|
|
"Lilliput Steps Exit": EBDungeonDoor(0x0F0B1A, 0x321090, 7),
|
|
"Factory Entrance": EBDungeonDoor(0x0F1277, 0x3210A0, 5),
|
|
"Factory Script Warp": EBDungeonDoor(0x15EECB, 0x3210B0, 5, True),
|
|
"Factory Exit": EBDungeonDoor(0x0F1159, 0x3210C0, 5),
|
|
"Factory Back Exit": EBDungeonDoor(0x0F11BC, 0x3210D0, 5),
|
|
"Factory Back Entrance": EBDungeonDoor(0x0F11FE, 0x3210E0, 3),
|
|
"Milky Well Entrance": EBDungeonDoor(0x0F12E9, 0x3210F0, 3),
|
|
"Milky Well Exit": EBDungeonDoor(0x0F11E8, 0x321100, 5),
|
|
"Mine Entrance": EBDungeonDoor(0x0F1378, 0x321110, 7),
|
|
"Mine Exit": EBDungeonDoor(0x0F1400, 0x321120, 5),
|
|
"Cafe Entrance": EBDungeonDoor(0x0F165D, 0x321150, 3),
|
|
"Cafe Exit": EBDungeonDoor(0x0F1A25, 0x321160, 7),
|
|
"Monotoli Entrance": EBDungeonDoor(0x0F1928, 0x321170, 7),
|
|
"Monotoli Exit": EBDungeonDoor(0x0F1862, 0x321180, 3),
|
|
"Maze Entrance": EBDungeonDoor(0x0F0EB6, 0x321190, 3),
|
|
"Maze Exit": EBDungeonDoor(0x0F0FD8, 0x3211A0, 5),
|
|
"Maze Back Exit": EBDungeonDoor(0x0F0FE3, 0x3211B0, 5),
|
|
"Maze Back Entrance": EBDungeonDoor(0x0F0EC1, 0x3211C0, 7),
|
|
"Rainy Entrance": EBDungeonDoor(0x0F0ED7, 0x3211D0, 7),
|
|
"Rainy Exit": EBDungeonDoor(0x0F1030, 0x3211E0, 5),
|
|
"Rainy Back Exit": EBDungeonDoor(0x0F0FEE, 0x3211F0, 5),
|
|
"Rainy Back Entrance": EBDungeonDoor(0x0F0EAB, 0x321200, 3),
|
|
"Sewer Entrance": EBDungeonDoor(0x0F1A3B, 0x321210, 5),
|
|
"Sewer Exit": EBDungeonDoor(0x0F1A9E, 0x321220, 1),
|
|
"Pink Cloud Entrance": EBDungeonDoor(0x0F1E32, 0x321230, 7),
|
|
"Pink Cloud Exit": EBDungeonDoor(0x0F1EAB, 0x321240, 5),
|
|
"Pyramid Entrance": EBDungeonDoor(0x0F1F3A, 0x321250, 3),
|
|
"Pyramid Exit": EBDungeonDoor(0x0F1FA9, 0x321260, 5),
|
|
"Pyramid Back Exit": EBDungeonDoor(0x0F20E8, 0x321290, 5),
|
|
"Pyramid Back Entrance": EBDungeonDoor(0x0F1F45, 0x3212A0, 3),
|
|
"D.M. Entrance Script": EBDungeonDoor(0x15F0A3, 0x321270, 7, True),
|
|
"D.M. Exit Script": EBDungeonDoor(0x15F0CB, 0x321280, 5, True),
|
|
"Stonehenge Entrance": EBDungeonDoor(0x0F105C, 0x3212B0, 7),
|
|
"Stonehenge Exit": EBDungeonDoor(0x0F1072, 0x3212C0, 3),
|
|
"Lumine Entrance": EBDungeonDoor(0x0F239C, 0x3212D0, 7),
|
|
"Lumine Exit": EBDungeonDoor(0x0F2318, 0x3212E0, 3),
|
|
"Fire Spring Entrance": EBDungeonDoor(0x0F23D4, 0x3212F0, 3),
|
|
"Fire Spring Exit": EBDungeonDoor(0x0F2437, 0x321300, 5),
|
|
"Sea Entrance Script": EBDungeonDoor(0x15F25B, 0x321310, 3, True),
|
|
"Sea Exit Script": EBDungeonDoor(0x15ECEB, 0x321320, 5, True),
|
|
"Post-Nightmare Script": EBDungeonDoor(0x15ED4B, 0x321330, 5, True),
|
|
"Carpainter Failure Script": EBDungeonDoor(0x15EEF3, 0x321340, 7, True)
|
|
}
|
|
|
|
paired_doors = {}
|
|
|
|
for door in all_dungeon_doors:
|
|
rom.copy_bytes(all_dungeon_doors[door].address, 6, all_dungeon_doors[door].copyaddress) # Copy 6 bytes at the source of the door to the destination of the door
|
|
|
|
for door in world.dungeon_connections:
|
|
for index, entrance in enumerate(dungeon_entrances[door]):
|
|
if "Exit" in entrance:
|
|
paired_doors[dungeon_entrances[world.dungeon_connections[door]][index]] = entrance
|
|
else:
|
|
paired_doors[entrance] = dungeon_entrances[world.dungeon_connections[door]][index]
|
|
|
|
paired_doors["Post-Nightmare Script"] = paired_doors["Sea Exit Script"]
|
|
paired_doors["Carpainter Failure Script"] = paired_doors["Happy-Happy HQ Exit"]
|
|
|
|
for door in paired_doors:
|
|
destination = all_dungeon_doors[paired_doors[door]]
|
|
source = all_dungeon_doors[door]
|
|
if source.is_script:
|
|
if destination.is_script:
|
|
rom.copy_bytes(destination.copyaddress, 6, source.address)
|
|
else:
|
|
rom.copy_bytes(destination.copyaddress + 2, 2, source.address)
|
|
rom.copy_bytes(destination.copyaddress, 2, source.address + 2)
|
|
rom.write_bytes(source.address + 4, bytearray([destination.direction]))
|
|
rom.copy_bytes(destination.copyaddress + 4, 1, source.address + 5)
|
|
else:
|
|
if destination.is_script:
|
|
rom.copy_bytes(destination.copyaddress + 2, 2, source.address)
|
|
rom.copy_bytes(destination.copyaddress, 2, source.address + 2)
|
|
rom.copy_bytes(destination.copyaddress + 5, 1, source.address + 4)
|
|
else:
|
|
rom.copy_bytes(destination.copyaddress, 5, source.address)
|
|
|
|
rom.write_bytes(0x101664, struct.pack("H", 0x041F)) # Flag controlling the Saturn Valley ladder
|
|
rom.write_bytes(0x0F19C7, struct.pack("I", 0xF3104C)) # Replacement for the Moonside deliveryman
|
|
rom.write_bytes(0x0F0A93, struct.pack("I", 0x000000)) # Skip Pokey walking up after HHHQ
|
|
rom.write_bytes(0x086DFC, struct.pack("H", 0x00C7)) # Flag-independent Moonside entry
|
|
rom.write_bytes(0x0F1657, struct.pack("I", 0xF311A7)) # Fourside Cafe Door Script
|
|
rom.write_bytes(0x0F165B, struct.pack("H", 0x8091)) # Lock Cafe
|
|
rom.write_bytes(0x0FC8C6, struct.pack("I", 0xF3120B)) # Everdred script
|
|
rom.write_bytes(0x10784A, struct.pack("H", 0x0000)) # Mook spawn in stonehenge anteroom
|
|
rom.write_bytes(0x0FC51E, bytearray([0x00])) # Moonside sparkle always active
|
|
|
|
rom.write_bytes(0x0FA4D6, bytearray([0xC7, 0x00, 0x01]))
|
|
|
|
moonside_reward = world.multiworld.get_location("Fourside - Post-Moonside Delivery", world.player).item
|
|
if (moonside_reward.player != world.player) or world.options.remote_items or moonside_reward.name not in item_id_table:
|
|
rom.write_bytes(0x3310F7, struct.pack("I", 0xF310FB))
|