KH2: Version 2 (#2009)

Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com>
Co-authored-by: Joe Prochaska <prochaska.joseph@gmail.com>
This commit is contained in:
JaredWeakStrike
2023-11-25 09:46:00 -05:00
committed by GitHub
parent c138918400
commit 2ccf11f3d7
24 changed files with 6341 additions and 4882 deletions

View File

@@ -1,15 +1,25 @@
from BaseClasses import Tutorial, ItemClassification
import logging
from typing import List
from BaseClasses import Tutorial, ItemClassification
from Fill import fill_restrictive
from worlds.LauncherComponents import Component, components, Type, launch_subprocess
from worlds.AutoWorld import World, WebWorld
from .Items import *
from .Locations import all_locations, setup_locations, exclusion_table, AllWeaponSlot
from .Names import ItemName, LocationName
from .Locations import *
from .Names import ItemName, LocationName, RegionName
from .OpenKH import patch_kh2
from .Options import KH2_Options
from .Options import KingdomHearts2Options
from .Regions import create_regions, connect_regions
from .Rules import set_rules
from ..AutoWorld import World, WebWorld
from .logic import KH2Logic
from .Rules import *
def launch_client():
from .Client import launch
launch_subprocess(launch, name="KH2Client")
components.append(Component("KH2 Client", "KH2Client", func=launch_client, component_type=Type.CLIENT))
class KingdomHearts2Web(WebWorld):
@@ -23,99 +33,119 @@ class KingdomHearts2Web(WebWorld):
)]
# noinspection PyUnresolvedReferences
class KH2World(World):
"""
Kingdom Hearts II is an action role-playing game developed and published by Square Enix and released in 2005.
It is the sequel to Kingdom Hearts and Kingdom Hearts: Chain of Memories, and like the two previous games,
focuses on Sora and his friends' continued battle against the Darkness.
"""
game: str = "Kingdom Hearts 2"
game = "Kingdom Hearts 2"
web = KingdomHearts2Web()
data_version = 1
required_client_version = (0, 4, 0)
option_definitions = KH2_Options
item_name_to_id = {name: data.code for name, data in item_dictionary_table.items()}
location_name_to_id = {item_name: data.code for item_name, data in all_locations.items() if data.code}
required_client_version = (0, 4, 4)
options_dataclass = KingdomHearts2Options
options: KingdomHearts2Options
item_name_to_id = {item: item_id
for item_id, item in enumerate(item_dictionary_table.keys(), 0x130000)}
location_name_to_id = {item: location
for location, item in enumerate(all_locations.keys(), 0x130000)}
item_name_groups = item_groups
visitlocking_dict: Dict[str, int]
plando_locations: Dict[str, str]
lucky_emblem_amount: int
lucky_emblem_required: int
bounties_required: int
bounties_amount: int
filler_items: List[str]
item_quantity_dict: Dict[str, int]
local_items: Dict[int, int]
sora_ability_dict: Dict[str, int]
goofy_ability_dict: Dict[str, int]
donald_ability_dict: Dict[str, int]
total_locations: int
# growth_list: list[str]
def __init__(self, multiworld: "MultiWorld", player: int):
super().__init__(multiworld, player)
self.valid_abilities = None
self.visitlocking_dict = None
self.plando_locations = None
self.luckyemblemamount = None
self.luckyemblemrequired = None
self.BountiesRequired = None
self.BountiesAmount = None
self.hitlist = None
self.LocalItems = {}
self.RandomSuperBoss = list()
self.filler_items = list()
self.item_quantity_dict = {}
self.donald_ability_pool = list()
self.goofy_ability_pool = list()
self.sora_keyblade_ability_pool = list()
self.keyblade_slot_copy = list(Locations.Keyblade_Slots.keys())
self.keyblade_slot_copy.remove(LocationName.KingdomKeySlot)
self.totalLocations = len(all_locations.items())
# random_super_boss_list List[str]
# has to be in __init__ or else other players affect each other's bounties
self.random_super_boss_list = list()
self.growth_list = list()
for x in range(4):
self.growth_list.extend(Movement_Table.keys())
self.slotDataDuping = set()
self.localItems = dict()
# lists of KH2Item
self.keyblade_ability_pool = list()
self.goofy_get_bonus_abilities = list()
self.goofy_weapon_abilities = list()
self.donald_get_bonus_abilities = list()
self.donald_weapon_abilities = list()
self.slot_data_goofy_weapon = dict()
self.slot_data_sora_weapon = dict()
self.slot_data_donald_weapon = dict()
def fill_slot_data(self) -> dict:
for values in CheckDupingItems.values():
if isinstance(values, set):
self.slotDataDuping = self.slotDataDuping.union(values)
else:
for inner_values in values.values():
self.slotDataDuping = self.slotDataDuping.union(inner_values)
self.LocalItems = {location.address: item_dictionary_table[location.item.name].code
for location in self.multiworld.get_filled_locations(self.player)
if location.item.player == self.player
and location.item.name in self.slotDataDuping
and location.name not in AllWeaponSlot}
for ability in self.slot_data_sora_weapon:
if ability in self.sora_ability_dict and self.sora_ability_dict[ability] >= 1:
self.sora_ability_dict[ability] -= 1
self.donald_ability_dict = {k: v.quantity for k, v in DonaldAbility_Table.items()}
for ability in self.slot_data_donald_weapon:
if ability in self.donald_ability_dict and self.donald_ability_dict[ability] >= 1:
self.donald_ability_dict[ability] -= 1
self.goofy_ability_dict = {k: v.quantity for k, v in GoofyAbility_Table.items()}
for ability in self.slot_data_goofy_weapon:
if ability in self.goofy_ability_dict and self.goofy_ability_dict[ability] >= 1:
self.goofy_ability_dict[ability] -= 1
return {"hitlist": self.hitlist,
"LocalItems": self.LocalItems,
"Goal": self.multiworld.Goal[self.player].value,
"FinalXemnas": self.multiworld.FinalXemnas[self.player].value,
"LuckyEmblemsRequired": self.multiworld.LuckyEmblemsRequired[self.player].value,
"BountyRequired": self.multiworld.BountyRequired[self.player].value}
slot_data = self.options.as_dict("Goal", "FinalXemnas", "LuckyEmblemsRequired", "BountyRequired")
slot_data.update({
"hitlist": [], # remove this after next update
"PoptrackerVersionCheck": 4.3,
"KeybladeAbilities": self.sora_ability_dict,
"StaffAbilities": self.donald_ability_dict,
"ShieldAbilities": self.goofy_ability_dict,
})
return slot_data
def create_item(self, name: str, ) -> Item:
data = item_dictionary_table[name]
if name in Progression_Dicts["Progression"]:
def create_item(self, name: str) -> Item:
"""
Returns created KH2Item
"""
# data = item_dictionary_table[name]
if name in progression_set:
item_classification = ItemClassification.progression
elif name in useful_set:
item_classification = ItemClassification.useful
else:
item_classification = ItemClassification.filler
created_item = KH2Item(name, item_classification, data.code, self.player)
created_item = KH2Item(name, item_classification, self.item_name_to_id[name], self.player)
return created_item
def create_items(self) -> None:
self.visitlocking_dict = Progression_Dicts["AllVisitLocking"].copy()
if self.multiworld.Schmovement[self.player] != "level_0":
for _ in range(self.multiworld.Schmovement[self.player].value):
for name in {ItemName.HighJump, ItemName.QuickRun, ItemName.DodgeRoll, ItemName.AerialDodge,
ItemName.Glide}:
"""
Fills ItemPool and manages schmovement, random growth, visit locking and random starting visit locking.
"""
self.visitlocking_dict = visit_locking_dict["AllVisitLocking"].copy()
if self.options.Schmovement != "level_0":
for _ in range(self.options.Schmovement.value):
for name in Movement_Table.keys():
self.item_quantity_dict[name] -= 1
self.growth_list.remove(name)
self.multiworld.push_precollected(self.create_item(name))
if self.multiworld.RandomGrowth[self.player] != 0:
max_growth = min(self.multiworld.RandomGrowth[self.player].value, len(self.growth_list))
if self.options.RandomGrowth:
max_growth = min(self.options.RandomGrowth.value, len(self.growth_list))
for _ in range(max_growth):
random_growth = self.multiworld.per_slot_randoms[self.player].choice(self.growth_list)
random_growth = self.random.choice(self.growth_list)
self.item_quantity_dict[random_growth] -= 1
self.growth_list.remove(random_growth)
self.multiworld.push_precollected(self.create_item(random_growth))
if self.multiworld.Visitlocking[self.player] == "no_visit_locking":
for item, amount in Progression_Dicts["AllVisitLocking"].items():
if self.options.Visitlocking == "no_visit_locking":
for item, amount in visit_locking_dict["AllVisitLocking"].items():
for _ in range(amount):
self.multiworld.push_precollected(self.create_item(item))
self.item_quantity_dict[item] -= 1
@@ -123,19 +153,19 @@ class KH2World(World):
if self.visitlocking_dict[item] == 0:
self.visitlocking_dict.pop(item)
elif self.multiworld.Visitlocking[self.player] == "second_visit_locking":
for item in Progression_Dicts["2VisitLocking"]:
elif self.options.Visitlocking == "second_visit_locking":
for item in visit_locking_dict["2VisitLocking"]:
self.item_quantity_dict[item] -= 1
self.visitlocking_dict[item] -= 1
if self.visitlocking_dict[item] == 0:
self.visitlocking_dict.pop(item)
self.multiworld.push_precollected(self.create_item(item))
for _ in range(self.multiworld.RandomVisitLockingItem[self.player].value):
for _ in range(self.options.RandomVisitLockingItem.value):
if sum(self.visitlocking_dict.values()) <= 0:
break
visitlocking_set = list(self.visitlocking_dict.keys())
item = self.multiworld.per_slot_randoms[self.player].choice(visitlocking_set)
item = self.random.choice(visitlocking_set)
self.item_quantity_dict[item] -= 1
self.visitlocking_dict[item] -= 1
if self.visitlocking_dict[item] == 0:
@@ -145,175 +175,258 @@ class KH2World(World):
itempool = [self.create_item(item) for item, data in self.item_quantity_dict.items() for _ in range(data)]
# Creating filler for unfilled locations
itempool += [self.create_filler()
for _ in range(self.totalLocations - len(itempool))]
itempool += [self.create_filler() for _ in range(self.total_locations - len(itempool))]
self.multiworld.itempool += itempool
def generate_early(self) -> None:
# Item Quantity dict because Abilities can be a problem for KH2's Software.
"""
Determines the quantity of items and maps plando locations to items.
"""
# Item: Quantity Map
# Example. Quick Run: 4
self.total_locations = len(all_locations.keys())
for x in range(4):
self.growth_list.extend(Movement_Table.keys())
self.item_quantity_dict = {item: data.quantity for item, data in item_dictionary_table.items()}
self.sora_ability_dict = {k: v.quantity for dic in [SupportAbility_Table, ActionAbility_Table] for k, v in
dic.items()}
# Dictionary to mark locations with their plandoed item
# Example. Final Xemnas: Victory
# 3 random support abilities because there are left over slots
support_abilities = list(SupportAbility_Table.keys())
for _ in range(6):
random_support_ability = self.random.choice(support_abilities)
self.item_quantity_dict[random_support_ability] += 1
self.sora_ability_dict[random_support_ability] += 1
self.plando_locations = dict()
self.hitlist = []
self.starting_invo_verify()
for k, v in self.options.CustomItemPoolQuantity.value.items():
# kh2's items cannot hold more than a byte
if 255 > v > self.item_quantity_dict[k] and k in default_itempool_option.keys():
self.item_quantity_dict[k] = v
elif 255 <= v:
logging.warning(
f"{self.player} has too many {k} in their CustomItemPool setting. Setting to default quantity")
# Option to turn off Promise Charm Item
if not self.multiworld.Promise_Charm[self.player]:
self.item_quantity_dict[ItemName.PromiseCharm] = 0
if not self.options.Promise_Charm:
del self.item_quantity_dict[ItemName.PromiseCharm]
if not self.options.AntiForm:
del self.item_quantity_dict[ItemName.AntiForm]
self.set_excluded_locations()
if self.multiworld.Goal[self.player] == "lucky_emblem_hunt":
self.luckyemblemamount = self.multiworld.LuckyEmblemsAmount[self.player].value
self.luckyemblemrequired = self.multiworld.LuckyEmblemsRequired[self.player].value
if self.options.Goal not in ["hitlist", "three_proofs"]:
self.lucky_emblem_amount = self.options.LuckyEmblemsAmount.value
self.lucky_emblem_required = self.options.LuckyEmblemsRequired.value
self.emblem_verify()
# hitlist
elif self.multiworld.Goal[self.player] == "hitlist":
self.RandomSuperBoss.extend(exclusion_table["Hitlist"])
self.BountiesAmount = self.multiworld.BountyAmount[self.player].value
self.BountiesRequired = self.multiworld.BountyRequired[self.player].value
if self.options.Goal not in ["lucky_emblem_hunt", "three_proofs"]:
self.random_super_boss_list.extend(exclusion_table["Hitlist"])
self.bounties_amount = self.options.BountyAmount.value
self.bounties_required = self.options.BountyRequired.value
self.hitlist_verify()
for bounty in range(self.BountiesAmount):
randomBoss = self.multiworld.per_slot_randoms[self.player].choice(self.RandomSuperBoss)
self.plando_locations[randomBoss] = ItemName.Bounty
self.hitlist.append(self.location_name_to_id[randomBoss])
self.RandomSuperBoss.remove(randomBoss)
self.totalLocations -= 1
prio_hitlist = [location for location in self.multiworld.priority_locations[self.player].value if
location in self.random_super_boss_list]
for bounty in range(self.options.BountyAmount.value):
if prio_hitlist:
random_boss = self.random.choice(prio_hitlist)
prio_hitlist.remove(random_boss)
else:
random_boss = self.random.choice(self.random_super_boss_list)
self.plando_locations[random_boss] = ItemName.Bounty
self.random_super_boss_list.remove(random_boss)
self.total_locations -= 1
self.donald_fill()
self.goofy_fill()
self.keyblade_fill()
self.donald_gen_early()
self.goofy_gen_early()
self.keyblade_gen_early()
if self.multiworld.FinalXemnas[self.player]:
self.plando_locations[LocationName.FinalXemnas] = ItemName.Victory
else:
self.plando_locations[LocationName.FinalXemnas] = self.create_filler().name
self.total_locations -= 1
# same item placed because you can only get one of these 2 locations
# they are both under the same flag so the player gets both locations just one of the two items
random_stt_item = self.create_filler().name
for location in {LocationName.JunkMedal, LocationName.JunkMedal}:
self.plando_locations[location] = random_stt_item
self.level_subtraction()
# subtraction from final xemnas and stt
self.totalLocations -= 3
if self.options.WeaponSlotStartHint:
for location in all_weapon_slot:
self.multiworld.start_location_hints[self.player].value.add(location)
if self.options.FillerItemsLocal:
for item in filler_items:
self.multiworld.local_items[self.player].value.add(item)
# By imitating remote this doesn't have to be plandoded filler anymore
# for location in {LocationName.JunkMedal, LocationName.JunkMedal}:
# self.plando_locations[location] = random_stt_item
if not self.options.SummonLevelLocationToggle:
self.total_locations -= 6
self.total_locations -= self.level_subtraction()
def pre_fill(self):
"""
Plandoing Events and Fill_Restrictive for donald,goofy and sora
"""
self.donald_pre_fill()
self.goofy_pre_fill()
self.keyblade_pre_fill()
for location, item in self.plando_locations.items():
self.multiworld.get_location(location, self.player).place_locked_item(
self.create_item(item))
def create_regions(self):
location_table = setup_locations()
create_regions(self.multiworld, self.player, location_table)
connect_regions(self.multiworld, self.player)
"""
Creates the Regions and Connects them.
"""
create_regions(self)
connect_regions(self)
def set_rules(self):
set_rules(self.multiworld, self.player)
"""
Sets the Logic for the Regions and Locations.
"""
universal_logic = Rules.KH2WorldRules(self)
form_logic = Rules.KH2FormRules(self)
fight_rules = Rules.KH2FightRules(self)
fight_rules.set_kh2_fight_rules()
universal_logic.set_kh2_rules()
form_logic.set_kh2_form_rules()
def generate_output(self, output_directory: str):
"""
Generates the .zip for OpenKH (The KH Mod Manager)
"""
patch_kh2(self, output_directory)
def donald_fill(self):
for item in DonaldAbility_Table:
data = self.item_quantity_dict[item]
for _ in range(data):
self.donald_ability_pool.append(item)
self.item_quantity_dict[item] = 0
# 32 is the amount of donald abilities
while len(self.donald_ability_pool) < 32:
self.donald_ability_pool.append(
self.multiworld.per_slot_randoms[self.player].choice(self.donald_ability_pool))
# Placing Donald Abilities on donald locations
for donaldLocation in Locations.Donald_Checks.keys():
random_ability = self.multiworld.per_slot_randoms[self.player].choice(self.donald_ability_pool)
self.plando_locations[donaldLocation] = random_ability
self.totalLocations -= 1
self.donald_ability_pool.remove(random_ability)
def goofy_fill(self):
for item in GoofyAbility_Table.keys():
data = self.item_quantity_dict[item]
for _ in range(data):
self.goofy_ability_pool.append(item)
self.item_quantity_dict[item] = 0
# 32 is the amount of goofy abilities
while len(self.goofy_ability_pool) < 33:
self.goofy_ability_pool.append(
self.multiworld.per_slot_randoms[self.player].choice(self.goofy_ability_pool))
# Placing Goofy Abilities on goofy locations
for goofyLocation in Locations.Goofy_Checks.keys():
random_ability = self.multiworld.per_slot_randoms[self.player].choice(self.goofy_ability_pool)
self.plando_locations[goofyLocation] = random_ability
self.totalLocations -= 1
self.goofy_ability_pool.remove(random_ability)
def keyblade_fill(self):
if self.multiworld.KeybladeAbilities[self.player] == "support":
self.sora_keyblade_ability_pool = {
**{item: data for item, data in self.item_quantity_dict.items() if item in SupportAbility_Table},
**{ItemName.NegativeCombo: 1, ItemName.AirComboPlus: 1, ItemName.ComboPlus: 1,
ItemName.FinishingPlus: 1}}
elif self.multiworld.KeybladeAbilities[self.player] == "action":
self.sora_keyblade_ability_pool = {item: data for item, data in self.item_quantity_dict.items() if
item in ActionAbility_Table}
# there are too little action abilities so 2 random support abilities are placed
for _ in range(3):
randomSupportAbility = self.multiworld.per_slot_randoms[self.player].choice(
list(SupportAbility_Table.keys()))
while randomSupportAbility in self.sora_keyblade_ability_pool:
randomSupportAbility = self.multiworld.per_slot_randoms[self.player].choice(
list(SupportAbility_Table.keys()))
self.sora_keyblade_ability_pool[randomSupportAbility] = 1
else:
# both action and support on keyblades.
# TODO: make option to just exclude scom
self.sora_keyblade_ability_pool = {
**{item: data for item, data in self.item_quantity_dict.items() if item in SupportAbility_Table},
**{item: data for item, data in self.item_quantity_dict.items() if item in ActionAbility_Table},
**{ItemName.NegativeCombo: 1, ItemName.AirComboPlus: 1, ItemName.ComboPlus: 1,
ItemName.FinishingPlus: 1}}
for ability in self.multiworld.BlacklistKeyblade[self.player].value:
if ability in self.sora_keyblade_ability_pool:
self.sora_keyblade_ability_pool.pop(ability)
# magic number for amount of keyblades
if sum(self.sora_keyblade_ability_pool.values()) < 28:
raise Exception(
f"{self.multiworld.get_file_safe_player_name(self.player)} has too little Keyblade Abilities in the Keyblade Pool")
self.valid_abilities = list(self.sora_keyblade_ability_pool.keys())
# Kingdom Key cannot have No Experience so plandoed here instead of checking 26 times if its kingdom key
random_ability = self.multiworld.per_slot_randoms[self.player].choice(self.valid_abilities)
while random_ability == ItemName.NoExperience:
random_ability = self.multiworld.per_slot_randoms[self.player].choice(self.valid_abilities)
self.plando_locations[LocationName.KingdomKeySlot] = random_ability
self.item_quantity_dict[random_ability] -= 1
self.sora_keyblade_ability_pool[random_ability] -= 1
if self.sora_keyblade_ability_pool[random_ability] == 0:
self.valid_abilities.remove(random_ability)
self.sora_keyblade_ability_pool.pop(random_ability)
# plando keyblades because they can only have abilities
for keyblade in self.keyblade_slot_copy:
random_ability = self.multiworld.per_slot_randoms[self.player].choice(self.valid_abilities)
self.plando_locations[keyblade] = random_ability
def donald_gen_early(self):
random_prog_ability = self.random.choice([ItemName.Fantasia, ItemName.FlareForce])
donald_master_ability = [donald_ability for donald_ability in DonaldAbility_Table.keys() for _ in
range(self.item_quantity_dict[donald_ability]) if
donald_ability != random_prog_ability]
self.donald_weapon_abilities = []
self.donald_get_bonus_abilities = []
# fill goofy weapons first
for _ in range(15):
random_ability = self.random.choice(donald_master_ability)
donald_master_ability.remove(random_ability)
self.donald_weapon_abilities += [self.create_item(random_ability)]
self.item_quantity_dict[random_ability] -= 1
self.sora_keyblade_ability_pool[random_ability] -= 1
if self.sora_keyblade_ability_pool[random_ability] == 0:
self.valid_abilities.remove(random_ability)
self.sora_keyblade_ability_pool.pop(random_ability)
self.totalLocations -= 1
self.total_locations -= 1
self.slot_data_donald_weapon = [item_name.name for item_name in self.donald_weapon_abilities]
if not self.multiworld.DonaldGoofyStatsanity[self.player]:
# pre plando donald get bonuses
self.donald_get_bonus_abilities += [self.create_item(random_prog_ability)]
self.total_locations -= 1
for item_name in donald_master_ability:
self.donald_get_bonus_abilities += [self.create_item(item_name)]
self.item_quantity_dict[item_name] -= 1
self.total_locations -= 1
def goofy_gen_early(self):
random_prog_ability = self.random.choice([ItemName.Teamwork, ItemName.TornadoFusion])
goofy_master_ability = [goofy_ability for goofy_ability in GoofyAbility_Table.keys() for _ in
range(self.item_quantity_dict[goofy_ability]) if goofy_ability != random_prog_ability]
self.goofy_weapon_abilities = []
self.goofy_get_bonus_abilities = []
# fill goofy weapons first
for _ in range(15):
random_ability = self.random.choice(goofy_master_ability)
goofy_master_ability.remove(random_ability)
self.goofy_weapon_abilities += [self.create_item(random_ability)]
self.item_quantity_dict[random_ability] -= 1
self.total_locations -= 1
self.slot_data_goofy_weapon = [item_name.name for item_name in self.goofy_weapon_abilities]
if not self.options.DonaldGoofyStatsanity:
# pre plando goofy get bonuses
self.goofy_get_bonus_abilities += [self.create_item(random_prog_ability)]
self.total_locations -= 1
for item_name in goofy_master_ability:
self.goofy_get_bonus_abilities += [self.create_item(item_name)]
self.item_quantity_dict[item_name] -= 1
self.total_locations -= 1
def keyblade_gen_early(self):
keyblade_master_ability = [ability for ability in SupportAbility_Table.keys() if ability not in progression_set
for _ in range(self.item_quantity_dict[ability])]
self.keyblade_ability_pool = []
for _ in range(len(Keyblade_Slots)):
random_ability = self.random.choice(keyblade_master_ability)
keyblade_master_ability.remove(random_ability)
self.keyblade_ability_pool += [self.create_item(random_ability)]
self.item_quantity_dict[random_ability] -= 1
self.total_locations -= 1
self.slot_data_sora_weapon = [item_name.name for item_name in self.keyblade_ability_pool]
def goofy_pre_fill(self):
"""
Removes donald locations from the location pool maps random donald items to be plandoded.
"""
goofy_weapon_location_list = [self.multiworld.get_location(location, self.player) for location in
Goofy_Checks.keys() if Goofy_Checks[location].yml == "Keyblade"]
# take one of the 2 out
# randomize the list with only
for location in goofy_weapon_location_list:
random_ability = self.random.choice(self.goofy_weapon_abilities)
location.place_locked_item(random_ability)
self.goofy_weapon_abilities.remove(random_ability)
if not self.multiworld.DonaldGoofyStatsanity[self.player]:
# plando goofy get bonuses
goofy_get_bonus_location_pool = [self.multiworld.get_location(location, self.player) for location in
Goofy_Checks.keys() if Goofy_Checks[location].yml != "Keyblade"]
for location in goofy_get_bonus_location_pool:
self.random.choice(self.goofy_get_bonus_abilities)
random_ability = self.random.choice(self.goofy_get_bonus_abilities)
location.place_locked_item(random_ability)
self.goofy_get_bonus_abilities.remove(random_ability)
def donald_pre_fill(self):
donald_weapon_location_list = [self.multiworld.get_location(location, self.player) for location in
Donald_Checks.keys() if Donald_Checks[location].yml == "Keyblade"]
# take one of the 2 out
# randomize the list with only
for location in donald_weapon_location_list:
random_ability = self.random.choice(self.donald_weapon_abilities)
location.place_locked_item(random_ability)
self.donald_weapon_abilities.remove(random_ability)
if not self.multiworld.DonaldGoofyStatsanity[self.player]:
# plando goofy get bonuses
donald_get_bonus_location_pool = [self.multiworld.get_location(location, self.player) for location in
Donald_Checks.keys() if Donald_Checks[location].yml != "Keyblade"]
for location in donald_get_bonus_location_pool:
random_ability = self.random.choice(self.donald_get_bonus_abilities)
location.place_locked_item(random_ability)
self.donald_get_bonus_abilities.remove(random_ability)
def keyblade_pre_fill(self):
"""
Fills keyblade slots with abilities determined on player's setting
"""
keyblade_locations = [self.multiworld.get_location(location, self.player) for location in Keyblade_Slots.keys()]
state = self.multiworld.get_all_state(False)
keyblade_ability_pool_copy = self.keyblade_ability_pool.copy()
fill_restrictive(self.multiworld, state, keyblade_locations, keyblade_ability_pool_copy, True, True)
def starting_invo_verify(self):
"""
Making sure the player doesn't put too many abilities in their starting inventory.
"""
for item, value in self.multiworld.start_inventory[self.player].value.items():
if item in ActionAbility_Table \
or item in SupportAbility_Table or exclusionItem_table["StatUps"] \
or item in SupportAbility_Table or exclusion_item_table["StatUps"] \
or item in DonaldAbility_Table or item in GoofyAbility_Table:
# cannot have more than the quantity for abilties
if value > item_dictionary_table[item].quantity:
@@ -324,78 +437,100 @@ class KH2World(World):
self.item_quantity_dict[item] -= value
def emblem_verify(self):
if self.luckyemblemamount < self.luckyemblemrequired:
"""
Making sure lucky emblems have amount>=required.
"""
if self.lucky_emblem_amount < self.lucky_emblem_required:
logging.info(
f"Lucky Emblem Amount {self.multiworld.LuckyEmblemsAmount[self.player].value} is less than required "
f"{self.multiworld.LuckyEmblemsRequired[self.player].value} for player {self.multiworld.get_file_safe_player_name(self.player)}."
f" Setting amount to {self.multiworld.LuckyEmblemsRequired[self.player].value}")
luckyemblemamount = max(self.luckyemblemamount, self.luckyemblemrequired)
self.multiworld.LuckyEmblemsAmount[self.player].value = luckyemblemamount
f"Lucky Emblem Amount {self.options.LuckyEmblemsAmount.value} is less than required "
f"{self.options.LuckyEmblemsRequired.value} for player {self.multiworld.get_file_safe_player_name(self.player)}."
f" Setting amount to {self.options.LuckyEmblemsRequired.value}")
luckyemblemamount = max(self.lucky_emblem_amount, self.lucky_emblem_required)
self.options.LuckyEmblemsAmount.value = luckyemblemamount
self.item_quantity_dict[ItemName.LuckyEmblem] = self.multiworld.LuckyEmblemsAmount[self.player].value
self.item_quantity_dict[ItemName.LuckyEmblem] = self.options.LuckyEmblemsAmount.value
# give this proof to unlock the final door once the player has the amount of lucky emblem required
self.item_quantity_dict[ItemName.ProofofNonexistence] = 0
if ItemName.ProofofNonexistence in self.item_quantity_dict:
del self.item_quantity_dict[ItemName.ProofofNonexistence]
def hitlist_verify(self):
"""
Making sure hitlist have amount>=required.
"""
for location in self.multiworld.exclude_locations[self.player].value:
if location in self.RandomSuperBoss:
self.RandomSuperBoss.remove(location)
if location in self.random_super_boss_list:
self.random_super_boss_list.remove(location)
if not self.options.SummonLevelLocationToggle:
self.random_super_boss_list.remove(LocationName.Summonlvl7)
# Testing if the player has the right amount of Bounties for Completion.
if len(self.RandomSuperBoss) < self.BountiesAmount:
if len(self.random_super_boss_list) < self.bounties_amount:
logging.info(
f"{self.multiworld.get_file_safe_player_name(self.player)} has more bounties than bosses."
f" Setting total bounties to {len(self.RandomSuperBoss)}")
self.BountiesAmount = len(self.RandomSuperBoss)
self.multiworld.BountyAmount[self.player].value = self.BountiesAmount
f" Setting total bounties to {len(self.random_super_boss_list)}")
self.bounties_amount = len(self.random_super_boss_list)
self.options.BountyAmount.value = self.bounties_amount
if len(self.RandomSuperBoss) < self.BountiesRequired:
if len(self.random_super_boss_list) < self.bounties_required:
logging.info(f"{self.multiworld.get_file_safe_player_name(self.player)} has too many required bounties."
f" Setting required bounties to {len(self.RandomSuperBoss)}")
self.BountiesRequired = len(self.RandomSuperBoss)
self.multiworld.BountyRequired[self.player].value = self.BountiesRequired
f" Setting required bounties to {len(self.random_super_boss_list)}")
self.bounties_required = len(self.random_super_boss_list)
self.options.BountyRequired.value = self.bounties_required
if self.BountiesAmount < self.BountiesRequired:
logging.info(f"Bounties Amount {self.multiworld.BountyAmount[self.player].value} is less than required "
f"{self.multiworld.BountyRequired[self.player].value} for player {self.multiworld.get_file_safe_player_name(self.player)}."
f" Setting amount to {self.multiworld.BountyRequired[self.player].value}")
self.BountiesAmount = max(self.BountiesAmount, self.BountiesRequired)
self.multiworld.BountyAmount[self.player].value = self.BountiesAmount
if self.bounties_amount < self.bounties_required:
logging.info(
f"Bounties Amount is less than required for player {self.multiworld.get_file_safe_player_name(self.player)}."
f" Swapping Amount and Required")
temp = self.options.BountyRequired.value
self.options.BountyRequired.value = self.options.BountyAmount.value
self.options.BountyAmount.value = temp
self.multiworld.start_hints[self.player].value.add(ItemName.Bounty)
self.item_quantity_dict[ItemName.ProofofNonexistence] = 0
if self.options.BountyStartingHintToggle:
self.multiworld.start_hints[self.player].value.add(ItemName.Bounty)
if ItemName.ProofofNonexistence in self.item_quantity_dict:
del self.item_quantity_dict[ItemName.ProofofNonexistence]
def set_excluded_locations(self):
"""
Fills excluded_locations from player's settings.
"""
# Option to turn off all superbosses. Can do this individually but its like 20+ checks
if not self.multiworld.SuperBosses[self.player] and not self.multiworld.Goal[self.player] == "hitlist":
for superboss in exclusion_table["Datas"]:
self.multiworld.exclude_locations[self.player].value.add(superboss)
if not self.options.SuperBosses:
for superboss in exclusion_table["SuperBosses"]:
self.multiworld.exclude_locations[self.player].value.add(superboss)
# Option to turn off Olympus Colosseum Cups.
if self.multiworld.Cups[self.player] == "no_cups":
if self.options.Cups == "no_cups":
for cup in exclusion_table["Cups"]:
self.multiworld.exclude_locations[self.player].value.add(cup)
# exclude only hades paradox. If cups and hades paradox then nothing is excluded
elif self.multiworld.Cups[self.player] == "cups":
elif self.options.Cups == "cups":
self.multiworld.exclude_locations[self.player].value.add(LocationName.HadesCupTrophyParadoxCups)
if not self.options.AtlanticaToggle:
for loc in exclusion_table["Atlantica"]:
self.multiworld.exclude_locations[self.player].value.add(loc)
def level_subtraction(self):
# there are levels but level 1 is there for the yamls
if self.multiworld.LevelDepth[self.player] == "level_99_sanity":
# level 99 sanity
self.totalLocations -= 1
elif self.multiworld.LevelDepth[self.player] == "level_50_sanity":
"""
Determine how many locations are on sora's levels.
"""
if self.options.LevelDepth == "level_50_sanity":
# level 50 sanity
self.totalLocations -= 50
elif self.multiworld.LevelDepth[self.player] == "level_1":
return 49
elif self.options.LevelDepth == "level_1":
# level 1. No checks on levels
self.totalLocations -= 99
return 98
elif self.options.LevelDepth in ["level_50", "level_99"]:
# could be if leveldepth!= 99 sanity but this reads better imo
return 75
else:
# level 50/99 since they contain the same amount of levels
self.totalLocations -= 76
return 0
def get_filler_item_name(self) -> str:
return self.multiworld.random.choice(
[ItemName.PowerBoost, ItemName.MagicBoost, ItemName.DefenseBoost, ItemName.APBoost])
"""
Returns random filler item name.
"""
return self.random.choice(filler_items)