mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-07 07:03:44 -08:00
* 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>
366 lines
16 KiB
Python
366 lines
16 KiB
Python
from ..game_data.local_data import item_id_table, character_item_table, party_id_nums
|
||
from ..game_data.text_data import text_encoder
|
||
from ..game_data.static_location_data import location_groups
|
||
from ..modules.shopsanity import shop_locations
|
||
from ..Options import ShopRandomizer, MagicantMode
|
||
import struct
|
||
from BaseClasses import Location
|
||
from typing import TYPE_CHECKING
|
||
if TYPE_CHECKING:
|
||
from .. import EarthBoundWorld
|
||
from ..Rom import LocalRom
|
||
|
||
def setup_hints(world: "EarthBoundWorld") -> None:
|
||
hint_types = [
|
||
# gives a hint for a specific out of the way location in this player's world, regardless of what item it is
|
||
"item_at_location",
|
||
"region_progression_check", # woth or foolish hint, checks specific location groups of this world so as to be more helpful.
|
||
"hint_for_good_item", # gives the exact location and sender of a good item for the local player
|
||
"item_in_local_region", # Hints a random item that can be found in a specific local location group
|
||
"prog_item_at_region", # Hints the region that a good item can be found for this player
|
||
"joke_hint", # Doesn't hint anything
|
||
"dungeon_location" # Hints what dungeon can be found at a specific entrance
|
||
]
|
||
world.in_game_hint_types = []
|
||
world.hinted_locations = {}
|
||
world.hinted_items = {}
|
||
world.hinted_regions = {}
|
||
world.hinted_dungeons = {}
|
||
|
||
# may not need to be world.
|
||
world.local_hintable_locations = [
|
||
"Onett - Mayor Pirkle",
|
||
"Onett - South Road Present",
|
||
"Onett - Meteor Item",
|
||
"Onett - Treehouse Guy",
|
||
"Twoson - Orange Kid Donation",
|
||
"Twoson - Everdred Meeting",
|
||
"Twoson - Apple Kid Invention",
|
||
"Fourside - Post-Moonside Delivery",
|
||
"Lost Underworld - Talking Rock",
|
||
"Dungeon Man - 2F Hole Present",
|
||
"Poo - Starting Item",
|
||
"Summers - Magic Cake",
|
||
"Deep Darkness - Teleporting Monkey",
|
||
"Twoson - Insignificant Location",
|
||
"Peaceful Rest Valley - North Side Present",
|
||
"Twoson - Paula's Mother",
|
||
"Happy-Happy Village - Prisoner",
|
||
"Threed - Boogey Tent Trashcan",
|
||
"Threed - Zombie Prisoner",
|
||
"Grapefruit Falls - Saturn Cave Present",
|
||
"Saturn Valley - Saturn Coffee",
|
||
"Dusty Dunes - South Side Present",
|
||
"Stonehenge - Tony Item",
|
||
"Stonehenge - Kidnapped Mr. Saturn",
|
||
"Stonehenge - Dead End Present",
|
||
"Stonehenge - Near End of the Maze Present",
|
||
"Stonehenge - Bridge Room East Balcony Present",
|
||
"Gold Mine - B1F Isolated Present",
|
||
"Fourside - Venus Gift",
|
||
"Fourside - Bakery 2F Gift",
|
||
"Fourside - Department Store Blackout",
|
||
"Monotoli Building - Monotoli Character",
|
||
"Dungeon Man - 1F Exit Ledge Present",
|
||
"Deep Darkness - Barf Character",
|
||
"Lumine Hall - B1F West Alcove Present",
|
||
"Cave of the Present - Star Master",
|
||
"Cave of the Present - Broken Phase Distorter",
|
||
"Fire Spring - 1st Cave Present",
|
||
"Tenda Village - Tenda Tea",
|
||
"Deep Darkness - Barf Character",
|
||
"Dalaam - Trial of Mu",
|
||
"Pyramid - Northwest Door Sarcophagus"
|
||
]
|
||
|
||
world.local_hintable_items = [
|
||
"Franklin Badge",
|
||
"Key to the Shack",
|
||
"Key to the Cabin",
|
||
"Key to the Tower",
|
||
"Key to the Locker",
|
||
"Bad Key Machine",
|
||
"Pencil Eraser",
|
||
"Eraser Eraser",
|
||
"UFO Engine",
|
||
"Yogurt Dispenser",
|
||
"Zombie Paper",
|
||
"King Banana",
|
||
"Signed Banana",
|
||
"Tendakraut",
|
||
"Jar of Fly Honey",
|
||
"Wad of Bills",
|
||
"Tiny Ruby",
|
||
"Diamond",
|
||
"Meteorite Piece",
|
||
"Hieroglyph Copy",
|
||
"Piggy Nose",
|
||
"Carrot Key",
|
||
"Police Badge",
|
||
"Letter For Tony",
|
||
"Mining Permit",
|
||
"Contact Lens",
|
||
"Insignificant Item",
|
||
"Pak of Bubble Gum",
|
||
"Sea Pendant",
|
||
"Shyness Book",
|
||
"Hawk Eye",
|
||
"Ness",
|
||
"Paula",
|
||
"Jeff",
|
||
"Poo",
|
||
"Onett Teleport",
|
||
"Twoson Teleport",
|
||
"Happy-Happy Village Teleport",
|
||
"Threed Teleport",
|
||
"Saturn Valley Teleport",
|
||
"Dusty Dunes Teleport",
|
||
"Fourside Teleport",
|
||
"Winters Teleport",
|
||
"Summers Teleport",
|
||
"Scaraba Teleport",
|
||
"Dalaam Teleport",
|
||
"Deep Darkness Teleport",
|
||
"Tenda Village Teleport",
|
||
"Lost Underworld Teleport"
|
||
]
|
||
|
||
world.joke_hints = [
|
||
"you can find 6 hint shops around the world.",
|
||
"if you want to hint for an item, use !hint in your text client!",
|
||
"the mouse at the Fourside Department Store knows what the Spook has.",
|
||
"my business may be a scam.",
|
||
"there's a guy near Threed's hotel who saw the Zombies take someone away.",
|
||
"you can use the Y button to run or mash through text.",
|
||
"you can submit custom window flavors in the game's Archipelago thread.",
|
||
"you can buy a ruler at the Fourside Department Store.",
|
||
"you can store a lot of items in your backpack with the R button.",
|
||
"Giygas's guard may actually be from another seed...",
|
||
"if you have multiple of something important, you can throw away the extras.",
|
||
"the chicken crossed the road because it was trying to use Teleport α.",
|
||
"Figment's Ice Palace can probably be found in Ice Palace.",
|
||
"you can talk to the hint shop owners for a hint.",
|
||
"deliveryman are bad at using keys.",
|
||
"PK scramble is a pretty good time.",
|
||
"Apple Kid researched the Power of the Earth with Dr. Andonuts.",
|
||
"you can find beta Archipelago games on the Archipelago discord.",
|
||
"you can randomize EarthBound with Archipelago.",
|
||
"hint prices double with each one bought.",
|
||
"you probably should have kept your money.",
|
||
"there's a secret option to plando Lumine Hall's text.",
|
||
"this isn't a very good hint.",
|
||
"some hints are good hints.\n@But not this one."
|
||
]
|
||
|
||
hintable_location_groups = location_groups.copy()
|
||
|
||
if world.options.shop_randomizer != ShopRandomizer.option_shopsanity:
|
||
hintable_location_groups["Onett"] = hintable_location_groups["Onett"] - shop_locations
|
||
hintable_location_groups["Twoson"] = hintable_location_groups["Twoson"] - shop_locations
|
||
hintable_location_groups["Happy-Happy Village"] = hintable_location_groups["Happy-Happy Village"] - shop_locations
|
||
hintable_location_groups["Threed"] = hintable_location_groups["Threed"] - shop_locations
|
||
hintable_location_groups["Grapefruit Falls"] = hintable_location_groups["Grapefruit Falls"] - shop_locations
|
||
hintable_location_groups["Saturn Valley"] = hintable_location_groups["Saturn Valley"] - shop_locations
|
||
hintable_location_groups["Dusty Dunes Desert"] = hintable_location_groups["Dusty Dunes Desert"] - shop_locations
|
||
hintable_location_groups["Winters"] = hintable_location_groups["Winters"] - shop_locations
|
||
hintable_location_groups["Dr. Andonuts's Lab"] = hintable_location_groups["Dr. Andonuts's Lab"] - shop_locations
|
||
hintable_location_groups["Fourside"] = hintable_location_groups["Fourside"] - shop_locations
|
||
hintable_location_groups["Moonside"] = hintable_location_groups["Moonside"] - shop_locations
|
||
hintable_location_groups["Summers"] = hintable_location_groups["Summers"] - shop_locations
|
||
hintable_location_groups["Dalaam"] = hintable_location_groups["Dalaam"] - shop_locations
|
||
hintable_location_groups["Scaraba"] = hintable_location_groups["Scaraba"] - shop_locations
|
||
hintable_location_groups["Deep Darkness"] = hintable_location_groups["Deep Darkness"] - shop_locations
|
||
hintable_location_groups["Lost Underworld"] = hintable_location_groups["Lost Underworld"] - shop_locations
|
||
hintable_location_groups["Magicant"] = hintable_location_groups["Magicant"] - shop_locations
|
||
|
||
del hintable_location_groups["Burglin Park"]
|
||
del hintable_location_groups["the Scaraba Bazaar"]
|
||
del hintable_location_groups["the Twoson Department Store"]
|
||
del hintable_location_groups["the Fourside Department Store"]
|
||
del hintable_location_groups["the Saturn Valley Shop"]
|
||
|
||
if world.options.magicant_mode >= 2:
|
||
del hintable_location_groups["Magicant"]
|
||
|
||
if not world.options.giygas_required:
|
||
del hintable_location_groups["Cave of the Past"]
|
||
|
||
if world.options.magicant_mode.value in [0, 3]:
|
||
world.local_hintable_items.append("Magicant Teleport")
|
||
|
||
for item in world.multiworld.precollected_items[world.player]:
|
||
if item.name in world.local_hintable_items:
|
||
world.local_hintable_items.remove(item.name)
|
||
|
||
for item in world.options.start_hints.value:
|
||
if item in world.local_hintable_items:
|
||
world.local_hintable_items.remove(item)
|
||
|
||
if world.starting_area_teleport in world.local_hintable_items:
|
||
world.local_hintable_items.remove(world.starting_area_teleport)
|
||
|
||
if world.local_hintable_items == []:
|
||
hint_types.remove("hint_for_good_item")
|
||
hint_types.remove("prog_item_at_region")
|
||
|
||
if not world.options.dungeon_shuffle:
|
||
hint_types.remove("dungeon_location")
|
||
|
||
if world.options.giygas_required:
|
||
world.local_hintable_locations.append("Cave of the Past - Present")
|
||
|
||
if world.options.magicant_mode == MagicantMode.option_psi_location:
|
||
world.local_hintable_locations.append("Magicant - Ness's Nightmare")
|
||
|
||
for i in range(6):
|
||
world.in_game_hint_types.append(world.random.choice(hint_types))
|
||
|
||
for index, hint in enumerate(world.in_game_hint_types):
|
||
if hint == "item_at_location":
|
||
location = world.random.choice(world.local_hintable_locations)
|
||
world.hinted_locations[index] = location
|
||
|
||
elif hint == "region_progression_check":
|
||
group, group_locs = world.random.choice(list(hintable_location_groups.items()))
|
||
world.hinted_regions[index] = group
|
||
|
||
elif hint == "hint_for_good_item" or hint == "prog_item_at_region":
|
||
item = world.random.choice(world.local_hintable_items)
|
||
world.hinted_items[index] = item
|
||
|
||
elif hint == "item_in_local_region":
|
||
group, group_locs = world.random.choice(list(hintable_location_groups.items()))
|
||
location = world.random.choice(sorted(group_locs))
|
||
world.hinted_regions[index] = group
|
||
world.hinted_locations[index] = location
|
||
|
||
elif hint == "dungeon_location":
|
||
dungeon = world.random.choice(list(world.dungeon_connections.keys()))
|
||
world.hinted_dungeons[index] = dungeon
|
||
|
||
|
||
def parse_hint_data(world: "EarthBoundWorld", location: Location, rom: "LocalRom", hint: str, index: int) -> None:
|
||
if hint == "item_at_location":
|
||
if world.player == location.item.player and location.item.name in character_item_table and location.item.name != "Photograph":
|
||
player_text = "your friend "
|
||
item_text = bytearray([0x1C, 0x02, party_id_nums[location.item.name]]) # In-game text command to display party member names
|
||
elif world.player == location.item.player:
|
||
player_text = "your "
|
||
if location.item.name in item_id_table:
|
||
item_text = bytearray([0x1C, 0x05, item_id_table[location.item.name]]) # In-game text command to display item names
|
||
else:
|
||
# if the item doesn't have a name (e.g it's PSI)
|
||
item_text = text_encoder(location.item.name, 128)
|
||
else:
|
||
player_text = f"{world.multiworld.get_player_name(location.item.player)}'s "
|
||
item_text = text_encoder(location.item.name, 128)
|
||
|
||
player_text = text_encoder(player_text, 255)
|
||
location_text = text_encoder(f" can be found at\n@{location.name}.", 255)
|
||
text = player_text + item_text + location_text
|
||
# [player]'s [item] can be found at [location].
|
||
text.append(0x02)
|
||
|
||
elif hint == "region_progression_check":
|
||
if world.progression_count == 1:
|
||
text = f"you can find {world.progression_count} important item at {world.hinted_area}."
|
||
else:
|
||
text = f"you can find {world.progression_count} important items at {world.hinted_area}."
|
||
text = text_encoder(text, 255)
|
||
text.append(0x02)
|
||
|
||
elif hint == "hint_for_good_item" or hint == "prog_item_at_region" or hint == "item_in_local_region":
|
||
if location.item.name in character_item_table and location.item.player == world.player and location.item.name != "Photograph":
|
||
item_text = text_encoder("your friend ", 255)
|
||
item_text.extend([0x1C, 0x02, party_id_nums[location.item.name]])
|
||
elif location.item.name in item_id_table and location.item.player == world.player:
|
||
item_text = text_encoder("your ", 255)
|
||
item_text.extend([0x1C, 0x05, item_id_table[location.item.name]])
|
||
elif location.item.player == world.player:
|
||
item_text = text_encoder(f"your {location.item.name}", 128)
|
||
else:
|
||
item_text = f"{world.multiworld.get_player_name(location.item.player)}'s {location.item.name}"
|
||
item_text = text_encoder(item_text, 255)
|
||
item_text.extend(text_encoder(" can be found ", 255))
|
||
|
||
if location.player != world.player:
|
||
player_text = text_encoder(f"by {world.multiworld.get_player_name(location.player)}\n", 255)
|
||
else:
|
||
player_text = text_encoder("\n", 255)
|
||
|
||
if hint == "hint_for_good_item":
|
||
location_text = text_encoder(f"@at {location.name}.", 255)
|
||
# your [item] can be found by [player] at [location]
|
||
|
||
else:
|
||
location_name_groups = world.multiworld.worlds[location.player].location_name_groups
|
||
possible_location_groups = [
|
||
group_name for group_name, group_locations in location_name_groups.items()
|
||
if location.name in group_locations and group_name != "Everywhere"
|
||
]
|
||
if not possible_location_groups:
|
||
if location.parent_region.name == "Menu":
|
||
area = ""
|
||
else:
|
||
area = f" near {location.parent_region.name}"
|
||
else:
|
||
area = f" near {world.random.choice(possible_location_groups)}"
|
||
location_text = text_encoder(f"@somewhere{area}.", 255)
|
||
# your [item] can be found by [player] somewhere near [location group]
|
||
text = item_text + player_text + location_text
|
||
text.append(0x02)
|
||
|
||
elif hint == "joke_hint":
|
||
text = world.random.choice(world.joke_hints)
|
||
text = text_encoder(text, 255)
|
||
text.append(0x02)
|
||
|
||
elif hint == "dungeon_location":
|
||
dungeon = world.hinted_dungeons[index]
|
||
text = f"{dungeon} leads to {world.dungeon_connections[dungeon]}."
|
||
text = text_encoder(text, 255)
|
||
text.append(0x02)
|
||
|
||
else:
|
||
text = 0x00
|
||
|
||
hint_addresses = [
|
||
0x070376,
|
||
0x0703A8,
|
||
0x0703DA,
|
||
0x07040C,
|
||
0x07043E,
|
||
0x070470
|
||
]
|
||
|
||
scoutable_hint_addresses = [
|
||
0x2EF3D5,
|
||
0x2EF3EB,
|
||
0x2EF401,
|
||
0x2EF417,
|
||
0x2EF42D,
|
||
0x2EF443
|
||
]
|
||
rom.write_bytes(0x310000 + world.hint_pointer, text)
|
||
rom.write_bytes(hint_addresses[world.hint_number], struct.pack("I", 0xF10000 + world.hint_pointer))
|
||
|
||
if hint in ["item_at_location", "hint_for_good_item"]:
|
||
rom.write_bytes(scoutable_hint_addresses[world.hint_number], struct.pack("I", 0xEEF451))
|
||
rom.write_bytes(0x310252 + (world.hint_number * 3), bytearray([0x01]))
|
||
world.hint_man_hints.append((location.address, location.player))
|
||
else:
|
||
rom.write_bytes(scoutable_hint_addresses[world.hint_number], struct.pack("I", 0xEEF4B2))
|
||
world.hint_man_hints.append(("NULL", 0))
|
||
|
||
world.hint_pointer = world.hint_pointer + len(text)
|
||
world.hint_number += 1
|
||
|
||
# Word on the street is that PLAYER's ITEM can be found at LOCATION
|
||
# Word on the street is that REGION has X important items
|
||
# Word on the street is that your ITEM can be found by PLAYER at LOCATION
|
||
# Word on the street is that PLAYER's ITEM can be found somewhere near REGION...
|
||
# Word on the street is that your ITEM can be found somewhere near REGION...
|
||
# char item hint?
|
||
# That's all for today.
|
||
# Like text part 1, extend 0x1C 0x05 0xItem Item, extend (the rest of the string)
|