Initial API (Kingdom + KF3)
This commit is contained in:
commit
12df873ead
|
@ -0,0 +1,2 @@
|
|||
__pycache__/
|
||||
data/
|
|
@ -0,0 +1,30 @@
|
|||
from flask import Flask
|
||||
from flask_restful import Api
|
||||
|
||||
from resources.KF3.friend import KF3_Friend
|
||||
from resources.KF3.friends import KF3_Friends
|
||||
from resources.Kingdom.friend import Kingdom_Friend
|
||||
from resources.Kingdom.friends import Kingdom_Friends
|
||||
from resources.Kingdom.item import Kingdom_Item
|
||||
from resources.Kingdom.items import Kingdom_Items
|
||||
from loaders.KF3.kf3db import KF3DB
|
||||
from loaders.Kingdom.kingdomdb import KingdomDB
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['JSON_AS_ASCII'] = False
|
||||
app.databases = {}
|
||||
|
||||
KF3DB(app)
|
||||
KingdomDB(app)
|
||||
|
||||
api = Api(app)
|
||||
|
||||
api.add_resource(KF3_Friend, "/KF3/Friend/<int:id>")
|
||||
api.add_resource(KF3_Friends, "/KF3/Friends")
|
||||
api.add_resource(Kingdom_Friend, "/Kingdom/Friend/<int:id>")
|
||||
api.add_resource(Kingdom_Friends, "/Kingdom/Friends")
|
||||
api.add_resource(Kingdom_Item, "/Kingdom/Item/<int:id>")
|
||||
api.add_resource(Kingdom_Items, "/Kingdom/Items")
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(host='127.0.0.1', port=8080, debug=True)
|
|
@ -0,0 +1,10 @@
|
|||
import multiprocessing
|
||||
|
||||
workers = multiprocessing.cpu_count() # * 2 + 1
|
||||
bind = 'unix:flaskrest.sock'
|
||||
umask = 0o007
|
||||
reload = False
|
||||
|
||||
#logging
|
||||
accesslog = '-'
|
||||
errorlog = '-'
|
|
@ -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
|
|
@ -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)
|
|
@ -0,0 +1,136 @@
|
|||
import re
|
||||
import math
|
||||
|
||||
def get_skill_materials(friend, kfk_kemono_skill, kfk_en_item):
|
||||
items_total = []
|
||||
kfk_skill = sorted(filter(lambda s: s["kemonosn"] == friend["sn"] and s["type"] == 2, kfk_kemono_skill.indexed_data.values()), key = lambda s: s["sn"])
|
||||
for idx, s in enumerate(kfk_skill):
|
||||
if len(s["item"]) == 0:
|
||||
continue
|
||||
items_total.append(
|
||||
{
|
||||
"lvl": s["skilllevel"],
|
||||
"awakenReq": s["wakenlevel"],
|
||||
"lvlReq": s["kemonolevel"],
|
||||
"items": list([{"count": num, "name": kfk_en_item.get(item)["name"]} for item, num in zip(s["item"], s["itemnum"])])
|
||||
}
|
||||
)
|
||||
return items_total
|
||||
|
||||
def get_awaken_materials(friend, kfk_kemono_waken, kfk_en_item):
|
||||
items_total = []
|
||||
kfk_waken = sorted(filter(lambda s: s["kemonosn"] == friend["sn"], kfk_kemono_waken.indexed_data.values()), key = lambda s: s["sn"])
|
||||
for idx, s in enumerate(kfk_waken):
|
||||
if len(s["item"]) == 0:
|
||||
continue
|
||||
items_total.append(
|
||||
{
|
||||
"lvl": idx+1,
|
||||
"items": list([{"count": num, "name": kfk_en_item.get(item)["name"]} for item, num in zip(s["item"], s["itemnum"])])
|
||||
}
|
||||
)
|
||||
return items_total
|
||||
|
||||
def calculate_stats(friend, awaken : int, level : int, trust_percent = 0, trustAttri = None):
|
||||
stats_array = friend["attrScriptParam"]
|
||||
max_levels = friend["maxLvl"]
|
||||
max_awaken = friend["maxAwaken"]
|
||||
|
||||
awaken_step = max_awaken + 2
|
||||
|
||||
atk_ptr = 1 * awaken_step
|
||||
satk_ptr = 2 * awaken_step
|
||||
pdef_ptr = 3 * awaken_step
|
||||
sdef_ptr = 4 * awaken_step
|
||||
hp_ptr = 5 * awaken_step
|
||||
speed_ptr = 6 * awaken_step
|
||||
|
||||
stats = {"patk":stats_array[atk_ptr], "satk":stats_array[satk_ptr], "pdef":stats_array[pdef_ptr], "sdef":stats_array[sdef_ptr], "hp":stats_array[hp_ptr], "speed":stats_array[speed_ptr]}
|
||||
for i in range(awaken + 1):
|
||||
if i == awaken:
|
||||
max_level = level
|
||||
else:
|
||||
max_level = max_levels[i]
|
||||
|
||||
stats["patk"] += stats_array[atk_ptr + 1 + i] * (max_level-1)
|
||||
stats["satk"] += stats_array[satk_ptr + 1 + i] * (max_level-1)
|
||||
stats["pdef"] += stats_array[pdef_ptr + 1 + i] * (max_level-1)
|
||||
stats["sdef"] += stats_array[sdef_ptr + 1 + i] * (max_level-1)
|
||||
stats["hp"] += stats_array[hp_ptr + 1 + i] * (max_level-1)
|
||||
stats["speed"] += stats_array[speed_ptr + 1 + i] * (max_level-1)
|
||||
|
||||
if trust_percent > 0 and trustAttri != None:
|
||||
if trustAttri["attribute1"] == 1001:
|
||||
stats["hp"] += trustAttri["attributeValue1"][trust_percent]
|
||||
elif trustAttri["attribute1"] == 1021:
|
||||
stats["patk"] += trustAttri["attributeValue1"][trust_percent]
|
||||
elif trustAttri["attribute1"] == 1031:
|
||||
stats["satk"] += trustAttri["attributeValue1"][trust_percent]
|
||||
else:
|
||||
print(trustAttri["attribute1"])
|
||||
|
||||
if trustAttri["attribute2"] == 1001:
|
||||
stats["hp"] += trustAttri["attributeValue2"][trust_percent]
|
||||
elif trustAttri["attribute2"] == 1021:
|
||||
stats["patk"] += trustAttri["attributeValue2"][trust_percent]
|
||||
elif trustAttri["attribute2"] == 1031:
|
||||
stats["satk"] += trustAttri["attributeValue2"][trust_percent]
|
||||
else:
|
||||
print(trustAttri["attribute2"])
|
||||
|
||||
stats["patk"] = int(round(stats["patk"]))
|
||||
stats["satk"] = int(round(stats["satk"]))
|
||||
stats["pdef"] = int(round(stats["pdef"]))
|
||||
stats["sdef"] = int(round(stats["sdef"]))
|
||||
stats["hp"] = int(round(stats["hp"]))
|
||||
stats["speed"] = int(round(stats["speed"]))
|
||||
|
||||
return stats
|
||||
|
||||
def clean_skill_string(input_string):
|
||||
if input_string is None:
|
||||
return None
|
||||
|
||||
color_tags_pattern = r"<color=#[A-Fa-f0-9]+>(.*?)<\/color>"
|
||||
output_string = re.sub(color_tags_pattern, r"\1", input_string)
|
||||
|
||||
link_tags_pattern = r"<link=\".*?\">(.*?)<\/link>"
|
||||
output_string = re.sub(link_tags_pattern, r"\1", output_string)
|
||||
|
||||
return output_string
|
||||
|
||||
def fill_miracle(input_string, miracle_level = 1, kemono_level = 0):
|
||||
if input_string is None:
|
||||
return ""
|
||||
|
||||
pattern = r"\{\{(.*?)\}\}"
|
||||
def replace_match(match):
|
||||
key : str = match.group(1).strip()
|
||||
|
||||
if key.startswith("skill | SkillDesc:"):
|
||||
values = key.replace("skill | SkillDesc:", "").split(',')
|
||||
values = [float(value) for value in values]
|
||||
level_up_values = [
|
||||
values[1],
|
||||
values[1],
|
||||
values[1],
|
||||
values[2],
|
||||
values[2],
|
||||
values[2],
|
||||
values[3],
|
||||
values[3],
|
||||
values[3],
|
||||
values[4],
|
||||
values[4],
|
||||
values[4],
|
||||
]
|
||||
base_value = values[0]
|
||||
level_up_value = sum(level_up_values[0:miracle_level-1]) + values[5]
|
||||
level_bonus = (kemono_level - values[7]) * values[6]
|
||||
return str(math.floor(base_value + level_up_value + level_bonus))
|
||||
|
||||
return "{{" + key + "}}"
|
||||
|
||||
output_string = re.sub(pattern, replace_match, input_string)
|
||||
|
||||
return clean_skill_string(output_string)
|
|
@ -0,0 +1,276 @@
|
|||
import json
|
||||
import platform
|
||||
from loaders.Kingdom.numeric import numeric
|
||||
from loaders.Kingdom.kemono import clean_skill_string, calculate_stats, fill_miracle, get_awaken_materials, get_skill_materials
|
||||
|
||||
#element 1-orange 2-blue 3-green
|
||||
#showSkillType 1-control 2-guard 3-heal 4-support 5-assault 6-aoe
|
||||
|
||||
class KingdomDB:
|
||||
processed_friends = {}
|
||||
item_stages = {}
|
||||
|
||||
def __init__(self, app) -> None:
|
||||
if "Kingdom" in app.databases:
|
||||
del app.databases["Kingdom"]
|
||||
|
||||
if platform.system() == "Windows":
|
||||
NUMERICPATH = "H:\\Apache\\Katworks\\KF\\assets\\Kingdom1\\NumericData"
|
||||
else:
|
||||
NUMERICPATH = "/media/USB2/Apache/Katworks/KF/assets/Kingdom1/NumericData"
|
||||
|
||||
self.kfk_drop = numeric(NUMERICPATH +"/Drop.num", 0)
|
||||
self.kfk_combo = numeric(NUMERICPATH +"/Combo.num", 0)
|
||||
self.kfk_skill = numeric(NUMERICPATH +"/Skill.num", 0)
|
||||
self.kfk_equip = numeric(NUMERICPATH +"/Equip.num", 0)
|
||||
self.kfk_kemono = numeric(NUMERICPATH +"/Kemono.num", 0)
|
||||
self.kfk_stages = numeric(NUMERICPATH +"/Stage.num", 0)
|
||||
self.kfk_drop_group = numeric(NUMERICPATH +"/DropGroup.num", 0)
|
||||
self.kfk_trust_attri = numeric(NUMERICPATH +"/TrustAttri.num", 0)
|
||||
self.kfk_kemono_skill = numeric(NUMERICPATH +"/KemonoSkill.num", 0)
|
||||
self.kfk_kemono_power = numeric(NUMERICPATH +"/KemonoPower.num", 0)
|
||||
self.kfk_kemono_waken = numeric(NUMERICPATH +"/KemonoWaken.num", 0)
|
||||
self.kfk_en_item = numeric(NUMERICPATH + "/en/Item.ntxt", 1)
|
||||
self.kfk_en_equip = numeric(NUMERICPATH + "/en/Equip.ntxt", 1)
|
||||
self.kfk_en_skill = numeric(NUMERICPATH + "/en/Skill.ntxt", 1)
|
||||
self.kfk_en_combo = numeric(NUMERICPATH + "/en/Combo.ntxt", 1)
|
||||
self.kfk_en_kemono = numeric(NUMERICPATH + "/en/Kemono.ntxt", 1)
|
||||
self.kfk_en_kemono_WkPaDc = numeric(NUMERICPATH + "/en/KemonoWkPaDc.ntxt", 1)
|
||||
self.kfk_en_kemono_power = numeric(NUMERICPATH + "/en/KemonoPower.ntxt", 1)
|
||||
self.kfk_en_kemono_waken = numeric(NUMERICPATH + "/en/KemonoWaken.ntxt", 1)
|
||||
self.kfk_en_kemono_garden = numeric(NUMERICPATH + "/en/KemonoGarden.ntxt", 1)
|
||||
self.kfk_en_str = numeric(NUMERICPATH + "/en/Str.ntxt", 1)
|
||||
|
||||
self.process_friends()
|
||||
self.process_stages()
|
||||
|
||||
app.databases["Kingdom"] = self
|
||||
|
||||
def process_friends(self):
|
||||
self.processed_friends = {}
|
||||
for friend_id in self.kfk_kemono.indexed_data:
|
||||
if friend_id > 19000:
|
||||
continue
|
||||
|
||||
friend = self.kfk_kemono.get(friend_id)
|
||||
|
||||
friend["comboSkill"] = ""
|
||||
combo_friends = []
|
||||
for entry in friend["combo"]:
|
||||
combo = self.kfk_combo.get(entry)
|
||||
if combo is not None:
|
||||
combo_en = self.kfk_en_combo.get(combo["comboSkill"])
|
||||
friend["comboSkill"] = {"name":combo_en["comboName"], "desc":combo_en["desc"]}
|
||||
for combo_friend_id in combo["comboKemono"]:
|
||||
combo_friend = self.kfk_en_kemono.get(combo_friend_id)
|
||||
if combo_friend is not None and "name" in combo_friend:
|
||||
combo_friends.append(combo_friend["name"])
|
||||
else:
|
||||
combo_friends.append(combo_friend_id)
|
||||
|
||||
friend["combo"] = combo_friends
|
||||
|
||||
limit_breaks = []
|
||||
for bk_value in self.kfk_kemono_power.indexed_data.values():
|
||||
if len(limit_breaks) == 5:
|
||||
break
|
||||
if(bk_value["kemonosn"] == friend_id):
|
||||
if bk_value["effectType1"] == 0:
|
||||
pass
|
||||
if bk_value["effectType1"] == 1 or bk_value["effectType1"] == 3:
|
||||
lb = self.kfk_en_kemono_power.get(friend_id * 100 + bk_value["powerlevel"])
|
||||
if "name" in lb:
|
||||
limit_breaks.append({"name": lb["name"], "desc": lb["describe"]})
|
||||
else:
|
||||
limit_breaks.append({"name": None, "desc": lb["describe"]})
|
||||
if bk_value["effectType1"] == 2:
|
||||
raise Exception()
|
||||
if bk_value["effectType1"] == 4:
|
||||
lb = self.kfk_en_kemono_WkPaDc.get(bk_value['effectParam1'][0] * 100)
|
||||
limit_breaks.append({"name": lb["name"], "desc": clean_skill_string(lb["describe"])})
|
||||
|
||||
habits = []
|
||||
for habit in friend["habitSn"]:
|
||||
hbt = self.kfk_en_kemono_WkPaDc.get(habit*100)
|
||||
habits.append({"name":hbt["name"], "desc":hbt["describe"]})
|
||||
for i in range (3):
|
||||
if len(habits) < i+1:
|
||||
habits.append({"name":None, "desc":None})
|
||||
|
||||
awakens = []
|
||||
max_levels = []
|
||||
for i in range(3):
|
||||
awaken_id = friend_id * 100 + i
|
||||
awaken = self.kfk_kemono_waken.get(awaken_id)
|
||||
awaken1 = self.kfk_en_kemono_waken.get(awaken_id)
|
||||
if awaken != None and awaken1 != None:
|
||||
awaken["en"] = awaken1
|
||||
awakens.append(awaken)
|
||||
max_levels.append(awaken["levelmax"])
|
||||
|
||||
def validateValue(obj, valName):
|
||||
if obj[valName] is None:
|
||||
obj[valName] = {"name": "", "desc": ""}
|
||||
if "desc" not in obj[valName]:
|
||||
obj[valName]["desc"] = None
|
||||
|
||||
friend["name"] = self.kfk_en_kemono.get(friend_id)["name"]
|
||||
ele = friend["element"]
|
||||
friend["color"] = "Orange" if ele == 1 else "Green" if ele == 2 else "Blue" if ele == 3 else "Error"
|
||||
ele = friend["showSkillType"]
|
||||
friend["role"] = "Control" if ele == 1 else "Guard" if ele == 2 else "Healer" if ele == 3 else "Support" if ele == 4 else "Assault" if ele == 5 else "Assault (AOE)" if ele == 6 else "Error"
|
||||
friend["maxLvl"] = max_levels
|
||||
friend["awakenings"] = awakens
|
||||
friend["maxAwaken"] = max_awaken = len(awakens)-1
|
||||
friend["base_stats"] = calculate_stats(friend, 0, 1)
|
||||
friend["max_stats"] = calculate_stats(friend, max_awaken, friend["maxLvl"][max_awaken], trust_percent=100, trustAttri= next((e for e in self.kfk_trust_attri.indexed_data.values() if e["kemonoSn"] == friend_id), None))
|
||||
friend["max_miracle"] = 13 if friend["star"] == 6 or friend["star"] == 5 else 12 if friend["star"] == 4 else 11
|
||||
friend["collisionSkill"] = self.kfk_en_skill.get(friend["collisionSkill"])
|
||||
friend["gardenSpec1"] = self.kfk_en_kemono_garden.get(friend["gardenSpec1"])
|
||||
friend["gardenSpec2"] = self.kfk_en_kemono_garden.get(friend["gardenSpec2"])
|
||||
friend["gardenSpec3"] = self.kfk_en_kemono_garden.get(friend["gardenSpec3"])
|
||||
friend["describe"] = self.kfk_en_str.get(friend["describe"])
|
||||
|
||||
friend["attack"] = self.kfk_en_skill.get(friend["attack"])
|
||||
|
||||
skill_id = friend["skill"]
|
||||
friend["skill"] = self.kfk_en_skill.get(skill_id)
|
||||
friend["skill1"] = self.kfk_en_skill.get(skill_id+1) if max_awaken >= 1 else None
|
||||
friend["skill2"] = self.kfk_en_skill.get(skill_id+2) if max_awaken >= 2 else None
|
||||
friend["spAttack"] = self.kfk_en_skill.get(friend["spAttack"])
|
||||
friend["habitSn"] = habits
|
||||
friend["limitBreaks"] = limit_breaks
|
||||
|
||||
validateValue(friend, "collisionSkill")
|
||||
validateValue(friend, "attack")
|
||||
validateValue(friend, "skill")
|
||||
validateValue(friend, "skill1")
|
||||
validateValue(friend, "skill2")
|
||||
validateValue(friend, "spAttack")
|
||||
|
||||
self.processed_friends[friend_id] = friend
|
||||
|
||||
def process_stages(self):
|
||||
self.item_stages = {}
|
||||
for stage in self.kfk_stages.indexed_data.values():
|
||||
merged_array = stage["fastReward2_star3"] + stage["fastReward3_star3"]
|
||||
if len(merged_array) > 0:
|
||||
for drop_sn in merged_array:
|
||||
drop = self.kfk_drop.get(drop_sn)
|
||||
for drop_group_sn in drop["groupSn"]:
|
||||
drop_group = self.kfk_drop_group.get(drop_group_sn)
|
||||
if drop_group["type"] == 2 or drop_group["type"] == 1:
|
||||
#materials
|
||||
item_id = drop_group["content"]
|
||||
item = self.kfk_en_item.get(item_id)
|
||||
if item_id not in self.item_stages:
|
||||
self.item_stages[item_id] = []
|
||||
if stage not in self.item_stages[item_id]:
|
||||
self.item_stages[item_id].append(stage)
|
||||
|
||||
def get_chara(self, id : int):
|
||||
#return next(lambda f: f["sn"] == id, None)
|
||||
if id not in self.processed_friends:
|
||||
return None
|
||||
else:
|
||||
friend = self.processed_friends[id]
|
||||
data = {
|
||||
"id": friend["sn"],
|
||||
"name": friend["name"],
|
||||
"element": friend["color"],
|
||||
"role": friend["role"],
|
||||
"star": friend["star"],
|
||||
"combo": friend["combo"],
|
||||
"comboSkill": friend["comboSkill"],
|
||||
"describe": friend["describe"],
|
||||
"awakenings": friend["awakenings"],
|
||||
"max_miracle": friend["max_miracle"],
|
||||
"stats": {
|
||||
"base":friend["base_stats"],
|
||||
"max":friend["max_stats"]
|
||||
},
|
||||
"collision": {"name": friend["collisionSkill"]["name"], "desc": clean_skill_string(friend["collisionSkill"]["desc"])},
|
||||
"attack": {"name": friend["attack"]["name"], "desc": clean_skill_string(friend["attack"]["desc"])},
|
||||
"skills":[
|
||||
{"name": friend["skill"]["name"], "desc": fill_miracle(friend["skill"]["desc"], miracle_level=friend["max_miracle"], kemono_level=friend["maxLvl"][-1])},
|
||||
{"name": friend["skill1"]["name"], "desc": fill_miracle(friend["skill1"]["desc"], miracle_level=friend["max_miracle"], kemono_level=friend["maxLvl"][-1])},
|
||||
{"name": friend["skill2"]["name"], "desc": fill_miracle(friend["skill2"]["desc"], miracle_level=friend["max_miracle"], kemono_level=friend["maxLvl"][-1])},
|
||||
],
|
||||
"garden":[
|
||||
friend["gardenSpec1"],
|
||||
friend["gardenSpec2"],
|
||||
friend["gardenSpec3"],
|
||||
],
|
||||
"habits":[
|
||||
{"name": friend["habitSn"][0]["name"], "desc": clean_skill_string(friend["habitSn"][0]["desc"])},
|
||||
{"name": friend["habitSn"][1]["name"], "desc": clean_skill_string(friend["habitSn"][1]["desc"])},
|
||||
{"name": friend["habitSn"][2]["name"], "desc": clean_skill_string(friend["habitSn"][2]["desc"])}
|
||||
],
|
||||
"limitBreaks":[
|
||||
friend["limitBreaks"][0],
|
||||
friend["limitBreaks"][1],
|
||||
friend["limitBreaks"][2],
|
||||
friend["limitBreaks"][3],
|
||||
friend["limitBreaks"][4]
|
||||
],
|
||||
"awakenMaterials": get_awaken_materials(friend, self.kfk_kemono_waken, self.kfk_en_item),
|
||||
"skillMaterials": get_skill_materials(friend, self.kfk_kemono_skill, self.kfk_en_item)
|
||||
}
|
||||
|
||||
return json.dumps(data, sort_keys=False, indent=1, ensure_ascii=False)
|
||||
|
||||
def get_chara_wiki(self, id : int):
|
||||
friend = self.processed_friends[id]
|
||||
lines = []
|
||||
lines.append("|name=" + friend["name"])
|
||||
|
||||
#stats
|
||||
lines.append("|maxLvls=" + ",".join([str(lvl) for lvl in friend["maxLvl"]]))
|
||||
lines.append("|hp=" + str(friend["base_stats"]["hp"]))
|
||||
lines.append("|maxhp=" + str(friend["max_stats"]["hp"]))
|
||||
lines.append("|atk=" + str(friend["base_stats"]["patk"]))
|
||||
lines.append("|maxatk=" + str(friend["max_stats"]["patk"]))
|
||||
lines.append("|satk=" + str(friend["base_stats"]["satk"]))
|
||||
lines.append("|maxsatk=" + str(friend["max_stats"]["satk"]))
|
||||
lines.append("|pdef=" + str(friend["base_stats"]["pdef"]))
|
||||
lines.append("|maxpdef=" + str(friend["max_stats"]["pdef"]))
|
||||
lines.append("|sdef=" + str(friend["base_stats"]["sdef"]))
|
||||
lines.append("|maxsdef=" + str(friend["max_stats"]["sdef"]))
|
||||
lines.append("|speed=" + str(friend["base_stats"]["speed"]))
|
||||
lines.append("|maxspeed=" + str(friend["max_stats"]["speed"]))
|
||||
|
||||
#skills
|
||||
lines.append("|collisionSkill=" + friend["collisionSkill"]["name"])
|
||||
lines.append("|collisionSkillEffect=" + clean_skill_string(friend["collisionSkill"]["desc"]))
|
||||
lines.append("|attack=" + friend["attack"]["name"])
|
||||
lines.append("|attackEffect=" + clean_skill_string(friend["attack"]["desc"]))
|
||||
lines.append("|skill1=" + friend["skill"]["name"])
|
||||
lines.append("|skill1Effect=" + clean_skill_string(friend["skill"]["desc"]))
|
||||
lines.append("|skill2=" + friend["skill1"]["name"])
|
||||
lines.append("|skill2Effect=" + clean_skill_string(friend["skill1"]["desc"]))
|
||||
lines.append("|skill3=" + friend["skill2"]["name"])
|
||||
lines.append("|skill3Effect=" + clean_skill_string(friend["skill2"]["desc"]))
|
||||
lines.append("|spAttack=" + friend["spAttack"]["name"])
|
||||
lines.append("|spAttackEffect=" + clean_skill_string(friend["spAttack"]["desc"]))
|
||||
|
||||
for i in range(0,5):
|
||||
lb = friend['limitBreaks'][i]
|
||||
if lb["name"] is None:
|
||||
lines.append(f"|b{i+1}={friend['limitBreaks'][i]['desc']}")
|
||||
else:
|
||||
lines.append(f"|b{i+1}='''{friend['limitBreaks'][i]['name']}'''\n{friend['limitBreaks'][i]['desc']}")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
def get_item(self, id):
|
||||
result = {"item": None, "stages": []}
|
||||
item = self.kfk_en_item.get(id)
|
||||
if item is None:
|
||||
return result
|
||||
|
||||
result["item"] = item
|
||||
|
||||
if id in self.item_stages:
|
||||
result["stages"] = self.item_stages[id]
|
||||
|
||||
return json.dumps(result, sort_keys=False, indent=1, ensure_ascii=False)
|
|
@ -0,0 +1,21 @@
|
|||
import json
|
||||
|
||||
|
||||
class numeric:
|
||||
def __init__(self, file_path:str, type:int):
|
||||
self.indexed_data = {}
|
||||
with open(file_path, "rt", encoding="utf-8") as f:
|
||||
if type == 0:
|
||||
values = json.load(f)["Data"]
|
||||
for value in values:
|
||||
self.indexed_data[value["sn"]] = value
|
||||
if type == 1:
|
||||
values = json.load(f)
|
||||
for i in range(0, len(values["IDs"])):
|
||||
id = values["IDs"][i]
|
||||
self.indexed_data[id] = values["Data"][i]
|
||||
|
||||
def get(self, id):
|
||||
if id not in self.indexed_data:
|
||||
return None
|
||||
return self.indexed_data[id]
|
|
@ -0,0 +1,7 @@
|
|||
class KingdomDB:
|
||||
|
||||
def __init__(self, app) -> None:
|
||||
if "Nexon" in app.databases:
|
||||
del app.databases["Nexon"]
|
||||
|
||||
app.databases["Nexon"] = self
|
|
@ -0,0 +1,27 @@
|
|||
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):
|
||||
if "wiki" in request.args:
|
||||
result = app.databases["KF3"].get_chara_wiki(id)
|
||||
|
||||
response = app.response_class(
|
||||
response=result,
|
||||
status=200,
|
||||
mimetype='text/plain'
|
||||
)
|
||||
else:
|
||||
result = app.databases["KF3"].get_chara(id)
|
||||
result = json.dumps(result)
|
||||
|
||||
response = app.response_class(
|
||||
response=result,
|
||||
status=200,
|
||||
mimetype='application/json'
|
||||
)
|
||||
|
||||
response.headers.add("Access-Control-Allow-Origin", "*")
|
||||
return response
|
|
@ -0,0 +1,21 @@
|
|||
import json
|
||||
from flask_restful import Resource
|
||||
from flask import current_app as app
|
||||
|
||||
class KF3_Friends(Resource):
|
||||
def get(self):
|
||||
db = app.databases["KF3"]
|
||||
result = []
|
||||
for value in db.charaData.values():
|
||||
result.append({"id": value["id"], "name": value["nameEn"]})
|
||||
|
||||
result = sorted(result, key=lambda f: f["id"])
|
||||
|
||||
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
|
|
@ -0,0 +1,25 @@
|
|||
from flask_restful import Resource
|
||||
from flask import current_app as app
|
||||
from flask import request
|
||||
|
||||
class Kingdom_Friend(Resource):
|
||||
def get(self, id:int):
|
||||
if "wiki" in request.args:
|
||||
result = app.databases["Kingdom"].get_chara_wiki(id)
|
||||
|
||||
response = app.response_class(
|
||||
response=result,
|
||||
status=200,
|
||||
mimetype='text/plain'
|
||||
)
|
||||
else:
|
||||
result = app.databases["Kingdom"].get_chara(id)
|
||||
|
||||
response = app.response_class(
|
||||
response=result,
|
||||
status=200,
|
||||
mimetype='application/json'
|
||||
)
|
||||
|
||||
response.headers.add("Access-Control-Allow-Origin", "*")
|
||||
return response
|
|
@ -0,0 +1,21 @@
|
|||
import json
|
||||
from flask_restful import Resource
|
||||
from flask import current_app as app, jsonify
|
||||
|
||||
class Kingdom_Friends(Resource):
|
||||
def get(self):
|
||||
db = app.databases["Kingdom"]
|
||||
result = []
|
||||
for value in db.processed_friends.values():
|
||||
result.append({"id": value["sn"], "name": value["name"], "describe": value["describe"]["content"] if "content" in value["describe"] else ""})
|
||||
|
||||
result = sorted(result, key=lambda f: f["id"])
|
||||
|
||||
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
|
|
@ -0,0 +1,16 @@
|
|||
from flask_restful import Resource
|
||||
from flask import current_app as app
|
||||
from flask import request
|
||||
|
||||
class Kingdom_Item(Resource):
|
||||
def get(self, id:int):
|
||||
result = app.databases["Kingdom"].get_item(id)
|
||||
|
||||
response = app.response_class(
|
||||
response=result,
|
||||
status=200,
|
||||
mimetype='application/json'
|
||||
)
|
||||
|
||||
response.headers.add("Access-Control-Allow-Origin", "*")
|
||||
return response
|
|
@ -0,0 +1,23 @@
|
|||
import json
|
||||
from flask_restful import Resource
|
||||
from flask import current_app as app, jsonify
|
||||
|
||||
class Kingdom_Items(Resource):
|
||||
def get(self):
|
||||
db = app.databases["Kingdom"]
|
||||
result = []
|
||||
for key in db.kfk_en_item.indexed_data.keys():
|
||||
new_value = db.kfk_en_item.indexed_data[key]
|
||||
new_value["id"] = key
|
||||
result.append(new_value)
|
||||
|
||||
result = sorted(result, key=lambda f: f["id"])
|
||||
|
||||
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
|
|
@ -0,0 +1,36 @@
|
|||
import requests
|
||||
from datetime import datetime
|
||||
from flask_restful import Resource
|
||||
from flask import jsonify, make_response
|
||||
from resources.shared import API_URL, VALID_CURRENCIES_A, ERROR_CURRENCY_CODE, ERROR_DATE_FORMAT, ERROR_NO_DATA
|
||||
|
||||
class AverageRate(Resource):
|
||||
def get(self, date : str, currency : str):
|
||||
"""
|
||||
Returns average exchange rate for the specified currency and date.
|
||||
|
||||
Weekends and holidays return code 404.
|
||||
"""
|
||||
if not currency.upper() in VALID_CURRENCIES_A:
|
||||
return make_response(jsonify({"error": ERROR_CURRENCY_CODE}), 400)
|
||||
|
||||
try:
|
||||
date_temp = datetime.strptime(date, "%Y-%m-%d")
|
||||
|
||||
if date_temp.weekday() in [5,6]:
|
||||
return make_response(jsonify({"error": ERROR_NO_DATA}), 404)
|
||||
|
||||
#ensure correct date string format
|
||||
date = date_temp.strftime("%Y-%m-%d")
|
||||
except:
|
||||
return make_response(jsonify({"error": ERROR_DATE_FORMAT}), 400)
|
||||
|
||||
url = f"{API_URL}/exchangerates/rates/a/{currency}/{date}/"
|
||||
response = requests.get(url)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
return make_response(jsonify({"mid": data["rates"][0]["mid"]}), 200)
|
||||
elif response.status_code == 404:
|
||||
return make_response(jsonify({"error": ERROR_NO_DATA}), 404)
|
||||
else:
|
||||
return make_response(jsonify({"error": response.text}), 400)
|
|
@ -0,0 +1,34 @@
|
|||
import requests
|
||||
from flask_restful import Resource
|
||||
from flask import jsonify, make_response
|
||||
from resources.shared import API_URL, ERROR_CURRENCY_CODE, ERROR_DATE_RANGE, VALID_CURRENCIES_C
|
||||
|
||||
class DifferenceLast(Resource):
|
||||
def get(self, currency : str, num_days : int):
|
||||
"""
|
||||
Returns the highest difference between "ask" and "bid" values, and the day it occured.
|
||||
|
||||
Weekends and holidays are skipped and do not count towards the 'num_days' limit."
|
||||
"""
|
||||
if not currency.upper() in VALID_CURRENCIES_C:
|
||||
return make_response(jsonify({"error": ERROR_CURRENCY_CODE}), 400)
|
||||
|
||||
if not (num_days > 0 and num_days < 256):
|
||||
return make_response(jsonify({"error": ERROR_DATE_RANGE}), 400)
|
||||
|
||||
url = f"{API_URL}/exchangerates/rates/c/{currency}/last/{num_days}/"
|
||||
response = requests.get(url)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
result = {"diff_max": 0, "date":""}
|
||||
for rate in data["rates"]:
|
||||
#ensure correct precision
|
||||
difference = round(rate["ask"] - rate["bid"],4)
|
||||
#spread can be negative on rare occasions
|
||||
if abs(difference) > abs(result["diff_max"]):
|
||||
result["diff_max"] = difference
|
||||
result["date"] = rate["effectiveDate"]
|
||||
|
||||
return make_response(jsonify(result), 200)
|
||||
else:
|
||||
return make_response(jsonify({"error": response.text}), 400)
|
|
@ -0,0 +1,38 @@
|
|||
import requests
|
||||
from flask_restful import Resource
|
||||
from flask import jsonify, make_response
|
||||
from resources.shared import API_URL, ERROR_CURRENCY_CODE, ERROR_DATE_RANGE, VALID_CURRENCIES_A
|
||||
|
||||
class MinMaxLast(Resource):
|
||||
def get(self, currency : str, num_days : int):
|
||||
"""
|
||||
Returns minimum and maximum average exchange rate values, and the days they occured.
|
||||
|
||||
Weekends and holidays are skipped and do not count towards the 'num_days' limit."
|
||||
"""
|
||||
if not currency.upper() in VALID_CURRENCIES_A:
|
||||
return make_response(jsonify({"error": ERROR_CURRENCY_CODE}), 400)
|
||||
|
||||
if not (num_days > 0 and num_days < 256):
|
||||
return make_response(jsonify({"error": ERROR_DATE_RANGE}), 400)
|
||||
|
||||
url = f"{API_URL}/exchangerates/rates/a/{currency}/last/{num_days}/"
|
||||
response = requests.get(url)
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
result = {"mid_min": data["rates"][0]["mid"], "date_min":"", "mid_max": data["rates"][0]["mid"], "date_max":""}
|
||||
for rate in data["rates"]:
|
||||
#if rates are equal, return the more recent one
|
||||
#recent values are at the end of the list
|
||||
|
||||
if rate["mid"] >= result["mid_max"]:
|
||||
result["mid_max"] = rate["mid"]
|
||||
result["date_max"] = rate["effectiveDate"]
|
||||
|
||||
if rate["mid"] <= result["mid_min"]:
|
||||
result["mid_min"] = rate["mid"]
|
||||
result["date_min"] = rate["effectiveDate"]
|
||||
|
||||
return make_response(jsonify(result), 200)
|
||||
else:
|
||||
return make_response(jsonify({"error": response.text}), 400)
|
|
@ -0,0 +1,13 @@
|
|||
# Root url for the NBP API
|
||||
API_URL = "https://api.nbp.pl/api/"
|
||||
|
||||
# List of currencies accepted by NBP as of 2023-04-21
|
||||
# https://nbp.pl/en/statistic-and-financial-reporting/rates/table-a/
|
||||
# https://nbp.pl/en/statistic-and-financial-reporting/rates/table-c/
|
||||
VALID_CURRENCIES_A = ["AUD","THB","BRL","BGN","CAD","CLP","CZK","DKK","EUR","HUF","HKD","UAH","ISK","INR","MYR","MXN","ILS","NZD","NOK","PHP","GBP","ZAR","RON","IDR","SGD","SEK","CHF","TRY","USD","KRW","JPY","CNY","XDR" ]
|
||||
VALID_CURRENCIES_C = ["AUD","CAD","CZK","DKK","EUR","HUF","NOK","GBP","SEK","CHF","USD","JPY","XDR"]
|
||||
|
||||
ERROR_CURRENCY_CODE = "Invalid currency code."
|
||||
ERROR_DATE_FORMAT = "Incorrect date format, expected YYYY-MM-DD"
|
||||
ERROR_DATE_RANGE = "Invalid currency code."
|
||||
ERROR_NO_DATA = "Exchange rate data is not available for this date."
|
Loading…
Reference in New Issue