KH2: init cleanup and random visit locking fix and docs update. (#1652)

Random Visit Locking wasn't copying correctly
init cleanup and moved itempool population to create_items
Updated docs due to a lot of people having issues setting it up.
This commit is contained in:
JaredWeakStrike 2023-04-09 22:12:23 -04:00 committed by GitHub
parent 6059b5ef66
commit 81411a191c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 320 additions and 286 deletions

View File

@ -141,8 +141,8 @@ class Cups(Choice):
class LevelDepth(Choice):
"""Determines How many locations you want on levels
Level 50:23 checks spread through 50 levels.
Level 99:23 checks spread through 99 levels.
Level 50: 23 checks spread through 50 levels.
Level 99: 23 checks spread through 99 levels.
Level 50 sanity: 49 checks spread through 50 levels.
Level 99 sanity: 98 checks spread through 99 levels.

View File

@ -1,4 +1,3 @@
from BaseClasses import Tutorial, ItemClassification
import logging
@ -41,6 +40,10 @@ class KH2World(World):
def __init__(self, multiworld: "MultiWorld", player: int):
super().__init__(multiworld, player)
self.visitlocking_dict = None
self.plando_locations = None
self.luckyemblemamount = None
self.luckyemblemrequired = None
self.BountiesRequired = None
self.BountiesAmount = None
self.hitlist = None
@ -57,7 +60,6 @@ class KH2World(World):
self.growth_list = list()
for x in range(4):
self.growth_list.extend(Movement_Table.keys())
self.visitlocking_dict = Progression_Dicts["AllVisitLocking"]
def fill_slot_data(self) -> dict:
return {"hitlist": self.hitlist,
@ -78,11 +80,168 @@ class KH2World(World):
return created_item
def create_items(self) -> None:
itempool: typing.List[Item] = []
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}:
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))
for _ in range(max_growth):
random_growth = self.multiworld.per_slot_randoms[self.player].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():
for _ in range(amount):
self.multiworld.push_precollected(self.create_item(item))
self.item_quantity_dict[item] -= 1
self.visitlocking_dict[item] -= 1
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"]:
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):
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)
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 item in item_dictionary_table:
data = self.item_quantity_dict[item]
itempool += [self.create_item(item) for _ in range(data)]
# Creating filler for unfilled locations
itempool += [self.create_filler()
for _ in range(self.totalLocations-len(itempool))]
self.multiworld.itempool += itempool
def generate_early(self) -> None:
# Item Quantity dict because Abilities can be a problem for KH2's Software.
for item, data in item_dictionary_table.items():
self.item_quantity_dict[item] = data.quantity
# Dictionary to mark locations with their plandoed item
# Example. Final Xemnas: Victory
self.plando_locations = dict()
self.hitlist = []
self.starting_invo_verify()
# Option to turn off Promise Charm Item
if not self.multiworld.Promise_Charm[self.player]:
self.item_quantity_dict[ItemName.PromiseCharm] = 0
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
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
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
self.donald_fill()
self.goofy_fill()
self.keyblade_fill()
if self.multiworld.FinalXemnas[self.player]:
self.plando_locations[LocationName.FinalXemnas] = ItemName.Victory
else:
self.plando_locations[LocationName.FinalXemnas] = self.create_filler().name
# 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
def pre_fill(self):
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)
def set_rules(self):
set_rules(self.multiworld, self.player)
def generate_output(self, output_directory: str):
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.extend(SupportAbility_Table.keys())
elif self.multiworld.KeybladeAbilities[self.player] == "action":
@ -93,64 +252,65 @@ class KH2World(World):
self.sora_keyblade_ability_pool.extend(ActionAbility_Table.keys())
self.sora_keyblade_ability_pool.extend(SupportAbility_Table.keys())
for item, value in self.multiworld.start_inventory[self.player].value.items():
if item in ActionAbility_Table.keys() or item in SupportAbility_Table.keys() or exclusionItem_table["StatUps"]:
# cannot have more than the quantity for abilties
if value > item_dictionary_table[item].quantity:
logging.info(f"{self.multiworld.get_file_safe_player_name(self.player)} cannot have more than {item_dictionary_table[item].quantity} of {item}"
f"Changing the amount to the max amount")
value = item_dictionary_table[item].quantity
self.item_quantity_dict[item] -= value
# Option to turn off Promise Charm Item
if not self.multiworld.Promise_Charm[self.player]:
self.item_quantity_dict[ItemName.PromiseCharm] = 0
for ability in self.multiworld.BlacklistKeyblade[self.player].value:
if ability in self.sora_keyblade_ability_pool:
self.sora_keyblade_ability_pool.remove(ability)
# 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)
for superboss in exclusion_table["SuperBosses"]:
self.multiworld.exclude_locations[self.player].value.add(superboss)
while len(self.sora_keyblade_ability_pool) < len(self.keyblade_slot_copy):
self.sora_keyblade_ability_pool.append(
self.multiworld.per_slot_randoms[self.player].choice(list(SupportAbility_Table.keys())))
# Option to turn off Olympus Colosseum Cups.
if self.multiworld.Cups[self.player] == "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":
self.multiworld.exclude_locations[self.player].value.add(LocationName.HadesCupTrophyParadoxCups)
# 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.sora_keyblade_ability_pool)
while random_ability == ItemName.NoExperience:
random_ability = self.multiworld.per_slot_randoms[self.player].choice(self.sora_keyblade_ability_pool)
self.plando_locations[LocationName.KingdomKeySlot] = random_ability
self.item_quantity_dict[random_ability] -= 1
self.sora_keyblade_ability_pool.remove(random_ability)
if self.multiworld.Goal[self.player] == "lucky_emblem_hunt":
luckyemblemamount = self.multiworld.LuckyEmblemsAmount[self.player].value
luckyemblemrequired = self.multiworld.LuckyEmblemsRequired[self.player].value
if luckyemblemamount < luckyemblemrequired:
logging.info(f"Lucky Emblem Amount {self.multiworld.LuckyEmblemsAmount[self.player].value} is less than required "
# 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.sora_keyblade_ability_pool)
self.plando_locations[keyblade] = random_ability
self.item_quantity_dict[random_ability] -= 1
self.sora_keyblade_ability_pool.remove(random_ability)
self.totalLocations -= 1
def starting_invo_verify(self):
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"]:
# cannot have more than the quantity for abilties
if value > item_dictionary_table[item].quantity:
logging.info(
f"{self.multiworld.get_file_safe_player_name(self.player)} cannot have more than {item_dictionary_table[item].quantity} of {item}"
f"Changing the amount to the max amount")
value = item_dictionary_table[item].quantity
self.item_quantity_dict[item] -= value
def emblem_verify(self):
if self.luckyemblemamount < self.luckyemblemrequired:
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(luckyemblemamount, luckyemblemrequired)
luckyemblemamount = max(self.luckyemblemamount, self.luckyemblemrequired)
self.multiworld.LuckyEmblemsAmount[self.player].value = luckyemblemamount
self.item_quantity_dict[ItemName.LuckyEmblem] = item_dictionary_table[ItemName.LuckyEmblem].quantity + luckyemblemamount
self.item_quantity_dict[ItemName.LuckyEmblem] = item_dictionary_table[
ItemName.LuckyEmblem].quantity + self.luckyemblemamount
# 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
# 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
def hitlist_verify(self):
for location in self.multiworld.exclude_locations[self.player].value:
if location in self.RandomSuperBoss:
self.RandomSuperBoss.remove(location)
# Testing if the player has the right amount of Bounties for Completion.
if len(self.RandomSuperBoss) < self.BountiesAmount:
logging.info(f"{self.multiworld.get_file_safe_player_name(self.player)} has too many bounties than bosses."
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
@ -171,139 +331,24 @@ class KH2World(World):
self.multiworld.start_hints[self.player].value.add(ItemName.Bounty)
self.item_quantity_dict[ItemName.ProofofNonexistence] = 0
while len(self.sora_keyblade_ability_pool) < len(self.keyblade_slot_copy):
self.sora_keyblade_ability_pool.append(
self.multiworld.per_slot_randoms[self.player].choice(list(SupportAbility_Table.keys())))
def set_excluded_locations(self):
# 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)
for superboss in exclusion_table["SuperBosses"]:
self.multiworld.exclude_locations[self.player].value.add(superboss)
for item in DonaldAbility_Table.keys():
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))
# Option to turn off Olympus Colosseum Cups.
if self.multiworld.Cups[self.player] == "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":
self.multiworld.exclude_locations[self.player].value.add(LocationName.HadesCupTrophyParadoxCups)
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))
def generate_basic(self):
itempool: typing.List[KH2Item] = []
self.hitlist = list()
self.filler_items.extend(item_groups["Filler"])
if self.multiworld.FinalXemnas[self.player]:
self.multiworld.get_location(LocationName.FinalXemnas, self.player).place_locked_item(
self.create_item(ItemName.Victory))
else:
self.multiworld.get_location(LocationName.FinalXemnas, self.player).place_locked_item(
self.create_item(self.multiworld.per_slot_randoms[self.player].choice(self.filler_items)))
self.totalLocations -= 1
if self.multiworld.Goal[self.player] == "hitlist":
for bounty in range(self.BountiesAmount):
randomBoss = self.multiworld.per_slot_randoms[self.player].choice(self.RandomSuperBoss)
self.multiworld.get_location(randomBoss, self.player).place_locked_item(
self.create_item(ItemName.Bounty))
self.hitlist.append(self.location_name_to_id[randomBoss])
self.RandomSuperBoss.remove(randomBoss)
self.totalLocations -= 1
# 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.sora_keyblade_ability_pool)
while random_ability == ItemName.NoExperience:
random_ability = self.multiworld.per_slot_randoms[self.player].choice(self.sora_keyblade_ability_pool)
self.multiworld.get_location(LocationName.KingdomKeySlot, self.player).place_locked_item(self.create_item(random_ability))
self.item_quantity_dict[random_ability] -= 1
self.sora_keyblade_ability_pool.remove(random_ability)
self.totalLocations -= 1
# 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.sora_keyblade_ability_pool)
self.multiworld.get_location(keyblade, self.player).place_locked_item(self.create_item(random_ability))
self.item_quantity_dict[random_ability] -= 1
self.sora_keyblade_ability_pool.remove(random_ability)
self.totalLocations -= 1
# 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.multiworld.get_location(donaldLocation, self.player).place_locked_item(
self.create_item(random_ability))
self.totalLocations -= 1
self.donald_ability_pool.remove(random_ability)
# 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.multiworld.get_location(goofyLocation, self.player).place_locked_item(self.create_item(random_ability))
self.totalLocations -= 1
self.goofy_ability_pool.remove(random_ability)
# 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.multiworld.per_slot_randoms[self.player].choice(self.filler_items)
self.multiworld.get_location(LocationName.JunkChampionBelt, self.player).place_locked_item(
self.create_item(random_stt_item))
self.multiworld.get_location(LocationName.JunkMedal, self.player).place_locked_item(
self.create_item(random_stt_item))
self.totalLocations -= 2
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}:
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))
for _ in range(max_growth):
random_growth = self.multiworld.per_slot_randoms[self.player].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))
# no visit locking
if self.multiworld.Visitlocking[self.player] == "no_visit_locking":
for item, amount in Progression_Dicts["AllVisitLocking"].items():
for _ in range(amount):
self.multiworld.push_precollected(self.create_item(item))
self.item_quantity_dict[item] -= 1
if self.visitlocking_dict[item] == 0:
self.visitlocking_dict.pop(item)
self.visitlocking_dict[item] -= 1
# first and second visit locking
elif self.multiworld.Visitlocking[self.player] == "second_visit_locking":
for item in Progression_Dicts["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):
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)
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))
# there are levels but level 1 is there to keep code clean
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
@ -317,24 +362,5 @@ class KH2World(World):
# level 50/99 since they contain the same amount of levels
self.totalLocations -= 76
for item in item_dictionary_table:
data = self.item_quantity_dict[item]
for _ in range(data):
itempool.append(self.create_item(item))
# Creating filler for unfilled locations
while len(itempool) < self.totalLocations:
item = self.multiworld.per_slot_randoms[self.player].choice(self.filler_items)
itempool += [self.create_item(item)]
self.multiworld.itempool += itempool
def create_regions(self):
location_table = setup_locations()
create_regions(self.multiworld, self.player, location_table)
connect_regions(self.multiworld, self.player)
def set_rules(self):
set_rules(self.multiworld, self.player)
def generate_output(self, output_directory: str):
patch_kh2(self, output_directory)
def get_filler_item_name(self) -> str:
return self.multiworld.random.choice([ItemName.PowerBoost, ItemName.MagicBoost, ItemName.DefenseBoost, ItemName.APBoost])

View File

@ -72,36 +72,3 @@ With the help of Shananas, Num, and ZakTheRobot we have many QoL features such a
- Removal of Absent Silhouette and go straight into the Data Fights.
- And much more can be found at [Kingdom Hearts 2 GoA Overview](https://tommadness.github.io/KH2Randomizer/overview/)
<h2 style="text-transform:none";>Recommendation</h2>
- Recommended making a save at the start of the GoA before opening anything. This will be the recommended file to load if/when your game crashes.
- If you don't want to have a save in the GoA. Disconnect the client, load the auto save, and then reconnect the client after it loads the auto save.
- Recommended to set fps limit to 60fps.
- Recommended to run the game in windows/borderless windowed mode. Fullscreen is stable but the game can crash if you alt-tab out.
- Recommend viewing [Requirements/logic sheet](https://docs.google.com/spreadsheets/d/1Embae0t7pIrbzvX-NRywk7bTHHEvuFzzQBUUpSUL7Ak/edit?usp=sharing)
<h2 style="text-transform:none";>F.A.Q.</h2>
- Why am I not getting magic?
- If you obtain magic, you will need to pause your game to have it show up in your inventory, then enter a new room for it to become properly usable.
- Why am I missing worlds/portals in the GoA?
- You are missing the required visit locking item to access the world/portal.
- What versions of Kingdom Hearts 2 are supported?
- Currently `only` the most up to date version on the Epic Game Store is supported `1.0.0.8_WW`. Emulator may be added in the future.
- Why did I crash?
- The port of Kingdom Hearts 2 can and will randomly crash, this is the fault of the game not the randomizer or the archipelago client.
- If you have a continuous/constant crash (in the same area/event every time) you will want to reverify your installed files. This can be done by doing the following: Open Epic Game Store --> Library --> Click Triple Dots --> Manage --> Verify
- Why am I getting dummy items or letters?
- You will need to get the `JaredWeakStrike/APCompanion` (you can find how to get this in the setup guide)
- Why is my HP/MP continuously increasing without stopping?
- You do not have `JaredWeakStrike/APCompanion` setup correctly. Make Sure it is above the GOA in the mod manager.
- Why am I not sending or receiving items?
- Make sure you are connected to the KH2 client and the correct room (for more information reference the setup guide)
- Why did I not load in to the correct visit
- You need to trigger a cutscene or visit The World That Never Was for it to update you have recevied the item.
- Why should I install the auto save mod at `KH2FM-Mods-equations19/auto-save`?
- Because Kingdom Hearts 2 is prone to crashes and will keep you from losing your progress.
- How do I load an auto save?
- To load an auto-save, hold down the Select or your equivalent on your prefered controller while choosing a file. Make sure to hold the button down the whole time.
- How do I do a soft reset?
- Hold L1+L2+R1+R2+Start or your equivalent on your prefered controller at the same time to immediately reset the game to the start screen.

View File

@ -1,48 +1,98 @@
# Kingdom Hearts 2 Archipelago Setup Guide
<h2 style="text-transform:none";>Quick Links</h2>
- [Main Page](../../../../games/Kingdom%20Hearts%202/info/en)
- [Settings Page](../../../../games/Kingdom%20Hearts%202/player-settings)
- [Game Info Page](../../../../games/Kingdom%20Hearts%202/info/en)
- [Player Settings Page](../../../../games/Kingdom%20Hearts%202/player-settings)
<h2 style="text-transform:none";>Setting up the Mod Manager</h2>
<h2 style="text-transform:none";>Required Software:</h2>
`Kingdom Hearts II Final Mix` from the [Epic Games Store](https://store.epicgames.com/en-US/discover/kingdom-hearts)
- Follow this Guide to set up these requirements [KH2Rando.com](https://tommadness.github.io/KH2Randomizer/setup/Panacea-ModLoader/)<br>
1. `3.0.0 OpenKH Mod Manager with Panacea`<br>
2. `Install mod from KH2FM-Mods-Num/GoA-ROM-Edition`<br>
3. `Setup Lua Backend From the 3.0.0 KH2Randomizer.exe per the setup guide linked above`<br>
Follow this Guide [KH2Rando.com](https://tommadness.github.io/KH2Randomizer/setup/Panacea-ModLoader/)
- Needed for Archipelago
1. [`ArchipelagoKH2Client.exe`](https://github.com/ArchipelagoMW/Archipelago/releases)<br>
2. `Install mod from JaredWeakStrike/APCompanion`<br>
3. `Install mod from KH2FM-Mods-equations19/auto-save`<br>
4. `AP Randomizer Seed`
<h3 style="text-transform:none";>Required: Archipelago Companion Mod</h3>
<h3 style="text-transform:none";>Loading A Seed</h3>
Load this mod just like the <b>GoA ROM</b> you did during the KH2 Rando setup. `JaredWeakStrike/APCompanion`<br>
Have this mod second-highest priority below the .zip seed.<br>
This mod is based upon Num's Garden of Assemblege Mod and requires it to work. Without Num this could not be possible.
When you generate a game you will see a download link for a KH2 .zip seed on the room page. Download the seed then open OpenKH Mod Manager and click the green plus and `Select and install Mod Archive`. Make sure the seed is on the top of the list (Highest Priority)
<h3 style="text-transform:none";>Required: Auto Save Mod</h3>
Load this mod just like the GoA ROM you did during the KH2 Rando setup. `KH2FM-Mods-equations19/auto-save` Location doesn't matter, required in case of crashes.
<h3 style="text-transform:none";>Archipelago Compainion Mod and recommended mods</h3>
<h3 style="text-transform:none";>Installing A Seed</h3>
Load this mod just like the GoA ROM you did during the KH2 Rando setup. `JaredWeakStrike/APCompanion` Have this mod second highest priority below the .zip seed
Load this mod just like the GoA ROM you did during the KH2 Rando setup. `KH2FM-Mods-equations19/auto-save` Location doesn't matter, recommended in case of crashes.
Load this mod just like the GoA ROM you did during the KH2 Rando setup. `KH2FM-Mods-equations19/soft-reset` Location doesn't matter, recommneded in case of soft locks.
When you generate a game you will see a download link for a KH2 .zip seed on the room page. Download the seed then open OpenKH Mod Manager and click the green plus and `Select and install Mod Archive`.<br>
Make sure the seed is on the top of the list (Highest Priority)<br>
After Installing the seed click `Mod Loader -> Build/Build and Run`. Every slot is a unique mod to install and will be needed be repatched for different slots/rooms.
<h2 style="text-transform:none";>What the Mod Manager Should Look Like.</h2>
![image](https://i.imgur.com/QgRfjP1.png)
<h2 style="text-transform:none";>Using the KH2 Client</h2>
Once you have started the game through OpenKH Mod Manager and are on the title screen run the ArchipelagoKH2Client.exe. When you successfully connect to the server the client will automatically hook into the game to send/receive checks. If the client ever loses connection to the game, it will also disconnect from the server and you will need to reconnect. Make sure the game is open whenever you try to connect the client to the server otherwise it will immediately disconnect you. Most checks will be sent to you anywhere outside of a load or cutscene but if you obtain magic, you will need to pause your game to have it show up in your inventory, then enter a new room for it to become properly usable.
Once you have started the game through OpenKH Mod Manager and are on the title screen run the [ArchipelagoKH2Client.exe](https://github.com/ArchipelagoMW/Archipelago/releases). <br>
When you successfully connect to the server the client will automatically hook into the game to send/receive checks. <br>
If the client ever loses connection to the game, it will also disconnect from the server and you will need to reconnect.<br>
`Make sure the game is open whenever you try to connect the client to the server otherwise it will immediately disconnect you.`<br>
Most checks will be sent to you anywhere outside a load or cutscene.<br>
`If you obtain magic, you will need to pause your game to have it show up in your inventory, then enter a new room for it to become properly usable.`
<br>
<h2 style="text-transform:none";>KH2 Client should look like this: </h2>
![image](https://i.imgur.com/qP6CmV8.png)
<br>
Enter `The room's port number` into the top box <b> where the x's are</b> and press "Connect". Follow the prompts there and you should be connected
<h2 style="text-transform:none";>Generating a game</h2>
<h3 style="text-transform:none";>What is a YAML?</h3>
<h2 style="text-transform:none";>Common Pitfalls</h2>
- Having an old GOA Lua Script in your `C:\Users\*YourName*\Documents\KINGDOM HEARTS HD 1.5+2.5 ReMIX\scripts\kh2` folder.
- Pressing F2 while in game should look like this. ![image](https://i.imgur.com/ABSdtPC.png)
<br>
- Not having Lua Backend Configured Correctly.
- To fix this look over the guide at [KH2Rando.com](https://tommadness.github.io/KH2Randomizer/setup/Panacea-ModLoader/). Specifically the Lua Backend Configuration Step.
<br>
- Loading into Simulated Twilight Town Instead of the GOA.
- To fix this look over the guide at [KH2Rando.com](https://tommadness.github.io/KH2Randomizer/setup/Panacea-ModLoader/). Specifically the Panacea and Lua Backend Steps.
YAML is the file format which Archipelago uses in order to configure a player's world. It allows you to dictate which
game you will be playing as well as the settings you would like for that game.
YAML is a format very similar to JSON however it is made to be more human-readable. If you are ever unsure of the
validity of your YAML file you may check the file by uploading it to the check page on the Archipelago website. Check
page: [YAML Validation Page](/mysterycheck)
<h2 style="text-transform:none";>Best Practices</h2>
<h3 style="text-transform:none";>Creating a YAML</h3>
- Make a save at the start of the GoA before opening anything. This will be the file to select when loading an autosave if/when your game crashes.
- If you don't want to have a save in the GoA. Disconnect the client, load the auto save, and then reconnect the client after it loads the auto save.
- Set fps limit to 60fps.
- Run the game in windows/borderless windowed mode. Fullscreen is stable but the game can crash if you alt-tab out.
YAML files may be generated on the Archipelago website by visiting the games page and clicking the "Settings Page" link
under any game. Clicking "Export Settings" in a game's settings page will download the YAML to your system. Games
page: [Archipelago Games List](/games)
<h2 style="text-transform:none";>Requirement/logic sheet</h2>
Have any questions on what's in logic? This spreadsheet has the answer [Requirements/logic sheet](https://docs.google.com/spreadsheets/d/1Embae0t7pIrbzvX-NRywk7bTHHEvuFzzQBUUpSUL7Ak/edit?usp=sharing)
<h2 style="text-transform:none";>F.A.Q.</h2>
In a multiworld there must be one YAML per world. Any number of players can play on each world using either the game's
native coop system or using Archipelago's coop support. Each world will hold one slot in the multiworld and will have a
slot name and, if the relevant game requires it, files to associate it with that multiworld.
- Why am I getting wallpapered while going into a world for the first time?
- Your `Lua Backend` was not configured correctly. Look over the step in the [KH2Rando.com](https://tommadness.github.io/KH2Randomizer/setup/Panacea-ModLoader/) guide.
- Why am I not getting magic?
- If you obtain magic, you will need to pause your game to have it show up in your inventory, then enter a new room for it to become properly usable.
- Why am I missing worlds/portals in the GoA?
- You are missing the required visit locking item to access the world/portal.
- What versions of Kingdom Hearts 2 are supported?
- Currently `only` the most up to date version on the Epic Game Store is supported `1.0.0.8_WW`. Emulator may be added in the future.
- Why did I crash?
- The port of Kingdom Hearts 2 can and will randomly crash, this is the fault of the game not the randomizer or the archipelago client.
- If you have a continuous/constant crash (in the same area/event every time) you will want to reverify your installed files. This can be done by doing the following: Open Epic Game Store --> Library --> Click Triple Dots --> Manage --> Verify
- Why am I getting dummy items or letters?
- You will need to get the `JaredWeakStrike/APCompanion` (you can find how to get this if you scroll up)
- Why is my HP/MP continuously increasing without stopping?
- You do not have `JaredWeakStrike/APCompanion` setup correctly. Make Sure it is above the GOA in the mod manager.
- Why am I not sending or receiving items?
- Make sure you are connected to the KH2 client and the correct room (for more information scroll up)
- Why did I not load in to the correct visit
- You need to trigger a cutscene or visit The World That Never Was for it to update you have recevied the item.
- Why should I install the auto save mod at `KH2FM-Mods-equations19/auto-save`?
- Because Kingdom Hearts 2 is prone to crashes and will keep you from losing your progress.
- How do I load an auto save?
- To load an auto-save, hold down the Select or your equivalent on your prefered controller while choosing a file. Make sure to hold the button down the whole time.
If multiple people plan to play in one world cooperatively then they will only need one YAML for their coop world. If
each player is planning on playing their own game then they will each need a YAML.

View File

@ -1,16 +1,4 @@
assets:
- method: binarc
multi:
- name: msg/us/jm.bar
- name: msg/uk/jm.bar
name: msg/jp/jm.bar
source:
- method: kh2msg
name: jm
source:
- language: en
name: jm.yml
type: list
- method: binarc
name: 00battle.bin
source:

View File

@ -1,5 +1,6 @@
from . import KH2TestBase
from ..Names import ItemName,LocationName,RegionName
from ..Names import ItemName
class TestDefault(KH2TestBase):
options = {}
@ -18,10 +19,12 @@ class TestLuckyEmblem(KH2TestBase):
self.collect_all_but([ItemName.LuckyEmblem])
self.assertBeatable(True)
class TestHitList(KH2TestBase):
options = {
"Goal": 2,
}
def testEverything(self):
self.collect_all_but([ItemName.Bounty])
self.assertBeatable(True)