Files
dockipelago/worlds/earthbound/__init__.py
PinkSwitch 55c70a5ba8 EarthBound: Implement New Game (#5159)
* 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>
2025-12-19 14:52:27 +01:00

610 lines
26 KiB
Python

import os
import typing
import threading
import pkgutil
from typing import List, Set, Dict, TextIO, Tuple
from BaseClasses import Item, MultiWorld, Location, Tutorial, ItemClassification
from Fill import fill_restrictive
from worlds.AutoWorld import World, WebWorld
import itertools
import settings
from .Items import get_item_names_per_category, item_table
from .Locations import get_locations
from .Regions import init_areas, connect_area_exits
from .Options import EBOptions, eb_option_groups
from .setup_game import setup_gamevars, place_static_items
from .modules.enemy_data import initialize_enemies
from .modules.flavor_data import create_flavors
from .game_data.local_data import item_id_table, world_version
from .modules.hint_data import setup_hints
from .game_data.text_data import spoiler_psi, spoiler_starts, spoiler_badges
from .Client import EarthBoundClient
from .Rules import set_location_rules
from .Rom import patch_rom, EBProcPatch, valid_hashes
from .game_data.static_location_data import location_ids, location_groups
from .modules.equipamizer import EBArmor, EBWeapon
from .modules.boss_shuffle import BossData, SlotInfo
from worlds.generic.Rules import add_item_rule
from Options import OptionError
class EBSettings(settings.Group):
class RomFile(settings.SNESRomPath):
"""File name of the EarthBound US ROM"""
description = "EarthBound ROM File"
copy_to = "EarthBound.sfc"
md5s = valid_hashes
rom_file: RomFile = RomFile(RomFile.copy_to)
class EBWeb(WebWorld):
theme = "ocean"
setup_en = Tutorial(
"Multiworld Setup Guide",
"A guide to setting up the EarthBound randomizer"
"and connecting to an Archipelago server.",
"English",
"setup_en.md",
"setup/en",
["Pink Switch"]
)
tutorials = [setup_en]
option_groups = eb_option_groups
# option_presets = eb_option_presets
class EBItem(Item):
game: str = "EarthBound"
class EarthBoundWorld(World):
"""EarthBound is a contemporary-themed JRPG. Take four psychically-endowed children
across the world in search of 8 Melodies to defeat Giygas, the cosmic evil."""
game = "EarthBound"
option_definitions = EBOptions
data_version = 1
required_client_version = (0, 5, 0)
item_name_to_id = {item: data.code for item, data in item_table.items() if data.code}
location_name_to_id = location_ids
item_name_groups = get_item_names_per_category()
location_name_groups = location_groups
web = EBWeb()
settings: typing.ClassVar[EBSettings]
# topology_present = True
options_dataclass = EBOptions
options: EBOptions
locked_locations: List[str]
location_cache: List[Location]
def __init__(self, multiworld: MultiWorld, player: int):
self.rom_name_available_event = threading.Event()
super().__init__(multiworld, player)
self.locked_locations = []
self.location_cache = []
self.event_count = 8
self.progressive_filler_bats: int = 0
self.progressive_filler_pans: int = 0
self.progressive_filler_guns: int = 0
self.progressive_filler_bracelets: int = 0
self.progressive_filler_other: int = 0
self.world_version: str = world_version
self.armor_list = Dict[str, EBArmor]
self.weapon_list = Dict[str, EBWeapon]
self.boss_slots = Dict[str, SlotInfo]
self.boss_info = Dict[str, BossData]
self.starting_character: str | None = None
self.locals = []
self.rom_name = None
self.starting_area_teleport = None
self.common_gear = []
self.uncommon_gear = []
self.rare_gear = []
self.get_all_spheres = threading.Event()
self.boss_list: List[str] = []
self.starting_region = str
self.start_location = int
self.dungeon_connections: dict[str, str] = {}
self.has_generated_output: bool = False
self.hint_man_hints: list[tuple[int | str, player]] = []
self.common_items = [
"Cookie",
"Bag of Fries",
"Teddy Bear",
"Hamburger",
"Boiled Egg",
"Fresh Egg",
"Picnic Lunch",
"Croissant",
"Bread Roll",
"Can of Fruit Juice",
"Royal Iced Tea",
"Protein Drink",
"Bottle of Water",
"Cold Remedy",
"Vial of Serum",
"Ketchup Packet",
"Sugar Packet",
"Tin of Cocoa",
"Carton of Cream",
"Sprig of Parsley",
"Jar of Hot Sauce",
"Salt Packet",
"Wet Towel",
"Refreshing Herb",
"Ruler",
"Protractor",
"Insecticide Spray",
"Rust Promoter",
"Stag Beetle",
"Toothbrush",
"Handbag Strap",
"Chick",
"Chicken",
"Trout Yogurt",
"Banana",
"Calorie Stick",
"Gelato de Resort",
"Snake",
"Cup of Noodles",
"Cup of Coffee",
"Double Burger",
"Bean Croquette",
"Molokheiya Soup",
"Plain Roll",
"Magic Tart",
"PSI Caramel",
"Popsicle",
"Bottle Rocket"
]
self.common_gear = [
"Yo-yo",
"Slingshot",
"Travel Charm",
"Great Charm",
"Ribbon",
"Red Ribbon"
]
self.uncommon_items = [
"Pasta di Summers",
"Pizza",
"Chef's Special",
"Super Plush Bear",
"Jar of Delisauce",
"Secret Herb",
"Xterminator Spray",
"Snake Bag",
"Bomb",
"Rust Promoter DX",
"Pair of Dirty Socks",
"Mummy Wrap",
"Pharaoh's Curse",
"Sudden Guts Pill",
"Picture Postcard",
"Viper",
"Repel Sandwich",
"Lucky Sandwich",
"Peanut Cheese Bar",
"Bowl of Rice Gruel",
"Kabob",
"Plain Yogurt",
"Beef Jerky",
"Mammoth Burger",
"Bottle of DXwater",
"Magic Pudding",
"Big Bottle Rocket",
"Bazooka",
"Meteornium"
]
self.uncommon_gear = [
"Trick Yo-yo",
"Bionic Slingshot",
"Crystal Charm",
"Defense Ribbon",
"Earth Pendant",
"Flame Pendant",
"Rain Pendant",
"Night Pendant"
]
self.rare_items = [
"Large Pizza",
"Magic Truffle",
"Brain Food Lunch",
"Rock Candy",
"Kraken Soup",
"IQ Capsule",
"Guts Capsule",
"Speed Capsule",
"Vital Capsule",
"Luck Capsule",
"Horn of Life",
"Multi Bottle Rocket",
"Super Bomb",
"Bag of Dragonite",
"Meteotite",
"Repel Superwich",
"Piggy Jelly",
"Spicy Jerky",
"Luxury Jerky",
"Cup of Lifenoodles"
]
self.rare_gear = [
"Combat Yo-yo",
"Sword of Kings",
"Sea Pendant",
"Star Pendant",
"Goddess Ribbon"
]
self.money = [
"$10",
"$100",
"$1000"
]
def generate_early(self) -> None: # Todo: place locked items in generate_early
self.starting_character = self.options.starting_character.current_key.capitalize()
self.locals = []
local_space_count = 0
max_counts = {
"Ness": 12,
"Paula": 11,
"Jeff": 9,
"Poo": 12
}
max_count = max_counts[self.starting_character]
for item_name, amount in itertools.chain(self.options.start_inventory.items(), self.options.start_inventory_from_pool.items()):
if item_name in item_id_table:
local_space_count += amount
if local_space_count > max_count and not self.options.remote_items:
player = self.multiworld.get_player_name(self.player)
raise OptionError(f"{player}: starting inventory cannot place more than {max_count} items into 'Goods' for {self.starting_character}. Attempted to place {local_space_count} Goods items.")
setup_gamevars(self)
create_flavors(self)
initialize_enemies(self)
if not self.options.character_shuffle:
self.options.local_items.value.update(["Paula", "Jeff", "Poo", "Flying Man"])
self.event_count += 6
if self.options.local_teleports:
self.options.local_items.value |= self.item_name_groups["PSI"]
def create_regions(self) -> None:
init_areas(self, get_locations(self))
connect_area_exits(self)
place_static_items(self)
def create_items(self) -> None:
pool = self.get_item_pool(self.get_excluded_items())
self.fill_item_pool(pool)
self.multiworld.itempool += pool
def set_rules(self) -> None:
set_location_rules(self)
self.multiworld.completion_condition[self.player] = lambda state: state.has('Saved Earth', self.player)
def pre_fill(self) -> None:
prefill_locations = []
prefill_items = []
if not self.options.character_shuffle:
main_characters = ["Ness", "Paula", "Jeff", "Poo"]
for character in main_characters:
if character != self.starting_character:
prefill_items.append(self.create_item(character))
prefill_items.extend([
self.create_item("Flying Man"),
self.create_item("Teddy Bear"),
self.create_item("Super Plush Bear")
])
prefill_locations.extend([
self.multiworld.get_location("Happy-Happy Village - Prisoner", self.player),
self.multiworld.get_location("Threed - Zombie Prisoner", self.player),
self.multiworld.get_location("Snow Wood - Bedroom", self.player),
self.multiworld.get_location("Monotoli Building - Monotoli Character", self.player),
self.multiworld.get_location("Dalaam - Throne Character", self.player),
self.multiworld.get_location("Deep Darkness - Barf Character", self.player),
])
self.random.shuffle(prefill_locations)
add_item_rule(self.multiworld.get_location("Happy-Happy Village - Prisoner", self.player), lambda item: item.name in self.item_name_groups["Characters"])
add_item_rule(self.multiworld.get_location("Threed - Zombie Prisoner", self.player), lambda item: item.name in self.item_name_groups["Characters"])
add_item_rule(self.multiworld.get_location("Snow Wood - Bedroom", self.player), lambda item: item.name in self.item_name_groups["Characters"])
add_item_rule(self.multiworld.get_location("Monotoli Building - Monotoli Character", self.player), lambda item: item.name in self.item_name_groups["Characters"])
add_item_rule(self.multiworld.get_location("Dalaam - Throne Character", self.player), lambda item: item.name in self.item_name_groups["Characters"])
add_item_rule(self.multiworld.get_location("Deep Darkness - Barf Character", self.player), lambda item: item.name in self.item_name_groups["Characters"])
fill_restrictive(self.multiworld, self.multiworld.get_all_state(False, collect_pre_fill_items=False), prefill_locations, prefill_items, True, True)
setup_hints(self)
def get_pre_fill_items(self) -> list[Item]:
characters = ["Ness", "Paula", "Jeff", "Poo"]
prefill_items = []
for character in characters:
if character != self.starting_character:
prefill_items.append(self.create_item(f"{character}"))
return prefill_items
@classmethod
def stage_generate_output(cls, multiworld: MultiWorld, output_directory: str) -> None:
try:
multiworld.earthbound_locations_by_sphere = list(multiworld.get_spheres())
except Exception:
raise
finally:
for world in multiworld.get_game_worlds("EarthBound"):
world.get_all_spheres.set()
def generate_output(self, output_directory: str) -> None:
self.has_generated_output = True # Make sure data defined in generate output doesn't get added to spoiler only mode
try:
patch = EBProcPatch(player=self.player, player_name=self.multiworld.player_name[self.player])
patch.write_file("earthbound_basepatch.bsdiff4", pkgutil.get_data(__name__, "src/earthbound_basepatch.bsdiff4"))
patch_rom(self, patch, self.player)
self.rom_name = patch.name
patch.write(os.path.join(output_directory,
f"{self.multiworld.get_out_file_name_base(self.player)}{patch.patch_file_ending}"))
except Exception:
raise
finally:
self.rom_name_available_event.set() # make sure threading continues and errors are collected
def extend_hint_information(self, hint_data: Dict[int, Dict[int, str]]) -> None:
if self.options.dungeon_shuffle:
dungeon_entrances = {}
dungeon_mapping = {}
for dungeon in self.dungeon_connections:
dungeon_entrances[self.dungeon_connections[dungeon]] = dungeon
for dungeon in dungeon_entrances:
for location in self.get_region(dungeon).locations:
if location.address:
dungeon_mapping[location.address] = dungeon_entrances[dungeon]
hint_data[self.player] = dungeon_mapping
def fill_slot_data(self) -> Dict[str, typing.Any]:
return {
"starting_area": self.start_location,
"pizza_logic": self.options.monkey_caves_mode.value,
"free_sancs": self.options.no_free_sanctuaries.value,
"shopsanity": self.options.shop_randomizer.value,
"hint_man_hints": self.hint_man_hints
}
def modify_multidata(self, multidata: dict) -> None:
import base64
# wait for self.rom_name to be available.
self.rom_name_available_event.wait()
rom_name = getattr(self, "rom_name", None)
if rom_name:
new_name = base64.b64encode(bytes(self.rom_name)).decode()
multidata["connect_names"][new_name] = multidata["connect_names"][self.multiworld.player_name[self.player]]
def write_spoiler_header(self, spoiler_handle: TextIO) -> None:
spoiler_handle.write(f"\nStarting Location: {spoiler_starts[self.start_location]}\n")
spoiler_handle.write(f"Franklin Badge Protection: {spoiler_badges[self.franklin_protection]}\n")
if self.options.psi_shuffle:
spoiler_handle.write("\nPSI Shuffle:\n")
spoiler_handle.write(f" Favorite Thing PSI Slot: {spoiler_psi[self.offensive_psi_slots[0]]}\n")
spoiler_handle.write(f" Ness Offensive PSI Middle Slot: {spoiler_psi[self.offensive_psi_slots[1]]}\n")
spoiler_handle.write(f" Paula Offensive PSI Top Slot: {spoiler_psi[self.offensive_psi_slots[2]]}\n")
spoiler_handle.write(f" Paula/Poo Offensive PSI Middle Slot: {spoiler_psi[self.offensive_psi_slots[3]]}\n")
spoiler_handle.write(f" Paula/Poo Offensive PSI Bottom Slot: {spoiler_psi[self.offensive_psi_slots[4]]}\n")
spoiler_handle.write(f" Poo Progressive PSI Slot: {spoiler_psi[self.offensive_psi_slots[5]]}\n")
spoiler_handle.write(f" Ness/Poo Shield Slot: {spoiler_psi[self.shield_slots[0]]}\n")
spoiler_handle.write(f" Paula Shield Slot: {spoiler_psi[self.shield_slots[1]]}\n")
spoiler_handle.write(f" Ness Assist PSI Middle Slot: {spoiler_psi[self.assist_psi_slots[0]]}\n")
spoiler_handle.write(f" Ness Assist PSI Bottom Slot: {spoiler_psi[self.assist_psi_slots[1]]}\n")
spoiler_handle.write(f" Paula Assist PSI Middle Slot: {spoiler_psi[self.assist_psi_slots[2]]}\n")
spoiler_handle.write(f" Paula Assist PSI Bottom Slot: {spoiler_psi[self.assist_psi_slots[3]]}\n")
spoiler_handle.write(f" Poo Assist PSI Slot: {spoiler_psi[self.assist_psi_slots[4]]}\n")
if self.options.psi_shuffle == 2:
spoiler_handle.write(f" Bomb/Bazooka Slot: {spoiler_psi[self.jeff_offense_items[0]]}\n")
spoiler_handle.write(f" Bottle Rocket Slot: {spoiler_psi[self.jeff_offense_items[1]]}\n")
spoiler_handle.write(f" Spray Can Slot: {spoiler_psi[self.jeff_assist_items[0]]}\n")
spoiler_handle.write(f" Multi-Level Gadget Slot 1: {spoiler_psi[self.jeff_assist_items[1]]}\n")
spoiler_handle.write(f" Single-Level Gadget Slot 1: {spoiler_psi[self.jeff_assist_items[2]]}\n")
spoiler_handle.write(f" Single-Level Gadget Slot 2: {spoiler_psi[self.jeff_assist_items[3]]}\n")
spoiler_handle.write(f" Multi-Level Gadget Slot 2: {spoiler_psi[self.jeff_assist_items[4]]}\n")
if self.options.boss_shuffle:
spoiler_handle.write("\nBoss Randomization:\n" +
f" Frank => {self.boss_list[0]}\n" +
f" Frankystein Mark II => {self.boss_list[1]}\n" +
f" Titanic Ant => {self.boss_list[2]}\n" +
f" Captain Strong => {self.boss_list[3]}\n" +
f" Everdred => {self.boss_list[4]}\n" +
f" Mr. Carpainter => {self.boss_list[5]}\n" +
f" Mondo Mole => {self.boss_list[6]}\n" +
f" Boogey Tent => {self.boss_list[7]}\n" +
f" Mini Barf => {self.boss_list[8]}\n" +
f" Master Belch => {self.boss_list[9]}\n" +
f" Trillionage Sprout => {self.boss_list[10]}\n" +
f" Guardian Digger => {self.boss_list[11]}\n" +
f" Dept. Store Spook => {self.boss_list[12]}\n" +
f" Evil Mani-Mani => {self.boss_list[13]}\n" +
f" Clumsy Robot => {self.boss_list[14]}\n" +
f" Shrooom! => {self.boss_list[15]}\n" +
f" Plague Rat of Doom => {self.boss_list[16]}\n" +
f" Thunder and Storm => {self.boss_list[17]}\n" +
f" Kraken => {self.boss_list[18]}\n" +
f" Guardian General => {self.boss_list[19]}\n" +
f" Master Barf => {self.boss_list[20]}\n" +
f" Starman Deluxe => {self.boss_list[21]}\n" +
f" Electro Specter => {self.boss_list[22]}\n" +
f" Carbon Dog => {self.boss_list[23]}\n" +
f" Ness's Nightmare => {self.boss_list[24]}\n" +
f" Heavily Armed Pokey => {self.boss_list[25]}\n" +
f" Starman Junior => {self.boss_list[26]}\n" +
f" Diamond Dog => {self.boss_list[27]}\n" +
f" Giygas (Phase 2) => {self.boss_list[28]}\n")
if self.options.dungeon_shuffle:
spoiler_handle.write("\nDungeon Entrances:\n")
for dungeon in self.dungeon_connections:
spoiler_handle.write(
f" {dungeon} => {self.dungeon_connections[dungeon]}\n"
)
if self.has_generated_output:
spoiler_handle.write("\nArea Levels:\n")
spoiler_excluded_areas = ["Ness's Mind", "Global ATM Access", "Common Condiment Shop"]
for area in self.area_levels:
if area not in spoiler_excluded_areas:
spoiler_handle.write(f" {area}: Level {self.area_levels[area]}\n")
def create_item(self, name: str) -> EBItem:
data = item_table[name]
return EBItem(name, data.classification, data.code, self.player)
def get_filler_item_name(self) -> str: # Todo: make this suck less
weights = {"rare": self.options.rare_filler_weight.value, "uncommon": self.options.uncommon_filler_weight.value, "common": self.options.common_filler_weight.value,
"rare_gear": int(self.options.rare_filler_weight.value * 0.5), "uncommon_gear": int(self.options.uncommon_filler_weight.value * 0.5),
"common_gear": int(self.options.common_filler_weight.value * 0.5), "money": self.options.money_weight.value}
filler_type = self.random.choices(list(weights), weights=list(weights.values()), k=1)[0]
weight_table = {
"common": self.common_items,
"common_gear": self.common_gear,
"uncommon": self.uncommon_items,
"uncommon_gear": self.uncommon_gear,
"rare": self.rare_items,
"rare_gear": self.rare_gear,
"money": self.money
}
return self.random.choice(weight_table[filler_type])
def get_excluded_items(self) -> Set[str]:
excluded_items: Set[str] = set()
excluded_items.add(self.starting_character)
starting_area_to_teleport = ["Onett Teleport", "Onett Teleport", "Twoson Teleport", "Happy-Happy Village Teleport",
"Threed Teleport", "Saturn Valley Teleport", "Fourside Teleport", "Winters Teleport",
"Summers Teleport", "Dalaam Teleport", "Scaraba Teleport", "Deep Darkness Teleport",
"Tenda Village Teleport", "Lost Underworld Teleport", "Magicant Teleport"]
self.starting_area_teleport = starting_area_to_teleport[self.start_location]
excluded_items.add(self.starting_area_teleport)
if self.options.random_start_location:
excluded_items.add(self.starting_teleport)
if self.options.magicant_mode not in [0, 3]:
excluded_items.add("Magicant Teleport")
if not self.options.character_shuffle:
excluded_items.add("Ness")
excluded_items.add("Paula")
excluded_items.add("Jeff")
excluded_items.add("Poo")
excluded_items.add("Flying Man")
if self.options.progressive_weapons:
excluded_items.add("Magicant Bat")
excluded_items.add("Legendary Bat")
excluded_items.add("Pop Gun")
excluded_items.add("Stun Gun")
excluded_items.add("Death Ray")
excluded_items.add("Moon Beam Gun")
if self.options.progressive_armor:
excluded_items.add("Platinum Band")
excluded_items.add("Diamond Band")
excluded_items.add("Pixie's Bracelet")
excluded_items.add("Cherub's Band")
excluded_items.add("Goddess Band")
excluded_items.add("Coin of Slumber")
excluded_items.add("Souvenir Coin")
excluded_items.add("Mr. Saturn Coin")
if not self.options.no_free_sanctuaries:
excluded_items.add("Tiny Key")
excluded_items.add("Tenda Lavapants")
return excluded_items
def set_classifications(self, name: str) -> Item:
data = item_table[name]
item = Item(name, data.classification, data.code, self.player)
if name == "Magicant Teleport" and self.options.magicant_mode == 3:
item.classification = ItemClassification.useful
return item
def fill_item_pool(self, pool: List[Item]) -> None:
item_to_counts = {
"Progressive Bat": self.progressive_filler_bats,
"Progressive Fry Pan": self.progressive_filler_pans,
"Progressive Gun": self.progressive_filler_guns,
"Progressive Bracelet": self.progressive_filler_bracelets,
"Progressive Other": self.progressive_filler_other
}
max_filler_counts = {
"Progressive Bat": 8,
"Progressive Fry Pan": 9,
"Progressive Gun": 6,
"Progressive Bracelet": 6,
"Progressive Other": 10
}
for _ in range(len(self.multiworld.get_unfilled_locations(self.player)) - len(pool) - self.event_count): # Change to fix event count
item = self.set_classifications(self.get_filler_item_name())
if item.name in ["Progressive Bat", "Progressive Fry Pan", "Progressive Other",
"Progressive Gun", "Progressive Bracelet"]:
item_to_counts[item.name] += 1
if item_to_counts[item.name] >= max_filler_counts[item.name]:
self.common_gear = [x for x in self.common_gear if x != item.name]
self.uncommon_gear = [x for x in self.uncommon_gear if x != item.name]
self.rare_gear = [x for x in self.rare_gear if x != item.name]
pool.append(item)
def get_item_pool(self, excluded_items: Set[str]) -> List[Item]:
pool: List[Item] = []
for name, data in item_table.items():
if name not in excluded_items:
for _ in range(data.amount):
item = self.set_classifications(name)
pool.append(item)
if self.options.progressive_weapons:
for i in range(2):
pool.append(self.set_classifications("Progressive Bat"))
for i in range(4):
pool.append(self.set_classifications("Progressive Gun"))
if self.options.progressive_armor:
for i in range(5):
pool.append(self.set_classifications("Progressive Bracelet"))
for i in range(3):
pool.append(self.set_classifications("Progressive Other"))
return pool