 a7384b4b63
			
		
	
	a7384b4b63
	
	
	
		
			
			* update the multiworld to options * Update worlds/kh2/Rules.py Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com> * does this work * namine sketches * wrong branch :) --------- Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>
		
			
				
	
	
		
			430 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			430 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import logging
 | |
| 
 | |
| import yaml
 | |
| import os
 | |
| import Utils
 | |
| import zipfile
 | |
| 
 | |
| from .Items import item_dictionary_table
 | |
| 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)
 | |
|         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()
 | |
| 
 | |
|     if self.options.Keyblade_Minimum.value > self.options.Keyblade_Maximum.value:
 | |
|         logging.info(
 | |
|                 f"{self.multiworld.get_file_safe_player_name(self.player)} has Keyblade Minimum greater than Keyblade Maximum")
 | |
|         keyblademin = self.options.Keyblade_Maximum.value
 | |
|         keyblademax = self.options.Keyblade_Minimum.value
 | |
|     else:
 | |
|         keyblademin = self.options.Keyblade_Minimum.value
 | |
|         keyblademax = self.options.Keyblade_Maximum.value
 | |
| 
 | |
|     if self.options.LevelDepth == "level_50":
 | |
|         levelsetting.extend(exclusion_table["Level50"])
 | |
| 
 | |
|     elif self.options.LevelDepth == "level_99":
 | |
|         levelsetting.extend(exclusion_table["Level99"])
 | |
| 
 | |
|     elif self.options.LevelDepth != "level_1":
 | |
|         levelsetting.extend(exclusion_table["Level50Sanity"])
 | |
| 
 | |
|         if self.options.LevelDepth == "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)}"
 | |
|     all_valid_locations = {location for location, data in all_locations.items()}
 | |
| 
 | |
|     for location in self.multiworld.get_filled_locations(self.player):
 | |
|         if location.name in all_valid_locations:
 | |
|             data = all_locations[location.name]
 | |
|         else:
 | |
|             continue
 | |
|         if location.item:
 | |
|             if location.item.player == self.player:
 | |
|                 itemcode = item_dictionary_table[location.item.name].kh2id
 | |
|             else:
 | |
|                 itemcode = 90  # castle map
 | |
|         else:
 | |
|             itemcode = 90
 | |
| 
 | |
|         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.random.randint(keyblademin, keyblademax),
 | |
|                 "Magic":               self.random.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.options.Valor_Form_EXP.value,
 | |
|                     2: self.options.Wisdom_Form_EXP.value,
 | |
|                     3: self.options.Limit_Form_EXP.value,
 | |
|                     4: self.options.Master_Form_EXP.value,
 | |
|                     5: self.options.Final_Form_EXP.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 actual locations so done down here.
 | |
|     self.formattedFmlv["Summon"] = []
 | |
|     for x in range(1, 7):
 | |
|         self.formattedFmlv["Summon"].append({
 | |
|             "Ability":            123,
 | |
|             "Experience":         int(formExp[0][x] / self.options.Summon_EXP.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 = 2
 | |
|     for location in SoraLevels:
 | |
|         increaseStat(self.random.randint(0, 3))
 | |
|         if location in levelsetting:
 | |
|             data = self.multiworld.get_location(location, self.player)
 | |
|             if data.item:
 | |
|                 if data.item.player == self.player:
 | |
|                     itemcode = item_dictionary_table[data.item.name].kh2id
 | |
|                 else:
 | |
|                     itemcode = 90  # castle map
 | |
|         else:
 | |
|             increaseStat(self.random.randint(0, 3))
 | |
|             itemcode = 0
 | |
|         self.formattedLvup["Sora"][self.i] = {
 | |
|             "Exp":           int(soraExp[self.i] / self.options.Sora_Level_EXP.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
 | |
|         })
 | |
|     self.formattedLvup["Sora"][1] = {
 | |
|         "Exp":           int(soraExp[1] / self.options.Sora_Level_EXP.value),
 | |
|         "Strength":      2,
 | |
|         "Magic":         6,
 | |
|         "Defense":       2,
 | |
|         "Ap":            0,
 | |
|         "SwordAbility":  0,
 | |
|         "ShieldAbility": 0,
 | |
|         "StaffAbility":  0,
 | |
|         "Padding":       0,
 | |
|         "Character":     "Sora",
 | |
|         "Level":         1
 | |
|     }
 | |
|     self.mod_yml = {
 | |
|         "assets": [
 | |
|             {
 | |
|                 'method': 'binarc',
 | |
|                 'name':   '00battle.bin',
 | |
|                 'source': [
 | |
|                     {
 | |
|                         'method': 'listpatch',
 | |
|                         'name':   'fmlv',
 | |
|                         'source': [
 | |
|                             {
 | |
|                                 'name': 'FmlvList.yml',
 | |
|                                 'type': 'fmlv'
 | |
|                             }
 | |
|                         ],
 | |
|                         'type':   'List'
 | |
|                     },
 | |
|                     {
 | |
|                         'method': 'listpatch',
 | |
|                         'name':   'lvup',
 | |
|                         'source': [
 | |
|                             {
 | |
|                                 'name': 'LvupList.yml',
 | |
|                                 'type': 'lvup'
 | |
|                             }
 | |
|                         ],
 | |
|                         'type':   'List'
 | |
|                     },
 | |
|                     {
 | |
|                         'method': 'listpatch',
 | |
|                         'name':   'bons',
 | |
|                         'source': [
 | |
|                             {
 | |
|                                 'name': 'BonsList.yml',
 | |
|                                 'type': 'bons'
 | |
|                             }
 | |
|                         ],
 | |
|                         'type':   'List'
 | |
|                     }
 | |
|                 ]
 | |
|             },
 | |
|             {
 | |
|                 'method': 'binarc',
 | |
|                 'name':   '03system.bin',
 | |
|                 'source': [
 | |
|                     {
 | |
|                         'method': 'listpatch',
 | |
|                         'name':   'trsr',
 | |
|                         'source': [
 | |
|                             {
 | |
|                                 'name': 'TrsrList.yml',
 | |
|                                 'type': 'trsr'
 | |
|                             }
 | |
|                         ],
 | |
|                         'type':   'List'
 | |
|                     },
 | |
|                     {
 | |
|                         'method': 'listpatch',
 | |
|                         'name':   'item',
 | |
|                         'source': [
 | |
|                             {
 | |
|                                 'name': 'ItemList.yml',
 | |
|                                 'type': 'item'
 | |
|                             }
 | |
|                         ],
 | |
|                         'type':   'List'
 | |
|                     }
 | |
|                 ]
 | |
|             },
 | |
|             {
 | |
|                 'name':   'msg/us/po.bar',
 | |
|                 'multi':  [
 | |
|                     {
 | |
|                         'name': 'msg/fr/po.bar'
 | |
|                     },
 | |
|                     {
 | |
|                         'name': 'msg/gr/po.bar'
 | |
|                     },
 | |
|                     {
 | |
|                         'name': 'msg/it/po.bar'
 | |
|                     },
 | |
|                     {
 | |
|                         'name': 'msg/sp/po.bar'
 | |
|                     }
 | |
|                 ],
 | |
|                 'method': 'binarc',
 | |
|                 'source': [
 | |
|                     {
 | |
|                         'name':   'po',
 | |
|                         'type':   'list',
 | |
|                         'method': 'kh2msg',
 | |
|                         'source': [
 | |
|                             {
 | |
|                                 'name':     'po.yml',
 | |
|                                 'language': 'en'
 | |
|                             }
 | |
|                         ]
 | |
|                     }
 | |
|                 ]
 | |
|             },
 | |
|             {
 | |
|                 'name':   'msg/us/sys.bar',
 | |
|                 'multi':  [
 | |
|                     {
 | |
|                         'name': 'msg/fr/sys.bar'
 | |
|                     },
 | |
|                     {
 | |
|                         'name': 'msg/gr/sys.bar'
 | |
|                     },
 | |
|                     {
 | |
|                         'name': 'msg/it/sys.bar'
 | |
|                     },
 | |
|                     {
 | |
|                         'name': 'msg/sp/sys.bar'
 | |
|                     }
 | |
|                 ],
 | |
|                 'method': 'binarc',
 | |
|                 'source': [
 | |
|                     {
 | |
|                         'name':   'sys',
 | |
|                         'type':   'list',
 | |
|                         'method': 'kh2msg',
 | |
|                         'source': [
 | |
|                             {
 | |
|                                 'name':     'sys.yml',
 | |
|                                 'language': 'en'
 | |
|                             }
 | |
|                         ]
 | |
|                     }
 | |
|                 ]
 | |
|             },
 | |
|         ],
 | |
|         'title':  'Randomizer Seed'
 | |
|     }
 | |
| 
 | |
|     goal_to_text = {
 | |
|         0: "Three Proofs",
 | |
|         1: "Lucky Emblem",
 | |
|         2: "Hitlist",
 | |
|         3: "Lucky Emblem and Hitlist",
 | |
|     }
 | |
|     lucky_emblem_text = {
 | |
|         0: "Your Goal is not Lucky Emblem. It is Hitlist or Three Proofs.",
 | |
|         1: f"Lucky Emblem Required: {self.options.LuckyEmblemsRequired} out of {self.options.LuckyEmblemsAmount}",
 | |
|         2: "Your Goal is not Lucky Emblem. It is Hitlist or Three Proofs.",
 | |
|         3: f"Lucky Emblem Required: {self.options.LuckyEmblemsRequired} out of {self.options.LuckyEmblemsAmount}"
 | |
|     }
 | |
|     hitlist_text = {
 | |
|         0: "Your Goal is not Hitlist. It is Lucky Emblem or Three Proofs",
 | |
|         1: "Your Goal is not Hitlist. It is Lucky Emblem or Three Proofs",
 | |
|         2: f"Bounties Required: {self.options.BountyRequired} out of {self.options.BountyAmount}",
 | |
|         3: f"Bounties Required: {self.options.BountyRequired} out of {self.options.BountyAmount}",
 | |
|     }
 | |
| 
 | |
|     self.pooh_text = [
 | |
|         {
 | |
|             'id': 18326,
 | |
|             'en': f"Your goal is {goal_to_text[self.options.Goal.value]}"
 | |
|         },
 | |
|         {
 | |
|             'id': 18327,
 | |
|             'en': lucky_emblem_text[self.options.Goal.value]
 | |
|         },
 | |
|         {
 | |
|             'id': 18328,
 | |
|             'en': hitlist_text[self.options.Goal.value]
 | |
|         }
 | |
|     ]
 | |
|     self.level_depth_text = [
 | |
|         {
 | |
|             'id': 0x3BF1,
 | |
|             'en': f"Your Level Depth is {self.options.LevelDepth.current_option_name}"
 | |
|         }
 | |
|     ]
 | |
|     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.yml":      yaml.dump(self.mod_yml, line_break="\n"),
 | |
|         "po.yml":       yaml.dump(self.pooh_text, line_break="\n"),
 | |
|         "sys.yml":      yaml.dump(self.level_depth_text, line_break="\n"),
 | |
|     }
 | |
| 
 | |
|     mod = KH2Container(openkhmod, mod_dir, output_directory, self.player,
 | |
|             self.multiworld.get_file_safe_player_name(self.player))
 | |
|     mod.write()
 |