Files
dockipelago/worlds/pmd_eos/__init__.py
Jonathan Tinney 7971961166
Some checks failed
Analyze modified files / flake8 (push) Failing after 2m28s
Build / build-win (push) Has been cancelled
Build / build-ubuntu2204 (push) Has been cancelled
ctest / Test C++ ubuntu-latest (push) Has been cancelled
ctest / Test C++ windows-latest (push) Has been cancelled
Analyze modified files / mypy (push) Has been cancelled
Build and Publish Docker Images / Push Docker image to Docker Hub (push) Successful in 5m4s
Native Code Static Analysis / scan-build (push) Failing after 5m2s
type check / pyright (push) Successful in 1m7s
unittests / Test Python 3.11.2 ubuntu-latest (push) Failing after 16m23s
unittests / Test Python 3.12 ubuntu-latest (push) Failing after 28m19s
unittests / Test Python 3.13 ubuntu-latest (push) Failing after 14m49s
unittests / Test hosting with 3.13 on ubuntu-latest (push) Successful in 5m0s
unittests / Test Python 3.13 macos-latest (push) Has been cancelled
unittests / Test Python 3.11 windows-latest (push) Has been cancelled
unittests / Test Python 3.13 windows-latest (push) Has been cancelled
add schedule I, sonic 1/frontiers/heroes, spirit island
2026-04-02 23:46:36 -07:00

821 lines
39 KiB
Python

import math
import threading
import typing
import os
import pkgutil
import settings
import random
from typing import List, Dict, Set, Any
from .Items import (
EOSItem,
item_table,
item_frequencies,
item_table_by_id,
item_table_by_groups,
filler_item_table,
filler_item_weights,
trap_item_table,
trap_item_weights,
exclusive_filler_item_table,
exclusive_filler_item_weights,
legendary_pool_dict,
filler_items,
exclusive_filler_items,
conditional_filler_useful_items,
)
from .Locations import EOS_location_table, EOSLocation, location_Dict_by_id, expanded_EOS_location_table
from .Options import EOSOptions
from .Rules import set_rules, ready_for_late_game, has_relic_shards
from BaseClasses import Tutorial, ItemClassification, Region, Location, LocationProgressType, Item
from worlds.AutoWorld import World, WebWorld
from worlds.generic.Rules import set_rule
from .Client import EoSClient, game_version
from .Rom import EOSProcedurePatch, write_tokens
class EOSWeb(WebWorld):
theme = "ocean"
game = "Pokemon Mystery Dungeon Explorers of Sky"
tutorials = [
Tutorial(
"Multiworld Setup Guide",
"A Guide to setting up Explorers of Sky for MultiWorld.",
"English",
"setup_en.md",
"setup/en",
["CrypticMonkey33", "Chesyon"],
)
]
class EOSSettings(settings.Group):
class RomFile(settings.UserFilePath):
"""File name of the EoS EU rom"""
copy_to = "POKEDUN_SORA_C2SP01_00.nds"
description = "Explorers of Sky (EU) ROM File"
md5s = ["6735749e060e002efd88e61560e45567"]
rom_file: RomFile = RomFile(RomFile.copy_to)
rom_start: bool = True
class EOSWorld(World):
"""
This is for Pokemon Mystery Dungeon Explorers of Sky, a game where you inhabit a pokemon and explore
through dungeons, solve quests, and help out other Pokemon in the colony
"""
game = "Pokemon Mystery Dungeon Explorers of Sky"
options: EOSOptions
options_dataclass = EOSOptions
web = EOSWeb()
settings: typing.ClassVar[EOSSettings]
item_name_to_id = {item.name: item.id for item in item_table.values()}
location_name_to_id = {location.name: location.id for location in expanded_EOS_location_table}
required_client_version = (0, 6, 1)
required_server_version = (0, 6, 1)
item_name_groups = item_table_by_groups
disabled_locations: Set[str] = []
extra_locations_added = 0
mission_start_id = 1000
excluded_locations = 0
dimensional_scream_list = []
dimensional_scream_list_ints: list[int] = []
starting_se: int = 0
slot_data_ready = threading.Event
excluded_tag_amount: int = 0
def get_filler_item_name(self) -> str:
"""Called when the item pool needs to be filled with additional items to match location count."""
# logging.warning(f"World {self} is generating a filler item without custom filler pool.")
return self.multiworld.random.choice(tuple(filler_item_table.keys()))
def generate_early(self) -> None:
self.slot_data_ready = threading.Event()
if self.options.bag_on_start.value:
item_name = "Bag Upgrade"
self.multiworld.push_precollected(self.create_item(item_name))
if self.options.hero_evolution.value:
item_name = "Hero Evolution"
self.multiworld.push_precollected(self.create_item(item_name))
if self.options.recruit_evo.value:
item_name = "Recruit Evolution"
self.multiworld.push_precollected(self.create_item(item_name))
if self.options.dojo_dungeons.value > 0:
dojo_amount = self.options.dojo_dungeons.value
dojo_table = item_table_by_groups["Dojo Dungeons"]
random_open_dungeons = self.random.sample(sorted(dojo_table), k=dojo_amount)
for item_name in random_open_dungeons:
self.multiworld.push_precollected(self.create_item(item_name))
if self.options.recruit.value:
item_name = "Recruitment"
self.multiworld.push_precollected(self.create_item(item_name))
if self.options.team_form.value:
item_name = "Formation Control"
self.multiworld.push_precollected(self.create_item(item_name))
if self.options.special_episode_sanity.value and not self.options.exclude_special.value:
possible_se = [
"Bidoof's Wish",
"Igglybuff the Prodigy",
'Today\'s "Oh My Gosh"',
"Here Comes Team Charm!",
"In the Future of Darkness",
]
se_num = self.random.randint(0, 4)
self.starting_se = se_num
item_name = possible_se[se_num]
self.multiworld.push_precollected(self.create_item(item_name))
else:
self.multiworld.push_precollected(self.create_item("Main Game Unlock"))
def create_regions(self) -> None:
menu_region = Region("Menu", self.player, self.multiworld)
self.multiworld.regions.append(menu_region)
early_dungeons_region = Region("Early Dungeons", self.player, self.multiworld)
self.multiworld.regions.append(early_dungeons_region)
# early_dungeons_region2 = Region("Early Dungeons2", self.player, self.multiworld)
# self.multiworld.regions.append(early_dungeons_region2)
late_dungeons_region = Region("Late Dungeons", self.player, self.multiworld)
self.multiworld.regions.append(late_dungeons_region)
end_game_region = Region("Boss Dungeons", self.player, self.multiworld)
self.multiworld.regions.append(end_game_region)
extra_items_region = Region("Extra Items", self.player, self.multiworld)
self.multiworld.regions.append(extra_items_region)
rule_dungeons_region = Region("Rule Dungeons", self.player, self.multiworld)
self.multiworld.regions.append(rule_dungeons_region)
for location in EOS_location_table:
if location.name == "Beach Cave":
menu_region.locations.append(EOSLocation(self.player, location.name, location.id, menu_region))
if "Mission" in location.group:
for j in range(self.options.early_mission_checks.value):
location_name: str = f"{location.name} Mission {j + 1}"
location_id = location.id + self.mission_start_id + (100 * location.id) + j
menu_region.locations.append(EOSLocation(self.player, location_name, location_id, menu_region))
self.extra_locations_added += 1
for j in range(self.options.early_outlaw_checks.value):
location_name = f"{location.name} Outlaw {j + 1}"
location_id = location.id + self.mission_start_id + (100 * location.id) + j + 50
menu_region.locations.append(EOSLocation(self.player, location_name, location_id, menu_region))
self.extra_locations_added += 1
elif location.classification == "Free":
menu_region.locations.append(EOSLocation(self.player, location.name, location.id, menu_region))
elif location.name == "Team Name Location":
menu_region.locations.append(EOSLocation(self.player, location.name, location.id, menu_region))
elif location.classification == "Rank":
rank_toid_dict = {
"Bronze Rank": 1,
"Silver Rank": 2,
"Gold Rank": 3,
"Diamond Rank": 4,
"Super Rank": 5,
"Ultra Rank": 6,
"Hyper Rank": 7,
"Master Rank": 8,
"Master ★ Rank": 9,
"Master ★★ Rank": 10,
"Master ★★★ Rank": 11,
"Guildmaster Rank": 12,
}
if rank_toid_dict[location.name] > self.options.max_rank:
# self.excluded_locations += 1
# continue
early_dungeon = EOSLocation(self.player, location.name, location.id, early_dungeons_region)
early_dungeon.progress_type = LocationProgressType.EXCLUDED
self.excluded_tag_amount += 1
early_dungeons_region.locations.append(early_dungeon)
# if dialga is the goal, we can't add master star rank+
elif self.options.goal.value == 0 and rank_toid_dict[location.name] > 8:
# self.excluded_locations += 1
# continue
late_dungeon = EOSLocation(self.player, location.name, location.id, late_dungeons_region)
late_dungeon.progress_type = LocationProgressType.EXCLUDED
self.excluded_tag_amount += 1
late_dungeons_region.locations.append(late_dungeon)
elif rank_toid_dict[location.name] <= 8:
early_dungeons_region.locations.append(
EOSLocation(self.player, location.name, location.id, early_dungeons_region)
)
else:
late_dungeon = EOSLocation(self.player, location.name, location.id, late_dungeons_region)
late_dungeons_region.locations.append(late_dungeon)
elif location.classification in ["EarlyDungeonComplete", "EarlySubX"]:
early_dungeons_region.locations.append(
EOSLocation(self.player, location.name, location.id, early_dungeons_region)
)
if "Mission" in location.group:
for j in range(self.options.early_mission_checks.value):
location_name = f"{location.name} Mission {j + 1}"
location_id = location.id + self.mission_start_id + (100 * location.id) + j
early_dungeons_region.locations.append(
EOSLocation(self.player, location_name, location_id, early_dungeons_region)
)
set_rule(
self.multiworld.get_location(location_name, self.player),
lambda state, ln=location.name, p=self.player: state.has(ln, p),
)
self.extra_locations_added += 1
for j in range(self.options.early_outlaw_checks.value):
location_name = f"{location.name} Outlaw {j + 1}"
location_id = location.id + self.mission_start_id + (100 * location.id) + j + 50
early_dungeons_region.locations.append(
EOSLocation(self.player, location_name, location_id, early_dungeons_region)
)
set_rule(
self.multiworld.get_location(location_name, self.player),
lambda state, ln=location.name, p=self.player: state.has(ln, p),
)
self.extra_locations_added += 1
elif location.classification == "SpecialDungeonComplete":
if not self.options.exclude_special.value:
early_dungeons_region.locations.append(
EOSLocation(self.player, location.name, location.id, early_dungeons_region)
)
else:
self.excluded_locations += 1
continue
elif location.classification in ["LateDungeonComplete", "LateSubX"]:
late_dungeon = EOSLocation(self.player, location.name, location.id, late_dungeons_region)
if self.options.goal.value == 0: # if dialga is the goal, make the location excluded
self.excluded_locations += 1
continue
late_dungeons_region.locations.append(late_dungeon)
if self.options.goal.value == 1 and ("Mission" in location.group):
for j in range(self.options.late_mission_checks.value):
location_name = f"{location.name} Mission {j + 1}"
location_id = location.id + self.mission_start_id + (100 * location.id) + j
late_dungeons_region.locations.append(
EOSLocation(self.player, location_name, location_id, late_dungeons_region)
)
set_rule(
self.multiworld.get_location(f"{location.name} Mission {j + 1}", self.player),
lambda state, ln=location.name, p=self.player: state.has(ln, p),
)
self.extra_locations_added += 1
for j in range(self.options.late_outlaw_checks.value):
location_name = f"{location.name} Outlaw {j + 1}"
location_id = location.id + self.mission_start_id + (100 * location.id) + j + 50
late_dungeons_region.locations.append(
EOSLocation(self.player, location_name, location_id, late_dungeons_region)
)
set_rule(
self.multiworld.get_location(f"{location.name} Outlaw {j + 1}", self.player),
lambda state, ln=location.name, p=self.player: state.has(ln, p),
)
self.extra_locations_added += 1
elif location.classification in ["Manaphy", "SecretRank", "Legendary", "Instrument"] or location.name in [
"Bag Upgrade 5",
"Recycle Shop Dungeon #4",
"Recycle Shop Dungeon #5",
]:
late_dungeon = EOSLocation(self.player, location.name, location.id, late_dungeons_region)
if self.options.goal.value == 0: # if dialga is the goal, make the location excluded
self.excluded_locations += 1
continue
# late_dungeon.progress_type = LocationProgressType.EXCLUDED
# Manaphy's discovery can only be collected if manaphy is in the game
# if location.name == "Manaphy's Discovery":
# continue
late_dungeons_region.locations.append(late_dungeon)
elif location.classification == "SpindaDrinkEvent":
if int(location.name[-2:]) <= self.options.drink_events:
menu_region.locations.append(EOSLocation(self.player, location.name, location.id, menu_region))
else:
self.excluded_locations += 1
elif location.classification == "SpindaDrink":
if int(location.name[-2:]) <= self.options.spinda_drinks:
menu_region.locations.append(EOSLocation(self.player, location.name, location.id, menu_region))
else:
self.excluded_locations += 1
elif location.classification == "BossDungeonComplete":
location_data = EOSLocation(self.player, location.name, location.id, end_game_region)
if location.name == "Dark Crater" and self.options.goal.value == 0:
location_data.progress_type = LocationProgressType.EXCLUDED
end_game_region.locations.append(location_data)
if (self.options.goal.value == 1) and ("Mission" in location.group):
for j in range(self.options.late_mission_checks.value):
location_name = f"{location.name} Mission {j + 1}"
location_id = location.id + self.mission_start_id + (100 * location.id) + j
late_dungeons_region.locations.append(
EOSLocation(self.player, location_name, location_id, late_dungeons_region)
)
set_rule(
self.multiworld.get_location(f"{location.name} Mission {j + 1}", self.player),
lambda state, ln=location.name, p=self.player: state.has(ln, p),
)
self.extra_locations_added += 1
for j in range(self.options.late_outlaw_checks.value):
location_name = f"{location.name} Outlaw {j + 1}"
location_id = location.id + self.mission_start_id + (100 * location.id) + j + 50
late_dungeons_region.locations.append(
EOSLocation(self.player, location_name, location_id, late_dungeons_region)
)
set_rule(
self.multiworld.get_location(f"{location.name} Outlaw {j + 1}", self.player),
lambda state, ln=location.name, p=self.player: state.has(ln, p),
)
self.extra_locations_added += 1
elif (
(location.classification == "ProgressiveBagUpgrade")
or (location.classification == "ShopItem")
or (location.classification == "DojoDungeonComplete")
or (location.classification == "SEDungeonUnlock")
):
extra_items_region.locations.append(
EOSLocation(self.player, location.name, location.id, extra_items_region)
)
elif location.classification in ["RuleDungeonComplete"]:
if self.options.long_location.value == 0:
self.excluded_locations += 1
continue
# location = EOSLocation(self.player, location.name, location.id, rule_dungeons_region)
# location.progress_type = LocationProgressType.EXCLUDED
# rule_dungeons_region.locations.append(location)
else:
location = EOSLocation(self.player, location.name, location.id, rule_dungeons_region)
rule_dungeons_region.locations.append(location)
elif location.classification in "OptionalSubX":
if self.options.long_location.value == 0:
location = EOSLocation(self.player, location.name, location.id, rule_dungeons_region)
location.progress_type = LocationProgressType.EXCLUDED
self.excluded_tag_amount += 1
rule_dungeons_region.locations.append(location)
else:
location = EOSLocation(self.player, location.name, location.id, rule_dungeons_region)
rule_dungeons_region.locations.append(location)
menu_region.connect(extra_items_region)
menu_region.connect(early_dungeons_region)
early_dungeons_region.connect(late_dungeons_region, "Late Game Door")
# early_dungeons_region.connect(early_dungeons_region2)
late_dungeons_region.connect(end_game_region, "Boss Door")
# lambda state: ready_for_final_boss(state, self.player))
boss_region = Region("Boss Room", self.player, self.multiworld)
boss_region.locations.append(EOSLocation(self.player, "Final Boss", None, boss_region))
end_game_region.connect(boss_region, "End Game")
# lambda state: state.has("Temporal Tower", self.player))
late_dungeons_region.connect(rule_dungeons_region, "Rule Dungeons")
self.get_location("Final Boss").place_locked_item(self.create_item("Victory"))
def create_item(self, name: str, classification: ItemClassification = None) -> EOSItem:
item_data = item_table[name]
if classification is not None:
return EOSItem(item_data.name, classification, item_data.id, self.player)
else:
return EOSItem(item_data.name, item_data.classification, item_data.id, self.player)
def fill_slot_data(self) -> Dict[str, Any]:
return {
"Goal": self.options.goal.value,
"ServerVersion": game_version,
"BagOnStart": self.options.bag_on_start.value,
"Recruitment": self.options.recruit.value,
"TeamFormation": self.options.team_form.value,
"LevelScaling": self.options.level_scale.value,
"RecruitmentEvolution": self.options.recruit_evo.value,
"DojoDungeonsRandomization": self.options.dojo_dungeons.value,
"ShardFragmentAmount": self.options.required_fragments.value,
"ExtraShardsAmount": self.options.total_shards.value,
"EarlyMissionsAmount": self.options.early_mission_checks.value,
"EarlyOutlawsAmount": self.options.early_outlaw_checks.value,
"LateMissionsAmount": self.options.late_mission_checks.value,
"LateOutlawsAmount": self.options.late_outlaw_checks.value,
"TypeSanity": self.options.type_sanity.value,
"StarterOption": self.options.starter_option.value,
"IQScaling": self.options.iq_scaling.value,
"XPScaling": self.options.xp_scaling.value,
"RequiredInstruments": self.options.req_instruments.value,
"ExtraInstruments": self.options.total_instruments.value,
"HeroEvolution": self.options.hero_evolution.value,
"Deathlink": self.options.deathlink.value,
"DeathlinkType": self.options.deathlink_type.value,
"LegendaryAmount": self.options.legendaries.value,
"AllowedLegendaries": self.options.allowed_legendaries.value,
"SkyPeakType": self.options.sky_peak_type.value,
"SpecialEpisodeSanity": self.options.special_episode_sanity.value,
"HintLocationList": self.dimensional_scream_list_ints,
"TrapsAllowed": self.options.allow_traps.value,
"InvisibleTraps": self.options.invisible_traps.value,
"TrapPercentage": self.options.trap_percent.value,
"LongLocations": self.options.long_location.value,
"CursedAegisCave": self.options.cursed_aegis_cave.value,
"DrinkEvents": self.options.drink_events.value,
"SpindaDrinks": self.options.spinda_drinks.value,
"ExcludeSpecial": self.options.exclude_special.value,
"AllowMissionsEarly": self.options.early_mission_floors.value,
"MaxRank": self.options.max_rank.value,
"GuestScaling": self.options.guest_scaling.value,
"MoveShortcuts": self.options.move_shortcuts.value,
"StartInventoryFromPool": self.options.start_inventory_from_pool.value,
}
def create_items(self) -> None:
required_items = []
filler_items_pool = []
exclusive_filler_pool = []
non_unique_trap_items = []
non_unique_trap_weights = []
unique_trap_items = []
unique_trap_weights = []
instruments = []
item_weights = []
conditional_count = 0
# conditional_item_table = []
if self.options.goal.value == 1:
instruments_to_add = 0
if self.options.total_instruments.value < self.options.req_instruments.value:
instruments_to_add = self.options.req_instruments.value
else:
instruments_to_add = self.options.total_instruments.value
instrument_table = item_table_by_groups["Instrument"]
instruments = self.random.sample(sorted(instrument_table), instruments_to_add)
for item in instruments:
required_items.append(self.create_item(item, ItemClassification.progression_skip_balancing))
precollected = [item.name for item in self.multiworld.precollected_items[self.player]]
precollected_from_pool = []
for item, value in self.options.start_inventory_from_pool.value.items():
for _ in range(value):
precollected_from_pool += [item]
precollected_added = []
for item, value in self.options.start_inventory.value.items():
for _ in range(value):
precollected_added += [item]
# precollected_from_pool = [item for item, value in self.multiworld.start_inventory_from_pool[self.player].value.items()]
# precollected_added = [item for item, value in self.multiworld.start_inventory[self.player].value.items()]
relics_to_add = 0
if self.options.required_fragments.value > self.options.total_shards.value:
relics_to_add = self.options.required_fragments.value
else:
relics_to_add = self.options.total_shards.value
for i in range(relics_to_add):
required_items.append(
self.create_item("Relic Fragment Shard", ItemClassification.progression_skip_balancing)
)
if self.options.goal == 1:
required_items.append(self.create_item("Manaphy", ItemClassification.progression))
required_items.append(self.create_item("Secret Rank", ItemClassification.progression))
else:
# self.excluded_locations += 1
test = 0
if self.options.goal.value == 1 and (
self.options.legendaries.value > len(self.options.allowed_legendaries.value)
):
for item in self.options.allowed_legendaries.value:
required_items.append(self.create_item(item, ItemClassification.filler))
elif self.options.goal.value == 1:
new_list = self.random.sample(
sorted(self.options.allowed_legendaries.value), self.options.legendaries.value
)
for item in new_list:
required_items.append(self.create_item(item, ItemClassification.filler))
for item_name in item_table:
# if (item_name == "Dark Crater") and (self.options.goal.value == 1):
# continue
if item_name in item_frequencies:
freq = 0
freq = item_frequencies.get(item_name, 1)
freq = max(
freq
- precollected.count(item_name)
+ precollected_added.count(item_name)
+ precollected_from_pool.count(item_name),
0,
)
required_items += [self.create_item(item_name) for _ in range(freq)]
elif "Special Dungeons" in item_table[item_name].group:
if self.options.exclude_special.value:
continue
else:
classification = ItemClassification.progression
required_items.append(self.create_item(item_name, classification))
elif item_table[item_name].name == "Main Game Unlock":
if not self.options.special_episode_sanity.value or self.options.exclude_special.value:
continue
else:
required_items.append(self.create_item(item_name, ItemClassification.progression))
elif item_table[item_name].name in ["Victory", "Relic Fragment Shard"]:
continue
elif "Legendary" in item_table[item_name].group:
continue
elif "Instrument" in item_table[item_name].group:
continue
elif "Rank" in item_table[item_name].group:
continue
elif item_table[item_name].name in conditional_filler_useful_items:
conditional_count += 1
# conditional_item_table.append(item_name)
continue
elif item_table[item_name].classification == ItemClassification.filler:
if item_name in ["Gold Ribbon"]:
continue
if "Exclusive" in item_table[item_name].group:
exclusive_filler_pool.append(self.create_item(item_name, ItemClassification.filler))
else:
filler_items_pool.append(self.create_item(item_name, ItemClassification.filler))
elif item_table[item_name].classification == ItemClassification.trap:
if "Late" in item_table[item_name].group and self.options.goal.value == 0:
continue
elif "Unique" in item_table[item_name].group:
unique_trap_items.append(self.create_item(item_name, ItemClassification.trap))
unique_trap_weights.append(item_table[item_name].start_number)
else:
non_unique_trap_items.append(self.create_item(item_name, ItemClassification.trap))
non_unique_trap_weights.append(item_table[item_name].start_number)
elif item_table[item_name].classification == ItemClassification.progression:
classification = ItemClassification.progression
if (self.options.goal.value == 0) and "LateDungeons" in item_table[item_name].group:
continue
# classification = ItemClassification.useful
if (self.options.long_location.value == 0) and "RuleDungeons" in item_table[item_name].group:
continue
if "Aegis" in item_table[item_name].group:
if self.options.goal.value == 0:
# classification = ItemClassification.useful
continue
else:
classification = ItemClassification.progression
if self.options.cursed_aegis_cave.value == 0:
if item_name == "Progressive Seal":
for i in range(3):
required_items.append(self.create_item(item_name, classification))
continue
else:
continue
else:
if item_name == "Progressive Seal":
continue
else:
required_items.append(self.create_item(item_name, classification))
continue
if "SkyPeak" in item_table[item_name].group:
if self.options.sky_peak_type.value == 1:
if item_name == "Progressive Sky Peak":
if self.options.goal.value == 0:
continue
# classification = ItemClassification.useful
else:
classification = ItemClassification.progression
for i in range(10):
required_items.append(self.create_item(item_name, classification))
continue
else:
continue
elif self.options.sky_peak_type.value == 2:
if item_name == "Progressive Sky Peak":
continue
else:
if self.options.goal.value == 0:
# classification = ItemClassification.useful
continue
else:
classification = ItemClassification.progression
elif self.options.sky_peak_type.value == 3:
if item_name == "1st Station Pass":
if self.options.goal.value == 0:
continue
# classification = ItemClassification.useful
else:
classification = ItemClassification.progression
else:
continue
required_items.append(self.create_item(item_name, classification))
else:
required_items.append(self.create_item(item_name, ItemClassification.useful))
remaining = (
len(EOS_location_table)
+ self.extra_locations_added
- len(required_items)
- conditional_count
- 1
- self.excluded_locations
) # subtracting 1 for the event check
for i in range(len(conditional_filler_useful_items)):
if (i <= (self.excluded_tag_amount - remaining)) and (remaining < self.excluded_tag_amount):
required_items.append(self.create_item(conditional_filler_useful_items[i], ItemClassification.filler))
else:
required_items.append(self.create_item(conditional_filler_useful_items[i], ItemClassification.useful))
self.multiworld.itempool += required_items
item_weights += filler_item_weights
for i in range(4):
filler_items_pool += filler_items_pool
non_unique_trap_items += non_unique_trap_items
item_weights += item_weights
non_unique_trap_weights += non_unique_trap_weights
filler_items_pool += exclusive_filler_pool
item_weights += exclusive_filler_item_weights
all_traps = []
all_traps += non_unique_trap_items
all_traps += unique_trap_items
all_trap_weights = []
all_trap_weights += non_unique_trap_weights
all_trap_weights += unique_trap_weights
if self.options.allow_traps.value in [1, 2]:
filler_items_toadd = math.ceil(remaining * (100 - self.options.trap_percent) / 100)
traps_toadd = math.floor(remaining * self.options.trap_percent / 100)
self.multiworld.itempool += [
self.create_item(filler_item.name)
for filler_item in self.random.sample(filler_items_pool, filler_items_toadd, counts=item_weights)
]
self.multiworld.itempool += [
self.create_item(trap.name)
for trap in self.random.sample(all_traps, traps_toadd, counts=all_trap_weights)
]
else:
self.multiworld.itempool += [
self.create_item(filler_item.name)
for filler_item in self.random.sample(filler_items_pool, remaining, counts=item_weights)
]
idek_var = self.excluded_tag_amount
wtf_items_list = [item.name for item in self.multiworld.itempool]
excluded_location_list = [
location.name
for location in self.multiworld.get_locations(self.player)
if location.progress_type is LocationProgressType.EXCLUDED
]
test = 0
def set_rules(self) -> None:
set_rules(self, self.disabled_locations)
self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player)
def generate_output(self, output_directory: str) -> None:
try:
patch = EOSProcedurePatch(player=self.player, player_name=self.multiworld.player_name[self.player])
patch.write_file("base_patch.bsdiff4", pkgutil.get_data(__name__, "data/archipelago-base.bsdiff"))
hint_item_list: list[Location] = []
self.dimensional_scream_list = self.hint_locations()
self.dimensional_scream_list_ints = [k.address for k in self.dimensional_scream_list]
except Exception:
raise
finally:
self.slot_data_ready.set()
for i in range(10):
hint_item_list += [self.multiworld.get_location(f"Shop Item {1 + i}", self.player)]
write_tokens(self, patch, hint_item_list, self.dimensional_scream_list, self.starting_se + 1)
rom_path = os.path.join(
output_directory, f"{self.multiworld.get_out_file_name_base(self.player)}{patch.patch_file_ending}"
)
patch.write(rom_path)
def hint_locations(self) -> list[Location]:
hint_loc = []
filler = 5
useful = 6
progressive = 9
min_internal_progressive = 4
max_external_progressive = progressive - min_internal_progressive
sky_dungeons = []
external_items = []
extra_useful_items = []
extra_filler_items = []
important_sky_items = [
"Icy Flute",
"Fiery Drum",
"Terra Cymbal",
"Aqua-Monica",
"Rock Horn",
"Grass Cornet",
"Sky Melodica",
"Stellar Symphony",
"Null Bagpipes",
"Glimmer Harp",
"Toxic Sax",
"Biting Bass",
"Knockout Bell",
"Spectral Chimes",
"Liar's Lyre",
"Charge Synth",
"Norma-ccordion",
"Psychic Cello",
"Dragu-teki",
"Steel Guitar",
"Relic Fragment Shard",
]
location_list = list(self.multiworld.get_locations(self.player))
random.shuffle(location_list)
for location in location_list:
if location.address is not None and location.item:
if filler <= 0 and useful <= 0 and progressive <= 0:
break
elif progressive > 0 and location.item.advancement:
if location.item.player == self.player and location.item.name not in important_sky_items:
sky_dungeons.append(location)
continue
if location.item.player != self.player:
if max_external_progressive > 0:
hint_loc.append(location)
max_external_progressive -= 1
progressive -= 1
else:
external_items.append(location)
continue
if location.item.player == self.player and location.item.name in important_sky_items:
hint_loc.append(location)
progressive -= 1
elif filler > 0 and not (location.item.advancement or location.item.useful):
hint_loc.append(location)
filler -= 1
elif not (location.item.advancement or location.item.useful):
extra_filler_items.append(location)
elif useful > 0 and location.item.useful:
hint_loc.append(location)
useful -= 1
elif location.item.useful:
extra_useful_items.append(location)
if progressive > 0:
for i in range(progressive):
if i < len(sky_dungeons):
hint_loc.append(sky_dungeons[i])
else:
if i - len(sky_dungeons) < len(external_items):
hint_loc.append(external_items[i - len(sky_dungeons)])
else:
if i - len(sky_dungeons) - len(external_items) < len(extra_useful_items):
hint_loc.append(extra_useful_items[i - len(sky_dungeons) - len(external_items)])
else:
if i - len(sky_dungeons) - len(external_items) - len(extra_useful_items) < len(
extra_filler_items
):
hint_loc.append(
extra_filler_items[
i - len(sky_dungeons) - len(external_items) - len(extra_useful_items)
]
)
else:
break
return hint_loc
def modify_multidata(self, multidata: Dict[str, Any]) -> None:
self.slot_data_ready.wait()
slot = self.player
if self.dimensional_scream_list_ints:
multidata["slot_data"][slot]["HintLocationList"] = self.dimensional_scream_list_ints