mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00

__init__: - Added exception for if the player has too many excluded abilities on keyblades. - Fixed Action Abilities only on keyblades from breaking. - Added proper support for ability quantity's instead of 1 of the ability - Moved filling the localitems slot data to init instead of generate_output so I could easily unit test it TestSlotData: - Checks if the "localItems" part of slot data is filled. This is used for keeping track of local items and making sure nothing dupes
245 lines
10 KiB
Python
245 lines
10 KiB
Python
import logging
|
|
|
|
import yaml
|
|
import os
|
|
import Utils
|
|
import zipfile
|
|
|
|
from .Items import item_dictionary_table, CheckDupingItems
|
|
from .Locations import all_locations, SoraLevels, exclusion_table
|
|
from .XPValues import lvlStats, formExp, soraExp
|
|
from worlds.Files import APContainer
|
|
|
|
|
|
class KH2Container(APContainer):
|
|
game: str = 'Kingdom Hearts 2'
|
|
|
|
def __init__(self, patch_data: dict, base_path: str, output_directory: str,
|
|
player=None, player_name: str = "", server: str = ""):
|
|
self.patch_data = patch_data
|
|
self.file_path = base_path
|
|
container_path = os.path.join(output_directory, base_path + ".zip")
|
|
super().__init__(container_path, player, player_name, server)
|
|
|
|
def write_contents(self, opened_zipfile: zipfile.ZipFile) -> None:
|
|
for filename, yml in self.patch_data.items():
|
|
opened_zipfile.writestr(filename, yml)
|
|
for root, dirs, files in os.walk(os.path.join(os.path.dirname(__file__), "mod_template")):
|
|
for file in files:
|
|
opened_zipfile.write(os.path.join(root, file),
|
|
os.path.relpath(os.path.join(root, file),
|
|
os.path.join(os.path.dirname(__file__), "mod_template")))
|
|
# opened_zipfile.writestr(self.zpf_path, self.patch_data)
|
|
super().write_contents(opened_zipfile)
|
|
|
|
|
|
def patch_kh2(self, output_directory):
|
|
def increaseStat(i):
|
|
if lvlStats[i] == "str":
|
|
self.strength += 2
|
|
elif lvlStats[i] == "mag":
|
|
self.magic += 2
|
|
elif lvlStats[i] == "def":
|
|
self.defense += 1
|
|
elif lvlStats[i] == "ap":
|
|
self.ap += 3
|
|
|
|
self.formattedTrsr = {}
|
|
self.formattedBons = []
|
|
self.formattedLvup = {"Sora": {}}
|
|
self.formattedBons = {}
|
|
self.formattedFmlv = {}
|
|
self.formattedItem = {"Stats": []}
|
|
self.formattedPlrp = []
|
|
self.strength = 2
|
|
self.magic = 6
|
|
self.defense = 2
|
|
self.ap = 0
|
|
self.dblbonus = 0
|
|
formexp = None
|
|
formName = None
|
|
levelsetting = list()
|
|
slotDataDuping = set()
|
|
for values in CheckDupingItems.values():
|
|
if isinstance(values, set):
|
|
slotDataDuping = slotDataDuping.union(values)
|
|
else:
|
|
for inner_values in values.values():
|
|
slotDataDuping = slotDataDuping.union(inner_values)
|
|
|
|
if self.multiworld.Keyblade_Minimum[self.player].value > self.multiworld.Keyblade_Maximum[self.player].value:
|
|
logging.info(
|
|
f"{self.multiworld.get_file_safe_player_name(self.player)} has Keyblade Minimum greater than Keyblade Maximum")
|
|
keyblademin = self.multiworld.Keyblade_Maximum[self.player].value
|
|
keyblademax = self.multiworld.Keyblade_Minimum[self.player].value
|
|
else:
|
|
keyblademin = self.multiworld.Keyblade_Minimum[self.player].value
|
|
keyblademax = self.multiworld.Keyblade_Maximum[self.player].value
|
|
|
|
if self.multiworld.LevelDepth[self.player] == "level_50":
|
|
levelsetting.extend(exclusion_table["Level50"])
|
|
|
|
elif self.multiworld.LevelDepth[self.player] == "level_99":
|
|
levelsetting.extend(exclusion_table["Level99"])
|
|
|
|
elif self.multiworld.LevelDepth[self.player] != "level_1":
|
|
levelsetting.extend(exclusion_table["Level50Sanity"])
|
|
|
|
if self.multiworld.LevelDepth[self.player] == "level_99_sanity":
|
|
levelsetting.extend(exclusion_table["Level99Sanity"])
|
|
|
|
mod_name = f"AP-{self.multiworld.seed_name}-P{self.player}-{self.multiworld.get_file_safe_player_name(self.player)}"
|
|
|
|
for location in self.multiworld.get_filled_locations(self.player):
|
|
|
|
data = all_locations[location.name]
|
|
if location.item.player == self.player:
|
|
itemcode = item_dictionary_table[location.item.name].kh2id
|
|
else:
|
|
itemcode = 90 # castle map
|
|
|
|
if data.yml == "Chest":
|
|
self.formattedTrsr[data.locid] = {"ItemId": itemcode}
|
|
|
|
elif data.yml in ["Get Bonus", "Double Get Bonus", "Second Get Bonus"]:
|
|
if data.yml == "Get Bonus":
|
|
self.dblbonus = 0
|
|
# if double bonus then addresses dbl bonus so the next check gets 2 items on it
|
|
if data.yml == "Double Get Bonus":
|
|
self.dblbonus = itemcode
|
|
continue
|
|
if data.locid not in self.formattedBons.keys():
|
|
self.formattedBons[data.locid] = {}
|
|
self.formattedBons[data.locid][data.charName] = {
|
|
"RewardId": data.locid,
|
|
"CharacterId": data.charNumber,
|
|
"HpIncrease": 0,
|
|
"MpIncrease": 0,
|
|
"DriveGaugeUpgrade": 0,
|
|
"ItemSlotUpgrade": 0,
|
|
"AccessorySlotUpgrade": 0,
|
|
"ArmorSlotUpgrade": 0,
|
|
"BonusItem1": itemcode,
|
|
"BonusItem2": self.dblbonus,
|
|
"Padding": 0
|
|
|
|
}
|
|
# putting dbl bonus at 0 again, so we don't have the same item placed multiple time
|
|
self.dblbonus = 0
|
|
elif data.yml == "Keyblade":
|
|
self.formattedItem["Stats"].append({
|
|
"Id": data.locid,
|
|
"Attack": self.multiworld.per_slot_randoms[self.player].randint(keyblademin, keyblademax),
|
|
"Magic": self.multiworld.per_slot_randoms[self.player].randint(keyblademin, keyblademax),
|
|
"Defense": 0,
|
|
"Ability": itemcode,
|
|
"AbilityPoints": 0,
|
|
"Unknown08": 100,
|
|
"FireResistance": 100,
|
|
"IceResistance": 100,
|
|
"LightningResistance": 100,
|
|
"DarkResistance": 100,
|
|
"Unknown0d": 100,
|
|
"GeneralResistance": 100,
|
|
"Unknown": 0
|
|
})
|
|
|
|
elif data.yml == "Forms":
|
|
# loc id is form lvl
|
|
# char name is the form name number :)
|
|
if data.locid == 2:
|
|
formDict = {1: "Valor", 2: "Wisdom", 3: "Limit", 4: "Master", 5: "Final"}
|
|
formDictExp = {
|
|
1: self.multiworld.Valor_Form_EXP[self.player].value,
|
|
2: self.multiworld.Wisdom_Form_EXP[self.player].value,
|
|
3: self.multiworld.Limit_Form_EXP[self.player].value,
|
|
4: self.multiworld.Master_Form_EXP[self.player].value,
|
|
5: self.multiworld.Final_Form_EXP[self.player].value}
|
|
formexp = formDictExp[data.charName]
|
|
formName = formDict[data.charName]
|
|
self.formattedFmlv[formName] = []
|
|
self.formattedFmlv[formName].append({
|
|
"Ability": 1,
|
|
"Experience": int(formExp[data.charName][data.locid] / formexp),
|
|
"FormId": data.charName,
|
|
"FormLevel": 1,
|
|
"GrowthAbilityLevel": 0,
|
|
})
|
|
# row is form column is lvl
|
|
self.formattedFmlv[formName].append({
|
|
"Ability": itemcode,
|
|
"Experience": int(formExp[data.charName][data.locid] / formexp),
|
|
"FormId": data.charName,
|
|
"FormLevel": data.locid,
|
|
"GrowthAbilityLevel": 0,
|
|
})
|
|
|
|
# Summons have no checks on them so done fully locally
|
|
self.formattedFmlv["Summon"] = []
|
|
for x in range(1, 7):
|
|
self.formattedFmlv["Summon"].append({
|
|
"Ability": 123,
|
|
"Experience": int(formExp[0][x] / self.multiworld.Summon_EXP[self.player].value),
|
|
"FormId": 0,
|
|
"FormLevel": x,
|
|
"GrowthAbilityLevel": 0,
|
|
})
|
|
# levels done down here because of optional settings that can take locations out of the pool.
|
|
self.i = 1
|
|
for location in SoraLevels:
|
|
increaseStat(self.multiworld.per_slot_randoms[self.player].randint(0, 3))
|
|
if location in levelsetting:
|
|
data = self.multiworld.get_location(location, self.player)
|
|
if data.item.player == self.player:
|
|
itemcode = item_dictionary_table[data.item.name].kh2id
|
|
else:
|
|
itemcode = 90 # castle map
|
|
else:
|
|
increaseStat(self.multiworld.per_slot_randoms[self.player].randint(0, 3))
|
|
itemcode = 0
|
|
self.formattedLvup["Sora"][self.i] = {
|
|
"Exp": int(soraExp[self.i] / self.multiworld.Sora_Level_EXP[self.player].value),
|
|
"Strength": self.strength,
|
|
"Magic": self.magic,
|
|
"Defense": self.defense,
|
|
"Ap": self.ap,
|
|
"SwordAbility": itemcode,
|
|
"ShieldAbility": itemcode,
|
|
"StaffAbility": itemcode,
|
|
"Padding": 0,
|
|
"Character": "Sora",
|
|
"Level": self.i
|
|
}
|
|
self.i += 1
|
|
# averaging stats for the struggle bats
|
|
for x in {122, 144, 145}:
|
|
self.formattedItem["Stats"].append({
|
|
"Id": x,
|
|
"Attack": int((keyblademin + keyblademax) / 2),
|
|
"Magic": int((keyblademin + keyblademax) / 2),
|
|
"Defense": 0,
|
|
"Ability": 405,
|
|
"AbilityPoints": 0,
|
|
"Unknown08": 100,
|
|
"FireResistance": 100,
|
|
"IceResistance": 100,
|
|
"LightningResistance": 100,
|
|
"DarkResistance": 100,
|
|
"Unknown0d": 100,
|
|
"GeneralResistance": 100,
|
|
"Unknown": 0
|
|
})
|
|
mod_dir = os.path.join(output_directory, mod_name + "_" + Utils.__version__)
|
|
|
|
openkhmod = {
|
|
"TrsrList.yml": yaml.dump(self.formattedTrsr, line_break="\n"),
|
|
"LvupList.yml": yaml.dump(self.formattedLvup, line_break="\n"),
|
|
"BonsList.yml": yaml.dump(self.formattedBons, line_break="\n"),
|
|
"ItemList.yml": yaml.dump(self.formattedItem, line_break="\n"),
|
|
"FmlvList.yml": yaml.dump(self.formattedFmlv, line_break="\n"),
|
|
}
|
|
|
|
mod = KH2Container(openkhmod, mod_dir, output_directory, self.player,
|
|
self.multiworld.get_file_safe_player_name(self.player))
|
|
mod.write()
|