feat: enhance asset management with weapon and relic stores, add fight property enums
This commit is contained in:
@@ -12,4 +12,7 @@ To start the development server run:
|
|||||||
bun run dev
|
bun run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
Open http://localhost:3000/ with your browser to see the result.
|
Open http://localhost:3000/ with your browser to see the result.
|
||||||
|
|
||||||
|
## Credit
|
||||||
|
enka
|
||||||
@@ -20,7 +20,7 @@ build({
|
|||||||
target: "bun",
|
target: "bun",
|
||||||
entrypoints: ["src/index.ts"],
|
entrypoints: ["src/index.ts"],
|
||||||
outdir: "dist",
|
outdir: "dist",
|
||||||
features: ["FAKE_REQUIRE"],
|
features: ["FAKE_REQUIRE", "API"],
|
||||||
});
|
});
|
||||||
|
|
||||||
for (const f of nativeFiles.scanSync()) {
|
for (const f of nativeFiles.scanSync()) {
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
// source: buzzy
|
||||||
|
|
||||||
|
export const star = (key: number) => (
|
||||||
|
<svg
|
||||||
|
key={key}
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 576 512"
|
||||||
|
style={{
|
||||||
|
fill: "rgba(255,204,50)",
|
||||||
|
filter: "drop-shadow(#e3721b 0px 0px 1px)",
|
||||||
|
height: 20,
|
||||||
|
width: 20,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<title>Star</title>
|
||||||
|
<path d="M381.2 150.3l143.7 21.2c11.9 1.7 21.9 10.1 25.7 21.6 3.8 11.6.7 24.2-7.9 32.8L438.5 328.1l24.6 146.6c2 12-2.9 24.2-12.9 31.3-9.9 7.1-23 8-33.7 2.3l-128.4-68.5-128.3 68.5c-10.8 5.7-23.9 4.8-33.8-2.3-9.9-7.1-14.9-19.3-12.8-31.3l24.6-146.6L33.58 225.9c-8.61-8.6-11.67-21.2-7.89-32.8 3.77-11.5 13.74-19.9 25.73-21.6L195 150.3l64.4-132.33C264.7 6.954 275.9-.04 288.1-.04c12.3 0 23.5 6.994 28.8 18.01l64.3 132.33z"></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import { CSSProperties } from "react";
|
||||||
|
|
||||||
|
export function WeaponGlazeSvg({
|
||||||
|
tw,
|
||||||
|
style,
|
||||||
|
}: {
|
||||||
|
tw: string;
|
||||||
|
style: CSSProperties;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 7 1"
|
||||||
|
tw={tw}
|
||||||
|
style={style}
|
||||||
|
>
|
||||||
|
<title>Weapon Glaze</title>
|
||||||
|
<defs>
|
||||||
|
<filter
|
||||||
|
id="svg-Sheen__a"
|
||||||
|
width="1.028"
|
||||||
|
height="2.42"
|
||||||
|
x="-.014"
|
||||||
|
y="-.71"
|
||||||
|
color-interpolation-filters="sRGB"
|
||||||
|
>
|
||||||
|
<feGaussianBlur stdDeviation=".037"></feGaussianBlur>
|
||||||
|
</filter>
|
||||||
|
<filter
|
||||||
|
id="svg-Sheen__b"
|
||||||
|
width="1"
|
||||||
|
height="1.013"
|
||||||
|
x="0"
|
||||||
|
y="-.007"
|
||||||
|
style={{ colorInterpolationFilters: "sRGB" }}
|
||||||
|
>
|
||||||
|
<feGaussianBlur></feGaussianBlur>
|
||||||
|
</filter>
|
||||||
|
<filter
|
||||||
|
id="svg-Sheen__c"
|
||||||
|
width="1.004"
|
||||||
|
height="1.613"
|
||||||
|
x="-.002"
|
||||||
|
y="-.306"
|
||||||
|
style={{ colorInterpolationFilters: "sRGB" }}
|
||||||
|
>
|
||||||
|
<feGaussianBlur stdDeviation=".005"></feGaussianBlur>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="m77.486 180.56 3.227-.063 3.227.063-3.227.063z"
|
||||||
|
filter="url(#svg-Sheen__a)"
|
||||||
|
style={{ mixBlendMode: "normal", filter: "url(#svg-Sheen__a)" }}
|
||||||
|
transform="matrix(.8616 0 0 3.6457 -66.042 -657.768)"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="m77.457 180.56 3.256-.059 3.257.059-3.257.059z"
|
||||||
|
opacity=".7"
|
||||||
|
style={{
|
||||||
|
opacity: 0.7,
|
||||||
|
strokeWidth: "1.09436",
|
||||||
|
filter: "url(#svg-Sheen__b)",
|
||||||
|
}}
|
||||||
|
transform="matrix(.8168 0 0 1.54812 -62.427 -279.03)"
|
||||||
|
></path>
|
||||||
|
<path
|
||||||
|
fill="#fff"
|
||||||
|
d="m77.457 180.56 3.256-.021 3.257.021-3.257.021z"
|
||||||
|
opacity=".7"
|
||||||
|
style={{
|
||||||
|
opacity: 1,
|
||||||
|
mixBlendMode: "normal",
|
||||||
|
strokeWidth: "0.661912",
|
||||||
|
filter: 'url("#svg-Sheen__c")',
|
||||||
|
}}
|
||||||
|
transform="matrix(.81587 0 0 1 -62.351 -180.06)"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
+55
-6
@@ -2,7 +2,7 @@ import { mkdir, readdir } from "node:fs/promises";
|
|||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { file, hash, write } from "bun";
|
import { file, hash, write } from "bun";
|
||||||
import { TTLCache } from "enka.ts";
|
import { TTLCache } from "enka.ts";
|
||||||
import { AvatarStore } from "@/type/enka";
|
import { AvatarStore, EnkaLocs, RelicStore, WeaponStore } from "@/type/enka";
|
||||||
|
|
||||||
export const cache = new TTLCache();
|
export const cache = new TTLCache();
|
||||||
|
|
||||||
@@ -10,17 +10,21 @@ export class AssetManager {
|
|||||||
private cache = new Map<string, ArrayBuffer>();
|
private cache = new Map<string, ArrayBuffer>();
|
||||||
|
|
||||||
async getMissingAssets() {
|
async getMissingAssets() {
|
||||||
const store = await getEnkaAvatarStore();
|
const avatars = await getEnkaAvatarStore();
|
||||||
|
const weapons = await getEnkaWeaponStore();
|
||||||
|
const relics = await getEnkaRelicStore();
|
||||||
await mkdir("store").catch(() => {});
|
await mkdir("store").catch(() => {});
|
||||||
const existing = await readdir("store");
|
const existing = await readdir("store");
|
||||||
const missing = new Set<string>();
|
const missing = new Set<string>();
|
||||||
Object.values(store)
|
[
|
||||||
.flatMap((a) => [
|
...Object.values(avatars).flatMap((a) => [
|
||||||
...a.Consts,
|
...a.Consts,
|
||||||
...Object.values(a.Skills),
|
...Object.values(a.Skills),
|
||||||
a.SideIconName.replace("AvatarIcon_Side", "Gacha_AvatarImg"),
|
a.SideIconName.replace("AvatarIcon_Side", "Gacha_AvatarImg"),
|
||||||
])
|
]),
|
||||||
.map((n) => existing.includes(hash(n).toString(36)) || missing.add(n));
|
...Object.values(weapons).flatMap((w) => [w.Icon, w.AwakenIcon]),
|
||||||
|
...Object.values(relics.Items).map((r) => r.Icon),
|
||||||
|
].map((n) => existing.includes(hash(n).toString(36)) || missing.add(n));
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`${missing.size} assets missing, ${existing.length} assets in store`,
|
`${missing.size} assets missing, ${existing.length} assets in store`,
|
||||||
@@ -89,6 +93,15 @@ export class AssetManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getEnkaStores() {
|
||||||
|
return Promise.all([
|
||||||
|
getEnkaAvatarStore(),
|
||||||
|
getEnkaWeaponStore(),
|
||||||
|
getEnkaRelicStore(),
|
||||||
|
getEnkaLocs(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
export async function getEnkaAvatarStore(): Promise<AvatarStore> {
|
export async function getEnkaAvatarStore(): Promise<AvatarStore> {
|
||||||
const cached = cache.get("enkaAvatarStore");
|
const cached = cache.get("enkaAvatarStore");
|
||||||
if (cached) return cached as AvatarStore;
|
if (cached) return cached as AvatarStore;
|
||||||
@@ -100,3 +113,39 @@ export async function getEnkaAvatarStore(): Promise<AvatarStore> {
|
|||||||
cache.set("enkaAvatarStore", data, 86400); // 24 hours
|
cache.set("enkaAvatarStore", data, 86400); // 24 hours
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getEnkaWeaponStore(): Promise<WeaponStore> {
|
||||||
|
const cached = cache.get("enkaWeaponStore");
|
||||||
|
if (cached) return cached as WeaponStore;
|
||||||
|
|
||||||
|
const response = await fetch(
|
||||||
|
"https://github.com/EnkaNetwork/API-docs/raw/refs/heads/master/store/gi/weapons.json",
|
||||||
|
);
|
||||||
|
const data = await response.json();
|
||||||
|
cache.set("enkaWeaponStore", data, 86400); // 24 hours
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getEnkaRelicStore(): Promise<RelicStore> {
|
||||||
|
const cached = cache.get("enkaRelicStore");
|
||||||
|
if (cached) return cached as RelicStore;
|
||||||
|
|
||||||
|
const response = await fetch(
|
||||||
|
"https://github.com/EnkaNetwork/API-docs/raw/refs/heads/master/store/gi/relics.json",
|
||||||
|
);
|
||||||
|
const data = await response.json();
|
||||||
|
cache.set("enkaRelicStore", data, 86400); // 24 hours
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getEnkaLocs(): Promise<EnkaLocs> {
|
||||||
|
const cached = cache.get("enkaLocs");
|
||||||
|
if (cached) return cached as EnkaLocs;
|
||||||
|
|
||||||
|
const response = await fetch(
|
||||||
|
"https://github.com/EnkaNetwork/API-docs/raw/refs/heads/master/store/gi/locs.json",
|
||||||
|
);
|
||||||
|
const data = await response.json();
|
||||||
|
cache.set("enkaLocs", data, 86400); // 24 hours
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|||||||
@@ -65,3 +65,44 @@ export type WeaponType =
|
|||||||
| "WEAPON_BOW";
|
| "WEAPON_BOW";
|
||||||
|
|
||||||
export type QualityType = "QUALITY_ORANGE" | "QUALITY_PURPLE";
|
export type QualityType = "QUALITY_ORANGE" | "QUALITY_PURPLE";
|
||||||
|
|
||||||
|
export interface WeaponConfig {
|
||||||
|
Rarity: number;
|
||||||
|
WeaponType: number;
|
||||||
|
Icon: string;
|
||||||
|
AwakenIcon: string;
|
||||||
|
NameTextMapHash: number;
|
||||||
|
|
||||||
|
BaseProps: Record<string, number>;
|
||||||
|
PropGrowCurves: Record<string, number>;
|
||||||
|
|
||||||
|
BasePromote: number[];
|
||||||
|
RefineProps: Record<string, unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type WeaponStore = Record<string, WeaponConfig>;
|
||||||
|
|
||||||
|
export interface RelicConfig {
|
||||||
|
Rarity: number;
|
||||||
|
EquipType: number;
|
||||||
|
Icon: string;
|
||||||
|
SetId: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RelicConfigMap = Record<string, RelicConfig>;
|
||||||
|
|
||||||
|
export interface RelicSetBonus {
|
||||||
|
Name: number; // TextMapHash
|
||||||
|
AddProps: Record<number, number>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RelicSetMap = Record<number, RelicSetBonus>;
|
||||||
|
|
||||||
|
export interface RelicStore {
|
||||||
|
Items: RelicConfigMap;
|
||||||
|
Sets: RelicSetMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EnkaLocs {
|
||||||
|
en: Record<string, string>;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,148 @@
|
|||||||
|
enum FightPropMapEnum {
|
||||||
|
None = 0,
|
||||||
|
|
||||||
|
// Base Stats
|
||||||
|
BaseHP = 1, // FIGHT_PROP_BASE_HP
|
||||||
|
HP_Flat = 2, // FIGHT_PROP_HP (flat HP bonus)
|
||||||
|
HPPercentage = 3, // FIGHT_PROP_HP_PERCENT
|
||||||
|
BaseAttack = 4, // FIGHT_PROP_BASE_ATTACK
|
||||||
|
Attack_Flat = 5, // FIGHT_PROP_ATTACK (flat ATK bonus)
|
||||||
|
AttackPercentage = 6, // FIGHT_PROP_ATTACK_PERCENT
|
||||||
|
BaseDefense = 7, // FIGHT_PROP_BASE_DEFENSE
|
||||||
|
Defense_Flat = 8, // FIGHT_PROP_DEFENSE (flat DEF bonus)
|
||||||
|
DefensePercentage = 9, // FIGHT_PROP_DEFENSE_PERCENT
|
||||||
|
BaseSpeed = 10, // FIGHT_PROP_BASE_SPEED
|
||||||
|
SpeedPercentage = 11, // FIGHT_PROP_SPEED_PERCENT
|
||||||
|
HPMPPercentage = 12, // FIGHT_PROP_HP_MP_PERCENT
|
||||||
|
AttackMPPercentage = 13, // FIGHT_PROP_ATTACK_MP_PERCENT
|
||||||
|
|
||||||
|
// Critical Stats
|
||||||
|
CriticalRate = 20, // FIGHT_PROP_CRITICAL
|
||||||
|
AntiCritical = 21, // FIGHT_PROP_ANTI_CRITICAL
|
||||||
|
CriticalDamage = 22, // FIGHT_PROP_CRITICAL_HURT
|
||||||
|
|
||||||
|
// Energy & Mastery
|
||||||
|
EnergyRecharge = 23, // FIGHT_PROP_CHARGE_EFFICIENCY
|
||||||
|
AddHurt = 24, // FIGHT_PROP_ADD_HURT (all damage bonus)
|
||||||
|
SubHurt = 25, // FIGHT_PROP_SUB_HURT (all damage reduction)
|
||||||
|
HealingBonus = 26, // FIGHT_PROP_HEAL_ADD
|
||||||
|
IncomingHealingBonus = 27, // FIGHT_PROP_HEALED_ADD
|
||||||
|
ElementalMastery = 28, // FIGHT_PROP_ELEMENT_MASTERY
|
||||||
|
|
||||||
|
// Physical Stats
|
||||||
|
PhysicalResistance = 29, // FIGHT_PROP_PHYSICAL_SUB_HURT
|
||||||
|
PhysicalDamageBonus = 30, // FIGHT_PROP_PHYSICAL_ADD_HURT
|
||||||
|
|
||||||
|
// Defense Ignore
|
||||||
|
DefenseIgnoreRatio = 31, // FIGHT_PROP_DEFENCE_IGNORE_RATIO
|
||||||
|
DefenseIgnoreDelta = 32, // FIGHT_PROP_DEFENCE_IGNORE_DELTA
|
||||||
|
|
||||||
|
// Elemental Damage Bonuses
|
||||||
|
PyroDamageBonus = 40, // FIGHT_PROP_FIRE_ADD_HURT
|
||||||
|
ElectroDamageBonus = 41, // FIGHT_PROP_ELEC_ADD_HURT
|
||||||
|
HydroDamageBonus = 42, // FIGHT_PROP_WATER_ADD_HURT
|
||||||
|
DendroDamageBonus = 43, // FIGHT_PROP_GRASS_ADD_HURT
|
||||||
|
AnemoDamageBonus = 44, // FIGHT_PROP_WIND_ADD_HURT
|
||||||
|
GeoDamageBonus = 45, // FIGHT_PROP_ROCK_ADD_HURT
|
||||||
|
CryoDamageBonus = 46, // FIGHT_PROP_ICE_ADD_HURT
|
||||||
|
HeadAddHurt = 47, // FIGHT_PROP_HIT_HEAD_ADD_HURT
|
||||||
|
|
||||||
|
// Elemental Resistances
|
||||||
|
PyroResistance = 50, // FIGHT_PROP_FIRE_SUB_HURT
|
||||||
|
ElectroResistance = 51, // FIGHT_PROP_ELEC_SUB_HURT
|
||||||
|
HydroResistance = 52, // FIGHT_PROP_WATER_SUB_HURT
|
||||||
|
DendroResistance = 53, // FIGHT_PROP_GRASS_SUB_HURT
|
||||||
|
AnemoResistance = 54, // FIGHT_PROP_WIND_SUB_HURT
|
||||||
|
GeoResistance = 55, // FIGHT_PROP_ROCK_SUB_HURT
|
||||||
|
CryoResistance = 56, // FIGHT_PROP_ICE_SUB_HURT
|
||||||
|
|
||||||
|
// Effect Stats
|
||||||
|
EffectHit = 60, // FIGHT_PROP_EFFECT_HIT
|
||||||
|
EffectResist = 61, // FIGHT_PROP_EFFECT_RESIST
|
||||||
|
FreezeResist = 62, // FIGHT_PROP_FREEZE_RESIST
|
||||||
|
DizzyResist = 64, // FIGHT_PROP_DIZZY_RESIST
|
||||||
|
FreezeShorten = 65, // FIGHT_PROP_FREEZE_SHORTEN
|
||||||
|
DizzyShorten = 67, // FIGHT_PROP_DIZZY_SHORTEN
|
||||||
|
|
||||||
|
// Max Energy
|
||||||
|
MaxPyroEnergy = 70, // FIGHT_PROP_MAX_FIRE_ENERGY
|
||||||
|
MaxElectroEnergy = 71, // FIGHT_PROP_MAX_ELEC_ENERGY
|
||||||
|
MaxHydroEnergy = 72, // FIGHT_PROP_MAX_WATER_ENERGY
|
||||||
|
MaxDendroEnergy = 73, // FIGHT_PROP_MAX_GRASS_ENERGY
|
||||||
|
MaxAnemoEnergy = 74, // FIGHT_PROP_MAX_WIND_ENERGY
|
||||||
|
MaxCryoEnergy = 75, // FIGHT_PROP_MAX_ICE_ENERGY
|
||||||
|
MaxGeoEnergy = 76, // FIGHT_PROP_MAX_ROCK_ENERGY
|
||||||
|
|
||||||
|
// Utility
|
||||||
|
CooldownReduction = 80, // FIGHT_PROP_SKILL_CD_MINUS_RATIO
|
||||||
|
ShieldStrength = 81, // FIGHT_PROP_SHIELD_COST_MINUS_RATIO
|
||||||
|
|
||||||
|
// Current Energy
|
||||||
|
CurrentPyroEnergy = 1000, // FIGHT_PROP_CUR_FIRE_ENERGY
|
||||||
|
CurrentElectroEnergy = 1001, // FIGHT_PROP_CUR_ELEC_ENERGY
|
||||||
|
CurrentHydroEnergy = 1002, // FIGHT_PROP_CUR_WATER_ENERGY
|
||||||
|
CurrentDendroEnergy = 1003, // FIGHT_PROP_CUR_GRASS_ENERGY
|
||||||
|
CurrentAnemoEnergy = 1004, // FIGHT_PROP_CUR_WIND_ENERGY
|
||||||
|
CurrentCryoEnergy = 1005, // FIGHT_PROP_CUR_ICE_ENERGY
|
||||||
|
CurrentGeoEnergy = 1006, // FIGHT_PROP_CUR_ROCK_ENERGY
|
||||||
|
CurrentHP = 1010, // FIGHT_PROP_CUR_HP
|
||||||
|
|
||||||
|
// Final Stats
|
||||||
|
HP = 2000, // FIGHT_PROP_MAX_HP
|
||||||
|
Attack = 2001, // FIGHT_PROP_CUR_ATTACK
|
||||||
|
Defense = 2002, // FIGHT_PROP_CUR_DEFENSE
|
||||||
|
Speed = 2003, // FIGHT_PROP_CUR_SPEED
|
||||||
|
CurrentHPDebts = 2004, // FIGHT_PROP_CUR_HP_DEBTS
|
||||||
|
CurrentHPPaidDebts = 2005, // FIGHT_PROP_CUR_HP_PAID_DEBTS
|
||||||
|
|
||||||
|
// Non-Extra Stats (base stats without bonuses)
|
||||||
|
NonExtraAttack = 3000, // FIGHT_PROP_NONEXTRA_ATTACK
|
||||||
|
NonExtraDefense = 3001, // FIGHT_PROP_NONEXTRA_DEFENSE
|
||||||
|
NonExtraCritical = 3002, // FIGHT_PROP_NONEXTRA_CRITICAL
|
||||||
|
NonExtraAntiCritical = 3003, // FIGHT_PROP_NONEXTRA_ANTI_CRITICAL
|
||||||
|
NonExtraCriticalHurt = 3004, // FIGHT_PROP_NONEXTRA_CRITICAL_HURT
|
||||||
|
NonExtraChargeEfficiency = 3005, // FIGHT_PROP_NONEXTRA_CHARGE_EFFICIENCY
|
||||||
|
NonExtraElementMastery = 3006, // FIGHT_PROP_NONEXTRA_ELEMENT_MASTERY
|
||||||
|
NonExtraPhysicalSubHurt = 3007, // FIGHT_PROP_NONEXTRA_PHYSICAL_SUB_HURT
|
||||||
|
NonExtraPyroDamageBonus = 3008, // FIGHT_PROP_NONEXTRA_FIRE_ADD_HURT
|
||||||
|
NonExtraElectroDamageBonus = 3009, // FIGHT_PROP_NONEXTRA_ELEC_ADD_HURT
|
||||||
|
NonExtraHydroDamageBonus = 3010, // FIGHT_PROP_NONEXTRA_WATER_ADD_HURT
|
||||||
|
NonExtraDendroDamageBonus = 3011, // FIGHT_PROP_NONEXTRA_GRASS_ADD_HURT
|
||||||
|
NonExtraAnemoDamageBonus = 3012, // FIGHT_PROP_NONEXTRA_WIND_ADD_HURT
|
||||||
|
NonExtraGeoDamageBonus = 3013, // FIGHT_PROP_NONEXTRA_ROCK_ADD_HURT
|
||||||
|
NonExtraCryoDamageBonus = 3014, // FIGHT_PROP_NONEXTRA_ICE_ADD_HURT
|
||||||
|
NonExtraPyroResistance = 3015, // FIGHT_PROP_NONEXTRA_FIRE_SUB_HURT
|
||||||
|
NonExtraElectroResistance = 3016, // FIGHT_PROP_NONEXTRA_ELEC_SUB_HURT
|
||||||
|
NonExtraHydroResistance = 3017, // FIGHT_PROP_NONEXTRA_WATER_SUB_HURT
|
||||||
|
NonExtraDendroResistance = 3018, // FIGHT_PROP_NONEXTRA_GRASS_SUB_HURT
|
||||||
|
NonExtraAnemoResistance = 3019, // FIGHT_PROP_NONEXTRA_WIND_SUB_HURT
|
||||||
|
NonExtraGeoResistance = 3020, // FIGHT_PROP_NONEXTRA_ROCK_SUB_HURT
|
||||||
|
NonExtraCryoResistance = 3021, // FIGHT_PROP_NONEXTRA_ICE_SUB_HURT
|
||||||
|
NonExtraCooldownReduction = 3022, // FIGHT_PROP_NONEXTRA_SKILL_CD_MINUS_RATIO
|
||||||
|
NonExtraShieldStrength = 3023, // FIGHT_PROP_NONEXTRA_SHIELD_COST_MINUS_RATIO
|
||||||
|
NonExtraPhysicalDamageBonus = 3024, // FIGHT_PROP_NONEXTRA_PHYSICAL_ADD_HURT
|
||||||
|
|
||||||
|
// Elemental Reaction Stats
|
||||||
|
ElementalReactionCritRate = 3025, // FIGHT_PROP_ELEM_REACT_CRITICAL
|
||||||
|
ElementalReactionCritDamage = 3026, // FIGHT_PROP_ELEM_REACT_CRITICAL_HURT
|
||||||
|
OverloadedCritRate = 3027, // FIGHT_PROP_ELEM_REACT_EXPLODE_CRITICAL
|
||||||
|
OverloadedCritDamage = 3028, // FIGHT_PROP_ELEM_REACT_EXPLODE_CRITICAL_HURT
|
||||||
|
SwirlCritRate = 3029, // FIGHT_PROP_ELEM_REACT_SWIRL_CRITICAL
|
||||||
|
SwirlCritDamage = 3030, // FIGHT_PROP_ELEM_REACT_SWIRL_CRITICAL_HURT
|
||||||
|
ElectroChargedCritRate = 3031, // FIGHT_PROP_ELEM_REACT_ELECTRIC_CRITICAL
|
||||||
|
ElectroChargedCritDamage = 3032, // FIGHT_PROP_ELEM_REACT_ELECTRIC_CRITICAL_HURT
|
||||||
|
SuperconductCritRate = 3033, // FIGHT_PROP_ELEM_REACT_SCONDUCT_CRITICAL
|
||||||
|
SuperconductCritDamage = 3034, // FIGHT_PROP_ELEM_REACT_SCONDUCT_CRITICAL_HURT
|
||||||
|
BurnCritRate = 3035, // FIGHT_PROP_ELEM_REACT_BURN_CRITICAL
|
||||||
|
BurnCritDamage = 3036, // FIGHT_PROP_ELEM_REACT_BURN_CRITICAL_HURT
|
||||||
|
ShatteredCritRate = 3037, // FIGHT_PROP_ELEM_REACT_FROZENBROKEN_CRITICAL
|
||||||
|
ShatteredCritDamage = 3038, // FIGHT_PROP_ELEM_REACT_FROZENBROKEN_CRITICAL_HURT
|
||||||
|
BloomCritRate = 3039, // FIGHT_PROP_ELEM_REACT_OVERGROW_CRITICAL
|
||||||
|
BloomCritDamage = 3040, // FIGHT_PROP_ELEM_REACT_OVERGROW_CRITICAL_HURT
|
||||||
|
BurgeonCritRate = 3041, // FIGHT_PROP_ELEM_REACT_OVERGROW_FIRE_CRITICAL
|
||||||
|
BurgeonCritDamage = 3042, // FIGHT_PROP_ELEM_REACT_OVERGROW_FIRE_CRITICAL_HURT
|
||||||
|
HyperbloomCritRate = 3043, // FIGHT_PROP_ELEM_REACT_OVERGROW_ELECTRIC_CRITICAL
|
||||||
|
HyperbloomCritDamage = 3044, // FIGHT_PROP_ELEM_REACT_OVERGROW_ELECTRIC_CRITICAL_HURT
|
||||||
|
BaseElementalReactionCritRate = 3045, // FIGHT_PROP_BASE_ELEM_REACT_CRITICAL
|
||||||
|
BaseElementalReactionCritDamage = 3046, // FIGHT_PROP_BASE_ELEM_REACT_CRITICAL_HURT
|
||||||
|
}
|
||||||
+103
-14
@@ -1,3 +1,4 @@
|
|||||||
|
import { feature } from "bun:bundle";
|
||||||
import { ImageResponse } from "@takumi-rs/image-response";
|
import { ImageResponse } from "@takumi-rs/image-response";
|
||||||
import { file, hash } from "bun";
|
import { file, hash } from "bun";
|
||||||
import { NotFoundError, status, t } from "elysia";
|
import { NotFoundError, status, t } from "elysia";
|
||||||
@@ -16,8 +17,11 @@ import { ConstsSvg } from "@/assets/consts";
|
|||||||
import { featherImage } from "@/assets/feather";
|
import { featherImage } from "@/assets/feather";
|
||||||
import shadeSvg from "@/assets/shade.svg" with { type: "file" };
|
import shadeSvg from "@/assets/shade.svg" with { type: "file" };
|
||||||
import fontFile from "@/assets/shinshin.woff2" with { type: "file" };
|
import fontFile from "@/assets/shinshin.woff2" with { type: "file" };
|
||||||
|
import { star } from "@/assets/stars";
|
||||||
import { TalentSvg } from "@/assets/talent";
|
import { TalentSvg } from "@/assets/talent";
|
||||||
import { AssetManager, getEnkaAvatarStore } from "@/lib/assets";
|
import { WeaponGlazeSvg } from "@/assets/weapon-glaze";
|
||||||
|
import { AssetManager, getEnkaStores } from "@/lib/assets";
|
||||||
|
import { mockData } from "@/lib/mock";
|
||||||
|
|
||||||
const assets = new AssetManager();
|
const assets = new AssetManager();
|
||||||
const cache = new TTLCache();
|
const cache = new TTLCache();
|
||||||
@@ -38,16 +42,19 @@ export function card(app: App) {
|
|||||||
async ({ params: { uid, char: charId }, store: { start } }) => {
|
async ({ params: { uid, char: charId }, store: { start } }) => {
|
||||||
try {
|
try {
|
||||||
// const data = mockData;
|
// const data = mockData;
|
||||||
const [avatarStore, data] = await Promise.all([
|
const [[avatarStore, weaponStore, relicStore, locs], data] =
|
||||||
getEnkaAvatarStore().finally(() =>
|
await Promise.all([
|
||||||
console.log(
|
getEnkaStores().finally(() =>
|
||||||
`\n${Date.now() - start}ms - Avatar store loaded`,
|
console.log(`\n${Date.now() - start}ms - Stores loaded`),
|
||||||
),
|
),
|
||||||
),
|
feature("API")
|
||||||
fetchEnka(uid, false).finally(() =>
|
? fetchEnka(uid, false).finally(() =>
|
||||||
console.log(`${Date.now() - start}ms - Enka data loaded`),
|
console.log(
|
||||||
),
|
`${Date.now() - start}ms - Enka data loaded`,
|
||||||
]);
|
),
|
||||||
|
)
|
||||||
|
: mockData,
|
||||||
|
]);
|
||||||
if (typeof data.avatarInfoList === "undefined")
|
if (typeof data.avatarInfoList === "undefined")
|
||||||
return status(400, "The showcase for this UID is private");
|
return status(400, "The showcase for this UID is private");
|
||||||
const char = data.avatarInfoList.find(
|
const char = data.avatarInfoList.find(
|
||||||
@@ -62,11 +69,33 @@ export function card(app: App) {
|
|||||||
"Unsupported character, avatar store doesn't have this character",
|
"Unsupported character, avatar store doesn't have this character",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!char.equipList)
|
||||||
|
return status(501, "INTERNAL: No equipList provided");
|
||||||
|
const weapon = {
|
||||||
|
data: char.equipList.find((e) => e.weapon)!.weapon!,
|
||||||
|
store:
|
||||||
|
weaponStore[
|
||||||
|
char.equipList.find((e) => e.weapon)!.itemId ?? "Unknown"
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
const cacheKey = hash(JSON.stringify(data)).toString(36);
|
const cacheKey = hash(JSON.stringify(data)).toString(36);
|
||||||
const cached = cache.get<ImageResponse>(cacheKey);
|
if (feature("API")) {
|
||||||
if (cached) return cached.clone();
|
const cached = cache.get<ImageResponse>(cacheKey);
|
||||||
|
if (cached) return cached.clone();
|
||||||
|
}
|
||||||
|
|
||||||
const resources = await Promise.all([
|
const resources = await Promise.all([
|
||||||
|
assets
|
||||||
|
.load(
|
||||||
|
weapon.data.promoteLevel > 2
|
||||||
|
? weapon.store.AwakenIcon
|
||||||
|
: weapon.store.Icon,
|
||||||
|
)
|
||||||
|
.then((data) => ({
|
||||||
|
src: "/weapon",
|
||||||
|
data,
|
||||||
|
})),
|
||||||
...[...avatar.Consts, ...Object.values(avatar.Skills)].map(
|
...[...avatar.Consts, ...Object.values(avatar.Skills)].map(
|
||||||
async (path) => ({
|
async (path) => ({
|
||||||
src: path,
|
src: path,
|
||||||
@@ -97,7 +126,10 @@ export function card(app: App) {
|
|||||||
src: "/bg",
|
src: "/bg",
|
||||||
data,
|
data,
|
||||||
})),
|
})),
|
||||||
]);
|
] satisfies Promise<{
|
||||||
|
src: string;
|
||||||
|
data: ArrayBuffer | Buffer;
|
||||||
|
}>[]);
|
||||||
console.log(
|
console.log(
|
||||||
`${Date.now() - start}ms - Image resources loaded\n${resources.map((r) => `- ${r.src} ${(r.data.byteLength / 1000).toFixed(2)}Kb`).join("\n")}`,
|
`${Date.now() - start}ms - Image resources loaded\n${resources.map((r) => `- ${r.src} ${(r.data.byteLength / 1000).toFixed(2)}Kb`).join("\n")}`,
|
||||||
);
|
);
|
||||||
@@ -232,7 +264,46 @@ export function card(app: App) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* middle section */}
|
{/* middle section */}
|
||||||
<div tw="absolute top-0 bottom-0 right-[23.1em] w-[21.5em] p-[1.4em_.7em_.7em]"></div>
|
<div tw="absolute top-0 bottom-0 right-[23.1em] w-[21.5em] p-[1.4em_.7em_.7em] flex flex-col">
|
||||||
|
{/* weapon */}
|
||||||
|
<div tw="flex flex-start" className="Weapon">
|
||||||
|
{/* display */}
|
||||||
|
<div tw="w-[27%] leading-none shrink-0 min-h-[5.5em] relative">
|
||||||
|
<img src="/weapon" alt="Weapon" tw="w-full" />
|
||||||
|
<WeaponGlazeSvg
|
||||||
|
tw="absolute top-[90%] -left-[15%] w-[130%]"
|
||||||
|
style={{
|
||||||
|
color: {
|
||||||
|
1: "#868a82",
|
||||||
|
2: "#70ca7a",
|
||||||
|
3: "#5cabff",
|
||||||
|
4: "#cc7bff",
|
||||||
|
5: "#dd8637",
|
||||||
|
}[weapon.store.Rarity],
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div tw="absolute w-full top-[93%] flex justify-center">
|
||||||
|
{Array.from({ length: weapon.store.Rarity }, (_, i) =>
|
||||||
|
star(i),
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* caption */}
|
||||||
|
<div tw="px-[5%] grow text-white self-center">
|
||||||
|
<div
|
||||||
|
tw="text-[108%] leading-[1.3]"
|
||||||
|
style={{
|
||||||
|
textShadow:
|
||||||
|
"0 .08em .1em rgba(0,0,0,.4),0 .1em .3em rgba(0,0,0,.4)",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>{locs.en[weapon.store.NameTextMapHash]}</span>
|
||||||
|
</div>
|
||||||
|
{/* stats */}
|
||||||
|
<div tw="pt-[3%] pb-[2%]"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{/* right section */}
|
{/* right section */}
|
||||||
<div tw="absolute top-0 bottom-0 right-0 w-[23.1em] p-[.7em]"></div>
|
<div tw="absolute top-0 bottom-0 right-0 w-[23.1em] p-[.7em]"></div>
|
||||||
</div>,
|
</div>,
|
||||||
@@ -261,6 +332,24 @@ export function card(app: App) {
|
|||||||
.Talents.Fire { color: #541900!important }
|
.Talents.Fire { color: #541900!important }
|
||||||
.Grass { color: #a5ffab; }
|
.Grass { color: #a5ffab; }
|
||||||
.Talents.Grass { color: #134517!important }
|
.Talents.Grass { color: #134517!important }
|
||||||
|
|
||||||
|
.Substat {
|
||||||
|
display: inline-block;
|
||||||
|
background: rgba(255,255,255,.15);
|
||||||
|
border-radius: .2em;
|
||||||
|
padding: 2% 3%;
|
||||||
|
box-sizing: content-box;
|
||||||
|
margin-right: .5em;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Weapon .Substat:before {
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
border-radius: .2em
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user