import gzip import json import platform import UnityPy from datetime import datetime from .statCalculation import get_all_stats, fill_miracle_numbers from .endpoints.friend import KF3_Friend from .endpoints.friends import KF3_Friends from .endpoints.item import KF3_Item from .endpoints.items import KF3_Items from .endpoints.update import KF3_Update class Database: def __init__(self, api) -> None: app = api.app if "KF3" in app.databases: del app.databases["KF3"] self.reload_data() app.databases["KF3"] = self api.add_resource(KF3_Friend, "/KF3/Friend/") api.add_resource(KF3_Friends, "/KF3/Friends") api.add_resource(KF3_Item, "/KF3/Item/") api.add_resource(KF3_Items, "/KF3/Items") api.add_resource(KF3_Update, "/KF3/Update") def unload_data(self): self.charaData = {} self.itemCommon = {} self.promoteData = {} self.charaClothesData = {} self.promotePresetData = {} self.limitlevel_rising_status = {} self.paramArts = {} self.paramAbilities = {} self.paramAbilities1 = {} self.paramAbilities2 = {} self.paramAlphaBases = {} self.paramWaitActions = {} self.paramSpecialAttacks = {} self.processed_friends = {} def reload_data(self): self.unload_data() if platform.system() == "Windows": path = "H:\\Apache\\Katworks\\KF\\assets\\KF3\\cache\\" else: path = "/var/www/html/Katworks/KF/assets/KF3/develop01/cache/" with gzip.open(path + "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(path + "ITEM_COMMON.d", mode="rt", encoding="utf-8") as file: for entry in json.loads(file.read()): self.itemCommon[entry["id"]] = entry with gzip.open(path + "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(path + "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(path + "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(path + "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.parse_parameter() self.process_friends() print("Data reload finished") self.update_in_progress = False def parse_parameter(self): if platform.system() == "Windows": path = "H:\\Apache\\Katworks\\KF\\assets\\KF3\\assets\\" else: path = "/var/www/html/Katworks/KF/assets/KF3/develop01/assets/Windows/" paramAsset = UnityPy.load(path + "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): 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] if id in self.paramAlphaBases else None 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"]]) emptyPromote = {"promoteAtk": 0, "promoteDef": 0, "promoteHp": 0, "promoteAvoid": 0, "promoteActionDamageRatio": 0, "promoteBeatDamageRatio": 0, "promoteTryDamageRatio": 0} promoteDatas = [[(self.promoteData[id] if id in self.promoteData else emptyPromote) 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["name"] = charaData["name"] chara["nameEn"] = charaData["nameEn"] chara["nickname"] = charaData["nickname"] chara["rankLow"] = charaData["rankLow"] chara["rankHigh"] = charaData["rankHigh"] chara["castName"] = charaData["castName"] chara["loginText"] = charaData["loginText"] chara["flavorText"] = charaData["flavorText"] chara["kizunaupText"] = charaData["kizunaupText"] chara["greetingText"] = charaData["greetingText"] chara["startTimeRaw"] = charaData["startTime"] chara["startTime"] = datetime.fromtimestamp(charaData["startTime"]/1000.0).strftime('%A, %B %d, %Y') 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"] if patternId not in self.limitlevel_rising_status: level_curve = None else: level_curve = self.limitlevel_rising_status[patternId] if patternId != 0 else None if alphaBase is None: chara["stats_min"] = {"level" : 0,"status" : 0,"wr" : 0,"hp" : 0,"atk" : 0,"def" : 0,"evd" : 0,"beat" : 0,"act" : 0,"try" : 0} chara["stats_max"] = {"level" : 0,"status" : 0,"wr" : 0,"hp" : 0,"atk" : 0,"def" : 0,"evd" : 0,"beat" : 0,"act" : 0,"try" : 0} chara["plasmPoint"] = 0 chara["cards"] = {0,0,0,0,0} else: 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 chara["costumes"] = self.get_costumes(id, chara["rankLow"]) return chara def get_item(self, id : int): if id in self.itemCommon: return self.itemCommon[id] else: return None 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) if chara is None: return None 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']).zfill(4)}}}}}" ] 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={chara['startTime']} (App)") 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']}%") lines.append("") 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("") lines.append(f"|bondup={chara['kizunaupText']}") lines.append(f"|profile={chara['flavorText']}") lines.append(f"|greeting={chara['loginText']}") lines.append(f"|selfintro={chara['greetingText']}") lines.append(f"|enselfintro=") cos_images = "|cos = " + ",".join([costume["image_name"] for costume in chara["costumes"]]) + ",end" cos_obtain = "|cosobt = " + ",".join([costume["obtain"] for costume in chara["costumes"]]) cos_names = "|cosname = " + ",".join([costume["name"] for costume in chara["costumes"]]) lines.append("") lines.append(cos_images) lines.append(cos_names) lines.append(cos_obtain) lines.append("}}") return "\n".join(lines) def get_costumes(self, id : int, defaultRarity : int): if id not in self.charaClothesData: return [] output = [] data = self.charaClothesData[id] for costume in data: cos = { "image_name" : f"icon dressup {costume['id']}.png", "obtain" : "Limited" } cosId = costume['clothesId'] if cosId == 1: cos["name"] = "Default" cos["obtain"] = "Default" elif cosId == 2: cos["name"] = "Personal Fashion" cos["obtain"] = f"Upgrade to {costume['getRank']} Stars" if defaultRarity < costume['getRank'] else "Default" elif cosId == 3: cos["name"] = "Tracksuit" cos["obtain"] = f"Upgrade to {costume['getRank']} Stars" if defaultRarity < costume['getRank'] else "Default" elif cosId == 4: cos["name"] = "Park Staff" cos["obtain"] = f"Upgrade to {costume['getRank']} Stars" if defaultRarity < costume['getRank'] else "Default" elif cosId == 5: cos["name"] = "Café Uniform" cos["obtain"] = f"Upgrade to {costume['getRank']} Stars" if defaultRarity < costume['getRank'] else "Default" elif cosId == 6: cos["name"] = "Special Outfit 1" elif cosId == 8: cos["name"] = "Party Dress" cos["obtain"] = "Increase Photo Pocket level to 12" elif cosId == 9: cos["name"] = "School Uniform" elif cosId == 10: cos["name"] = "Gym Clothes" elif cosId == 11: cos["name"] = "Raincoat" elif cosId == 12: cos["name"] = "Hanamaru Animal Swimsuit" elif cosId == 13: cos["name"] = "Cafe Uniform (Green)" cos["obtain"] = "Dojo reward" elif cosId == 14: cos["name"] = "Cafe Uniform (Blue)" cos["obtain"] = "Dojo reward" elif cosId == 15: cos["name"] = "Cafe Uniform (Pink)" cos["obtain"] = "Dojo reward" elif cosId == 16: cos["name"] = "Orihime Costume" elif cosId == 17: cos["name"] = "Arle's Outfit" elif cosId == 18: cos["name"] = "Marine Nanoda! Swimsuit" elif cosId == 19: cos["name"] = "Batten Japari Dan Swimsuit" elif cosId == 20: cos["name"] = "Lifeguard Swimsuit" elif cosId == 21: cos["name"] = "Harvest Festival Outfit" elif cosId == 22: cos["name"] = "Halloween Costume" elif cosId == 23: cos["name"] = "Classic Maid Uniform" elif cosId == 24: cos["name"] = "Arle's Outfit" elif cosId == 25: cos["name"] = "Swimsuit" elif cosId == 26: cos["name"] = "Swimsuit" elif cosId == 27: cos["name"] = "Swimsuit" elif cosId == 28: cos["name"] = "Matching Clothes" elif cosId == 29: cos["name"] = "Frilly Frilly PPP Bikini" elif cosId == 30: cos["name"] = "Venus Flower Bikini" elif cosId == 31: cos["name"] = "Christmas Dress" elif cosId == 32: cos["name"] = "Chinese Shrine Maiden" elif cosId == 33: cos["name"] = "Chinese Dress" elif cosId == 34: cos["name"] = "Hanamaru Stage Costume" elif cosId == 35: cos["name"] = "Camo Outfit" elif cosId == 36: cos["name"] = "でびでび・ふりる" elif cosId == 37: cos["name"] = "夜に紛れし闇の衣" elif cosId == 38: cos["name"] = "Santa" elif cosId == 39: cos["name"] = "Fluffy Pajamas" elif cosId == 40: cos["name"] = "Japanese Armor" elif cosId == 41: cos["name"] = "Miko Outfit" elif cosId == 42: cos["name"] = "PPP Hoodie" elif cosId == 43: cos["name"] = "Blue Tracksuit" elif cosId == 44: cos["name"] = "Park Staff (beige)" elif cosId == 45: cos["name"] = "Sailor School Uniform" elif cosId == 46: cos["name"] = "Snow Knight Dress" elif cosId == 47: cos["name"] = "Sparkling Tree Dress" elif cosId == 48: cos["name"] = "Retro Detective" elif cosId == 49: cos["name"] = "Steampunk Detective" elif cosId == 50: cos["name"] = "鞠と花びらのはんなり和風服" elif cosId == 51: cos["name"] = "Numazu Deep Sea Aquarium Clothes" elif cosId == 52: cos["name"] = "たのしいことをお届け!" elif cosId == 53: cos["name"] = "かわいい勝負ですよ!ワンピース水着" elif cosId == 54: cos["name"] = "どきどき♡ずっきゅんビキニ" elif cosId == 55: cos["name"]= "のんほいパークの服" elif cosId == 56: cos["name"]= "なないろのはごろも" elif cosId == 57: cos["name"]= "4周年!ハッピーラッキーTシャツ" elif cosId == 58: cos["name"]= "純情色のワンピース" else: print(f"{costume['clothesId']}\t{costume['id']}\t{costume['name']}") continue output.append(cos) return output