KemoFureApi/modules/KF3/database.py

563 lines
24 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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/<int:id>")
api.add_resource(KF3_Friends, "/KF3/Friends")
api.add_resource(KF3_Item, "/KF3/Item/<int:id>")
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