enka-network-api
Version:
Enka-network API wrapper for Genshin Impact.
172 lines (171 loc) • 11.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CharacterData = void 0;
const ImageAssets_1 = require("../assets/ImageAssets");
const TextAssets_1 = require("../assets/TextAssets");
const Skill_1 = require("./talents/Skill");
const AssetsNotFoundError_1 = require("../../errors/AssetsNotFoundError");
const Constellation_1 = require("./Constellation");
const ElementalBurst_1 = require("./talents/ElementalBurst");
const Costume_1 = require("./Costume");
const PassiveTalent_1 = require("./talents/PassiveTalent");
const ElementalSkill_1 = require("./talents/ElementalSkill");
const NormalAttack_1 = require("./talents/NormalAttack");
const Material_1 = require("../material/Material");
const CharacterDetails_1 = require("./CharacterDetails");
const CharacterAscension_1 = require("./CharacterAscension");
const config_file_js_1 = require("config_file.js");
const StatProperty_1 = require("../StatProperty");
const ts_utils_1 = require("../../utils/ts_utils");
const ExcelTransformer_1 = require("../../client/ExcelTransformer");
class CharacterData {
constructor(data, enka, candSkillDepotId) {
var _a, _b, _c;
/** This will be null if the character is Traveler */
this.nameCard = null;
/** Information in the profile menu in in-game character screen */
this.details = null;
this._data = data;
this.enka = enka;
const json = new config_file_js_1.JsonReader(ExcelTransformer_1.excelJsonOptions, this._data);
this.id = json.getAsNumber("id");
this.name = new TextAssets_1.TextAssets(json.getAsNumber("nameTextMapHash"), enka);
this.description = new TextAssets_1.TextAssets(json.getAsNumber("descTextMapHash"), enka);
this.bodyType = json.getAsString("bodyType");
this.weaponType = json.getAsString("weaponType");
this.gender = this.bodyType === "BODY_MALE" || this.bodyType === "BODY_BOY" ? "MALE" : "FEMALE";
this._nameId = json.getAsString("iconName").slice("UI_AvatarIcon_".length);
this.icon = new ImageAssets_1.ImageAssets(json.getAsString("iconName"), enka);
this.sideIcon = new ImageAssets_1.ImageAssets(json.getAsString("sideIconName"), enka);
this.splashImage = new ImageAssets_1.ImageAssets(`UI_Gacha_AvatarImg_${this._nameId}`, enka);
this.gachaSlice = new ImageAssets_1.ImageAssets(`UI_Gacha_AvatarIcon_${this._nameId}`, enka);
this.cardIcon = new ImageAssets_1.ImageAssets(`UI_AvatarIcon_${this._nameId}_Card`, enka);
const releaseData = enka.cachedAssetsManager.getExcelData("AvatarCodexExcelConfigData", this.id);
this._releaseData = releaseData !== null && releaseData !== void 0 ? releaseData : null;
this.releasedAt = releaseData ? new Date(`${new config_file_js_1.JsonReader(ExcelTransformer_1.excelJsonOptions, releaseData).getAsString("beginTime")}+8:00`) : null;
const archonsIds = Object.keys(enka.cachedAssetsManager.getExcelData("TrialAvatarFetterDataConfigData"));
this.isArchon = archonsIds.includes(this.id.toString());
// should always 10000005 and 10000007
const travelerIds = Object.keys(enka.cachedAssetsManager.getExcelData("AvatarHeroEntityExcelConfigData"));
this.isTraveler = travelerIds.includes(this.id.toString());
try {
this.details = CharacterDetails_1.CharacterDetails.getByCharacterId(this.id, this.isArchon, this.enka);
}
catch (_d) {
// do nothing
}
const fetterCardData = enka.cachedAssetsManager.getExcelData("FetterCharacterCardExcelConfigData", this.id);
if (fetterCardData) {
const friendshipRewardId = new config_file_js_1.JsonReader(ExcelTransformer_1.excelJsonOptions, fetterCardData).getAsNumber("rewardId");
const reward = enka.cachedAssetsManager.getExcelData("RewardExcelConfigData", friendshipRewardId);
if (!reward)
throw new AssetsNotFoundError_1.AssetsNotFoundError("Reward", friendshipRewardId);
const nameCardId = new config_file_js_1.JsonReader(ExcelTransformer_1.excelJsonOptions, reward).getAsNumber("rewardItemList", 0, "itemId");
this.nameCard = Material_1.Material.getMaterialById(nameCardId, enka);
}
this.rarity = json.getAsString("qualityType");
this.stars = this.rarity.startsWith("QUALITY_ORANGE") ? 5 : 4;
const costumeData = enka.cachedAssetsManager.getExcelData("AvatarCostumeExcelConfigData", this.id);
if (!costumeData)
throw new AssetsNotFoundError_1.AssetsNotFoundError("Costumes by character id", this.id);
this.costumes = Object.values(costumeData)
.map(c => new Costume_1.Costume(c, enka));
this.skillDepotId = candSkillDepotId !== null && candSkillDepotId !== void 0 ? candSkillDepotId : json.getAsNumber("skillDepotId");
const skillData = enka.cachedAssetsManager.getExcelData("AvatarSkillDepotExcelConfigData", this.skillDepotId);
if (!skillData)
throw new AssetsNotFoundError_1.AssetsNotFoundError("Skill Depot", this.skillDepotId);
this._skillData = skillData;
const skillDataJson = new config_file_js_1.JsonReader(ExcelTransformer_1.excelJsonOptions, skillData);
this.arkhe = skillDataJson.getAsStringWithDefault(null, enka.cachedAssetsManager.getObjectKeysManager().characterArkheKey);
// if the character is "Traveler" and no skillDepotId (which indicates its element type) provided,
// `elementalBurst`, `elementalSkill`, and `element` cannot be retrieved.
const energySkillId = skillDataJson.getAsNumberWithDefault(0, "energySkill");
const hasElement = energySkillId !== 0;
this.elementalBurst = hasElement ? ElementalBurst_1.ElementalBurst.getById(energySkillId, enka) : null;
this.element = (_b = (_a = this.elementalBurst) === null || _a === void 0 ? void 0 : _a.costElemType) !== null && _b !== void 0 ? _b : null;
const _skills = skillDataJson.get("skills").mapArray((_, p) => p.getAsNumber()).map((skillId, index) => {
if (!skillId)
return null;
if (index === 0)
return NormalAttack_1.NormalAttack.getById(skillId, enka);
if (index === 1)
return ElementalSkill_1.ElementalSkill.getById(skillId, enka);
return Skill_1.Skill.getById(skillId, enka);
}).filter(ts_utils_1.nonNullable);
if (this.elementalBurst)
_skills.push(this.elementalBurst);
this.skills = _skills;
this.elementalSkill = (_c = _skills.find(s => s instanceof ElementalSkill_1.ElementalSkill)) !== null && _c !== void 0 ? _c : null;
this.normalAttack = _skills.find(s => s instanceof NormalAttack_1.NormalAttack);
this.passiveTalents = skillDataJson.get("inherentProudSkillOpens").filterArray((_, p) => p.has("proudSkillGroupId")).map(([, p]) => {
const proudSkillGroupId = p.getAsNumber("proudSkillGroupId");
if (proudSkillGroupId === 0)
return null;
return PassiveTalent_1.PassiveTalent.getById(proudSkillGroupId * 100 + 1, enka);
}).filter(ts_utils_1.nonNullable);
this.constellations = skillDataJson.get("talents").mapArray((_, p) => p.getAsNumber()).filter(cId => cId !== 0).map(cId => Constellation_1.Constellation.getById(cId, enka));
}
/**
* @returns character's original name (Travelers -> Aether, Lumine)
*/
getOriginalName() {
switch (this.id) {
case 10000005:
return new TextAssets_1.TextAssets(2329553598, this.enka);
case 10000007:
return new TextAssets_1.TextAssets(3241049361, this.enka);
default:
return this.name;
}
}
/**
* @param ascension ascension level between 0 and 6
*/
getAscensionData(ascension) {
return CharacterAscension_1.CharacterAscension.getById(new config_file_js_1.JsonReader(ExcelTransformer_1.excelJsonOptions, this._data).getAsNumber("avatarPromoteId"), ascension, this.enka);
}
/**
* @param ascension ascension level between 0 and 6
* @param level character level between 1 and 90
*/
getStats(ascension, level) {
if (ascension < 0 || 6 < ascension)
throw new Error("Ascension levels must be between 0 and 6.");
if (level < 1 || 90 < level)
throw new Error("Character levels must be between 1 and 90.");
const curveData = this.enka.cachedAssetsManager.getExcelData("AvatarCurveExcelConfigData", level);
if (!curveData)
throw new AssetsNotFoundError_1.AssetsNotFoundError("Character curve data", level);
const curve = new config_file_js_1.JsonReader(ExcelTransformer_1.excelJsonOptions, curveData).get("curveInfos");
const ascensionData = this.getAscensionData(ascension);
const characterJson = new config_file_js_1.JsonReader(ExcelTransformer_1.excelJsonOptions, this._data);
const baseValues = {
"FIGHT_PROP_BASE_HP": characterJson.getAsNumber("hpBase"),
"FIGHT_PROP_BASE_ATTACK": characterJson.getAsNumber("attackBase"),
"FIGHT_PROP_BASE_DEFENSE": characterJson.getAsNumber("defenseBase"),
"FIGHT_PROP_CRITICAL": characterJson.getAsNumber("critical"),
"FIGHT_PROP_CRITICAL_HURT": characterJson.getAsNumber("criticalHurt"),
};
const curves = characterJson.get("propGrowCurves");
const statPropertiesWithBaseValues = Object.entries(baseValues).map(([fightProp, baseValue]) => {
var _a, _b, _c, _d;
const curveJson = (_a = curves.findArray((_, c) => c.getAsString("type") === fightProp)) === null || _a === void 0 ? void 0 : _a[1];
const curveType = curveJson === null || curveJson === void 0 ? void 0 : curveJson.getAsString("growCurve");
const targetCurve = curveType ? (_b = curve.findArray((_, c) => c.getAsString("type") === curveType)) === null || _b === void 0 ? void 0 : _b[1] : null;
const ascensionValue = (_d = (_c = ascensionData.addProps.find(p => p.fightProp === fightProp)) === null || _c === void 0 ? void 0 : _c.value) !== null && _d !== void 0 ? _d : 0;
const value = baseValue * (targetCurve ? targetCurve.getAsNumber("value") : 1) + ascensionValue;
return new StatProperty_1.StatProperty(fightProp, value, this.enka);
});
const statPropertiesWithoutBaseValues = ascensionData.addProps.filter(p => !baseValues[p.fightProp]);
const levelingProps = [...statPropertiesWithBaseValues, ...statPropertiesWithoutBaseValues];
const talents = this.passiveTalents.filter(t => t.requiredAscension <= ascension && t.addProps.length > 0);
return StatProperty_1.StatProperty.sumStatProperties([...levelingProps, ...talents.flatMap(t => t.addProps)], this.enka);
}
static getById(id, enka, candSkillDepotId) {
const data = enka.cachedAssetsManager.getExcelData("AvatarExcelConfigData", id);
if (!data)
throw new AssetsNotFoundError_1.AssetsNotFoundError("Character", id); // Characters
return new CharacterData(data, enka, candSkillDepotId);
}
}
exports.CharacterData = CharacterData;