You've already forked KemoFureApi
refactor
This commit is contained in:
554
modules/KF3/database.py
Normal file
554
modules/KF3/database.py
Normal file
@@ -0,0 +1,554 @@
|
||||
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]
|
||||
|
||||
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["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
|
||||
|
||||
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
|
||||
|
||||
213
modules/KF3/downloader.py
Normal file
213
modules/KF3/downloader.py
Normal file
@@ -0,0 +1,213 @@
|
||||
|
||||
import base64
|
||||
import os
|
||||
import gzip
|
||||
import json
|
||||
import asyncio
|
||||
import hashlib
|
||||
import platform
|
||||
|
||||
import aiohttp
|
||||
from UnityPy import enums
|
||||
|
||||
from ..Shared.utility import divide_chunks
|
||||
from ..Shared.downloading import download_bytes, download_text
|
||||
from ..Shared.convert import convert, extract
|
||||
|
||||
servers = [
|
||||
"https://parade-mobile-stg-app.kemono-friends-3.jp/",
|
||||
"https://parade-mobile-develop01-app.kemono-friends-3.jp/",
|
||||
#"https://parade-mobile-develop02-app.kemono-friends-3.jp/paradesv/",
|
||||
#"https://parade-mobile-develop03-app.kemono-friends-3.jp/paradesv/",
|
||||
#"https://parade-mobile-develop04-app.kemono-friends-3.jp/paradesv/",
|
||||
]
|
||||
|
||||
def decode(data):
|
||||
# cut off the md5 checksum at the end and the four bytes at the start
|
||||
hash = bytearray.fromhex(data[:-(2*16)][2*4:])
|
||||
key = hashlib.md5(bytearray.fromhex(data[:2*4])).digest() # md5 the key
|
||||
# xor with the key
|
||||
for i in range(0, len(hash)):
|
||||
hash[i] = hash[i] ^ key[i % (len(key))]
|
||||
return hash.decode("utf-8")
|
||||
|
||||
def encode(text):
|
||||
hashed = hashlib.md5(text.encode()).digest()
|
||||
checksum = hashlib.md5((text + "DARAPAB ").encode()).digest()
|
||||
key = hashlib.md5(hashed[:4]).digest() # md5 the key
|
||||
# xor the data with the key
|
||||
text = bytearray(text.encode())
|
||||
for i in range(0, len(text)):
|
||||
text[i] = text[i] ^ key[i % (len(key))]
|
||||
return hashed[:4].hex() + text.hex() + checksum.hex()
|
||||
|
||||
async def download_cache(server_name, server : str):
|
||||
session = aiohttp.ClientSession()
|
||||
downloaded_files = []
|
||||
|
||||
if platform.system() == "Windows":
|
||||
path = f"D:\\Codebase\\KFKDecrypt\\assets\\KF3\\{server_name}\\cache\\"
|
||||
else:
|
||||
path = f"/var/www/html/Katworks/KF/assets/KF3/{server_name}/cache/"
|
||||
|
||||
os.makedirs(path, exist_ok=True)
|
||||
|
||||
new_mst_ver = {}
|
||||
old_mst_ver = {}
|
||||
file_path_mst = path + "mstVersion.txt"
|
||||
if os.path.exists(file_path_mst):
|
||||
with open(file_path_mst, "rt", encoding="utf-8") as file:
|
||||
old_mst_ver = dict([(entry["type"], entry["version"]) for entry in json.load(file)])
|
||||
|
||||
param = encode(json.dumps({'dmm_viewer_id':0}))
|
||||
request = await download_bytes(server + "paradesv/common/MstVersion.do?param=" + param, session)
|
||||
result = gzip.decompress(request)
|
||||
new_mst = json.loads(result)
|
||||
new_mst_ver = dict([(entry["type"], entry["version"]) for entry in new_mst["mst_ver"]])
|
||||
|
||||
for key in new_mst_ver:
|
||||
file_path_gzip = path + key + ".d"
|
||||
file_path_json = path + key + ".json"
|
||||
if os.path.exists(file_path_gzip) and os.path.exists(file_path_json) and key in old_mst_ver:
|
||||
if new_mst_ver[key] == old_mst_ver[key]:
|
||||
continue
|
||||
|
||||
downloaded_files.append(key)
|
||||
param = encode(json.dumps({'type':key, 'dmm_viewer_id':0}))
|
||||
request = await download_bytes(server + "paradesv/common/MstData.do?param=" + param, session)
|
||||
result = gzip.decompress(request)
|
||||
response = json.loads(result)
|
||||
data = base64.b64decode(response["data"])
|
||||
with open(file_path_gzip, "wb") as file:
|
||||
file.write(data)
|
||||
with open(file_path_json, "wt", encoding="utf-8") as out_file:
|
||||
data = gzip.decompress(data)
|
||||
json.dump(json.loads(data), out_file, ensure_ascii=False, indent=1)
|
||||
|
||||
old_mst_ver[key] = new_mst_ver[key]
|
||||
|
||||
with open(file_path_mst, "wt", encoding="utf-8") as file:
|
||||
new_json = [{"type":type, "version":version} for type, version in zip(old_mst_ver.keys(), old_mst_ver.values())]
|
||||
json.dump(new_json, file, ensure_ascii=False)
|
||||
|
||||
await session.close()
|
||||
return downloaded_files
|
||||
|
||||
async def download_files(server_name, asset_bundle_url, srv_platform : str):
|
||||
def parse_ab_list(filecontent : str):
|
||||
out = {}
|
||||
lines = filecontent.replace("\r\n", "\n").split('\n')
|
||||
for line in lines:
|
||||
split = line.split('\t')
|
||||
if len(split) > 1:
|
||||
out[split[0]] = split[-2]
|
||||
return out
|
||||
|
||||
async def download_file(url_assets : str, file_name : str, download_path : str, session : aiohttp.ClientSession):
|
||||
data = await download_bytes(url_assets + file_name, session)
|
||||
if data != None:
|
||||
with open(download_path + file_name, "wb") as file:
|
||||
file.write(data)
|
||||
if server_name == "develop01":
|
||||
convert_path = f"/var/www/html/Katworks/KF/assets/KF3/WebGL/assets/" + file_name
|
||||
extract_path = f"/var/www/html/Katworks/KF/assets/KF3/extracted/"
|
||||
try:
|
||||
convert(data, convert_path, enums.BuildTarget.WebGL, True)
|
||||
except:
|
||||
with open(convert_path, "wb") as file:
|
||||
file.write(data)
|
||||
if file_name.endswith(".png"):
|
||||
extract(data, extract_path)
|
||||
|
||||
session = aiohttp.ClientSession()
|
||||
path = f"/var/www/html/Katworks/KF/assets/KF3/{server_name}/assets/{srv_platform}/"
|
||||
|
||||
os.makedirs(path, exist_ok=True)
|
||||
|
||||
files_to_download = []
|
||||
url_base = asset_bundle_url + "/" + srv_platform + "/1.0.0/ja/"
|
||||
url_list = url_base + "ab_list.txt"
|
||||
url_env = url_base + "ab_env.txt"
|
||||
url_assets = url_base + "assets/"
|
||||
|
||||
file_path_env = path + "ab_env.txt"
|
||||
file_path_list = path + "ab_list.txt"
|
||||
old_ab_env = ""
|
||||
|
||||
if os.path.exists(file_path_env):
|
||||
with open(file_path_env, "rt") as file:
|
||||
old_ab_env = file.read()
|
||||
|
||||
new_ab_env = await download_text(url_env, session)
|
||||
|
||||
if new_ab_env != old_ab_env:
|
||||
old_ab_list = {}
|
||||
if os.path.exists(file_path_list):
|
||||
with open(file_path_list, "rt") as file:
|
||||
old_ab_list = parse_ab_list(file.read())
|
||||
|
||||
new_ab_list_file = await download_text(url_list, session)
|
||||
new_ab_list = parse_ab_list(new_ab_list_file)
|
||||
|
||||
for key in new_ab_list:
|
||||
file_path = path + key
|
||||
if os.path.exists(file_path) and key in old_ab_list:
|
||||
if new_ab_list[key] == old_ab_list[key]:
|
||||
continue
|
||||
|
||||
files_to_download.append(key)
|
||||
|
||||
chunked_files_to_download = list(divide_chunks(files_to_download, 5))
|
||||
|
||||
for chunk in chunked_files_to_download:
|
||||
tasks = [asyncio.create_task(download_file(url_assets, file, path, session)) for file in chunk]
|
||||
await asyncio.wait(tasks)
|
||||
print(chunk)
|
||||
print()
|
||||
|
||||
with open(file_path_env, "wt", encoding="utf-8") as file:
|
||||
file.write(new_ab_env)
|
||||
with open(file_path_list, "wt", encoding="utf-8") as file:
|
||||
file.write(new_ab_list_file)
|
||||
|
||||
await session.close()
|
||||
return files_to_download
|
||||
|
||||
async def manual():
|
||||
downloaded_cache = {}
|
||||
downloaded_files = {}
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
param = encode(json.dumps({"version":"1.0.0","dmm_viewer_id":0,"platform":1}))
|
||||
request = await download_bytes(servers[0] + "paradesv/common/GetUrl.do?param=" + param, session)
|
||||
result = gzip.decompress(request)
|
||||
response = json.loads(result)
|
||||
asset_bundle_url = response["asset_bundle_url"]
|
||||
urlName = asset_bundle_url.split("-")[2]
|
||||
|
||||
print("downloading from", servers[0])
|
||||
downloaded_cache = await download_cache(urlName, servers[0])
|
||||
downloaded_files = await download_files(urlName, asset_bundle_url, "Windows")
|
||||
|
||||
if downloaded_cache != [] and downloaded_cache != None:
|
||||
with open("/var/www/html/Katworks/KF/assets/KF3/lastUpdate_prod_cache.json", "wt", encoding="utf-8") as file:
|
||||
json.dump(downloaded_cache, file, ensure_ascii=False, indent=1)
|
||||
if downloaded_files != [] and downloaded_files != None:
|
||||
with open("/var/www/html/Katworks/KF/assets/KF3/lastUpdate_prod_files.json", "wt", encoding="utf-8") as file:
|
||||
json.dump(downloaded_files, file, ensure_ascii=False, indent=1)
|
||||
|
||||
print("downloading from", servers[1])
|
||||
asset_bundle_url = "https://parade-mobile-develop01-app.kemono-friends-3.jp/AssetBundles/0.0.0/latest"
|
||||
urlName = asset_bundle_url.split("-")[2]
|
||||
downloaded_cache = await download_cache(urlName, servers[1])
|
||||
downloaded_files = await download_files(urlName, asset_bundle_url, "Windows")
|
||||
|
||||
if downloaded_cache != [] and downloaded_cache != None:
|
||||
with open("/var/www/html/Katworks/KF/assets/KF3/lastUpdate_dev_cache.json", "wt", encoding="utf-8") as file:
|
||||
json.dump(downloaded_cache, file, ensure_ascii=False, indent=1)
|
||||
if downloaded_files != [] and downloaded_files != None:
|
||||
with open("/var/www/html/Katworks/KF/assets/KF3/lastUpdate_dev_files.json", "wt", encoding="utf-8") as file:
|
||||
json.dump(downloaded_files, file, ensure_ascii=False, indent=1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(manual())
|
||||
30
modules/KF3/endpoints/friend.py
Normal file
30
modules/KF3/endpoints/friend.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from flask_restful import Resource
|
||||
from flask import current_app as app
|
||||
from flask import request
|
||||
import json
|
||||
|
||||
class KF3_Friend(Resource):
|
||||
def get(self, id:int):
|
||||
from KF3.database import Database
|
||||
db : Database = app.databases["KF3"]
|
||||
|
||||
if "wiki" in request.args:
|
||||
result = db.get_chara_wiki(id)
|
||||
|
||||
response = app.response_class(
|
||||
response=result,
|
||||
status=200,
|
||||
mimetype='text/plain'
|
||||
)
|
||||
else:
|
||||
result = db.get_chara(id)
|
||||
result = json.dumps(result, ensure_ascii=False, indent=1)
|
||||
|
||||
response = app.response_class(
|
||||
response=result,
|
||||
status=200,
|
||||
mimetype='application/json'
|
||||
)
|
||||
|
||||
response.headers.add("Access-Control-Allow-Origin", "*")
|
||||
return response
|
||||
28
modules/KF3/endpoints/friends.py
Normal file
28
modules/KF3/endpoints/friends.py
Normal file
@@ -0,0 +1,28 @@
|
||||
import json
|
||||
from flask_restful import Resource
|
||||
from flask import current_app as app
|
||||
from flask import request
|
||||
|
||||
class KF3_Friends(Resource):
|
||||
def get(self):
|
||||
from KF3.database import Database
|
||||
db : Database = app.databases["KF3"]
|
||||
|
||||
result = []
|
||||
for value in db.processed_friends.values():
|
||||
result.append({"id": value["id"], "name": value["nameEn"], "startTime" : value["startTime"], "startTimeRaw" : value["startTimeRaw"]})
|
||||
|
||||
sort_arg = request.args["sort"] if "sort" in request.args and request.args["sort"] in result[0] else "id"
|
||||
if sort_arg == "startTime":
|
||||
sort_arg = "startTimeRaw"
|
||||
|
||||
result = sorted(result, key=lambda f: f[sort_arg])
|
||||
|
||||
response = app.response_class(
|
||||
response=json.dumps(result, ensure_ascii=False, indent=1),
|
||||
status=200,
|
||||
mimetype='application/json'
|
||||
)
|
||||
response.headers.add("Access-Control-Allow-Origin", "*")
|
||||
|
||||
return response
|
||||
21
modules/KF3/endpoints/item.py
Normal file
21
modules/KF3/endpoints/item.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from flask_restful import Resource
|
||||
from flask import current_app as app
|
||||
from flask import request_tearing_down
|
||||
import json
|
||||
|
||||
class KF3_Item(Resource):
|
||||
def get(self, id:int):
|
||||
from KF3.database import Database
|
||||
db : Database = app.databases["KF3"]
|
||||
|
||||
result = db.get_item(id)
|
||||
result = json.dumps(result, ensure_ascii=False, indent=1)
|
||||
|
||||
response = app.response_class(
|
||||
response=result,
|
||||
status=200,
|
||||
mimetype='application/json'
|
||||
)
|
||||
|
||||
response.headers.add("Access-Control-Allow-Origin", "*")
|
||||
return response
|
||||
21
modules/KF3/endpoints/items.py
Normal file
21
modules/KF3/endpoints/items.py
Normal file
@@ -0,0 +1,21 @@
|
||||
import json
|
||||
from flask_restful import Resource
|
||||
from flask import current_app as app
|
||||
|
||||
class KF3_Items(Resource):
|
||||
def get(self):
|
||||
from KF3.database import Database
|
||||
db : Database = app.databases["KF3"]
|
||||
|
||||
result = []
|
||||
for value in db.itemCommon.values():
|
||||
result.append({"id": value["id"], "name": value["name"]})
|
||||
|
||||
response = app.response_class(
|
||||
response=json.dumps(result, ensure_ascii=False, indent=1),
|
||||
status=200,
|
||||
mimetype='application/json'
|
||||
)
|
||||
response.headers.add("Access-Control-Allow-Origin", "*")
|
||||
|
||||
return response
|
||||
36
modules/KF3/endpoints/update.py
Normal file
36
modules/KF3/endpoints/update.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import asyncio
|
||||
from flask_restful import Resource
|
||||
from flask import current_app as app
|
||||
from ..downloader import manual
|
||||
|
||||
class KF3_Update(Resource):
|
||||
def post(self):
|
||||
from ..database import Database
|
||||
db : Database = app.databases["KF3"]
|
||||
|
||||
if db.update_in_progress:
|
||||
response = app.response_class(
|
||||
response="update in progress",
|
||||
status=200,
|
||||
mimetype='text/plain'
|
||||
)
|
||||
else:
|
||||
self.perform_update()
|
||||
response = app.response_class(
|
||||
response="update started, reload the site in a while",
|
||||
status=200,
|
||||
mimetype='text/plain'
|
||||
)
|
||||
|
||||
response.headers.add("Access-Control-Allow-Origin", "*")
|
||||
return response
|
||||
|
||||
def get(self):
|
||||
return self.post()
|
||||
|
||||
def perform_update(self):
|
||||
from ..database import Database
|
||||
db : Database = app.databases["KF3"]
|
||||
print("Update")
|
||||
#await manual()
|
||||
db.reload_data()
|
||||
133
modules/KF3/statCalculation.py
Normal file
133
modules/KF3/statCalculation.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
|
||||
Reference in New Issue
Block a user