You've already forked KemoFureApi
							
							Initial API (Kingdom + KF3)
This commit is contained in:
		
							
								
								
									
										133
									
								
								loaders/KF3/charaData.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								loaders/KF3/charaData.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,133 @@ | ||||
| import json | ||||
| import math | ||||
| from collections import defaultdict | ||||
|  | ||||
| def GetName(charaData): | ||||
|     name = "no name" | ||||
|     if charaData is not None: | ||||
|         if charaData["nameEn"] != "": | ||||
|             name = charaData["nameEn"] | ||||
|         elif charaData["name"] != "": | ||||
|             name = charaData["name"] | ||||
|         if charaData["nickname"] != "": | ||||
|             name += " " + charaData["nickname"] | ||||
|     return name | ||||
|  | ||||
| def GetAttackBase(alphaBase, level): | ||||
|     if level <= alphaBase["atkLvMiddleNum"]: | ||||
|         t = (level - 1) / (alphaBase["atkLvMiddleNum"]-1) | ||||
|         return lerp(alphaBase["atkParamLv1"], alphaBase["atkParamLvMiddle"], t) | ||||
|     else: | ||||
|         t = (level - alphaBase["atkLvMiddleNum"]) / (99 - alphaBase["atkLvMiddleNum"]) | ||||
|         return lerp(alphaBase["atkParamLvMiddle"], alphaBase["atkParamLv99"], t) | ||||
|  | ||||
| def GetDefenseBase(alphaBase, level): | ||||
|     if level <= alphaBase["defLvMiddleNum"]: | ||||
|         t = (level - 1) / (alphaBase["defLvMiddleNum"]-1) | ||||
|         return lerp(alphaBase["defParamLv1"], alphaBase["defParamLvMiddle"], t) | ||||
|     else: | ||||
|         t = (level - alphaBase["defLvMiddleNum"]) / (99 - alphaBase["defLvMiddleNum"]) | ||||
|         return lerp(alphaBase["defParamLvMiddle"], alphaBase["defParamLv99"], t) | ||||
|  | ||||
| def GetHealthBase(alphaBase, level): | ||||
|     if level <= alphaBase["hpLvMiddleNum"]: | ||||
|         t = (level - 1) / (alphaBase["hpLvMiddleNum"]-1) | ||||
|         return lerp(alphaBase["hpParamLv1"], alphaBase["hpParamLvMiddle"], t) | ||||
|     else: | ||||
|         t = (level - alphaBase["hpLvMiddleNum"]) / (99 - alphaBase["hpLvMiddleNum"]) | ||||
|         return lerp(alphaBase["hpParamLvMiddle"], alphaBase["hpParamLv99"], t) | ||||
|  | ||||
| def get_all_stats(chara, alphaBase, max_level: bool, rising_status_pattern): | ||||
|     level = 99 if max_level else 1 | ||||
|      | ||||
|     hp = GetHealthBase(alphaBase, level) | ||||
|     atk = GetAttackBase(alphaBase, level) | ||||
|     defe = GetDefenseBase(alphaBase, level) | ||||
|  | ||||
|     if max_level: | ||||
|         starBoost = 1 + (chara["rankHigh"] - 1) * 0.02 | ||||
|         hp = int(math.ceil((hp + chara["promoteBonus"]["hp"]) * starBoost) + chara["costumeBonus"]["hp"]) | ||||
|         atk = int(math.ceil((atk + chara["promoteBonus"]["atk"]) * starBoost) + chara["costumeBonus"]["atk"]) | ||||
|         defe = int(math.ceil((defe + chara["promoteBonus"]["def"]) * starBoost) + chara["costumeBonus"]["def"]) | ||||
|         evd = 10 * alphaBase["avoidRatio"] + chara["promoteBonus"]["evd"] | ||||
|         beatBonus = chara["promoteBonus"]["beat"] / 10 | ||||
|         actBonus = chara["promoteBonus"]["act"] / 10 | ||||
|         tryBonus = chara["promoteBonus"]["try"] / 10 | ||||
|  | ||||
|         if rising_status_pattern is not None: | ||||
|             for i in range(51): | ||||
|                 hp += rising_status_pattern["hp"] | ||||
|                 atk += rising_status_pattern["atk"] | ||||
|                 defe += rising_status_pattern["def"] | ||||
|     else: | ||||
|         starBoost = 1 + (chara["rankLow"] - 1) * 0.02 | ||||
|         hp = int(math.ceil(hp * starBoost)) | ||||
|         atk = int(math.ceil(atk * starBoost)) | ||||
|         defe = int(math.ceil(defe * starBoost)) | ||||
|         evd = 10 * alphaBase["avoidRatio"] | ||||
|         beatBonus = 0 | ||||
|         actBonus = 0 | ||||
|         tryBonus = 0 | ||||
|  | ||||
|  | ||||
|     status = hp * 8 / 10 | ||||
|     if hp * 8 % 10 > 0: | ||||
|         status += 1 | ||||
|     status += atk * 3 + defe * 2 | ||||
|  | ||||
|     result = { | ||||
|         "level" : level, | ||||
|         "status" : int(status), | ||||
|         "wr" : chara["rankHigh"] if max_level else chara["rankLow"], | ||||
|         "hp" : hp, | ||||
|         "atk" : atk, | ||||
|         "def" : defe, | ||||
|         "evd" : evd, | ||||
|         "beat" : beatBonus, | ||||
|         "act" : actBonus, | ||||
|         "try" : tryBonus, | ||||
|     } | ||||
|  | ||||
|     return result | ||||
|  | ||||
| def fill_miracle_numbers(chara): | ||||
|     output = [] | ||||
|     damages = {} | ||||
|     buffs = defaultdict(list) | ||||
|  | ||||
|     for i, damage_param in enumerate(chara["arts"]["damageList"]): | ||||
|         damages[f"[DAMAGE{i}]"] = damage_param | ||||
|  | ||||
|     for i, buff_param in enumerate(chara["arts"]["buffList"]): | ||||
|         buffs[f"[BUFF{i}]"].append(buff_param) | ||||
|         buffs[f"[HEAL{i}]"].append(buff_param) | ||||
|         buffs[f"[INCREMENT{i}]"].append(buff_param) | ||||
|  | ||||
|     for i in range(1, 7): | ||||
|         base_text = chara["arts"]["actionEffect"] | ||||
|         for damage_key, damage_value in damages.items(): | ||||
|             new_value = str(int(damage_value["damageRate"] * (1 + damage_value["growthRate"] * (i - 1)) * 100 + 0.01)) | ||||
|             base_text = base_text.replace(damage_key, new_value) | ||||
|          | ||||
|         for buff_key, buff_value_list in buffs.items(): | ||||
|             buff_value = buff_value_list[0] | ||||
|             if buff_key[1] == 'B': | ||||
|                 new_value = str(int(abs(buff_value["coefficient"] * (1 + buff_value["growthRate"] * (i - 1)) - 1) * 100 + 0.01)) | ||||
|                 base_text = base_text.replace(buff_key, new_value) | ||||
|             elif buff_key[1] == 'H': | ||||
|                 new_value = str(int(buff_value["coefficient"] * (1 + buff_value["growthRate"] * (i - 1)) * 100 + 0.01)) | ||||
|                 base_text = base_text.replace(buff_key, new_value) | ||||
|             elif buff_key[1] == 'I': | ||||
|                 new_value = str(int(abs(buff_value["increment"]) * (1 + buff_value["growthRate"] * (i - 1)) + 0.01)) | ||||
|                 base_text = base_text.replace(buff_key, new_value) | ||||
|          | ||||
|         output.append(base_text) | ||||
|  | ||||
|     return output | ||||
|  | ||||
| def toJSON(chara): | ||||
|     return json.dumps(chara, default=lambda o: o.__dict__,  | ||||
|         sort_keys=True, indent=1, ensure_ascii=False) | ||||
|  | ||||
| def lerp(a, b, t): | ||||
|     return (1 - t) * a + t * b | ||||
							
								
								
									
										276
									
								
								loaders/KF3/kf3db.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										276
									
								
								loaders/KF3/kf3db.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,276 @@ | ||||
| import gzip | ||||
| import json | ||||
| import UnityPy | ||||
| from loaders.KF3.charaData import get_all_stats, fill_miracle_numbers | ||||
|  | ||||
| class KF3DB: | ||||
|     processed_friends = {} | ||||
|     paramAbilities = {} | ||||
|     paramAbilities1 = {} | ||||
|     paramAbilities2 = {} | ||||
|     paramAlphaBases = {} | ||||
|     paramArts = {} | ||||
|     paramSpecialAttacks = {} | ||||
|     paramWaitActions = {} | ||||
|     charaData = {} | ||||
|     promoteData = {} | ||||
|     promotePresetData = {} | ||||
|     charaClothesData = {} | ||||
|     limitlevel_rising_status = {} | ||||
|  | ||||
|     def __init__(self, app) -> None: | ||||
|         if "KF3" in app.databases: | ||||
|             del app.databases["KF3"] | ||||
|  | ||||
|         self.parse_parameter() | ||||
|  | ||||
|         with gzip.open("data/KF3/CHARA_DATA.d", mode="rt", encoding="utf-8") as file: | ||||
|             for entry in json.loads(file.read()): | ||||
|                 self.charaData[entry["id"]] = entry | ||||
|  | ||||
|         with gzip.open("data/KF3/CHARA_PROMOTE_DATA.d", mode="rt", encoding="utf-8") as file: | ||||
|             for entry in json.loads(file.read()): | ||||
|                 self.promoteData[entry["promoteId"]] = entry | ||||
|  | ||||
|         with gzip.open("data/KF3/CHARA_PROMOTE_PRESET_DATA.d", mode="rt", encoding="utf-8") as file: | ||||
|             for entry in json.loads(file.read()): | ||||
|                 if entry["promotePresetId"] not in self.promotePresetData: | ||||
|                     self.promotePresetData[entry["promotePresetId"]] = [] | ||||
|                 self.promotePresetData[entry["promotePresetId"]].append(entry) | ||||
|  | ||||
|         with gzip.open("data/KF3/CHARA_CLOTHES_DATA.d", mode="rt", encoding="utf-8") as file: | ||||
|             for entry in json.loads(file.read()): | ||||
|                 if entry["clothesPresetId"] not in self.charaClothesData: | ||||
|                     self.charaClothesData[entry["clothesPresetId"]] = [] | ||||
|                 self.charaClothesData[entry["clothesPresetId"]].append(entry) | ||||
|  | ||||
|         with gzip.open("data/KF3/LIMITLEVEL_RISING_STATUS.d", mode="rt", encoding="utf-8") as file: | ||||
|             for entry in json.loads(file.read()): | ||||
|                 self.limitlevel_rising_status[entry["patternId"]] = entry | ||||
|  | ||||
|         self.process_friends() | ||||
|         app.databases["KF3"] = self | ||||
|          | ||||
|     def parse_parameter(self): | ||||
|         paramAsset = UnityPy.load("data/KF3/assets/parameter.asset") | ||||
|         for obj in paramAsset.objects: | ||||
|             data = obj.read() | ||||
|             if data.name.split('_')[0] == "ParamAbility": | ||||
|                 id = int(data.name.split('_')[1]) | ||||
|                 if data.name.endswith("_1"): | ||||
|                     self.paramAbilities1[id] = obj.read_typetree() | ||||
|                 elif data.name.endswith("_2"): | ||||
|                     self.paramAbilities2[id] = obj.read_typetree() | ||||
|                 else: | ||||
|                     self.paramAbilities[id] = obj.read_typetree() | ||||
|             elif data.name.split('_')[0] == "ParamAlphaBase": | ||||
|                 id = int(data.name.split('_')[1]) | ||||
|                 self.paramAlphaBases[id] = obj.read_typetree() | ||||
|             elif data.name.split('_')[0] == "ParamArts": | ||||
|                 id = int(data.name.split('_')[1]) | ||||
|                 self.paramArts[id] = obj.read_typetree() | ||||
|             elif data.name.split('_')[0] == "ParamSpecialAttack": | ||||
|                 id = int(data.name.split('_')[1]) | ||||
|                 self.paramSpecialAttacks[id] = obj.read_typetree() | ||||
|             elif data.name.split('_')[0] == "ParamWaitAction": | ||||
|                 id = int(data.name.split('_')[1]) | ||||
|                 self.paramWaitActions[id] = obj.read_typetree() | ||||
|  | ||||
|     def process_friends(self): | ||||
|         self.processed_friends = {} | ||||
|         for charaID in self.charaData: | ||||
|             self.processed_friends[charaID] = self.process_chara(charaID) | ||||
|  | ||||
|     def process_chara(self, id : int): | ||||
|         chara = {} | ||||
|          | ||||
|         charaData = self.charaData[id] | ||||
|         alphaBase = self.paramAlphaBases[id] | ||||
|  | ||||
|         wrLocked = False | ||||
|         promoIds = [] | ||||
|         promotePresetId = charaData["promotePresetId"] | ||||
|         promos = self.promotePresetData[promotePresetId] if promotePresetId in self.promotePresetData else [] | ||||
|         for promo in promos: | ||||
|             if promo["promoteStepDatetime"] == 1917860400000: | ||||
|                 wrLocked = True | ||||
|                 continue | ||||
|             else: | ||||
|                 promoIds.append([promo["promoteId00"], promo["promoteId01"], promo["promoteId02"], promo["promoteId03"], promo["promoteId04"], promo["promoteId05"]]) | ||||
|  | ||||
|         promoteDatas = [[self.promoteData[id] for id in promo] for promo in promoIds] | ||||
|         promote_bonus = {"atk" : 0, "def" : 0, "hp" : 0, "evd" : 0, "beat" : 0, "act" : 0, "try" : 0} | ||||
|         for promoTier in promoteDatas: | ||||
|             for promoteStep in promoTier: | ||||
|                     promote_bonus["atk"] += promoteStep["promoteAtk"]  | ||||
|                     promote_bonus["def"] += promoteStep["promoteDef"]  | ||||
|                     promote_bonus["hp"] += promoteStep["promoteHp"]  | ||||
|                     promote_bonus["evd"] += promoteStep["promoteAvoid"]  | ||||
|                     promote_bonus["act"] += promoteStep["promoteActionDamageRatio"]  | ||||
|                     promote_bonus["beat"] += promoteStep["promoteBeatDamageRatio"]  | ||||
|                     promote_bonus["try"] += promoteStep["promoteTryDamageRatio"] | ||||
|  | ||||
|         clothesPresetId = charaData["clothesPresetId"] | ||||
|         clothesDatas = self.charaClothesData[clothesPresetId] if clothesPresetId in self.charaClothesData else [] | ||||
|         costume_bonus = {"atk" : 0, "def" : 0, "hp" : 0, "evd" : 0, "beat" : 0, "act" : 0, "try" : 0} | ||||
|         for clothesData in clothesDatas: | ||||
|             costume_bonus["atk"] += clothesData["atkBonus"]  | ||||
|             costume_bonus["def"] += clothesData["defBonus"]  | ||||
|             costume_bonus["hp"] += clothesData["hpBonus"]  | ||||
|  | ||||
|         chara["id"] = id | ||||
|         chara["promoteBonus"] = promote_bonus | ||||
|         chara["costumeBonus"] = costume_bonus | ||||
|         chara["attribute"] = charaData["attribute"] | ||||
|         chara["nameEn"] = charaData["nameEn"] | ||||
|         chara["rankLow"] = charaData["rankLow"] | ||||
|         chara["rankHigh"] = charaData["rankHigh"] | ||||
|         chara["castName"] = charaData["castName"] | ||||
|  | ||||
|         chara["wr"] = 4 if wrLocked else 5 | ||||
|         chara["is_wr5"] = chara["wr"] == 5 | ||||
|         chara["has_party_dress"] = False | ||||
|         for clothesData in clothesDatas: | ||||
|             if clothesData["clothesId"] == 8: | ||||
|                 chara["has_party_dress"] = True | ||||
|                 break | ||||
|         chara["has_rainbow_trait"] = id in self.paramAbilities2 | ||||
|  | ||||
|         chara["risingStatusPatternId"] = patternId = charaData["risingStatusPatternId"] | ||||
|         level_curve = self.limitlevel_rising_status[patternId] if patternId != 0 else None | ||||
|  | ||||
|         chara["stats_min"] = get_all_stats(chara, alphaBase, max_level = False, rising_status_pattern=level_curve) | ||||
|         chara["stats_max"] = get_all_stats(chara, alphaBase, max_level = True, rising_status_pattern=level_curve) | ||||
|         chara["plasmPoint"] = alphaBase["plasmPoint"] | ||||
|         chara["cards"] = [ | ||||
|             {"type":alphaBase["orderCardType00"], "value":alphaBase["orderCardValue00"]}, | ||||
|             {"type":alphaBase["orderCardType01"], "value":alphaBase["orderCardValue01"]}, | ||||
|             {"type":alphaBase["orderCardType02"], "value":alphaBase["orderCardValue02"]}, | ||||
|             {"type":alphaBase["orderCardType03"], "value":alphaBase["orderCardValue03"]}, | ||||
|             {"type":alphaBase["orderCardType04"], "value":alphaBase["orderCardValue04"]} | ||||
|         ] | ||||
|         chara["synergy_flag"] = self.paramArts[id]["authParam"]["SynergyFlag"] if id in self.paramArts else 0 | ||||
|          | ||||
|         chara["arts"] = self.paramArts[id] if id in self.paramArts else None | ||||
|         chara["ability"] = self.paramAbilities[id] if id in self.paramAbilities else None | ||||
|         chara["ability1"] = self.paramAbilities1[id] if id in self.paramAbilities1 else None | ||||
|         chara["ability2"] = self.paramAbilities2[id] if id in self.paramAbilities2 else None | ||||
|         chara["wait_action"] = self.paramWaitActions[id] if id in self.paramWaitActions else None | ||||
|         chara["special_attack"] = self.paramSpecialAttacks[id] if id in self.paramSpecialAttacks else None | ||||
|  | ||||
|         return chara | ||||
|      | ||||
|     def get_chara(self, id : int): | ||||
|         if id in self.processed_friends: | ||||
|             return self.processed_friends[id] | ||||
|         else: | ||||
|             return None | ||||
|  | ||||
|     def get_chara_wiki(self, id : int): | ||||
|         chara = self.get_chara(id) | ||||
|  | ||||
|         lines = [] | ||||
|          | ||||
|         stars_word = chara["rankLow"] | ||||
|         stars_word = "Two" if stars_word == 2 else "Three" if stars_word == 3 else "Four" if stars_word == 4 else "Five" if stars_word == 5 else "Unknown" | ||||
|          | ||||
|         attribute_word = chara["attribute"] | ||||
|         attribute_word = "Funny" if attribute_word == 1 else "Friendly" if attribute_word == 2 else "Relaxed" if attribute_word == 3 else "Lovely" if attribute_word == 4 else "Active" if attribute_word == 5 else "Carefree" if attribute_word == 6 else "Unknown" | ||||
|  | ||||
|         categories = [ | ||||
|             f"[[Category:{attribute_word} KF3 Friends]]", | ||||
|             f"[[Category:{stars_word} Star KF3 Friends]]", | ||||
|             f"[[Category:Missing Content]]", | ||||
|             f"[[Category:Needs Audio]]", | ||||
|             f"{{{{#vardefine:id|{str(chara['id'])}}}}}" | ||||
|         ] | ||||
|          | ||||
|         if chara['has_party_dress']: | ||||
|             categories.append("[[Category:Party Dress KF3 Friends]]") | ||||
|         if chara['is_wr5']: | ||||
|             categories.append("[[Category:Wild Release 5 KF3 Friends]]") | ||||
|         if chara['has_rainbow_trait']: | ||||
|             categories.append("[[Category:Rainbow Trait KF3 Friends]]") | ||||
|  | ||||
|         lines.append(" ".join(categories)) | ||||
|         lines.append("") | ||||
|         lines.append("{{FriendBox/KF3") | ||||
|         lines.append(f"|name={chara['nameEn'].replace('_', ' ')}") | ||||
|         lines.append(f"|apppic={chara['nameEn'].replace('_', ' ')}KF3.png") | ||||
|         lines.append(f"|apprarity={{{{KF3{chara['rankLow']}Star}}}}") | ||||
|         lines.append(f"|seiyuu={chara['castName']}") | ||||
|         lines.append(f"|attribute={attribute_word} {{{{KF3{attribute_word}}}}}") | ||||
|         lines.append(f"|implemented={0}") | ||||
|         lines.append(f"|id={chara['id']}") | ||||
|         lines.append(f"|wr5={'Yes' if chara['is_wr5'] else 'No'}") | ||||
|         lines.append(f"|rainbowtrait={'Yes' if chara['has_rainbow_trait'] else 'No'}") | ||||
|         lines.append(f"|partydress={'Yes' if chara['has_party_dress'] else 'No'}") | ||||
|         lines.append("}}") | ||||
|         lines.append("") | ||||
|         lines.append("{{FriendBuilder/KF3") | ||||
|         lines.append(f"|introduction = '''{chara['nameEn'].replace('_', ' ')}''' is a Friend that appears in the app version of [[Kemono Friends 3]].") | ||||
|         lines.append(f"|status={chara['stats_min']['status']}") | ||||
|         lines.append(f"|hp={chara['stats_min']['hp']}") | ||||
|         lines.append(f"|atk={chara['stats_min']['atk']}") | ||||
|         lines.append(f"|def={chara['stats_min']['def']}") | ||||
|         lines.append(f"|evd={round(chara['stats_min']['evd']/10,1)}%") | ||||
|         lines.append(f"|beat={chara['stats_min']['beat']}%") | ||||
|         lines.append(f"|action={chara['stats_min']['act']}%") | ||||
|         lines.append(f"|try={chara['stats_min']['try']}%") | ||||
|         lines.append(f"|plasm={chara['plasmPoint']}") | ||||
|         lines.append(f"|maxstatus={chara['stats_max']['status']}") | ||||
|         lines.append(f"|maxhp={chara['stats_max']['hp']}") | ||||
|         lines.append(f"|maxatk={chara['stats_max']['atk']}") | ||||
|         lines.append(f"|maxdef={chara['stats_max']['def']}") | ||||
|         lines.append(f"|maxevd={round(chara['stats_max']['evd']/10,1)}%") | ||||
|         lines.append(f"|maxbeat={chara['stats_max']['beat']}%") | ||||
|         lines.append(f"|maxaction={chara['stats_max']['act']}%") | ||||
|         lines.append(f"|maxtry={chara['stats_max']['try']}%") | ||||
|         cards_str = "|flags=" | ||||
|         for card in chara["cards"]: | ||||
|             cards_str += " {{" | ||||
|             if card["type"] == 1: | ||||
|                 cards_str += "Beat}}" | ||||
|             elif card["type"] == 2: | ||||
|                 cards_str += f"Action{card['value']}}}}}" | ||||
|             elif card["type"] == 3: | ||||
|                 if card["value"] == 20: | ||||
|                     cards_str += "TryLow}}" | ||||
|                 if card["value"] == 30: | ||||
|                     cards_str += "TryMiddle}}" | ||||
|                 if card["value"] == 40: | ||||
|                     cards_str += "TryHigh}}" | ||||
|         lines.append(cards_str) | ||||
|         cardType = "Beat" if chara["synergy_flag"] == 1 else "Action20" if chara["synergy_flag"] == 2 else "TryHigh" if chara["synergy_flag"] == 3 else "" | ||||
|         lines.append(f"|miracleplus={{{{{cardType}}}}}") | ||||
|  | ||||
|         if chara["arts"] is not None: | ||||
|             miracles = fill_miracle_numbers(chara) | ||||
|             lines.append(f"|miracle={chara['arts']['actionName']}") | ||||
|             for idx,miracle in enumerate(miracles): | ||||
|                 lines.append("|miracle" + str(idx + 1) + "=" + miracle.replace("\r\n", "\n").replace("\\n", "\n").replace("\\", " ")) | ||||
|  | ||||
|         if chara["special_attack"] is not None: | ||||
|             lines.append(f"|beatname={chara['special_attack']['actionName']}") | ||||
|             lines.append(f"|beatskill={chara['special_attack']['actionEffect']}") | ||||
|         if chara["wait_action"] is not None: | ||||
|             lines.append(f"|standby={chara['wait_action']['skillName']}") | ||||
|             lines.append(f"|standbyskill={chara['wait_action']['skillEffect']}") | ||||
|         if chara["ability"] is not None: | ||||
|             lines.append(f"|unique={chara['ability']['abilityName']}") | ||||
|             lines.append(f"|uniqueskill={chara['ability']['abilityEffect']}") | ||||
|         if chara["ability1"] is not None: | ||||
|             lines.append(f"|miracletrait={chara['ability1']['abilityName']}") | ||||
|             lines.append(f"|miracletraitskill={chara['ability1']['abilityEffect']}") | ||||
|         else: | ||||
|             lines.append(f"|miracletrait=N/A") | ||||
|             lines.append(f"|miracletraitskill=Not Implemented.") | ||||
|         if chara["ability2"] is not None: | ||||
|             lines.append(f"|rainbowtrait={chara['ability2']['abilityName']}") | ||||
|             lines.append(f"|rainbowtraitskill={chara['ability2']['abilityEffect']}") | ||||
|         else: | ||||
|             lines.append(f"|rainbowtrait=N/A") | ||||
|             lines.append(f"|rainbowtraitskill=Not Implemented.") | ||||
|         lines.append("}}") | ||||
|  | ||||
|         return "\n".join(lines) | ||||
		Reference in New Issue
	
	Block a user