UPDATE: New monster data
All checks were successful
Gitea Auto Deploy / Deploy-Container (push) Successful in 1m20s
All checks were successful
Gitea Auto Deploy / Deploy-Container (push) Successful in 1m20s
This commit is contained in:
BIN
data/as.json.br
Normal file
BIN
data/as.json.br
Normal file
Binary file not shown.
BIN
data/avatar.json.br
Normal file
BIN
data/avatar.json.br
Normal file
Binary file not shown.
@@ -1,4 +1,12 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "4.0.6",
|
||||||
|
"date": "17/03/2026",
|
||||||
|
"type": "update",
|
||||||
|
"items": [
|
||||||
|
"New monster data"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "4.0.5",
|
"version": "4.0.5",
|
||||||
"date": "09/02/2026",
|
"date": "09/02/2026",
|
||||||
BIN
data/lightcone.json.br
Normal file
BIN
data/lightcone.json.br
Normal file
Binary file not shown.
BIN
data/metadata.json.br
Normal file
BIN
data/metadata.json.br
Normal file
Binary file not shown.
BIN
data/moc.json.br
Normal file
BIN
data/moc.json.br
Normal file
Binary file not shown.
BIN
data/monster.json.br
Normal file
BIN
data/monster.json.br
Normal file
Binary file not shown.
BIN
data/peak.json.br
Normal file
BIN
data/peak.json.br
Normal file
Binary file not shown.
BIN
data/pf.json.br
Normal file
BIN
data/pf.json.br
Normal file
Binary file not shown.
BIN
data/relic.json.br
Normal file
BIN
data/relic.json.br
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,189 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"id": "3001",
|
|
||||||
"begin": "2024-05-06 04:00:02",
|
|
||||||
"end": "2024-07-29 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Stormwind Knight",
|
|
||||||
"cn": "冽风骑士",
|
|
||||||
"jp": "凛冽たる風の騎士",
|
|
||||||
"kr": "한풍의 기사"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "3002",
|
|
||||||
"begin": "2024-06-17 04:00:00",
|
|
||||||
"end": "2024-09-06 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Dominated Evils",
|
|
||||||
"cn": "支配恶兽",
|
|
||||||
"jp": "悪獣の支配",
|
|
||||||
"kr": "괴수 지배"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "3003",
|
|
||||||
"begin": "2024-07-29 04:00:02",
|
|
||||||
"end": "2024-10-21 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Gamer's Instigation",
|
|
||||||
"cn": "煽动博弈",
|
|
||||||
"jp": "博打煽動",
|
|
||||||
"kr": "도박과 선동"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "3004",
|
|
||||||
"begin": "2024-09-06 04:00:02",
|
|
||||||
"end": "2024-12-02 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Sovereign Control",
|
|
||||||
"cn": "支配指挥",
|
|
||||||
"jp": "指揮の支配",
|
|
||||||
"kr": "지휘 지배"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "3005",
|
|
||||||
"begin": "2024-10-21 04:00:02",
|
|
||||||
"end": "2025-01-13 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Gusty Primate",
|
|
||||||
"cn": "冽风猢狲",
|
|
||||||
"jp": "凛冽たる風のサル",
|
|
||||||
"kr": "한풍의 원숭이"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "3006",
|
|
||||||
"begin": "2024-12-02 04:00:02",
|
|
||||||
"end": "2025-02-24 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Locust's Instigation",
|
|
||||||
"cn": "煽动螟蝗",
|
|
||||||
"jp": "扇動螟蝗",
|
|
||||||
"kr": "선동과 해충"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "3007",
|
|
||||||
"begin": "2025-01-12 04:00:02",
|
|
||||||
"end": "2025-04-07 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Gambling Primate",
|
|
||||||
"cn": "猢狲博弈",
|
|
||||||
"jp": "博打打ちのサル",
|
|
||||||
"kr": "도박과 원숭이"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "3008",
|
|
||||||
"begin": "2025-02-24 04:00:02",
|
|
||||||
"end": "2025-05-19 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Warlord of the Locusts",
|
|
||||||
"cn": "螟蝗战首",
|
|
||||||
"jp": "螟蝗戦首",
|
|
||||||
"kr": "해충과 수장"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "3009",
|
|
||||||
"begin": "2025-04-07 04:00:02",
|
|
||||||
"end": "2025-06-30 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Cutting Mistral",
|
|
||||||
"cn": "冽风支配",
|
|
||||||
"jp": "凛冽たる風の支配",
|
|
||||||
"kr": "한풍과 지배"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "3010",
|
|
||||||
"begin": "2025-05-19 04:00:02",
|
|
||||||
"end": "2025-08-11 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Ichor Beast",
|
|
||||||
"cn": "金血恶兽",
|
|
||||||
"jp": "黄金の血の悪獣",
|
|
||||||
"kr": "황금 피와 괴수"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "3011",
|
|
||||||
"begin": "2025-06-30 04:00:02",
|
|
||||||
"end": "2025-09-22 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Lupine Warhead",
|
|
||||||
"cn": "天狼战首",
|
|
||||||
"jp": "天狼戦首",
|
|
||||||
"kr": "천랑과 수장"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "3012",
|
|
||||||
"begin": "2025-08-11 04:00:02",
|
|
||||||
"end": "2025-11-03 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Gale of Netherveil",
|
|
||||||
"cn": "冥茫冽风",
|
|
||||||
"jp": "苛烈なる寒風",
|
|
||||||
"kr": "어둠과 한풍"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "3013",
|
|
||||||
"begin": "2025-09-22 04:00:02",
|
|
||||||
"end": "2025-12-15 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Instigation of the Locusts",
|
|
||||||
"cn": "螟蝗煽动",
|
|
||||||
"jp": "螟蝗の扇動",
|
|
||||||
"kr": "해충과 선동"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "3014",
|
|
||||||
"begin": "2025-11-03 04:00:02",
|
|
||||||
"end": "2025-12-15 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Primate Knight",
|
|
||||||
"cn": "猢狲骑士",
|
|
||||||
"jp": "サルとナイト",
|
|
||||||
"kr": "원숭이와 기사"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "3015",
|
|
||||||
"begin": "2025-12-15 04:00:00",
|
|
||||||
"end": "2026-02-09 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Dominance of Netherveil",
|
|
||||||
"cn": "支配冥茫",
|
|
||||||
"jp": "幽暗の支配者",
|
|
||||||
"kr": "지배와 어둠"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "3016",
|
|
||||||
"begin": "2026-01-05 04:00:02",
|
|
||||||
"end": "2026-03-16 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Militant Lupine",
|
|
||||||
"cn": "兵锋天狼",
|
|
||||||
"jp": "兵鋒天狼",
|
|
||||||
"kr": "창날과 천랑"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "3017",
|
|
||||||
"begin": "2026-03-16 04:00:00",
|
|
||||||
"end": "2026-05-16 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Militant Lupine",
|
|
||||||
"cn": "兵锋天狼",
|
|
||||||
"jp": "兵鋒天狼",
|
|
||||||
"kr": "창날과 천랑"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
422543
public/data/config_maze.json
422543
public/data/config_maze.json
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,762 +0,0 @@
|
|||||||
{
|
|
||||||
"21": {
|
|
||||||
"1": {
|
|
||||||
"property": "HPDelta",
|
|
||||||
"base": 45.15840148925781,
|
|
||||||
"step": 15.805439949035645,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"22": {
|
|
||||||
"1": {
|
|
||||||
"property": "AttackDelta",
|
|
||||||
"base": 22.579200744628903,
|
|
||||||
"step": 7.902719974517822,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"23": {
|
|
||||||
"1": {
|
|
||||||
"property": "HPAddedRatio",
|
|
||||||
"base": 0.027648000046610832,
|
|
||||||
"step": 0.009677000343799593,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"property": "AttackAddedRatio",
|
|
||||||
"base": 0.027648000046610832,
|
|
||||||
"step": 0.009677000343799593,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"property": "DefenceAddedRatio",
|
|
||||||
"base": 0.03456000238656998,
|
|
||||||
"step": 0.012096000835299492,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"4": {
|
|
||||||
"property": "CriticalChanceBase",
|
|
||||||
"base": 0.020736001431941983,
|
|
||||||
"step": 0.00725799985229969,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"5": {
|
|
||||||
"property": "CriticalDamageBase",
|
|
||||||
"base": 0.041471999138593674,
|
|
||||||
"step": 0.014515000395476818,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"6": {
|
|
||||||
"property": "HealRatioBase",
|
|
||||||
"base": 0.02211800031363964,
|
|
||||||
"step": 0.0077410005033016205,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"7": {
|
|
||||||
"property": "StatusProbabilityBase",
|
|
||||||
"base": 0.027648000046610832,
|
|
||||||
"step": 0.009677000343799593,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"24": {
|
|
||||||
"1": {
|
|
||||||
"property": "HPAddedRatio",
|
|
||||||
"base": 0.027648000046610832,
|
|
||||||
"step": 0.009677000343799593,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"property": "AttackAddedRatio",
|
|
||||||
"base": 0.027648000046610832,
|
|
||||||
"step": 0.009677000343799593,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"property": "DefenceAddedRatio",
|
|
||||||
"base": 0.03456000238656998,
|
|
||||||
"step": 0.012096000835299492,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"4": {
|
|
||||||
"property": "SpeedDelta",
|
|
||||||
"base": 1.6128000020980835,
|
|
||||||
"step": 1.0,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"25": {
|
|
||||||
"1": {
|
|
||||||
"property": "HPAddedRatio",
|
|
||||||
"base": 0.027648000046610832,
|
|
||||||
"step": 0.009677000343799593,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"property": "AttackAddedRatio",
|
|
||||||
"base": 0.027648000046610832,
|
|
||||||
"step": 0.009677000343799593,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"property": "DefenceAddedRatio",
|
|
||||||
"base": 0.03456000238656998,
|
|
||||||
"step": 0.012096000835299492,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"4": {
|
|
||||||
"property": "PhysicalAddedRatio",
|
|
||||||
"base": 0.024883000180125237,
|
|
||||||
"step": 0.008709000423550606,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"5": {
|
|
||||||
"property": "FireAddedRatio",
|
|
||||||
"base": 0.024883000180125237,
|
|
||||||
"step": 0.008709000423550606,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"6": {
|
|
||||||
"property": "IceAddedRatio",
|
|
||||||
"base": 0.024883000180125237,
|
|
||||||
"step": 0.008709000423550606,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"7": {
|
|
||||||
"property": "ThunderAddedRatio",
|
|
||||||
"base": 0.024883000180125237,
|
|
||||||
"step": 0.008709000423550606,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"8": {
|
|
||||||
"property": "WindAddedRatio",
|
|
||||||
"base": 0.024883000180125237,
|
|
||||||
"step": 0.008709000423550606,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"9": {
|
|
||||||
"property": "QuantumAddedRatio",
|
|
||||||
"base": 0.024883000180125237,
|
|
||||||
"step": 0.008709000423550606,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"10": {
|
|
||||||
"property": "ImaginaryAddedRatio",
|
|
||||||
"base": 0.024883000180125237,
|
|
||||||
"step": 0.008709000423550606,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"26": {
|
|
||||||
"1": {
|
|
||||||
"property": "BreakDamageAddedRatioBase",
|
|
||||||
"base": 0.041471999138593674,
|
|
||||||
"step": 0.014515000395476818,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"property": "SPRatioBase",
|
|
||||||
"base": 0.012442000210285189,
|
|
||||||
"step": 0.004355000331997871,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"property": "HPAddedRatio",
|
|
||||||
"base": 0.027648000046610832,
|
|
||||||
"step": 0.009677000343799593,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"4": {
|
|
||||||
"property": "AttackAddedRatio",
|
|
||||||
"base": 0.027648000046610832,
|
|
||||||
"step": 0.009677000343799593,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"5": {
|
|
||||||
"property": "DefenceAddedRatio",
|
|
||||||
"base": 0.03456000238656998,
|
|
||||||
"step": 0.012096000835299492,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"31": {
|
|
||||||
"1": {
|
|
||||||
"property": "HPDelta",
|
|
||||||
"base": 67.73760223388672,
|
|
||||||
"step": 23.708160400390625,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"32": {
|
|
||||||
"1": {
|
|
||||||
"property": "AttackDelta",
|
|
||||||
"base": 33.86880111694336,
|
|
||||||
"step": 11.854080200195312,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"33": {
|
|
||||||
"1": {
|
|
||||||
"property": "HPAddedRatio",
|
|
||||||
"base": 0.041471999138593674,
|
|
||||||
"step": 0.014515000395476818,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"property": "AttackAddedRatio",
|
|
||||||
"base": 0.041471999138593674,
|
|
||||||
"step": 0.014515000395476818,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"property": "DefenceAddedRatio",
|
|
||||||
"base": 0.05183999985456467,
|
|
||||||
"step": 0.018144000321626663,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"4": {
|
|
||||||
"property": "CriticalChanceBase",
|
|
||||||
"base": 0.03110400028526783,
|
|
||||||
"step": 0.010886001400649548,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"5": {
|
|
||||||
"property": "CriticalDamageBase",
|
|
||||||
"base": 0.06220800057053566,
|
|
||||||
"step": 0.021773001179099083,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"6": {
|
|
||||||
"property": "HealRatioBase",
|
|
||||||
"base": 0.03317800164222717,
|
|
||||||
"step": 0.011611999943852425,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"7": {
|
|
||||||
"property": "StatusProbabilityBase",
|
|
||||||
"base": 0.041471999138593674,
|
|
||||||
"step": 0.014515000395476818,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"34": {
|
|
||||||
"1": {
|
|
||||||
"property": "HPAddedRatio",
|
|
||||||
"base": 0.041471999138593674,
|
|
||||||
"step": 0.014515000395476818,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"property": "AttackAddedRatio",
|
|
||||||
"base": 0.041471999138593674,
|
|
||||||
"step": 0.014515000395476818,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"property": "DefenceAddedRatio",
|
|
||||||
"base": 0.05183999985456467,
|
|
||||||
"step": 0.018144000321626663,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"4": {
|
|
||||||
"property": "SpeedDelta",
|
|
||||||
"base": 2.4191999435424805,
|
|
||||||
"step": 1.0,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"35": {
|
|
||||||
"1": {
|
|
||||||
"property": "HPAddedRatio",
|
|
||||||
"base": 0.041471999138593674,
|
|
||||||
"step": 0.014515000395476818,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"property": "AttackAddedRatio",
|
|
||||||
"base": 0.041471999138593674,
|
|
||||||
"step": 0.014515000395476818,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"property": "DefenceAddedRatio",
|
|
||||||
"base": 0.05183999985456467,
|
|
||||||
"step": 0.018144000321626663,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"4": {
|
|
||||||
"property": "PhysicalAddedRatio",
|
|
||||||
"base": 0.037324998527765274,
|
|
||||||
"step": 0.013064000755548475,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"5": {
|
|
||||||
"property": "FireAddedRatio",
|
|
||||||
"base": 0.037324998527765274,
|
|
||||||
"step": 0.013064000755548475,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"6": {
|
|
||||||
"property": "IceAddedRatio",
|
|
||||||
"base": 0.037324998527765274,
|
|
||||||
"step": 0.013064000755548475,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"7": {
|
|
||||||
"property": "ThunderAddedRatio",
|
|
||||||
"base": 0.037324998527765274,
|
|
||||||
"step": 0.013064000755548475,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"8": {
|
|
||||||
"property": "WindAddedRatio",
|
|
||||||
"base": 0.037324998527765274,
|
|
||||||
"step": 0.013064000755548475,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"9": {
|
|
||||||
"property": "QuantumAddedRatio",
|
|
||||||
"base": 0.037324998527765274,
|
|
||||||
"step": 0.013064000755548475,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"10": {
|
|
||||||
"property": "ImaginaryAddedRatio",
|
|
||||||
"base": 0.037324998527765274,
|
|
||||||
"step": 0.013064000755548475,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"36": {
|
|
||||||
"1": {
|
|
||||||
"property": "BreakDamageAddedRatioBase",
|
|
||||||
"base": 0.06220800057053566,
|
|
||||||
"step": 0.021773001179099083,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"property": "SPRatioBase",
|
|
||||||
"base": 0.018662000074982643,
|
|
||||||
"step": 0.006532000377774239,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"property": "HPAddedRatio",
|
|
||||||
"base": 0.041471999138593674,
|
|
||||||
"step": 0.014515000395476818,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"4": {
|
|
||||||
"property": "AttackAddedRatio",
|
|
||||||
"base": 0.041471999138593674,
|
|
||||||
"step": 0.014515000395476818,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"5": {
|
|
||||||
"property": "DefenceAddedRatio",
|
|
||||||
"base": 0.05183999985456467,
|
|
||||||
"step": 0.018144000321626663,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"41": {
|
|
||||||
"1": {
|
|
||||||
"property": "HPDelta",
|
|
||||||
"base": 90.31680297851562,
|
|
||||||
"step": 31.61087989807129,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"42": {
|
|
||||||
"1": {
|
|
||||||
"property": "AttackDelta",
|
|
||||||
"base": 45.15840148925781,
|
|
||||||
"step": 15.805439949035645,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"43": {
|
|
||||||
"1": {
|
|
||||||
"property": "HPAddedRatio",
|
|
||||||
"base": 0.055296000093221664,
|
|
||||||
"step": 0.019354000687599186,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"property": "AttackAddedRatio",
|
|
||||||
"base": 0.055296000093221664,
|
|
||||||
"step": 0.019354000687599186,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"property": "DefenceAddedRatio",
|
|
||||||
"base": 0.06911999732255936,
|
|
||||||
"step": 0.024191999807953835,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"4": {
|
|
||||||
"property": "CriticalChanceBase",
|
|
||||||
"base": 0.041471999138593674,
|
|
||||||
"step": 0.014515000395476818,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"5": {
|
|
||||||
"property": "CriticalDamageBase",
|
|
||||||
"base": 0.08294399827718735,
|
|
||||||
"step": 0.029029998928308487,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"6": {
|
|
||||||
"property": "HealRatioBase",
|
|
||||||
"base": 0.04423699900507927,
|
|
||||||
"step": 0.015483000315725803,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"7": {
|
|
||||||
"property": "StatusProbabilityBase",
|
|
||||||
"base": 0.055296000093221664,
|
|
||||||
"step": 0.019354000687599186,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"44": {
|
|
||||||
"1": {
|
|
||||||
"property": "HPAddedRatio",
|
|
||||||
"base": 0.055296000093221664,
|
|
||||||
"step": 0.019354000687599186,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"property": "AttackAddedRatio",
|
|
||||||
"base": 0.055296000093221664,
|
|
||||||
"step": 0.019354000687599186,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"property": "DefenceAddedRatio",
|
|
||||||
"base": 0.06911999732255936,
|
|
||||||
"step": 0.024191999807953835,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"4": {
|
|
||||||
"property": "SpeedDelta",
|
|
||||||
"base": 3.225600004196167,
|
|
||||||
"step": 1.100000023841858,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"45": {
|
|
||||||
"1": {
|
|
||||||
"property": "HPAddedRatio",
|
|
||||||
"base": 0.055296000093221664,
|
|
||||||
"step": 0.019354000687599186,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"property": "AttackAddedRatio",
|
|
||||||
"base": 0.055296000093221664,
|
|
||||||
"step": 0.019354000687599186,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"property": "DefenceAddedRatio",
|
|
||||||
"base": 0.06911999732255936,
|
|
||||||
"step": 0.024191999807953835,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"4": {
|
|
||||||
"property": "PhysicalAddedRatio",
|
|
||||||
"base": 0.04976600036025047,
|
|
||||||
"step": 0.01741800084710121,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"5": {
|
|
||||||
"property": "FireAddedRatio",
|
|
||||||
"base": 0.04976600036025047,
|
|
||||||
"step": 0.01741800084710121,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"6": {
|
|
||||||
"property": "IceAddedRatio",
|
|
||||||
"base": 0.04976600036025047,
|
|
||||||
"step": 0.01741800084710121,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"7": {
|
|
||||||
"property": "ThunderAddedRatio",
|
|
||||||
"base": 0.04976600036025047,
|
|
||||||
"step": 0.01741800084710121,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"8": {
|
|
||||||
"property": "WindAddedRatio",
|
|
||||||
"base": 0.04976600036025047,
|
|
||||||
"step": 0.01741800084710121,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"9": {
|
|
||||||
"property": "QuantumAddedRatio",
|
|
||||||
"base": 0.04976600036025047,
|
|
||||||
"step": 0.01741800084710121,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"10": {
|
|
||||||
"property": "ImaginaryAddedRatio",
|
|
||||||
"base": 0.04976600036025047,
|
|
||||||
"step": 0.01741800084710121,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"46": {
|
|
||||||
"1": {
|
|
||||||
"property": "BreakDamageAddedRatioBase",
|
|
||||||
"base": 0.08294399827718735,
|
|
||||||
"step": 0.029029998928308487,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"property": "SPRatioBase",
|
|
||||||
"base": 0.024883000180125237,
|
|
||||||
"step": 0.008709000423550606,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"property": "HPAddedRatio",
|
|
||||||
"base": 0.055296000093221664,
|
|
||||||
"step": 0.019354000687599186,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"4": {
|
|
||||||
"property": "AttackAddedRatio",
|
|
||||||
"base": 0.055296000093221664,
|
|
||||||
"step": 0.019354000687599186,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"5": {
|
|
||||||
"property": "DefenceAddedRatio",
|
|
||||||
"base": 0.06911999732255936,
|
|
||||||
"step": 0.024191999807953835,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"51": {
|
|
||||||
"1": {
|
|
||||||
"property": "HPDelta",
|
|
||||||
"base": 112.89600372314452,
|
|
||||||
"step": 39.51359939575195,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"52": {
|
|
||||||
"1": {
|
|
||||||
"property": "AttackDelta",
|
|
||||||
"base": 56.448001861572266,
|
|
||||||
"step": 19.756799697875977,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"53": {
|
|
||||||
"1": {
|
|
||||||
"property": "HPAddedRatio",
|
|
||||||
"base": 0.06911999732255936,
|
|
||||||
"step": 0.024191999807953835,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"property": "AttackAddedRatio",
|
|
||||||
"base": 0.06911999732255936,
|
|
||||||
"step": 0.024191999807953835,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"property": "DefenceAddedRatio",
|
|
||||||
"base": 0.08640000224113464,
|
|
||||||
"step": 0.030240001156926155,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"4": {
|
|
||||||
"property": "CriticalChanceBase",
|
|
||||||
"base": 0.05183999985456467,
|
|
||||||
"step": 0.018144000321626663,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"5": {
|
|
||||||
"property": "CriticalDamageBase",
|
|
||||||
"base": 0.10367999970912932,
|
|
||||||
"step": 0.036288000643253326,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"6": {
|
|
||||||
"property": "HealRatioBase",
|
|
||||||
"base": 0.055296000093221664,
|
|
||||||
"step": 0.019354000687599186,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"7": {
|
|
||||||
"property": "StatusProbabilityBase",
|
|
||||||
"base": 0.06911999732255936,
|
|
||||||
"step": 0.024191999807953835,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"54": {
|
|
||||||
"1": {
|
|
||||||
"property": "HPAddedRatio",
|
|
||||||
"base": 0.06911999732255936,
|
|
||||||
"step": 0.024191999807953835,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"property": "AttackAddedRatio",
|
|
||||||
"base": 0.06911999732255936,
|
|
||||||
"step": 0.024191999807953835,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"property": "DefenceAddedRatio",
|
|
||||||
"base": 0.08640000224113464,
|
|
||||||
"step": 0.030240001156926155,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"4": {
|
|
||||||
"property": "SpeedDelta",
|
|
||||||
"base": 4.0320000648498535,
|
|
||||||
"step": 1.399999976158142,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"55": {
|
|
||||||
"1": {
|
|
||||||
"property": "HPAddedRatio",
|
|
||||||
"base": 0.06911999732255936,
|
|
||||||
"step": 0.024191999807953835,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"property": "AttackAddedRatio",
|
|
||||||
"base": 0.06911999732255936,
|
|
||||||
"step": 0.024191999807953835,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"property": "DefenceAddedRatio",
|
|
||||||
"base": 0.08640000224113464,
|
|
||||||
"step": 0.030240001156926155,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"4": {
|
|
||||||
"property": "PhysicalAddedRatio",
|
|
||||||
"base": 0.06220800057053566,
|
|
||||||
"step": 0.021773001179099083,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"5": {
|
|
||||||
"property": "FireAddedRatio",
|
|
||||||
"base": 0.06220800057053566,
|
|
||||||
"step": 0.021773001179099083,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"6": {
|
|
||||||
"property": "IceAddedRatio",
|
|
||||||
"base": 0.06220800057053566,
|
|
||||||
"step": 0.021773001179099083,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"7": {
|
|
||||||
"property": "ThunderAddedRatio",
|
|
||||||
"base": 0.06220800057053566,
|
|
||||||
"step": 0.021773001179099083,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"8": {
|
|
||||||
"property": "WindAddedRatio",
|
|
||||||
"base": 0.06220800057053566,
|
|
||||||
"step": 0.021773001179099083,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"9": {
|
|
||||||
"property": "QuantumAddedRatio",
|
|
||||||
"base": 0.06220800057053566,
|
|
||||||
"step": 0.021773001179099083,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"10": {
|
|
||||||
"property": "ImaginaryAddedRatio",
|
|
||||||
"base": 0.06220800057053566,
|
|
||||||
"step": 0.021773001179099083,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"56": {
|
|
||||||
"1": {
|
|
||||||
"property": "BreakDamageAddedRatioBase",
|
|
||||||
"base": 0.10367999970912932,
|
|
||||||
"step": 0.036288000643253326,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"property": "SPRatioBase",
|
|
||||||
"base": 0.03110400028526783,
|
|
||||||
"step": 0.010886001400649548,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"property": "HPAddedRatio",
|
|
||||||
"base": 0.06911999732255936,
|
|
||||||
"step": 0.024191999807953835,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"4": {
|
|
||||||
"property": "AttackAddedRatio",
|
|
||||||
"base": 0.06911999732255936,
|
|
||||||
"step": 0.024191999807953835,
|
|
||||||
"step_num": null
|
|
||||||
},
|
|
||||||
"5": {
|
|
||||||
"property": "DefenceAddedRatio",
|
|
||||||
"base": 0.08640000224113464,
|
|
||||||
"step": 0.030240001156926155,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"433": {
|
|
||||||
"1": {
|
|
||||||
"property": "DefenceAddedRatio",
|
|
||||||
"base": 0.06911999732255936,
|
|
||||||
"step": 0.024191999807953835,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"434": {
|
|
||||||
"1": {
|
|
||||||
"property": "CriticalChanceBase",
|
|
||||||
"base": 0.041471999138593674,
|
|
||||||
"step": 0.014515000395476818,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"436": {
|
|
||||||
"1": {
|
|
||||||
"property": "HealRatioBase",
|
|
||||||
"base": 0.04423699900507927,
|
|
||||||
"step": 0.015483000315725803,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"441": {
|
|
||||||
"1": {
|
|
||||||
"property": "HPAddedRatio",
|
|
||||||
"base": 0.055296000093221664,
|
|
||||||
"step": 0.019354000687599186,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"443": {
|
|
||||||
"1": {
|
|
||||||
"property": "DefenceAddedRatio",
|
|
||||||
"base": 0.06911999732255936,
|
|
||||||
"step": 0.024191999807953835,
|
|
||||||
"step_num": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because one or more lines are too long
71643
public/data/moc.en.json
71643
public/data/moc.en.json
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -1,574 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"id": "100",
|
|
||||||
"begin": "",
|
|
||||||
"end": "",
|
|
||||||
"lang": {
|
|
||||||
"en": "The Last Vestiges of Towering Citadel",
|
|
||||||
"cn": "永屹之城遗秘",
|
|
||||||
"jp": "永屹の城の秘密",
|
|
||||||
"kr": "영원히 굳건한 도시에 남겨진 비밀"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "900",
|
|
||||||
"begin": "",
|
|
||||||
"end": "",
|
|
||||||
"lang": {
|
|
||||||
"en": "The Voyage of Navis Astriger",
|
|
||||||
"cn": "天艟求仙迷航录",
|
|
||||||
"jp": "天艟求仙放浪記",
|
|
||||||
"kr": "불사의 약 원정기"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "101",
|
|
||||||
"begin": "2023-02-06 04:00:00",
|
|
||||||
"end": "2023-03-06 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Favor of Amber",
|
|
||||||
"cn": "琥珀恩赐",
|
|
||||||
"jp": "琥珀の賜物",
|
|
||||||
"kr": "앰버의 은혜"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "102",
|
|
||||||
"begin": "2022-11-14 04:00:00",
|
|
||||||
"end": "2022-11-28 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Frostscar Reverie",
|
|
||||||
"cn": "霜痕旧梦",
|
|
||||||
"jp": "霜の跡に旧夢",
|
|
||||||
"kr": "서리 내린 꿈"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "103",
|
|
||||||
"begin": "2022-11-28 04:00:00",
|
|
||||||
"end": "2022-12-12 00:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Everwinter Trials",
|
|
||||||
"cn": "永冬试炼",
|
|
||||||
"jp": "常冬の試練",
|
|
||||||
"kr": "영원한 겨울의 시련"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "104",
|
|
||||||
"begin": "2023-03-06 04:00:00",
|
|
||||||
"end": "2023-03-20 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Favor of Amber",
|
|
||||||
"cn": "琥珀恩赐",
|
|
||||||
"jp": "琥珀の賜物",
|
|
||||||
"kr": "앰버의 은혜"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "105",
|
|
||||||
"begin": "2022-12-26 04:00:00",
|
|
||||||
"end": "2023-01-09 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Frostscar Reverie",
|
|
||||||
"cn": "霜痕旧梦",
|
|
||||||
"jp": "霜の跡に旧夢",
|
|
||||||
"kr": "서리 내린 꿈"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "106",
|
|
||||||
"begin": "2023-01-09 04:00:00",
|
|
||||||
"end": "2023-01-23 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Everwinter Trials",
|
|
||||||
"cn": "永冬试炼",
|
|
||||||
"jp": "常冬の試練",
|
|
||||||
"kr": "영원한 겨울의 시련"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "107",
|
|
||||||
"begin": "2023-03-20 04:00:00",
|
|
||||||
"end": "2023-04-03 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Favor of Amber",
|
|
||||||
"cn": "琥珀恩赐",
|
|
||||||
"jp": "琥珀の賜物",
|
|
||||||
"kr": "앰버의 은혜"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "108",
|
|
||||||
"begin": "2033-02-06 04:00:00",
|
|
||||||
"end": "2033-02-20 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Frostscar Reverie",
|
|
||||||
"cn": "霜痕旧梦",
|
|
||||||
"jp": "霜の跡に旧夢",
|
|
||||||
"kr": "서리 내린 꿈"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "109",
|
|
||||||
"begin": "2033-02-20 04:00:00",
|
|
||||||
"end": "2033-03-06 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Everwinter Trials",
|
|
||||||
"cn": "永冬试炼",
|
|
||||||
"jp": "常冬の試練",
|
|
||||||
"kr": "영원한 겨울의 시련"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "110",
|
|
||||||
"begin": "2000-06-12 04:00:00",
|
|
||||||
"end": "2000-06-26 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Coldiron Tribulation",
|
|
||||||
"cn": "寒铁砥砺",
|
|
||||||
"jp": "寒鉄練磨",
|
|
||||||
"kr": "한철 연마"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "111",
|
|
||||||
"begin": "2000-06-26 04:00:00",
|
|
||||||
"end": "2000-07-10 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Hyperborean Search for Warmth",
|
|
||||||
"cn": "蹈冰寻火",
|
|
||||||
"jp": "氷踏みて炎求む",
|
|
||||||
"kr": "불을 찾는 얼음"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "112",
|
|
||||||
"begin": "2000-07-10 04:00:00",
|
|
||||||
"end": "2000-07-24 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Stormquell",
|
|
||||||
"cn": "风暴止息",
|
|
||||||
"jp": "止息せし嵐",
|
|
||||||
"kr": "잦아든 폭풍"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "113",
|
|
||||||
"begin": "2003-06-05 04:00:00",
|
|
||||||
"end": "2003-06-12 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Adrift in Astral Seas",
|
|
||||||
"cn": "孤航天海",
|
|
||||||
"jp": "天海の孤航",
|
|
||||||
"kr": "고독한 천공 항행"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "114",
|
|
||||||
"begin": "2003-06-12 04:00:00",
|
|
||||||
"end": "2003-06-16 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Raintear Strife",
|
|
||||||
"cn": "泪雨长战",
|
|
||||||
"jp": "涙雨戦争",
|
|
||||||
"kr": "눈물의 전쟁"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "115",
|
|
||||||
"begin": "2003-06-16 04:00:00",
|
|
||||||
"end": "2003-07-05 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Traces of Sanctus Medicus",
|
|
||||||
"cn": "药王垂迹",
|
|
||||||
"jp": "薬王の垂迹",
|
|
||||||
"kr": "약왕의 화신"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "116",
|
|
||||||
"begin": "2023-04-03 04:00:00",
|
|
||||||
"end": "2023-04-17 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Favor of Amber",
|
|
||||||
"cn": "琥珀恩赐",
|
|
||||||
"jp": "琥珀の賜物",
|
|
||||||
"kr": "앰버의 은혜"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "117",
|
|
||||||
"begin": "2000-04-17 04:00:00",
|
|
||||||
"end": "2000-05-15 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Favor of Amber",
|
|
||||||
"cn": "琥珀恩赐",
|
|
||||||
"jp": "琥珀の賜物",
|
|
||||||
"kr": "앰버의 은혜"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "118",
|
|
||||||
"begin": "2000-05-15 04:00:00",
|
|
||||||
"end": "2000-05-29 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Favor of Amber",
|
|
||||||
"cn": "琥珀恩赐",
|
|
||||||
"jp": "琥珀の賜物",
|
|
||||||
"kr": "앰버의 은혜"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "119",
|
|
||||||
"begin": "2000-05-29 04:00:00",
|
|
||||||
"end": "2000-06-12 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Favor of Amber",
|
|
||||||
"cn": "琥珀恩赐",
|
|
||||||
"jp": "琥珀の賜物",
|
|
||||||
"kr": "앰버의 은혜"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1001",
|
|
||||||
"begin": "2000-09-04 04:00:00",
|
|
||||||
"end": "2000-09-18 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Ethereal Shipcraft",
|
|
||||||
"cn": "迷梦造舸",
|
|
||||||
"jp": "迷夢造舟",
|
|
||||||
"kr": "공상으로 만든 배"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1002",
|
|
||||||
"begin": "2023-07-17 04:00:00",
|
|
||||||
"end": "2023-08-21 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "A Shot From the Sky",
|
|
||||||
"cn": "天裂一射",
|
|
||||||
"jp": "天裂の一射",
|
|
||||||
"kr": "하늘을 가르는 화살"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1003",
|
|
||||||
"begin": "2000-10-02 04:00:00",
|
|
||||||
"end": "2000-10-16 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Mara and Null",
|
|
||||||
"cn": "魔阴空劫",
|
|
||||||
"jp": "魔陰空劫",
|
|
||||||
"kr": "마각의 공겁"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1004",
|
|
||||||
"begin": "2000-10-16 04:00:00",
|
|
||||||
"end": "2000-10-30 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Living and Flaming Catastrophes",
|
|
||||||
"cn": "生劫火劫",
|
|
||||||
"jp": "生劫火劫",
|
|
||||||
"kr": "생겁과 화겁"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1005",
|
|
||||||
"begin": "2023-08-27 04:00:00",
|
|
||||||
"end": "2023-09-25 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Ambrosial Arbor's Arrival",
|
|
||||||
"cn": "建木降临",
|
|
||||||
"jp": "建木降臨",
|
|
||||||
"kr": "불멸의 거목 강림"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1006",
|
|
||||||
"begin": "2000-11-13 04:00:00",
|
|
||||||
"end": "2000-11-27 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Divine Root Subdual",
|
|
||||||
"cn": "镇伏玄根",
|
|
||||||
"jp": "玄根鎮伏",
|
|
||||||
"kr": "현근 제압"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1007",
|
|
||||||
"begin": "2000-11-27 04:00:00",
|
|
||||||
"end": "2000-12-11 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Oath of Eternal Alliance",
|
|
||||||
"cn": "万载盟誓",
|
|
||||||
"jp": "万年移ろわぬ盟約の誓い",
|
|
||||||
"kr": "영원한 맹세"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1008",
|
|
||||||
"begin": "2023-09-25 04:00:00",
|
|
||||||
"end": "2023-11-13 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Sedition of Imbibitor Lunae",
|
|
||||||
"cn": "饮月之乱",
|
|
||||||
"jp": "飲月の乱",
|
|
||||||
"kr": "음월의 난"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1009",
|
|
||||||
"begin": "2023-10-30 04:00:03",
|
|
||||||
"end": "2023-12-24 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Enigma in Deep Space",
|
|
||||||
"cn": "藏于深空之秘",
|
|
||||||
"jp": "深空に隠された秘密",
|
|
||||||
"kr": "심우주에 숨겨진 비밀"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1010",
|
|
||||||
"begin": "2023-10-30 04:00:01",
|
|
||||||
"end": "2023-12-24 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Light of Reignition",
|
|
||||||
"cn": "重燃之光",
|
|
||||||
"jp": "再燃の光",
|
|
||||||
"kr": "다시 빛나는 불꽃"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1011",
|
|
||||||
"begin": "2023-12-04 04:00:03",
|
|
||||||
"end": "2024-02-05 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Dreamland of Longing",
|
|
||||||
"cn": "难舍梦乡",
|
|
||||||
"jp": "醒めたくない夢",
|
|
||||||
"kr": "애틋한 꿈세계"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1012",
|
|
||||||
"begin": "2023-12-04 04:00:01",
|
|
||||||
"end": "2024-03-27 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Eve of Wanton Feast",
|
|
||||||
"cn": "一晌荒宴",
|
|
||||||
"jp": "荒唐な宴",
|
|
||||||
"kr": "찰나의 연회"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1013",
|
|
||||||
"begin": "2024-02-05 04:00:00",
|
|
||||||
"end": "2024-04-24 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "White Night Chronicles",
|
|
||||||
"cn": "白夜梦国记",
|
|
||||||
"jp": "白昼夢国記",
|
|
||||||
"kr": "백야몽국 연대기"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1014",
|
|
||||||
"begin": "2024-03-11 04:00:00",
|
|
||||||
"end": "2024-05-06 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Dream Within Dream",
|
|
||||||
"cn": "梦中之梦",
|
|
||||||
"jp": "夢の中の夢",
|
|
||||||
"kr": "꿈속의 꿈"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1015",
|
|
||||||
"begin": "2024-03-11 04:00:01",
|
|
||||||
"end": "2024-06-17 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "A Song's True Theme (<unbreak>2.2</unbreak>)",
|
|
||||||
"cn": "弦外之声(<unbreak>2.2</unbreak>)",
|
|
||||||
"jp": "弦外の音(<unbreak>2.2</unbreak>)",
|
|
||||||
"kr": "현 외의 소리(<unbreak>2.2</unbreak>)"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1016",
|
|
||||||
"begin": "2024-05-06 04:00:00",
|
|
||||||
"end": "2024-07-29 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Dissipation of Dreams (<unbreak>2.3</unbreak>)",
|
|
||||||
"cn": "曲尽梦散(<unbreak>2.3</unbreak>)",
|
|
||||||
"jp": "曲は尽き夢は散る(<unbreak>2.3</unbreak>)",
|
|
||||||
"kr": "끝난 곡조와 흩어진 꿈(<unbreak>2.3</unbreak>)"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1017",
|
|
||||||
"begin": "2024-06-17 04:00:02",
|
|
||||||
"end": "2024-09-06 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "The Big Sleep",
|
|
||||||
"cn": "长眠不醒",
|
|
||||||
"jp": "大いなる眠り",
|
|
||||||
"kr": "깨지 않는 깊은 잠"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1018",
|
|
||||||
"begin": "2024-07-29 04:00:00",
|
|
||||||
"end": "2024-10-21 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Scalegorge Tidalflow",
|
|
||||||
"cn": "鳞渊潮动",
|
|
||||||
"jp": "鱗淵の潮騒",
|
|
||||||
"kr": "인연의 파도"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1019",
|
|
||||||
"begin": "2024-09-06 04:00:00",
|
|
||||||
"end": "2024-12-02 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Dancing with the Dreams",
|
|
||||||
"cn": "与梦共舞",
|
|
||||||
"jp": "夢と踊る",
|
|
||||||
"kr": "꿈과 춤을"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1020",
|
|
||||||
"begin": "2024-10-21 04:00:00",
|
|
||||||
"end": "2025-01-13 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Troopship Mayhem",
|
|
||||||
"cn": "舸舰迷津",
|
|
||||||
"jp": "遭難した巨艦",
|
|
||||||
"kr": "길 잃은 거함"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1021",
|
|
||||||
"begin": "2024-12-02 04:00:00",
|
|
||||||
"end": "2025-02-24 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Strife of Creation",
|
|
||||||
"cn": "创世纷争",
|
|
||||||
"jp": "創世の争い",
|
|
||||||
"kr": "창세의 분쟁"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1022",
|
|
||||||
"begin": "2025-01-12 04:00:00",
|
|
||||||
"end": "2025-04-07 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Out of Home",
|
|
||||||
"cn": "出故乡记",
|
|
||||||
"jp": "故郷より旅立つ",
|
|
||||||
"kr": "출향기"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1023",
|
|
||||||
"begin": "2025-02-24 04:00:00",
|
|
||||||
"end": "2025-05-19 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Breath of the Othershore",
|
|
||||||
"cn": "彼岸之息",
|
|
||||||
"jp": "向こう岸の息吹",
|
|
||||||
"kr": "피안의 숨결"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1024",
|
|
||||||
"begin": "2025-04-07 04:00:00",
|
|
||||||
"end": "2025-06-30 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Lupine Moon-Devourer",
|
|
||||||
"cn": "赤月吞狼",
|
|
||||||
"jp": "紅月を呑む狼",
|
|
||||||
"kr": "늑대를 삼킨 붉은 달"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1025",
|
|
||||||
"begin": "2025-05-19 04:00:00",
|
|
||||||
"end": "2025-08-11 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Gambler's Plight",
|
|
||||||
"cn": "博徒困境",
|
|
||||||
"jp": "博打うちのジレンマ",
|
|
||||||
"kr": "노름꾼의 곤경"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1026",
|
|
||||||
"begin": "2025-06-30 04:00:00",
|
|
||||||
"end": "2025-09-22 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Pillar of Genesis",
|
|
||||||
"cn": "创世之柱",
|
|
||||||
"jp": "創世の柱",
|
|
||||||
"kr": "창세의 기둥"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1027",
|
|
||||||
"begin": "2025-08-11 04:00:00",
|
|
||||||
"end": "2025-11-03 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Category Mistake",
|
|
||||||
"cn": "范畴错误",
|
|
||||||
"jp": "カテゴリーエラー",
|
|
||||||
"kr": "범주 오류"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1028",
|
|
||||||
"begin": "2025-09-22 04:00:00",
|
|
||||||
"end": "2025-12-15 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Monkey Business",
|
|
||||||
"cn": "猴子把戏",
|
|
||||||
"jp": "サルのトリック",
|
|
||||||
"kr": "원숭이 트릭"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1029",
|
|
||||||
"begin": "2025-11-03 04:00:00",
|
|
||||||
"end": "2026-02-09 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Breached Nest",
|
|
||||||
"cn": "堤溃蚁穴",
|
|
||||||
"jp": "蟻の一穴",
|
|
||||||
"kr": "제방을 허무는 개미굴"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1030",
|
|
||||||
"begin": "2026-01-05 04:00:00",
|
|
||||||
"end": "2026-03-16 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Cyber Mystery",
|
|
||||||
"cn": "网络谜踪",
|
|
||||||
"jp": "サイバーミステリー",
|
|
||||||
"kr": "사이버 미스터리"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "1031",
|
|
||||||
"begin": "2026-02-08 04:00:00",
|
|
||||||
"end": "2026-03-16 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Grand Finale",
|
|
||||||
"cn": "演剧终焉",
|
|
||||||
"jp": "演劇の終焉",
|
|
||||||
"kr": "연극의 종결"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
File diff suppressed because one or more lines are too long
55232
public/data/monster.json
55232
public/data/monster.json
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -1,57 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"id": "1",
|
|
||||||
"begin": "",
|
|
||||||
"end": "",
|
|
||||||
"lang": {
|
|
||||||
"en": "Intellitron Endgame",
|
|
||||||
"cn": "智械残局",
|
|
||||||
"jp": "オムニックの終局",
|
|
||||||
"kr": "지능 기계 종반전"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2",
|
|
||||||
"begin": "",
|
|
||||||
"end": "",
|
|
||||||
"lang": {
|
|
||||||
"en": "Illusory Realm of the Blazing Sun",
|
|
||||||
"cn": "烈阳幻域",
|
|
||||||
"jp": "烈日の幻域",
|
|
||||||
"kr": "태양의 <unbreak>환상 영역</unbreak>"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "3",
|
|
||||||
"begin": "",
|
|
||||||
"end": "",
|
|
||||||
"lang": {
|
|
||||||
"en": "Dissonance",
|
|
||||||
"cn": "不协和音",
|
|
||||||
"jp": "不協和音",
|
|
||||||
"kr": "불협화음"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "4",
|
|
||||||
"begin": "",
|
|
||||||
"end": "",
|
|
||||||
"lang": {
|
|
||||||
"en": "Cyber Crisis",
|
|
||||||
"cn": "网络风波",
|
|
||||||
"jp": "ネット騒動",
|
|
||||||
"kr": "네트워크 소동"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "5",
|
|
||||||
"begin": "",
|
|
||||||
"end": "",
|
|
||||||
"lang": {
|
|
||||||
"en": "Don't Mess With Pom-Pom",
|
|
||||||
"cn": "别惹帕姆",
|
|
||||||
"jp": "パムを怒らせるな",
|
|
||||||
"kr": "폼폼 조심"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,244 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"id": "2001",
|
|
||||||
"begin": "2023-10-30 04:00:04",
|
|
||||||
"end": "2023-12-24 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Youci's Wandering Words",
|
|
||||||
"cn": "游辞漫说",
|
|
||||||
"jp": "遊辞漫談",
|
|
||||||
"kr": "유사의 허황된 이야기"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2002",
|
|
||||||
"begin": "2023-10-30 04:00:02",
|
|
||||||
"end": "2024-02-05 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Tales of a Tethered Bird",
|
|
||||||
"cn": "羁鸟奇谭",
|
|
||||||
"jp": "籠鳥奇譚",
|
|
||||||
"kr": "새장 속 새 이야기"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2003",
|
|
||||||
"begin": "2023-12-04 04:00:02",
|
|
||||||
"end": "2024-02-05 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "An Expression of Eloquence",
|
|
||||||
"cn": "舌灿莲花",
|
|
||||||
"jp": "優れた弁舌",
|
|
||||||
"kr": "찬란한 감언"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2004",
|
|
||||||
"begin": "2023-02-05 04:00:00",
|
|
||||||
"end": "2024-04-24 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Deceitful Chaos",
|
|
||||||
"cn": "撒诈捣虚",
|
|
||||||
"jp": "噓八百",
|
|
||||||
"kr": "새빨간 거짓말"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2005",
|
|
||||||
"begin": "2023-02-05 04:00:01",
|
|
||||||
"end": "2024-04-24 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Fictitious Wordsmithing",
|
|
||||||
"cn": "作言造语",
|
|
||||||
"jp": "口から出まかせ",
|
|
||||||
"kr": "꾸며낸 이야기"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2006",
|
|
||||||
"begin": "2024-03-11 04:00:00",
|
|
||||||
"end": "2024-06-17 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Lexical Enigma (<unbreak>2.2</unbreak>)",
|
|
||||||
"cn": "新词迷离(<unbreak>2.2</unbreak>)",
|
|
||||||
"jp": "あやふやな新語(<unbreak>2.2</unbreak>)",
|
|
||||||
"kr": "흐릿한 새말(<unbreak>2.2</unbreak>)"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2007",
|
|
||||||
"begin": "2024-05-06 04:00:01",
|
|
||||||
"end": "2024-07-29 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Out of Thin Air (<unbreak>2.3</unbreak>)",
|
|
||||||
"cn": "向壁虚造(<unbreak>2.3</unbreak>)",
|
|
||||||
"jp": "向壁虚造(<unbreak>2.3</unbreak>)",
|
|
||||||
"kr": "터무니없는 날조(<unbreak>2.3</unbreak>)"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2008",
|
|
||||||
"begin": "2024-06-17 04:00:01",
|
|
||||||
"end": "2024-09-06 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Words of Deceit",
|
|
||||||
"cn": "欺人虚言",
|
|
||||||
"jp": "虚言",
|
|
||||||
"kr": "거짓된 말마디"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2009",
|
|
||||||
"begin": "2024-07-29 04:00:01",
|
|
||||||
"end": "2024-10-21 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Volubility",
|
|
||||||
"cn": "巧言如流",
|
|
||||||
"jp": "巧言流水の如し",
|
|
||||||
"kr": "청산유수"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2010",
|
|
||||||
"begin": "2024-09-06 04:00:01",
|
|
||||||
"end": "2024-12-02 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Rumor Mill",
|
|
||||||
"cn": "论黄数黑",
|
|
||||||
"jp": "数黒論黄",
|
|
||||||
"kr": "허튼소리"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2011",
|
|
||||||
"begin": "2024-10-21 04:00:01",
|
|
||||||
"end": "2025-01-13 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Technicality Entrapment",
|
|
||||||
"cn": "深文巧诋",
|
|
||||||
"jp": "手練手管",
|
|
||||||
"kr": "중상모략"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2012",
|
|
||||||
"begin": "2024-12-02 04:00:01",
|
|
||||||
"end": "2025-02-24 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Clichéd Sayings",
|
|
||||||
"cn": "陈腔滥调",
|
|
||||||
"jp": "陳腐な表現",
|
|
||||||
"kr": "진부한 말"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2013",
|
|
||||||
"begin": "2025-01-12 04:00:01",
|
|
||||||
"end": "2025-04-07 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Self-Fulfilling Prophecy",
|
|
||||||
"cn": "自证预言",
|
|
||||||
"jp": "自己証明の預言",
|
|
||||||
"kr": "자기실현적 예언"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2014",
|
|
||||||
"begin": "2025-02-24 04:00:01",
|
|
||||||
"end": "2025-05-19 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Structural Rules",
|
|
||||||
"cn": "结构规律",
|
|
||||||
"jp": "構造規則",
|
|
||||||
"kr": "구조적 규율"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2015",
|
|
||||||
"begin": "2025-04-07 04:00:01",
|
|
||||||
"end": "2025-06-30 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Narrative Analysis",
|
|
||||||
"cn": "叙事分析",
|
|
||||||
"jp": "叙事分析",
|
|
||||||
"kr": "내러티브 분석"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2016",
|
|
||||||
"begin": "2025-05-19 04:00:01",
|
|
||||||
"end": "2025-08-11 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Three Act Structure",
|
|
||||||
"cn": "三幕架构",
|
|
||||||
"jp": "三幕構成",
|
|
||||||
"kr": "3막 구조"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2017",
|
|
||||||
"begin": "2025-06-30 04:00:01",
|
|
||||||
"end": "2025-09-22 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Subjective Narrative",
|
|
||||||
"cn": "主观叙事",
|
|
||||||
"jp": "主観的叙事",
|
|
||||||
"kr": "주관적 서사"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2018",
|
|
||||||
"begin": "2025-08-11 04:00:01",
|
|
||||||
"end": "2025-11-03 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Iambic Pentameter",
|
|
||||||
"cn": "五步抑扬",
|
|
||||||
"jp": "弱強五歩格",
|
|
||||||
"kr": "약강 오보격"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2019",
|
|
||||||
"begin": "2025-09-22 04:00:01",
|
|
||||||
"end": "2025-12-15 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Syntax Rule",
|
|
||||||
"cn": "程式句法",
|
|
||||||
"jp": "シンタックスルール",
|
|
||||||
"kr": "프로그래밍 문법"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2020",
|
|
||||||
"begin": "2025-11-03 04:00:01",
|
|
||||||
"end": "2026-02-09 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Epic Collection",
|
|
||||||
"cn": "史诗集群",
|
|
||||||
"jp": "叙事詩集",
|
|
||||||
"kr": "서사시 모음집"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2021",
|
|
||||||
"begin": "2026-01-05 04:00:01",
|
|
||||||
"end": "2026-03-16 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Wordless Novel",
|
|
||||||
"cn": "无字小说",
|
|
||||||
"jp": "ワードレスノベル",
|
|
||||||
"kr": "글자 없는 소설"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2022",
|
|
||||||
"begin": "2026-02-08 04:00:01",
|
|
||||||
"end": "2026-03-16 04:00:00",
|
|
||||||
"lang": {
|
|
||||||
"en": "Virtual Made Manifest",
|
|
||||||
"cn": "虚境成章",
|
|
||||||
"jp": "仮想の章節",
|
|
||||||
"kr": "허상으로 이룬 장"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1,698 +0,0 @@
|
|||||||
{
|
|
||||||
"1001": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/memory-of-you-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/memory-of-it-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/memory-of-everything-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/never-forfeit-again-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/never-forget-again-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/just-like-this-always-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1002": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/the-higher-you-fly-the-harder-you-fall-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/quell-the-venom-octet-quench-the-vice-oflame-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/seen-and-unseen-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/roaring-dragon-and-soaring-sun-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/a-drop-of-rain-feeds-a-torrent-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/the-troubled-soul-lies-in-wait-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1003": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/childhood-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/convergence-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/poised-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/dedication-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/aspiration-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/trailblaze-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1004": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/legacy-of-honor-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/conflux-of-stars-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/prayer-of-peace-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/appellation-of-justice-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/power-of-kindness-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/prospect-of-glory-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1005": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/da-capo-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/fortississimo-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/capriccio-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/recitativo-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/doloroso-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/leggiero-eidolon-2_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1006": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/social-engineering-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/zombie-network-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/payload-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/bounce-attack-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/brute-force-attack-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/overlay-network-eidolon-2_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1008": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/to-the-bitter-end-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/breaking-free-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/power-through-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/turn-the-tables-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/hammer-and-tongs-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/self-sacrifice-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1009": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/star-sings-sans-verses-or-vocals-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/moon-speaks-in-wax-and-wane-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/meteor-showers-for-wish-and-want-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/aurora-basks-in-beauty-and-bliss-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/nebula-secludes-in-runes-and-riddles-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/cosmos-dreams-in-calm-and-comfort-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1013": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/kick-you-when-youre-down-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/keep-the-ball-rolling-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/thats-the-kind-of-girl-i-am-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/hit-where-it-hurts-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/cuss-big-or-cuss-nothing-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/no-one-can-betray-me-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1014": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/the-lost-white-walls-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/the-lost-oath-of-the-round-table-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/a-wish-across-fifteen-centuries-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/the-saga-of-sixteen-winter-days-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/the-dreamed-utopian-dawn-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/the-long-fated-night-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1015": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/the-unreached-dream-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/the-unfulfilled-happiness-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/the-untamed-will-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/the-unsung-life-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/the-nameless-watch-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/the-endless-pilgrimage-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1101": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/hone-your-strength-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/quick-march-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/bombardment-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/take-by-surprise-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/unstoppable-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/piercing-rainbow-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1102": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/extirpating-slash-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/dancing-butterfly-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/dazzling-tumult-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/flitting-phantasm-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/piercing-shards-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/shattering-shambles-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1103": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/echo-chamber-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/encore-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/listen-the-heartbeat-of-the-gears-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/make-some-noise-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/belobogs-loudest-roar-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/this-song-rocks-to-heaven-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1104": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/due-diligence-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/lingering-cold-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/never-surrender-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/faith-moves-mountains-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/cold-iron-fist-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/unyielding-resolve-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1105": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/pharmacology-expertise-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/clinical-research-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/the-right-cure-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/miracle-cure-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/preventive-treatment-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/doctors-grace-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1106": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/victory-report-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/adamant-charge-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/suppressive-force-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/full-analysis-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/absolute-jeopardy-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/feeble-pursuit-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1107": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/a-tall-figure-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/a-tight-embrace-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/cold-steel-armor-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/familys-warmth-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/a-small-promise-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/long-company-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1108": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/rising-love-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/infectious-enthusiasm-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/big-money-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/the-deeper-the-love-the-stronger-the-hate-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/huuuuge-money-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/increased-spending-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1109": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/early-to-bed-early-to-rise-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/happy-tummy-happy-body-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/dont-be-picky-nothings-icky-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/its-okay-to-not-know-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/let-the-moles-deeds-be-known-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/always-ready-to-punch-and-kick-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1110": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/morning-of-snow-hike-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/noon-of-portable-furnace-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/afternoon-of-avalanche-beacon-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/dusk-of-warm-campfire-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/night-of-aurora-tea-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/dawn-of-explorers-chart-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1111": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/fighting-endlessly-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/the-enemy-is-weak-i-am-strong-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/born-for-the-ring-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/never-turning-back-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/the-spirit-of-wildfire-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/a-champions-applause-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1112": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/future-market-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/bona-fide-acquisition-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/seize-the-big-and-free-the-small-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/agile-operation-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/inflationary-demand-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/incentive-mechanism-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1201": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/rise-through-the-tiles-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/sleep-on-the-tiles-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/read-between-the-tiles-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/right-on-the-tiles-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/gambit-for-the-tiles-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/prevail-beyond-the-tiles-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1202": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/windfall-of-lucky-springs-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/gainfully-gives-givingly-gains-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/halcyon-bequest-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/jovial-versatility-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/sauntering-coquette-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/peace-brings-wealth-to-all-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1203": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/ablution-of-the-quick-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/bestowal-from-the-pure-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/surveyal-by-the-fool-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/heavy-lies-the-crown-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/cicatrix-neath-the-pain-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/reunion-with-the-dust-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1204": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/slash-seas-split-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/swing-skies-squashed-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/strike-suns-subdued-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/spin-stars-sieged-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/stride-spoils-seized-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/sweep-souls-slain-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1205": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/blade-cuts-the-deepest-in-hell-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/ten-thousand-sorrows-from-one-broken-dream-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/hardened-blade-bleeds-coldest-shade-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/rejected-by-death-infected-with-life-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/death-by-ten-lords-gaze-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/reborn-into-an-empty-husk-eidolon-2_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1206": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/cut-with-ease-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/refine-in-toil-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/rise-from-fame-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/cleave-with-heart-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/prevail-via-taixu-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/dwell-like-water-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1207": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/aerial-marshal-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/skyward-command-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/torrential-fusillade-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/zephyrean-echoes-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/august-deadshot-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/bowstring-thunderclap-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1208": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/dominus-pacis-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/optimus-felix-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/apex-nexus-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/fortuna-stellaris-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/arbiter-primus-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/omnia-vita-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1209": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/svelte-saber-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/supine-serenade-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/sword-savant-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/searing-sting-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/surging-strife-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/swift-swoop-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1210": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/slurping-noodles-during-handstand-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/brushing-teeth-while-whistling-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/smashing-boulder-on-chest-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/blocking-pike-with-neck-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/swallowing-sword-to-stomach-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/catching-bullet-with-hands-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1211": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/ambrosial-aqua-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/sylphic-slumber-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/omniscient-opulence-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/evil-excision-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/waning-worries-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/drooling-drop-of-draconic-divinity-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1212": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/moon-crashes-tianguan-gate-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/crescent-shadows-qixing-dipper-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/halfmoon-gapes-mercurial-haze-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/lunarlance-shines-skyward-dome-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/night-shades-astral-radiance-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/eclipse-hollows-corporeal-husk-eidolon-2_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1213": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/tethered-to-sky-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/imperium-on-cloud-nine-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/clothed-in-clouds-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/zephyrs-bliss-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/fall-is-the-pride-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/reign-returned-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1214": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/dvesha-inhibited-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/klesha-breached-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/dukha-ceased-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/karma-severed-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/deva-enthralled-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/sasra-mastered-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1215": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/one-heart-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/two-views-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/three-temptations-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/four-truths-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/five-skandhas-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/six-reverences-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1217": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/anchored-to-vessel-specters-nestled-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/sealed-in-tail-wraith-subdued-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/cursed-by-fate-moths-to-flame-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/tied-in-life-bound-to-strife-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/mandated-by-edict-evils-evicted-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/woven-together-cohere-forever-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1218": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/pentapathic-transference-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/from-savor-comes-suffer-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/flavored-euphony-reigns-supreme-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/leisure-in-luster-out-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/duel-in-dawn-dash-in-dusk-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/nonamorphic-pyrobind-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1220": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/skyward-i-quell-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/moonward-i-wish-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/starward-i-bode-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/stormward-i-hear-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/heavenward-i-leap-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/homeward-i-near-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1221": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/weathered-blade-does-not-sully-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/first-luster-breaks-dawn-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/mastlength-twirls-mountweight-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/artisans-ironsong-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/blade-of-old-outlasts-all-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/walk-in-blade-talk-in-zither-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1222": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/bloom-on-vileward-bouquet-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/leisure-in-carmine-smokeveil-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/shine-of-floral-wick-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/redolence-from-canopied-banquet-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/poise-atop-twists-and-turns-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/arcadia-under-deep-seclusion-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1223": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/oathkeeper-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/wrathbearer-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/deathchaser-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/heathprowler-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/truthbender-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/faithbinder-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1224": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/my-sword-stirs-starlight-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/blade-dances-on-waves-fight-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/sharp-wit-in-martial-might-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/being-fabulous-never-frights-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/sword-delights-sugar-blights-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/me-the-best-girl-in-sight-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1225": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/earthbound-i-was-cloudward-i-be-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/beatitude-dawns-for-the-worthy-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/verity-weaves-thoughts-to-blade-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/bereft-of-form-which-name-to-bear-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/colored-cloud-rains-fortune-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/clairvoyance-of-boom-and-doom-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1301": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/salty-dog-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/lions-tail-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/corpse-reviver-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/last-word-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/death-in-the-afternoon-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/blood-and-sand-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1302": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/a-lacuna-in-kingdom-of-aesthetics-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/agates-humility-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/thorny-roads-glory-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/trumpets-dedication-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/snow-from-somewhere-in-cosmos-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/your-resplendence-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1303": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/neuronic-embroidery-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/reedside-promenade-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/viridescent-pirouette-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/chatoyant-clat-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/languid-barrette-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/sash-cascade-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1304": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/prisoners-dilemma-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/bounded-rationality-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/droprate-maxing-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/unexpected-hanging-paradox-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/ambiguity-aversion-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/stag-hunt-game-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1305": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/pride-comes-before-a-fall-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/the-divine-is-in-the-details-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/know-thyself-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/ignorance-is-blight-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/sic-itur-ad-astra-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/vincit-omnia-veritas-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1306": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/suspension-of-disbelief-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/purely-fictitious-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/pipedream-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/life-is-a-gamble-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/parallax-truth-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/narrative-polysemy-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1307": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/seven-pillars-of-wisdom-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/weep-not-for-me-my-lamb-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/as-above-so-below-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/in-tears-we-gift-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/linnutee-flyway-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/pantheon-merciful-masses-pitiful-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1308": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/silenced-sky-spake-sooth-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/mute-thunder-in-still-tempest-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/frost-bites-in-death-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/shrined-fire-for-mirrored-soul-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/strewn-souls-on-erased-earths-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/apocalypse-the-emancipator-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1309": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/land-of-smiles-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/afternoon-tea-for-two-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/inverted-tuning-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/raindrop-key-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/lonestars-lament-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/moonless-midnight-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1310": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/in-reddened-chrysalis-i-once-rest-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/from-shattered-sky-i-free-fall-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/amidst-silenced-stars-i-deep-sleep-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/upon-lighted-fyrefly-i-soon-gaze-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/from-undreamt-night-i-thence-shine-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/in-finalized-morrow-i-full-bloom-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1312": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/whimsicality-of-fancy-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/yearning-of-youth-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/vestige-of-happiness-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/visage-of-kinship-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/genesis-of-first-love-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/estrangement-of-dream-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1313": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/millenniums-quietus-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/faith-outstrips-frailty-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/hermitage-of-thorns-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/sculptures-preamble-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/paper-raft-in-silver-bay-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/dawn-of-sidereal-cacophony-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1314": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/altruism-nevertheless-tradable-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/morality-herein-authenticated-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/honesty-soon-mortgaged-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/sincerity-put-option-only-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/hope-hitherto-forfeited-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/equity-pending-sponsorship-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1315": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/dusty-trails-lone-star-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/milestonemonger-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/marble-orchards-guard-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/cold-cuts-chef-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/stump-speech-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/crowbar-hotels-raccoon-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1317": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/returned-is-the-revenant-with-no-ferry-toll-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/free-is-the-mind-enlightened-by-haikus-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/many-are-the-shrines-that-repel-no-hell-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/lost-is-the-nind-devoured-by-time-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/steady-is-the-ranger-with-unerring-arrows-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/righteous-is-the-wrath-that-spares-no-evil-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1321": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/when-a-bud-readies-to-bloom-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/fresh-ethereal-and-beloved-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/pity-its-petals-thin-as-mist-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/pity-its-heart-gnawed-by-worms-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/fallen-decayed-and-despised-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/and-yet-always-deathly-beautiful-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1401": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/night-at-shorefall-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/wind-through-keyhole-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/door-into-summer-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/the-sixteenth-key-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/bitter-pill-of-truth-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/sweet-lure-of-answer-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1402": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/drift-at-the-whim-of-venus-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/sail-on-the-raft-of-eyelids-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/bequeath-in-the-coalescence-of-dew-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/flicker-below-the-surface-of-marble-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/weave-under-the-shroud-of-woe-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/fluctuate-in-the-tapestry-of-fates-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1403": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/rite-of-sugar-scoop-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/guide-of-dream-tour-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/trove-of-morning-glow-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/peace-of-empathy-bond-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/clock-of-wonder-origin-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/morrow-of-star-shine-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1404": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/frost-hones-spine-of-steel-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/strife-beholds-cry-of-dead-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/honor-exalts-feast-of-faith-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/siren-jolts-the-laconic-lion-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/war-chisels-flesh-of-flame-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/legacy-scales-mound-of-blood-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1405": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/magician-isolated-by-stars-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/soul-true-to-history-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/pupil-etched-into-cosmos-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/blaze-plunged-to-canyon-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/embryo-set-beyond-vortex-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/everything-is-in-everything-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1406": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/read-the-room-seek-the-glee-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/in-the-fray-nab-on-a-spree-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/from-thin-air-hard-to-foresee-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/the-jig-is-up-quick-to-flee-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/safe-in-numbers-light-as-a-bee-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/the-thiefs-game-unsung-and-free-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1407": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/snowbound-maiden-memory-to-tomb-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/crown-on-wings-of-bloom-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/pious-pilgrim-dance-in-doom-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/rest-in-songs-of-gloom-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/pristine-pages-prophecy-as-plume-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/await-for-years-to-loom-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1408": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/fire-and-light-bind-virtue-and-vice-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/sky-and-earth-churn-mortal-froth-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/depths-of-quiet-entombed-in-ruin-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/faces-of-titans-blurred-by-time-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/the-wheel-spins-forevermore-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/embers-of-old-rise-still-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1409": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/cradle-the-candle-of-night-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/come-sit-in-my-courtyard-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/depart-unto-the-sun-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/sunlit-amber-yours-to-keep-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/twilight-drapes-the-tide-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/o-sky-heed-my-plea-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1410": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/you-ask-why-hearts-cry-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/tell-me-why-waves-roar-high-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/why-do-lights-bid-goodbye-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/lo-how-time-flows-by-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/in-ablution-i-hum-and-sigh-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/when-to-return-from-where-you-lie-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1412": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/seize-the-crowns-of-all-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/forge-the-dreams-of-many-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/torch-the-laws-of-old-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/remake-the-realms-of-men-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/help-and-hurt-repaid-in-full-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/a-journey-set-starward-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1413": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/sleep-tight-the-night-dreams-long-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/listen-up-the-slumber-speaks-soft-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/fear-not-the-nightmare-lies-past-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/wake-up-the-tomorrow-is-yours-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/let-go-the-me-in-memories-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/like-this-always-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1414": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/shed-scales-of-old-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/watch-trails-to-blaze-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/bear-weight-of-worlds-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/by-oath-this-vessel-is-i-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/the-path-of-permanence-sweeps-far-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/one-dream-to-enfold-all-wilds-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1415": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/epics-born-on-a-blank-slate-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/a-tomorrow-in-thirteen-shades-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/by-thy-being-as-ive-written-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/please-write-on-with-a-smile-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/gaze-steeped-in-yesterbloom-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/remembrance-sung-in-ripples-%E2%99%AA-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1501": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/goingviral-whoisshe-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/audienceknows-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/linkup-heartskip-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/lockedin-facecard-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/healingtheworld-goodvibesonly-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/builtdifferent-goingextinct-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1502": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/chuckle-chimes-where-jade-falls-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/blind-arrows-guided-by-feathers-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/auspices-mirrored-in-decalight-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/threads-of-fate-colored-by-plumes-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/bejeweled-in-radiant-grace-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/ferried-along-the-astral-arc-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"8001": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/a-falling-star-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/an-unwilling-host-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/a-leading-whisper-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/a-destructing-glance-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/a-surviving-hope-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/a-trailblazing-will-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"8002": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/a-falling-star-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/an-unwilling-host-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/a-leading-whisper-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/a-destructing-glance-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/a-surviving-hope-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/a-trailblazing-will-eidolon-2_icon_small.webp"
|
|
||||||
],
|
|
||||||
"8003": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/earth-shaking-resonance-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/time-defying-tenacity-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/trail-blazing-blueprint-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/nation-building-oath-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/spirit-warming-flame-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/city-forging-bulwarks-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"8004": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/earth-shaking-resonance-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/time-defying-tenacity-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/trail-blazing-blueprint-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/nation-building-oath-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/spirit-warming-flame-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/city-forging-bulwarks-eidolon-2_icon_small.webp"
|
|
||||||
],
|
|
||||||
"8005": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/best-seat-in-the-house-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/jailbreaking-rainbowwalk-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/sanatorium-for-rest-notes-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/dove-in-tophat-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/poem-favors-rhythms-of-old-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/tomorrow-rest-in-spotlight-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"8006": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/best-seat-in-the-house-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/jailbreaking-rainbowwalk-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/sanatorium-for-rest-notes-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/dove-in-tophat-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/poem-favors-rhythms-of-old-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/tomorrow-rest-in-spotlight-eidolon-2_icon_small.webp"
|
|
||||||
],
|
|
||||||
"8007": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/narrator-of-the-present-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/gleaner-of-the-past-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/chanter-of-the-future-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/dancer-of-the-muse-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/seamster-of-the-ode-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/bearer-of-the-revelation-eidolon_icon_small.webp"
|
|
||||||
],
|
|
||||||
"8008": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/narrator-of-the-present-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/gleaner-of-the-past-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/chanter-of-the-future-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/dancer-of-the-muse-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/seamster-of-the-ode-eidolon-2_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/bearer-of-the-revelation-eidolon-2_icon_small.webp"
|
|
||||||
],
|
|
||||||
"1504": [
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/beware-wander-not-in-full-moon-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/knock-where-snickers-echo-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/hush-weight-of-unspoken-truths-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/heed-truth-needs-no-mastication-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/advisory-the-sleuth-is-but-the-slayer-eidolon_icon_small.webp",
|
|
||||||
"https://starrail.honeyhunterworld.com/img/eidolon/finale-and-then-there-were-none-eidolon_icon_small.webp"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,479 +0,0 @@
|
|||||||
{
|
|
||||||
"101": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "HealRatioBase",
|
|
||||||
"value": 0.10000000149011612
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": []
|
|
||||||
},
|
|
||||||
"102": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "AttackAddedRatio",
|
|
||||||
"value": 0.119999997317791
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": [
|
|
||||||
{
|
|
||||||
"type": "SpeedAddedRatio",
|
|
||||||
"value": 0.05999999865889549
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"103": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "DefenceAddedRatio",
|
|
||||||
"value": 0.15000000596046448
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": []
|
|
||||||
},
|
|
||||||
"104": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "IceAddedRatio",
|
|
||||||
"value": 0.10000000149011612
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": []
|
|
||||||
},
|
|
||||||
"105": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "PhysicalAddedRatio",
|
|
||||||
"value": 0.10000000149011612
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": []
|
|
||||||
},
|
|
||||||
"106": {
|
|
||||||
"2": [],
|
|
||||||
"4": []
|
|
||||||
},
|
|
||||||
"107": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "FireAddedRatio",
|
|
||||||
"value": 0.10000000149011612
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": []
|
|
||||||
},
|
|
||||||
"108": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "QuantumAddedRatio",
|
|
||||||
"value": 0.10000000149011612
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": []
|
|
||||||
},
|
|
||||||
"109": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "ThunderAddedRatio",
|
|
||||||
"value": 0.10000000149011612
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": []
|
|
||||||
},
|
|
||||||
"110": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "WindAddedRatio",
|
|
||||||
"value": 0.10000000149011612
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": []
|
|
||||||
},
|
|
||||||
"111": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "BreakDamageAddedRatioBase",
|
|
||||||
"value": 0.1599999964237213
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": [
|
|
||||||
{
|
|
||||||
"type": "BreakDamageAddedRatioBase",
|
|
||||||
"value": 0.1599999964237213
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"112": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "ImaginaryAddedRatio",
|
|
||||||
"value": 0.10000000149011612
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": []
|
|
||||||
},
|
|
||||||
"113": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "HPAddedRatio",
|
|
||||||
"value": 0.119999997317791
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": []
|
|
||||||
},
|
|
||||||
"114": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "SpeedAddedRatio",
|
|
||||||
"value": 0.05999999865889549
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": []
|
|
||||||
},
|
|
||||||
"115": {
|
|
||||||
"2": [],
|
|
||||||
"4": []
|
|
||||||
},
|
|
||||||
"116": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "AttackAddedRatio",
|
|
||||||
"value": 0.119999997317791
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": []
|
|
||||||
},
|
|
||||||
"117": {
|
|
||||||
"2": [],
|
|
||||||
"4": [
|
|
||||||
{
|
|
||||||
"type": "CriticalChanceBase",
|
|
||||||
"value": 0.03999999910593033
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"118": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "BreakDamageAddedRatioBase",
|
|
||||||
"value": 0.1599999964237213
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": []
|
|
||||||
},
|
|
||||||
"119": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "BreakDamageAddedRatioBase",
|
|
||||||
"value": 0.1599999964237213
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": []
|
|
||||||
},
|
|
||||||
"120": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "AttackAddedRatio",
|
|
||||||
"value": 0.119999997317791
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": [
|
|
||||||
{
|
|
||||||
"type": "CriticalChanceBase",
|
|
||||||
"value": 0.05999999865889549
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"121": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "SpeedAddedRatio",
|
|
||||||
"value": 0.05999999865889549
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": []
|
|
||||||
},
|
|
||||||
"122": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "CriticalChanceBase",
|
|
||||||
"value": 0.07999999821186066
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": []
|
|
||||||
},
|
|
||||||
"123": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "AttackAddedRatio",
|
|
||||||
"value": 0.119999997317791
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": []
|
|
||||||
},
|
|
||||||
"124": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "QuantumAddedRatio",
|
|
||||||
"value": 0.10000000149011612
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": [
|
|
||||||
{
|
|
||||||
"type": "SpeedAddedRatio",
|
|
||||||
"value": -0.07999999821186066
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"125": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "SpeedAddedRatio",
|
|
||||||
"value": 0.05999999865889549
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": []
|
|
||||||
},
|
|
||||||
"126": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "CriticalDamageBase",
|
|
||||||
"value": 0.1599999964237213
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": []
|
|
||||||
},
|
|
||||||
"127": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "CriticalChanceBase",
|
|
||||||
"value": 0.0800000000745058
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": []
|
|
||||||
},
|
|
||||||
"128": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "CriticalDamageBase",
|
|
||||||
"value": 0.1599999964237213
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"4": [
|
|
||||||
{
|
|
||||||
"type": "CriticalDamageBase",
|
|
||||||
"value": 0.1599999964237213
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"301": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "AttackAddedRatio",
|
|
||||||
"value": 0.119999997317791
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"302": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "HPAddedRatio",
|
|
||||||
"value": 0.119999997317791
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"303": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "StatusProbabilityBase",
|
|
||||||
"value": 0.10000000149011612
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"304": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "DefenceAddedRatio",
|
|
||||||
"value": 0.15000000596046448
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"305": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "CriticalDamageBase",
|
|
||||||
"value": 0.1599999964237213
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"306": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "CriticalChanceBase",
|
|
||||||
"value": 0.07999999821186066
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"307": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "BreakDamageAddedRatioBase",
|
|
||||||
"value": 0.1599999964237213
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"308": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "SPRatioBase",
|
|
||||||
"value": 0.05000000074505806
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"309": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "CriticalChanceBase",
|
|
||||||
"value": 0.07999999821186066
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"310": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "StatusResistanceBase",
|
|
||||||
"value": 0.10000000149011612
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"311": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "AttackAddedRatio",
|
|
||||||
"value": 0.119999997317791
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"312": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "SPRatioBase",
|
|
||||||
"value": 0.05000000074505806
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"313": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "CriticalChanceBase",
|
|
||||||
"value": 0.03999999910593033
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"314": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "AttackAddedRatio",
|
|
||||||
"value": 0.119999997317791
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"315": {
|
|
||||||
"2": []
|
|
||||||
},
|
|
||||||
"316": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "SpeedAddedRatio",
|
|
||||||
"value": 0.05999999865889549
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"317": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "SPRatioBase",
|
|
||||||
"value": 0.05000000074505806
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"318": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "CriticalDamageBase",
|
|
||||||
"value": 0.1599999964237213
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"319": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "HPAddedRatio",
|
|
||||||
"value": 0.119999997317791
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"320": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "SpeedAddedRatio",
|
|
||||||
"value": 0.05999999865889549
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"321": {
|
|
||||||
"2": []
|
|
||||||
},
|
|
||||||
"322": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "AttackAddedRatio",
|
|
||||||
"value": 0.119999997317791
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"323": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "CriticalChanceBase",
|
|
||||||
"value": 0.07999999821186066
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"324": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "CriticalDamageBase",
|
|
||||||
"value": 0.1599999964237213
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"130": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "SpeedAddedRatio",
|
|
||||||
"value": 0.05999999865889549
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"129": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "CriticalDamageBase",
|
|
||||||
"value": 0.1599999964237213
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"325": {
|
|
||||||
"2": [
|
|
||||||
{
|
|
||||||
"type": "ElationDamageAddedRatioBase",
|
|
||||||
"value": 0.1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"326": {
|
|
||||||
"2": [
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,298 +0,0 @@
|
|||||||
{
|
|
||||||
"2": {
|
|
||||||
"1": {
|
|
||||||
"property": "HPDelta",
|
|
||||||
"base": 13.548015594482422,
|
|
||||||
"step": 1.6935019493103027,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"property": "AttackDelta",
|
|
||||||
"base": 6.774007797241211,
|
|
||||||
"step": 0.8467509746551514,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"property": "DefenceDelta",
|
|
||||||
"base": 6.774007797241211,
|
|
||||||
"step": 0.8467509746551514,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"4": {
|
|
||||||
"property": "HPAddedRatio",
|
|
||||||
"base": 0.01382400095462799,
|
|
||||||
"step": 0.001728000584989786,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"5": {
|
|
||||||
"property": "AttackAddedRatio",
|
|
||||||
"base": 0.01382400095462799,
|
|
||||||
"step": 0.001728000584989786,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"6": {
|
|
||||||
"property": "DefenceAddedRatio",
|
|
||||||
"base": 0.01728000119328499,
|
|
||||||
"step": 0.002160000381991267,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"7": {
|
|
||||||
"property": "SpeedDelta",
|
|
||||||
"base": 1.0,
|
|
||||||
"step": 0.10000000149011612,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"8": {
|
|
||||||
"property": "CriticalChanceBase",
|
|
||||||
"base": 0.010368000715970991,
|
|
||||||
"step": 0.001296000787988305,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"9": {
|
|
||||||
"property": "CriticalDamageBase",
|
|
||||||
"base": 0.020736001431941983,
|
|
||||||
"step": 0.0025920008774846792,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"10": {
|
|
||||||
"property": "StatusProbabilityBase",
|
|
||||||
"base": 0.01382400095462799,
|
|
||||||
"step": 0.001728000584989786,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"11": {
|
|
||||||
"property": "StatusResistanceBase",
|
|
||||||
"base": 0.01382400095462799,
|
|
||||||
"step": 0.001728000584989786,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"12": {
|
|
||||||
"property": "BreakDamageAddedRatioBase",
|
|
||||||
"base": 0.020736001431941983,
|
|
||||||
"step": 0.0025920008774846792,
|
|
||||||
"step_num": 2
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"1": {
|
|
||||||
"property": "HPDelta",
|
|
||||||
"base": 20.322023391723633,
|
|
||||||
"step": 2.540252923965454,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"property": "AttackDelta",
|
|
||||||
"base": 10.161011695861816,
|
|
||||||
"step": 1.2701259851455688,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"property": "DefenceDelta",
|
|
||||||
"base": 10.161011695861816,
|
|
||||||
"step": 1.2701259851455688,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"4": {
|
|
||||||
"property": "HPAddedRatio",
|
|
||||||
"base": 0.020736001431941983,
|
|
||||||
"step": 0.0025920008774846792,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"5": {
|
|
||||||
"property": "AttackAddedRatio",
|
|
||||||
"base": 0.020736001431941983,
|
|
||||||
"step": 0.0025920008774846792,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"6": {
|
|
||||||
"property": "DefenceAddedRatio",
|
|
||||||
"base": 0.025919999927282333,
|
|
||||||
"step": 0.0032400002237409353,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"7": {
|
|
||||||
"property": "SpeedDelta",
|
|
||||||
"base": 1.2000000476837158,
|
|
||||||
"step": 0.10000000149011612,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"8": {
|
|
||||||
"property": "CriticalChanceBase",
|
|
||||||
"base": 0.015552000142633917,
|
|
||||||
"step": 0.001944000832736492,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"9": {
|
|
||||||
"property": "CriticalDamageBase",
|
|
||||||
"base": 0.03110400028526783,
|
|
||||||
"step": 0.003888000966981054,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"10": {
|
|
||||||
"property": "StatusProbabilityBase",
|
|
||||||
"base": 0.020736001431941983,
|
|
||||||
"step": 0.0025920008774846792,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"11": {
|
|
||||||
"property": "StatusResistanceBase",
|
|
||||||
"base": 0.020736001431941983,
|
|
||||||
"step": 0.0025920008774846792,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"12": {
|
|
||||||
"property": "BreakDamageAddedRatioBase",
|
|
||||||
"base": 0.03110400028526783,
|
|
||||||
"step": 0.003888000966981054,
|
|
||||||
"step_num": 2
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"4": {
|
|
||||||
"1": {
|
|
||||||
"property": "HPDelta",
|
|
||||||
"base": 27.096031188964844,
|
|
||||||
"step": 3.3870038986206055,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"property": "AttackDelta",
|
|
||||||
"base": 13.548015594482422,
|
|
||||||
"step": 1.6935019493103027,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"property": "DefenceDelta",
|
|
||||||
"base": 13.548015594482422,
|
|
||||||
"step": 1.6935019493103027,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"4": {
|
|
||||||
"property": "HPAddedRatio",
|
|
||||||
"base": 0.027648000046610832,
|
|
||||||
"step": 0.0034560004714876413,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"5": {
|
|
||||||
"property": "AttackAddedRatio",
|
|
||||||
"base": 0.027648000046610832,
|
|
||||||
"step": 0.0034560004714876413,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"6": {
|
|
||||||
"property": "DefenceAddedRatio",
|
|
||||||
"base": 0.03456000238656998,
|
|
||||||
"step": 0.004320000298321247,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"7": {
|
|
||||||
"property": "SpeedDelta",
|
|
||||||
"base": 1.600000023841858,
|
|
||||||
"step": 0.20000000298023224,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"8": {
|
|
||||||
"property": "CriticalChanceBase",
|
|
||||||
"base": 0.020736001431941983,
|
|
||||||
"step": 0.0025920008774846792,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"9": {
|
|
||||||
"property": "CriticalDamageBase",
|
|
||||||
"base": 0.041471999138593674,
|
|
||||||
"step": 0.0051840003579854965,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"10": {
|
|
||||||
"property": "StatusProbabilityBase",
|
|
||||||
"base": 0.027648000046610832,
|
|
||||||
"step": 0.0034560004714876413,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"11": {
|
|
||||||
"property": "StatusResistanceBase",
|
|
||||||
"base": 0.027648000046610832,
|
|
||||||
"step": 0.0034560004714876413,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"12": {
|
|
||||||
"property": "BreakDamageAddedRatioBase",
|
|
||||||
"base": 0.041471999138593674,
|
|
||||||
"step": 0.0051840003579854965,
|
|
||||||
"step_num": 2
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"5": {
|
|
||||||
"1": {
|
|
||||||
"property": "HPDelta",
|
|
||||||
"base": 33.87004089355469,
|
|
||||||
"step": 4.233755111694336,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"2": {
|
|
||||||
"property": "AttackDelta",
|
|
||||||
"base": 16.93501853942871,
|
|
||||||
"step": 2.1168770790100098,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"3": {
|
|
||||||
"property": "DefenceDelta",
|
|
||||||
"base": 16.93501853942871,
|
|
||||||
"step": 2.1168770790100098,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"4": {
|
|
||||||
"property": "HPAddedRatio",
|
|
||||||
"base": 0.03456000238656998,
|
|
||||||
"step": 0.004320000298321247,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"5": {
|
|
||||||
"property": "AttackAddedRatio",
|
|
||||||
"base": 0.03456000238656998,
|
|
||||||
"step": 0.004320000298321247,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"6": {
|
|
||||||
"property": "DefenceAddedRatio",
|
|
||||||
"base": 0.04320000112056732,
|
|
||||||
"step": 0.005400000140070915,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"7": {
|
|
||||||
"property": "SpeedDelta",
|
|
||||||
"base": 2.0,
|
|
||||||
"step": 0.30000001192092896,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"8": {
|
|
||||||
"property": "CriticalChanceBase",
|
|
||||||
"base": 0.025919999927282333,
|
|
||||||
"step": 0.0032400002237409353,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"9": {
|
|
||||||
"property": "CriticalDamageBase",
|
|
||||||
"base": 0.05183999985456467,
|
|
||||||
"step": 0.006480000447481871,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"10": {
|
|
||||||
"property": "StatusProbabilityBase",
|
|
||||||
"base": 0.03456000238656998,
|
|
||||||
"step": 0.004320000298321247,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"11": {
|
|
||||||
"property": "StatusResistanceBase",
|
|
||||||
"base": 0.03456000238656998,
|
|
||||||
"step": 0.004320000298321247,
|
|
||||||
"step_num": 2
|
|
||||||
},
|
|
||||||
"12": {
|
|
||||||
"property": "BreakDamageAddedRatioBase",
|
|
||||||
"base": 0.05183999985456467,
|
|
||||||
"step": 0.006480000447481871,
|
|
||||||
"step_num": 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
25
src/app/api/data/[name]/route.ts
Normal file
25
src/app/api/data/[name]/route.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { getDataCache } from "@/lib/cache/cache"
|
||||||
|
|
||||||
|
export async function GET(
|
||||||
|
req: Request,
|
||||||
|
{ params }: { params: Promise<{ name: string }> }
|
||||||
|
) {
|
||||||
|
const { name } = await params
|
||||||
|
|
||||||
|
const item = getDataCache(name)
|
||||||
|
|
||||||
|
if (!item) {
|
||||||
|
return new Response("Not found", { status: 404 })
|
||||||
|
}
|
||||||
|
|
||||||
|
const headers: Record<string, string> = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Cache-Control": "public, max-age=3600"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.type === "br") {
|
||||||
|
headers["Content-Encoding"] = "br"
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(new Uint8Array(item.buf), { headers })
|
||||||
|
}
|
||||||
@@ -73,22 +73,22 @@ export default async function RootLayout({
|
|||||||
<QueryProviderWrapper>
|
<QueryProviderWrapper>
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
<ClientThemeWrapper>
|
<ClientThemeWrapper>
|
||||||
<ClientDataFetcher />
|
<ClientDataFetcher >
|
||||||
<div className="min-h-screen w-full">
|
<div className="min-h-screen w-full">
|
||||||
<Header />
|
<Header />
|
||||||
<div className="grid grid-cols-12 w-full">
|
<div className="grid grid-cols-12 w-full">
|
||||||
<div className="hidden sm:block md:col-span-4 lg:col-span-3 sticky top-0 self-start h-fit">
|
<div className="hidden sm:block md:col-span-4 lg:col-span-3 sticky top-0 self-start h-fit">
|
||||||
<AvatarBar />
|
<AvatarBar />
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-12 sm:col-span-8 lg:col-span-9">
|
<div className="col-span-12 sm:col-span-8 lg:col-span-9">
|
||||||
<ActionBar />
|
<ActionBar />
|
||||||
{children}
|
{children}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
|
</ClientDataFetcher>
|
||||||
<Footer />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</ClientThemeWrapper>
|
</ClientThemeWrapper>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
</QueryProviderWrapper>
|
</QueryProviderWrapper>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import useListAvatarStore from "@/stores/avatarStore";
|
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
@@ -19,12 +18,14 @@ import { connectToPS, syncDataToPS } from "@/helper";
|
|||||||
import CopyImport from "../importBar/copy";
|
import CopyImport from "../importBar/copy";
|
||||||
import useCopyProfileStore from "@/stores/copyProfile";
|
import useCopyProfileStore from "@/stores/copyProfile";
|
||||||
import AvatarBar from "../avatarBar";
|
import AvatarBar from "../avatarBar";
|
||||||
|
import useCurrentDataStore from "@/stores/currentDataStore";
|
||||||
|
import useDetailDataStore from "@/stores/detailDataStore";
|
||||||
|
|
||||||
export default function ActionBar() {
|
export default function ActionBar() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const { avatarSelected, listRawAvatar } = useListAvatarStore()
|
const { avatarSelected } = useCurrentDataStore()
|
||||||
const { setListCopyAvatar } = useCopyProfileStore()
|
const { damageType, baseType } = useDetailDataStore()
|
||||||
|
const { setResetData } = useCopyProfileStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
const {
|
const {
|
||||||
@@ -43,21 +44,20 @@ export default function ActionBar() {
|
|||||||
|
|
||||||
const profileCurrent = useMemo(() => {
|
const profileCurrent = useMemo(() => {
|
||||||
if (!avatarSelected) return null;
|
if (!avatarSelected) return null;
|
||||||
const avatar = avatars[avatarSelected.id];
|
const avatar = avatars[avatarSelected.ID];
|
||||||
return avatar?.profileList[avatar.profileSelect] || null;
|
return avatar?.profileList[avatar.profileSelect] || null;
|
||||||
}, [avatarSelected, avatars]);
|
}, [avatarSelected, avatars]);
|
||||||
|
|
||||||
const listProfile = useMemo(() => {
|
const listProfile = useMemo(() => {
|
||||||
if (!avatarSelected) return [];
|
if (!avatarSelected) return [];
|
||||||
const avatar = avatars[avatarSelected.id];
|
const avatar = avatars[avatarSelected.ID];
|
||||||
return avatar?.profileList || [];
|
return avatar?.profileList || [];
|
||||||
}, [avatarSelected, avatars]);
|
}, [avatarSelected, avatars]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleUpdateProfile = () => {
|
const handleUpdateProfile = () => {
|
||||||
if (!profileName.trim()) return;
|
if (!profileName.trim()) return;
|
||||||
if (formState === "CREATE" && avatarSelected && avatars[avatarSelected.id]) {
|
if (formState === "CREATE" && avatarSelected && avatars[avatarSelected.ID]) {
|
||||||
const newListProfile = [...listProfile]
|
const newListProfile = [...listProfile]
|
||||||
const newProfile = {
|
const newProfile = {
|
||||||
profile_name: profileName,
|
profile_name: profileName,
|
||||||
@@ -65,12 +65,12 @@ export default function ActionBar() {
|
|||||||
relics: {} as Record<string, RelicStore>
|
relics: {} as Record<string, RelicStore>
|
||||||
}
|
}
|
||||||
newListProfile.push(newProfile)
|
newListProfile.push(newProfile)
|
||||||
setAvatar({ ...avatars[avatarSelected.id], profileList: newListProfile, profileSelect: newListProfile.length - 1 })
|
setAvatar({ ...avatars[avatarSelected.ID], profileList: newListProfile, profileSelect: newListProfile.length - 1 })
|
||||||
toast.success("Profile created successfully")
|
toast.success("Profile created successfully")
|
||||||
} else if (formState === "EDIT" && profileCurrent && avatarSelected && profileEdit !== -1) {
|
} else if (formState === "EDIT" && profileCurrent && avatarSelected && profileEdit !== -1) {
|
||||||
const newListProfile = [...listProfile]
|
const newListProfile = [...listProfile]
|
||||||
newListProfile[profileEdit].profile_name = profileName;
|
newListProfile[profileEdit].profile_name = profileName;
|
||||||
setAvatar({ ...avatars[avatarSelected.id], profileList: newListProfile })
|
setAvatar({ ...avatars[avatarSelected.ID], profileList: newListProfile })
|
||||||
toast.success("Profile updated successfully")
|
toast.success("Profile updated successfully")
|
||||||
}
|
}
|
||||||
handleCloseModal("update_profile_modal");
|
handleCloseModal("update_profile_modal");
|
||||||
@@ -99,20 +99,19 @@ export default function ActionBar() {
|
|||||||
|
|
||||||
const handleProfileSelect = (profileId: number) => {
|
const handleProfileSelect = (profileId: number) => {
|
||||||
if (!avatarSelected) return;
|
if (!avatarSelected) return;
|
||||||
if (avatars[avatarSelected.id].profileSelect === profileId) return;
|
if (avatars[avatarSelected.ID].profileSelect === profileId) return;
|
||||||
setAvatar({ ...avatars[avatarSelected.id], profileSelect: profileId })
|
setAvatar({ ...avatars[avatarSelected.ID], profileSelect: profileId })
|
||||||
toast.success(`Profile changed to Profile: ${avatars[avatarSelected.id].profileList[profileId].profile_name}`)
|
toast.success(`Profile changed to Profile: ${avatars[avatarSelected.ID].profileList[profileId].profile_name}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const handleDeleteProfile = (profileId: number, e: React.MouseEvent) => {
|
const handleDeleteProfile = (profileId: number, e: React.MouseEvent) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
if (!avatarSelected || profileId == 0) return;
|
if (!avatarSelected || profileId == 0) return;
|
||||||
if (window.confirm(`Are you sure you want to delete profile: ${avatars[avatarSelected.id].profileList[profileId].profile_name}?`)) {
|
if (window.confirm(`Are you sure you want to delete profile: ${avatars[avatarSelected.ID].profileList[profileId].profile_name}?`)) {
|
||||||
const newListProfile = [...listProfile]
|
const newListProfile = [...listProfile]
|
||||||
newListProfile.splice(profileId, 1)
|
newListProfile.splice(profileId, 1)
|
||||||
setAvatar({ ...avatars[avatarSelected.id], profileList: newListProfile, profileSelect: profileId - 1 })
|
setAvatar({ ...avatars[avatarSelected.ID], profileList: newListProfile, profileSelect: profileId - 1 })
|
||||||
toast.success(`Profile ${avatars[avatarSelected.id].profileList[profileId].profile_name} deleted successfully`)
|
toast.success(`Profile ${avatars[avatarSelected.ID].profileList[profileId].profile_name} deleted successfully`)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -219,28 +218,26 @@ export default function ActionBar() {
|
|||||||
<div className="flex flex-wrap items-center gap-2 ">
|
<div className="flex flex-wrap items-center gap-2 ">
|
||||||
<div className="flex flex-wrap items-center h-full opacity-80 lg:hover:opacity-100 cursor-pointer text-base md:text-lg lg:text-xl">
|
<div className="flex flex-wrap items-center h-full opacity-80 lg:hover:opacity-100 cursor-pointer text-base md:text-lg lg:text-xl">
|
||||||
{avatarSelected && (
|
{avatarSelected && (
|
||||||
<div className="flex items-center justify-start h-full w-full">
|
<div className="flex flex-wrap items-center justify-start h-full w-full">
|
||||||
<Image
|
<Image
|
||||||
src={`/icon/${avatarSelected.damageType.toLowerCase()}.webp`}
|
src={`${process.env.CDN_URL}/${damageType?.[avatarSelected?.DamageType]?.Icon}`}
|
||||||
alt={'fire'}
|
alt={'damage type'}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
className="h-10 w-10 object-contain"
|
className="h-10 w-10 object-contain"
|
||||||
width={100}
|
width={100}
|
||||||
height={100}
|
height={100}
|
||||||
/>
|
/>
|
||||||
<p className="text-center font-bold text-xl">
|
<p className="text-center font-bold text-lg">
|
||||||
{transI18n(avatarSelected.baseType.toLowerCase())}
|
{transI18n(avatarSelected.BaseType.toLowerCase())}
|
||||||
</p>
|
</p>
|
||||||
<div className="text-center font-bold text-xl">{" / "}</div>
|
<div className="text-center font-bold text-lg">{" / "}</div>
|
||||||
<ParseText
|
<ParseText
|
||||||
locale={locale}
|
locale={locale}
|
||||||
text={getNameChar(locale, transI18n, avatarSelected).toWellFormed()}
|
text={getNameChar(locale, transI18n, avatarSelected).toWellFormed()}
|
||||||
className={"font-bold text-xl"}
|
className={"font-bold text-lg"}
|
||||||
/>
|
/>
|
||||||
{avatarSelected?.id && (
|
<div className="text-center italic text-sm ml-2"> {`(${avatarSelected.ID})`}</div>
|
||||||
<div className="text-center italic text-sm ml-2"> {`(${avatarSelected.id})`}</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -315,7 +312,7 @@ export default function ActionBar() {
|
|||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsOpenCopy(true)
|
setIsOpenCopy(true)
|
||||||
setListCopyAvatar(listRawAvatar)
|
setResetData(baseType, damageType)
|
||||||
handleShow("copy_profile_modal")
|
handleShow("copy_profile_modal")
|
||||||
}}
|
}}
|
||||||
className="btn btn-ghost flex justify-start px-3 py-2 h-full w-full hover:bg-base-200 cursor-pointer text-primary z-20"
|
className="btn btn-ghost flex justify-start px-3 py-2 h-full w-full hover:bg-base-200 cursor-pointer text-primary z-20"
|
||||||
|
|||||||
@@ -1,32 +1,57 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import Image from "next/image"
|
import Image from "next/image"
|
||||||
import { useEffect } from "react"
|
import { useMemo } from "react"
|
||||||
import CharacterCard from "../card/characterCard"
|
import CharacterCard from "../card/characterCard"
|
||||||
import useLocaleStore from "@/stores/localeStore"
|
import useLocaleStore from "@/stores/localeStore"
|
||||||
import useAvatarStore from "@/stores/avatarStore"
|
|
||||||
import { useTranslations } from "next-intl"
|
import { useTranslations } from "next-intl"
|
||||||
|
import useDetailDataStore from "@/stores/detailDataStore"
|
||||||
|
import useCurrentDataStore from '@/stores/currentDataStore';
|
||||||
|
import { calcRarity, getNameChar } from "@/helper"
|
||||||
|
|
||||||
export default function AvatarBar({ onClose }: { onClose?: () => void }) {
|
export default function AvatarBar({ onClose }: { onClose?: () => void }) {
|
||||||
const {
|
const {
|
||||||
listAvatar,
|
avatarSearch,
|
||||||
|
mapAvatarElementActive,
|
||||||
|
mapAvatarPathActive,
|
||||||
|
setAvatarSearch,
|
||||||
setAvatarSelected,
|
setAvatarSelected,
|
||||||
setSkillSelected,
|
setMapAvatarElementActive,
|
||||||
setFilter,
|
setMapAvatarPathActive,
|
||||||
filter,
|
setSkillIDSelected,
|
||||||
listElement,
|
} = useCurrentDataStore()
|
||||||
listPath,
|
const { mapAvatar, baseType, damageType } = useDetailDataStore()
|
||||||
setListElement,
|
|
||||||
setListPath
|
|
||||||
} = useAvatarStore()
|
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
const { locale } = useLocaleStore()
|
const {locale} = useLocaleStore()
|
||||||
|
|
||||||
|
const listAvatar = useMemo(() => {
|
||||||
|
if (!mapAvatar || !locale || !transI18n) return []
|
||||||
|
|
||||||
useEffect(() => {
|
let list = Object.values(mapAvatar)
|
||||||
setFilter({ ...filter, locale: locale, element: Object.keys(listElement).filter((key) => listElement[key]), path: Object.keys(listPath).filter((key) => listPath[key]) })
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
if (avatarSearch) {
|
||||||
}, [locale, listElement, listPath])
|
list = list.filter(item =>
|
||||||
|
getNameChar(locale, transI18n, item)
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(avatarSearch.toLowerCase())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const allElementFalse = !Object.values(mapAvatarElementActive).some(v => v)
|
||||||
|
const allPathFalse = !Object.values(mapAvatarPathActive).some(v => v)
|
||||||
|
|
||||||
|
list = list.filter(item =>
|
||||||
|
(allElementFalse || mapAvatarElementActive[item.DamageType]) &&
|
||||||
|
(allPathFalse || mapAvatarPathActive[item.BaseType])
|
||||||
|
)
|
||||||
|
|
||||||
|
list.sort((a, b) => {
|
||||||
|
const r = calcRarity(b.Rarity) - calcRarity(a.Rarity)
|
||||||
|
if (r !== 0) return r
|
||||||
|
return a.ID - b.ID
|
||||||
|
})
|
||||||
|
|
||||||
|
return list
|
||||||
|
}, [mapAvatar, mapAvatarElementActive, mapAvatarPathActive, avatarSearch, locale, transI18n])
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -39,23 +64,23 @@ export default function AvatarBar({ onClose }: { onClose?: () => void }) {
|
|||||||
<input type="text"
|
<input type="text"
|
||||||
placeholder={transI18n("placeholderCharacter")}
|
placeholder={transI18n("placeholderCharacter")}
|
||||||
className="input input-bordered input-primary w-full"
|
className="input input-bordered input-primary w-full"
|
||||||
value={filter.name}
|
value={avatarSearch}
|
||||||
onChange={(e) => setFilter({ ...filter, name: e.target.value, locale: locale })}
|
onChange={(e) => setAvatarSearch(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-7 sm:grid-cols-4 lg:grid-cols-7 mb-1 mx-1 gap-2 w-full max-h-[17vh] min-h-[5vh] overflow-y-auto">
|
<div className="grid grid-cols-7 sm:grid-cols-4 lg:grid-cols-7 mb-1 mx-1 gap-2 w-full max-h-[17vh] min-h-[5vh] overflow-y-auto">
|
||||||
{Object.keys(listElement).map((key, index) => (
|
{Object.entries(damageType).map(([key, value]) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={key}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setListElement({ ...listElement, [key]: !listElement[key] })
|
setMapAvatarElementActive({ ...mapAvatarElementActive, [key]: !mapAvatarElementActive[key] })
|
||||||
}}
|
}}
|
||||||
className="hover:bg-gray-600 grid items-center justify-items-center cursor-pointer rounded-md shadow-lg"
|
className="hover:bg-gray-600 grid items-center justify-items-center cursor-pointer rounded-md shadow-lg"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: listElement[key] ? "#374151" : "#6B7280"
|
backgroundColor: mapAvatarElementActive[key] ? "#374151" : "#6B7280"
|
||||||
}}>
|
}}>
|
||||||
<Image
|
<Image
|
||||||
src={`/icon/${key}.webp`}
|
src={`${process.env.CDN_URL}/${value.Icon}`}
|
||||||
alt={key}
|
alt={key}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
@@ -67,20 +92,20 @@ export default function AvatarBar({ onClose }: { onClose?: () => void }) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-9 sm:grid-cols-5 lg:grid-cols-9 mb-1 mx-1 gap-2 overflow-y-auto w-full max-h-[17vh] min-h-[5vh]">
|
<div className="grid grid-cols-9 sm:grid-cols-5 lg:grid-cols-9 mb-1 mx-1 gap-2 overflow-y-auto w-full max-h-[17vh] min-h-[5vh]">
|
||||||
{Object.keys(listPath).map((key, index) => (
|
{Object.entries(baseType).map(([key, value]) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={key}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setListPath({ ...listPath, [key]: !listPath[key] })
|
setMapAvatarPathActive({ ...mapAvatarPathActive, [key]: !mapAvatarPathActive[key] })
|
||||||
}}
|
}}
|
||||||
className="hover:bg-gray-600 grid items-center justify-items-center rounded-md shadow-lg cursor-pointer"
|
className="hover:bg-gray-600 grid items-center justify-items-center rounded-md shadow-lg cursor-pointer"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: listPath[key] ? "#374151" : "#6B7280"
|
backgroundColor: mapAvatarPathActive[key] ? "#374151" : "#6B7280"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
src={`/icon/${key}.webp`}
|
src={`${process.env.CDN_URL}/${value.Icon}`}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
alt={key}
|
alt={key}
|
||||||
@@ -98,7 +123,7 @@ export default function AvatarBar({ onClose }: { onClose?: () => void }) {
|
|||||||
{listAvatar.map((item, index) => (
|
{listAvatar.map((item, index) => (
|
||||||
<div key={index} onClick={() => {
|
<div key={index} onClick={() => {
|
||||||
setAvatarSelected(item);
|
setAvatarSelected(item);
|
||||||
setSkillSelected(null)
|
setSkillIDSelected(null)
|
||||||
if (onClose) onClose()
|
if (onClose) onClose()
|
||||||
}}>
|
}}>
|
||||||
<CharacterCard data={item} />
|
<CharacterCard data={item} />
|
||||||
|
|||||||
@@ -1,41 +1,38 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import useAvatarStore from "@/stores/avatarStore"
|
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import { useEffect, useMemo } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import LightconeBar from '../lightconeBar'
|
import LightconeBar from '../lightconeBar'
|
||||||
import useLightconeStore from '@/stores/lightconeStore'
|
|
||||||
import { calcPromotion, calcRarity, replaceByParam } from '@/helper';
|
import { calcPromotion, calcRarity, replaceByParam } from '@/helper';
|
||||||
import { getSkillTree } from '@/helper/getSkillTree';
|
import { getSkillTree } from '@/helper/getSkillTree';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import ParseText from '../parseText';
|
import ParseText from '../parseText';
|
||||||
import useLocaleStore from '@/stores/localeStore';
|
import useLocaleStore from '@/stores/localeStore';
|
||||||
import useModelStore from '@/stores/modelStore';
|
import useModelStore from '@/stores/modelStore';
|
||||||
import useMazeStore from '@/stores/mazeStore';
|
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
|
import useCurrentDataStore from "@/stores/currentDataStore";
|
||||||
|
import useDetailDataStore from "@/stores/detailDataStore";
|
||||||
|
import { getLocaleName } from "@/helper/getName";
|
||||||
export default function AvatarInfo() {
|
export default function AvatarInfo() {
|
||||||
const { avatarSelected, mapAvatarInfo } = useAvatarStore()
|
const { avatarSelected, setResetDataLightcone } = useCurrentDataStore()
|
||||||
const { Technique } = useMazeStore()
|
|
||||||
const { avatars, setAvatars, setAvatar } = useUserDataStore()
|
const { avatars, setAvatars, setAvatar } = useUserDataStore()
|
||||||
const { isOpenLightcone, setIsOpenLightcone } = useModelStore()
|
const { isOpenLightcone, setIsOpenLightcone } = useModelStore()
|
||||||
const { listLightcone, mapLightconeInfo, setDefaultFilter } = useLightconeStore()
|
const { mapLightCone, baseType } = useDetailDataStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
const { locale } = useLocaleStore();
|
const { locale } = useLocaleStore();
|
||||||
|
|
||||||
const lightcone = useMemo(() => {
|
const lightcone = useMemo(() => {
|
||||||
if (!avatarSelected) return null;
|
if (!avatarSelected) return null;
|
||||||
const avatar = avatars[avatarSelected.id];
|
const avatar = avatars[avatarSelected.ID];
|
||||||
return avatar?.profileList[avatar.profileSelect]?.lightcone || null;
|
return avatar?.profileList[avatar.profileSelect]?.lightcone || null;
|
||||||
}, [avatarSelected, avatars]);
|
}, [avatarSelected, avatars]);
|
||||||
|
|
||||||
const lightconeDetail = useMemo(() => {
|
const lightconeDetail = useMemo(() => {
|
||||||
if (!lightcone) return null;
|
if (!mapLightCone || !lightcone?.item_id) return null;
|
||||||
return listLightcone.find((item) => Number(item.id) === Number(lightcone.item_id)) || null;
|
return mapLightCone?.[lightcone.item_id.toString()] || null;
|
||||||
}, [lightcone, listLightcone]);
|
}, [lightcone, mapLightCone]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleShow = (modalId: string) => {
|
const handleShow = (modalId: string) => {
|
||||||
const modal = document.getElementById(modalId) as HTMLDialogElement | null;
|
const modal = document.getElementById(modalId) as HTMLDialogElement | null;
|
||||||
@@ -74,7 +71,7 @@ export default function AvatarInfo() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-base-100 max-h-[77vh] min-h-[50vh] overflow-y-scroll overflow-x-hidden">
|
<div className="bg-base-100 max-h-[77vh] min-h-[50vh] overflow-y-scroll overflow-x-hidden">
|
||||||
{avatarSelected && avatars[avatarSelected?.id || ""] && (
|
{avatarSelected && avatars[avatarSelected?.ID.toString() || ""] && (
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 w-full">
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4 w-full">
|
||||||
<div className="m-2 min-h-96">
|
<div className="m-2 min-h-96">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
@@ -99,25 +96,25 @@ export default function AvatarInfo() {
|
|||||||
<div className="form-control">
|
<div className="form-control">
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-medium">{transI18n("characterLevel")}</span>
|
<span className="label-text font-medium">{transI18n("characterLevel")}</span>
|
||||||
<span className="label-text-alt text-info font-mono">{avatars[avatarSelected?.id || ""]?.level}/80</span>
|
<span className="label-text-alt text-info font-mono">{avatars[avatarSelected?.ID.toString() || ""]?.level}/80</span>
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
min="1"
|
min="1"
|
||||||
max="80"
|
max="80"
|
||||||
value={avatars[avatarSelected?.id || ""]?.level}
|
value={avatars[avatarSelected?.ID.toString() || ""]?.level}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const newLevel = Math.min(80, Math.max(1, parseInt(e.target.value) || 1));
|
const newLevel = Math.min(80, Math.max(1, parseInt(e.target.value) || 1));
|
||||||
|
|
||||||
setAvatars({ ...avatars, [avatarSelected?.id || ""]: { ...avatars[avatarSelected?.id || ""], level: newLevel, promotion: calcPromotion(newLevel) } });
|
setAvatars({ ...avatars, [avatarSelected?.ID.toString() || ""]: { ...avatars[avatarSelected?.ID.toString() || ""], level: newLevel, promotion: calcPromotion(newLevel) } });
|
||||||
}}
|
}}
|
||||||
className="input input-bordered w-full pr-16 font-mono"
|
className="input input-bordered w-full pr-16 font-mono"
|
||||||
placeholder={transI18n("placeholderLevel")}
|
placeholder={transI18n("placeholderLevel")}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setAvatars({ ...avatars, [avatarSelected?.id || ""]: { ...avatars[avatarSelected?.id || ""], level: 80, promotion: calcPromotion(80) } });
|
setAvatars({ ...avatars, [avatarSelected?.ID.toString() || ""]: { ...avatars[avatarSelected?.ID.toString() || ""], level: 80, promotion: calcPromotion(80) } });
|
||||||
}}
|
}}
|
||||||
className="absolute right-3 top-1/2 -translate-y-1/2 text-base-content/60 cursor-pointer">
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-base-content/60 cursor-pointer">
|
||||||
<span className="text-sm">{transI18n("max")}</span>
|
<span className="text-sm">{transI18n("max")}</span>
|
||||||
@@ -128,10 +125,10 @@ export default function AvatarInfo() {
|
|||||||
type="range"
|
type="range"
|
||||||
min="1"
|
min="1"
|
||||||
max="80"
|
max="80"
|
||||||
value={avatars[avatarSelected?.id || ""]?.level}
|
value={avatars[avatarSelected?.ID.toString() || ""]?.level}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const newLevel = Math.min(80, Math.max(1, parseInt(e.target.value) || 1));
|
const newLevel = Math.min(80, Math.max(1, parseInt(e.target.value) || 1));
|
||||||
setAvatars({ ...avatars, [avatarSelected?.id || ""]: { ...avatars[avatarSelected?.id || ""], level: newLevel, promotion: calcPromotion(newLevel) } });
|
setAvatars({ ...avatars, [avatarSelected?.ID.toString() || ""]: { ...avatars[avatarSelected?.ID.toString() || ""], level: newLevel, promotion: calcPromotion(newLevel) } });
|
||||||
}}
|
}}
|
||||||
className="range range-info range-sm w-full"
|
className="range range-info range-sm w-full"
|
||||||
/>
|
/>
|
||||||
@@ -150,33 +147,33 @@ export default function AvatarInfo() {
|
|||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-medium">{transI18n("currentEnergy")}</span>
|
<span className="label-text font-medium">{transI18n("currentEnergy")}</span>
|
||||||
<span className="label-text text-warning font-mono">
|
<span className="label-text text-warning font-mono">
|
||||||
{Math.round(avatars[avatarSelected?.id || ""]?.sp_value)}/{avatars[avatarSelected?.id || ""]?.sp_max}
|
{Math.round(avatars[avatarSelected?.ID.toString() || ""]?.sp_value)}/{avatars[avatarSelected?.ID.toString() || ""]?.sp_max}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
min="0"
|
min="0"
|
||||||
max={avatars[avatarSelected?.id || ""]?.sp_max}
|
max={avatars[avatarSelected?.ID.toString() || ""]?.sp_max}
|
||||||
value={avatars[avatarSelected?.id || ""]?.sp_value}
|
value={avatars[avatarSelected?.ID.toString() || ""]?.sp_value}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
if (!avatars[avatarSelected?.id || ""]?.can_change_sp) return
|
if (!avatars[avatarSelected?.ID.toString() || ""]?.can_change_sp) return
|
||||||
const newSpValue = Math.min(avatars[avatarSelected?.id || ""]?.sp_max, Math.max(0, parseInt(e.target.value) || 0));
|
const newSpValue = Math.min(avatars[avatarSelected?.ID.toString() || ""]?.sp_max, Math.max(0, parseInt(e.target.value) || 0));
|
||||||
setAvatars({ ...avatars, [avatarSelected?.id || ""]: { ...avatars[avatarSelected?.id || ""], sp_value: newSpValue } });
|
setAvatars({ ...avatars, [avatarSelected?.ID.toString() || ""]: { ...avatars[avatarSelected?.ID.toString() || ""], sp_value: newSpValue } });
|
||||||
}}
|
}}
|
||||||
className="range range-warning range-sm w-full"
|
className="range range-warning range-sm w-full"
|
||||||
/>
|
/>
|
||||||
<div className="flex justify-between text-sm text-base-content/60 mt-1">
|
<div className="flex justify-between text-sm text-base-content/60 mt-1">
|
||||||
<span>0%</span>
|
<span>0%</span>
|
||||||
<span className="font-mono text-warning">{((avatars[avatarSelected?.id || ""]?.sp_value / avatars[avatarSelected?.id || ""]?.sp_max) * 100).toFixed(1)}%</span>
|
<span className="font-mono text-warning">{((avatars[avatarSelected?.ID.toString() || ""]?.sp_value / avatars[avatarSelected?.ID.toString() || ""]?.sp_max) * 100).toFixed(1)}%</span>
|
||||||
<span>100%</span>
|
<span>100%</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-3">
|
<div className="mt-3">
|
||||||
<button
|
<button
|
||||||
className="btn btn-sm btn-outline btn-warning"
|
className="btn btn-sm btn-outline btn-warning"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!avatars[avatarSelected?.id || ""]?.can_change_sp) return
|
if (!avatars[avatarSelected?.ID.toString() || ""]?.can_change_sp) return
|
||||||
const newSpValue = Math.ceil(avatars[avatarSelected?.id || ""]?.sp_max / 2);
|
const newSpValue = Math.ceil(avatars[avatarSelected?.ID.toString() || ""]?.sp_max / 2);
|
||||||
const newAvatar = { ...avatars[avatarSelected?.id || ""], sp_value: newSpValue }
|
const newAvatar = { ...avatars[avatarSelected?.ID.toString() || ""], sp_value: newSpValue }
|
||||||
setAvatar(newAvatar)
|
setAvatar(newAvatar)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -203,11 +200,11 @@ export default function AvatarInfo() {
|
|||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={avatars[avatarSelected?.id || ""]?.techniques.length > 0}
|
checked={(!avatarSelected || avatarSelected?.MazeBuff?.length > 0)}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
if (!Technique[avatarSelected?.id || ""] || Technique[avatarSelected?.id || ""]?.maze_buff.length === 0) return
|
if (!avatarSelected || avatarSelected?.MazeBuff?.length > 0) return
|
||||||
const techniques = e.target.checked ? Technique[avatarSelected?.id || ""]?.maze_buff : [];
|
const techniques = e.target.checked ? avatarSelected?.MazeBuff : [];
|
||||||
const newAvatar = { ...avatars[avatarSelected?.id || ""], techniques };
|
const newAvatar = { ...avatars[avatarSelected?.ID.toString() || ""], techniques: techniques };
|
||||||
setAvatar(newAvatar);
|
setAvatar(newAvatar);
|
||||||
}}
|
}}
|
||||||
className="toggle toggle-accent"
|
className="toggle toggle-accent"
|
||||||
@@ -220,7 +217,7 @@ export default function AvatarInfo() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Enhancement Selection */}
|
{/* Enhancement Selection */}
|
||||||
{Object.entries(mapAvatarInfo[avatarSelected?.id || ""]?.Enhanced || {}).length > 0 && (
|
{avatarSelected?.Enhanced && (
|
||||||
<div className="bg-base-100 rounded-xl p-6 border border-base-content/10">
|
<div className="bg-base-100 rounded-xl p-6 border border-base-content/10">
|
||||||
<h4 className="text-lg font-semibold mb-4 flex items-center gap-2">
|
<h4 className="text-lg font-semibold mb-4 flex items-center gap-2">
|
||||||
<svg className="w-5 h-5 text-secondary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5 text-secondary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
@@ -233,16 +230,16 @@ export default function AvatarInfo() {
|
|||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-medium">{transI18n("enhancementLevel")}</span>
|
<span className="label-text font-medium">{transI18n("enhancementLevel")}</span>
|
||||||
<span className="label-text-alt text-secondary font-mono">
|
<span className="label-text-alt text-secondary font-mono">
|
||||||
{avatars[avatarSelected?.id || ""]?.enhanced || transI18n("origin")}
|
{avatars[avatarSelected?.ID.toString() || ""]?.enhanced || transI18n("origin")}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
value={avatars[avatarSelected?.id || ""]?.enhanced || ""}
|
value={avatars[avatarSelected?.ID.toString() || ""]?.enhanced || ""}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const newAvatar = avatars[avatarSelected?.id || ""]
|
const newAvatar = avatars[avatarSelected?.ID.toString() || ""]
|
||||||
if (newAvatar) {
|
if (newAvatar) {
|
||||||
newAvatar.enhanced = e.target.value
|
newAvatar.enhanced = e.target.value
|
||||||
const skillTree = getSkillTree(e.target.value)
|
const skillTree = getSkillTree(avatarSelected, e.target.value)
|
||||||
if (skillTree) {
|
if (skillTree) {
|
||||||
newAvatar.data.skills = skillTree
|
newAvatar.data.skills = skillTree
|
||||||
}
|
}
|
||||||
@@ -252,7 +249,7 @@ export default function AvatarInfo() {
|
|||||||
className="select select-bordered select-secondary"
|
className="select select-bordered select-secondary"
|
||||||
>
|
>
|
||||||
<option value="">{transI18n("origin")}</option>
|
<option value="">{transI18n("origin")}</option>
|
||||||
{Object.keys(mapAvatarInfo[avatarSelected?.id || ""]?.Enhanced || {}).map((key) => (
|
{Object.keys(avatarSelected?.Enhanced || {}).map((key) => (
|
||||||
<option key={key} value={key}>
|
<option key={key} value={key}>
|
||||||
{key}
|
{key}
|
||||||
</option>
|
</option>
|
||||||
@@ -308,7 +305,7 @@ export default function AvatarInfo() {
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const newLightconeLevel = Math.min(80, Math.max(1, parseInt(e.target.value) || 1))
|
const newLightconeLevel = Math.min(80, Math.max(1, parseInt(e.target.value) || 1))
|
||||||
const newLightcone = { ...lightcone, level: newLightconeLevel, promotion: calcPromotion(newLightconeLevel) }
|
const newLightcone = { ...lightcone, level: newLightconeLevel, promotion: calcPromotion(newLightconeLevel) }
|
||||||
const newAvatar = { ...avatars[avatarSelected.id] }
|
const newAvatar = { ...avatars[avatarSelected.ID] }
|
||||||
newAvatar.profileList[newAvatar.profileSelect].lightcone = newLightcone
|
newAvatar.profileList[newAvatar.profileSelect].lightcone = newLightcone
|
||||||
setAvatar(newAvatar)
|
setAvatar(newAvatar)
|
||||||
}}
|
}}
|
||||||
@@ -318,7 +315,7 @@ export default function AvatarInfo() {
|
|||||||
<div
|
<div
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const newLightcone = { ...lightcone, level: 80, promotion: calcPromotion(80) }
|
const newLightcone = { ...lightcone, level: 80, promotion: calcPromotion(80) }
|
||||||
const newAvatar = { ...avatars[avatarSelected.id] }
|
const newAvatar = { ...avatars[avatarSelected.ID] }
|
||||||
newAvatar.profileList[newAvatar.profileSelect].lightcone = newLightcone
|
newAvatar.profileList[newAvatar.profileSelect].lightcone = newLightcone
|
||||||
setAvatar(newAvatar)
|
setAvatar(newAvatar)
|
||||||
}}
|
}}
|
||||||
@@ -335,7 +332,7 @@ export default function AvatarInfo() {
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const newLightconeLevel = Math.min(80, Math.max(1, parseInt(e.target.value) || 1))
|
const newLightconeLevel = Math.min(80, Math.max(1, parseInt(e.target.value) || 1))
|
||||||
const newLightcone = { ...lightcone, level: newLightconeLevel, promotion: calcPromotion(newLightconeLevel) }
|
const newLightcone = { ...lightcone, level: newLightconeLevel, promotion: calcPromotion(newLightconeLevel) }
|
||||||
const newAvatar = { ...avatars[avatarSelected.id] }
|
const newAvatar = { ...avatars[avatarSelected.ID] }
|
||||||
newAvatar.profileList[newAvatar.profileSelect].lightcone = newLightcone
|
newAvatar.profileList[newAvatar.profileSelect].lightcone = newLightcone
|
||||||
setAvatar(newAvatar)
|
setAvatar(newAvatar)
|
||||||
}}
|
}}
|
||||||
@@ -360,7 +357,7 @@ export default function AvatarInfo() {
|
|||||||
key={r}
|
key={r}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const newLightcone = { ...lightcone, rank: r }
|
const newLightcone = { ...lightcone, rank: r }
|
||||||
const newAvatar = { ...avatars[avatarSelected.id] }
|
const newAvatar = { ...avatars[avatarSelected.ID] }
|
||||||
newAvatar.profileList[newAvatar.profileSelect].lightcone = newLightcone
|
newAvatar.profileList[newAvatar.profileSelect].lightcone = newLightcone
|
||||||
setAvatar(newAvatar)
|
setAvatar(newAvatar)
|
||||||
}}
|
}}
|
||||||
@@ -391,8 +388,7 @@ export default function AvatarInfo() {
|
|||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (avatarSelected) {
|
if (avatarSelected) {
|
||||||
const newDefaultFilter = { path: [avatarSelected.baseType.toLowerCase()], rarity: [] }
|
setResetDataLightcone(avatarSelected, baseType)
|
||||||
setDefaultFilter(newDefaultFilter)
|
|
||||||
handleShow("action_detail_modal")
|
handleShow("action_detail_modal")
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@@ -405,7 +401,7 @@ export default function AvatarInfo() {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const newAvatar = { ...avatars[avatarSelected.id] }
|
const newAvatar = { ...avatars[avatarSelected.ID] }
|
||||||
newAvatar.profileList[newAvatar.profileSelect].lightcone = null
|
newAvatar.profileList[newAvatar.profileSelect].lightcone = null
|
||||||
setAvatar(newAvatar)
|
setAvatar(newAvatar)
|
||||||
}}
|
}}
|
||||||
@@ -428,7 +424,7 @@ export default function AvatarInfo() {
|
|||||||
width={904}
|
width={904}
|
||||||
height={1260}
|
height={1260}
|
||||||
priority
|
priority
|
||||||
src={`${process.env.CDN_URL}/${lightconeDetail.image}`}
|
src={`${process.env.CDN_URL}/${lightconeDetail?.Image?.ImagePath}`}
|
||||||
className="w-full h-full rounded-lg object-cover shadow-lg"
|
className="w-full h-full rounded-lg object-cover shadow-lg"
|
||||||
alt="Lightcone"
|
alt="Lightcone"
|
||||||
/>
|
/>
|
||||||
@@ -438,20 +434,20 @@ export default function AvatarInfo() {
|
|||||||
{/* Lightcone Info & Controls */}
|
{/* Lightcone Info & Controls */}
|
||||||
<div className="lg:col-span-2 space-y-6">
|
<div className="lg:col-span-2 space-y-6">
|
||||||
{/* Basic Info */}
|
{/* Basic Info */}
|
||||||
{mapLightconeInfo[lightcone.item_id] && (
|
{lightconeDetail && (
|
||||||
<div className="bg-base-300 rounded-xl p-6 border border-base-content/10">
|
<div className="bg-base-300 rounded-xl p-6 border border-base-content/10">
|
||||||
<div className="flex flex-wrap items-center gap-3 mb-4">
|
<div className="flex flex-wrap items-center gap-3 mb-4">
|
||||||
<h3 className="text-2xl font-bold text-base-content">
|
<h3 className="text-2xl font-bold text-base-content">
|
||||||
<ParseText
|
<ParseText
|
||||||
locale={locale}
|
locale={locale}
|
||||||
text={mapLightconeInfo[lightcone.item_id].Name}
|
text={getLocaleName(locale, lightconeDetail.Name)}
|
||||||
/>
|
/>
|
||||||
</h3>
|
</h3>
|
||||||
<div className="badge badge-outline badge-lg">
|
<div className="badge badge-outline badge-lg">
|
||||||
{transI18n(mapLightconeInfo[lightcone.item_id].BaseType.toLowerCase())}
|
{transI18n(lightconeDetail.BaseType.toLowerCase())}
|
||||||
</div>
|
</div>
|
||||||
<div className="badge badge-outline badge-lg">
|
<div className="badge badge-outline badge-lg">
|
||||||
{calcRarity(mapLightconeInfo[lightcone.item_id].Rarity) + "⭐"}
|
{calcRarity(lightconeDetail.Rarity) + "⭐"}
|
||||||
</div>
|
</div>
|
||||||
<div className="badge badge-outline badge-lg">
|
<div className="badge badge-outline badge-lg">
|
||||||
{"id: " + lightcone.item_id}
|
{"id: " + lightcone.item_id}
|
||||||
@@ -463,8 +459,8 @@ export default function AvatarInfo() {
|
|||||||
className="text-base-content/80 leading-relaxed"
|
className="text-base-content/80 leading-relaxed"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: replaceByParam(
|
__html: replaceByParam(
|
||||||
mapLightconeInfo[lightcone.item_id].Refinements.Desc,
|
getLocaleName(locale, lightconeDetail.Skills.Desc),
|
||||||
mapLightconeInfo[lightcone.item_id].Refinements.Level[lightcone.rank.toString()]?.ParamList || []
|
lightconeDetail.Skills.Level[lightcone.rank.toString()]?.Param || []
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -491,8 +487,7 @@ export default function AvatarInfo() {
|
|||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (avatarSelected) {
|
if (avatarSelected) {
|
||||||
const newDefaultFilter = { path: [avatarSelected.baseType.toLowerCase()], rarity: [] }
|
setResetDataLightcone(avatarSelected, baseType)
|
||||||
setDefaultFilter(newDefaultFilter)
|
|
||||||
handleShow("action_detail_modal")
|
handleShow("action_detail_modal")
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -2,18 +2,20 @@
|
|||||||
|
|
||||||
import { getNameChar } from '@/helper';
|
import { getNameChar } from '@/helper';
|
||||||
import useLocaleStore from '@/stores/localeStore';
|
import useLocaleStore from '@/stores/localeStore';
|
||||||
import { CharacterBasic } from '@/types';
|
import { AvatarDetail } from '@/types';
|
||||||
import ParseText from '../parseText';
|
import ParseText from '../parseText';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
|
import useDetailDataStore from '@/stores/detailDataStore';
|
||||||
|
|
||||||
interface CharacterCardProps {
|
interface CharacterCardProps {
|
||||||
data: CharacterBasic
|
data: AvatarDetail
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function CharacterCard({ data }: CharacterCardProps) {
|
export default function CharacterCard({ data }: CharacterCardProps) {
|
||||||
const { locale } = useLocaleStore();
|
const { locale } = useLocaleStore();
|
||||||
const transI18n = useTranslations("DataPage");
|
const transI18n = useTranslations("DataPage");
|
||||||
|
const { baseType, damageType } = useDetailDataStore()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
@@ -23,7 +25,7 @@ export default function CharacterCard({ data }: CharacterCardProps) {
|
|||||||
hover:scale-105 cursor-pointer min-h-45 sm:min-h-45 md:min-h-52.5 lg:min-h-55 xl:min-h-60 2xl:min-h-85"
|
hover:scale-105 cursor-pointer min-h-45 sm:min-h-45 md:min-h-52.5 lg:min-h-55 xl:min-h-60 2xl:min-h-85"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`w-full rounded-md bg-linear-to-br ${data.rank === "CombatPowerAvatarRarityType5"
|
className={`w-full rounded-md bg-linear-to-br ${data.Rarity === "CombatPowerAvatarRarityType5"
|
||||||
? "from-yellow-400 via-yellow-600/70 to-yellow-800/50"
|
? "from-yellow-400 via-yellow-600/70 to-yellow-800/50"
|
||||||
: "from-purple-400 via-purple-600/70 to-purple-800/50"
|
: "from-purple-400 via-purple-600/70 to-purple-800/50"
|
||||||
}`}
|
}`}
|
||||||
@@ -35,7 +37,7 @@ export default function CharacterCard({ data }: CharacterCardProps) {
|
|||||||
height={512}
|
height={512}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/${data.icon}`}
|
src={`${process.env.CDN_URL}/${data.Image.AvatarIconPath}`}
|
||||||
priority={true}
|
priority={true}
|
||||||
className="rounded-md w-full h-full object-contain"
|
className="rounded-md w-full h-full object-contain"
|
||||||
alt="ALT"
|
alt="ALT"
|
||||||
@@ -45,18 +47,18 @@ export default function CharacterCard({ data }: CharacterCardProps) {
|
|||||||
height={32}
|
height={32}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`/icon/${data.damageType.toLowerCase()}.webp`}
|
src={`${process.env.CDN_URL}/${damageType?.[data.DamageType].Icon}`}
|
||||||
className="absolute top-0 left-0 w-6 h-6 rounded-full"
|
className="absolute top-0 left-0 w-6 h-6 rounded-full"
|
||||||
alt={data.damageType.toLowerCase()}
|
alt={data.DamageType.toLowerCase()}
|
||||||
/>
|
/>
|
||||||
<Image
|
<Image
|
||||||
width={32}
|
width={32}
|
||||||
height={32}
|
height={32}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`/icon/${data.baseType.toLowerCase()}.webp`}
|
src={`${process.env.CDN_URL}/${baseType?.[data.BaseType].Icon}`}
|
||||||
className="absolute top-0 right-0 w-6 h-6 rounded-full"
|
className="absolute top-0 right-0 w-6 h-6 rounded-full"
|
||||||
alt={data.baseType.toLowerCase()}
|
alt={data.BaseType.toLowerCase()}
|
||||||
style={{
|
style={{
|
||||||
boxShadow: "inset 0 0 8px 4px #9CA3AF"
|
boxShadow: "inset 0 0 8px 4px #9CA3AF"
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -3,16 +3,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { CharacterInfoCardType } from '@/types';
|
import { CharacterInfoCardType } from '@/types';
|
||||||
import useLocaleStore from '@/stores/localeStore';
|
import useLocaleStore from '@/stores/localeStore';
|
||||||
import useAvatarStore from '@/stores/avatarStore';
|
|
||||||
import useLightconeStore from '@/stores/lightconeStore';
|
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import ParseText from '../parseText';
|
import ParseText from '../parseText';
|
||||||
|
import useDetailDataStore from '@/stores/detailDataStore';
|
||||||
|
import { getLocaleName } from '@/helper/getName';
|
||||||
|
|
||||||
export default function CharacterInfoCard({ character, selectedCharacters, onCharacterToggle }: { character: CharacterInfoCardType, selectedCharacters: CharacterInfoCardType[], onCharacterToggle: (characterId: CharacterInfoCardType) => void }) {
|
export default function CharacterInfoCard({ character, selectedCharacters, onCharacterToggle }: { character: CharacterInfoCardType, selectedCharacters: CharacterInfoCardType[], onCharacterToggle: (characterId: CharacterInfoCardType) => void }) {
|
||||||
const isSelected = selectedCharacters.some((selectedCharacter) => selectedCharacter.avatar_id === character.avatar_id);
|
const isSelected = selectedCharacters.some((selectedCharacter) => selectedCharacter.avatar_id === character.avatar_id);
|
||||||
const { mapAvatarInfo } = useAvatarStore();
|
const { mapAvatar, mapLightCone, baseType, damageType } = useDetailDataStore();
|
||||||
const { mapLightconeInfo } = useLightconeStore();
|
|
||||||
const { locale } = useLocaleStore();
|
const { locale } = useLocaleStore();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -29,8 +27,8 @@ export default function CharacterInfoCard({ character, selectedCharacters, onCha
|
|||||||
<div className="relative mb-4">
|
<div className="relative mb-4">
|
||||||
<div className="w-full h-48 rounded-lg overflow-hidden relative">
|
<div className="w-full h-48 rounded-lg overflow-hidden relative">
|
||||||
<Image
|
<Image
|
||||||
src={`${process.env.CDN_URL}/spriteoutput/avatarshopicon/avatar/${character.avatar_id}.png`}
|
src={`${process.env.CDN_URL}/${mapAvatar?.[character.avatar_id.toString()]?.Image?.AvatarIconPath}`}
|
||||||
alt={mapAvatarInfo[character.avatar_id.toString()]?.Name || ""}
|
alt={getLocaleName(locale, mapAvatar?.[character.avatar_id.toString()]?.Name)}
|
||||||
width={376}
|
width={376}
|
||||||
height={512}
|
height={512}
|
||||||
unoptimized
|
unoptimized
|
||||||
@@ -42,18 +40,18 @@ export default function CharacterInfoCard({ character, selectedCharacters, onCha
|
|||||||
height={48}
|
height={48}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`/icon/${mapAvatarInfo[character.avatar_id.toString()]?.DamageType.toLowerCase()}.webp`}
|
src={`${process.env.CDN_URL}/${damageType?.[mapAvatar?.[character.avatar_id.toString()]?.DamageType || ""].Icon}`}
|
||||||
className="absolute top-0 left-0 w-10 h-10 rounded-full"
|
className="absolute top-0 left-0 w-10 h-10 rounded-full"
|
||||||
alt={mapAvatarInfo[character.avatar_id.toString()]?.DamageType.toLowerCase()}
|
alt={mapAvatar[character.avatar_id.toString()]?.DamageType.toLowerCase()}
|
||||||
/>
|
/>
|
||||||
<Image
|
<Image
|
||||||
width={48}
|
width={48}
|
||||||
height={48}
|
height={48}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`/icon/${mapAvatarInfo[character.avatar_id.toString()]?.BaseType.toLowerCase()}.webp`}
|
src={`${process.env.CDN_URL}/${baseType?.[mapAvatar?.[character.avatar_id.toString()]?.BaseType || ""].Icon}`}
|
||||||
className="absolute top-0 right-0 w-10 h-10 rounded-full"
|
className="absolute top-0 right-0 w-10 h-10 rounded-full"
|
||||||
alt={mapAvatarInfo[character.avatar_id.toString()]?.BaseType.toLowerCase()}
|
alt={mapAvatar[character.avatar_id.toString()]?.BaseType.toLowerCase()}
|
||||||
style={{
|
style={{
|
||||||
boxShadow: "inset 0 0 8px 4px #9CA3AF"
|
boxShadow: "inset 0 0 8px 4px #9CA3AF"
|
||||||
}}
|
}}
|
||||||
@@ -65,7 +63,7 @@ export default function CharacterInfoCard({ character, selectedCharacters, onCha
|
|||||||
<div className="w-full rounded-lg flex items-center justify-center mb-2">
|
<div className="w-full rounded-lg flex items-center justify-center mb-2">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<ParseText className="text-lg font-bold"
|
<ParseText className="text-lg font-bold"
|
||||||
text={mapAvatarInfo[character.avatar_id.toString()]?.Name}
|
text={getLocaleName(locale, mapAvatar[character.avatar_id.toString()]?.Name)}
|
||||||
locale={locale}
|
locale={locale}
|
||||||
/>
|
/>
|
||||||
<div className="text-base mb-1">Lv.{character.level} E{character.rank}</div>
|
<div className="text-base mb-1">Lv.{character.level} E{character.rank}</div>
|
||||||
@@ -101,8 +99,8 @@ export default function CharacterInfoCard({ character, selectedCharacters, onCha
|
|||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/spriteoutput/lightconemaxfigures/${character.lightcone.item_id}.png`}
|
src={`${process.env.CDN_URL}/${mapLightCone?.[character.lightcone.item_id.toString()]?.Image?.ImagePath}`}
|
||||||
alt={mapLightconeInfo[character.lightcone.item_id.toString()]?.Name}
|
alt={getLocaleName(locale, mapLightCone?.[character.lightcone.item_id.toString()]?.Name)}
|
||||||
width={348}
|
width={348}
|
||||||
height={408}
|
height={408}
|
||||||
className="w-full h-full object-contain rounded-lg"
|
className="w-full h-full object-contain rounded-lg"
|
||||||
@@ -113,7 +111,7 @@ export default function CharacterInfoCard({ character, selectedCharacters, onCha
|
|||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<div className="text-lg font-bold">
|
<div className="text-lg font-bold">
|
||||||
<ParseText
|
<ParseText
|
||||||
text={mapLightconeInfo[character.lightcone.item_id.toString()]?.Name}
|
text={getLocaleName(locale, mapLightCone[character.lightcone.item_id.toString()]?.Name)}
|
||||||
locale={locale}
|
locale={locale}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,27 +2,27 @@
|
|||||||
|
|
||||||
import { getLocaleName } from '@/helper';
|
import { getLocaleName } from '@/helper';
|
||||||
import useLocaleStore from '@/stores/localeStore';
|
import useLocaleStore from '@/stores/localeStore';
|
||||||
import { LightConeBasic } from '@/types';
|
|
||||||
import ParseText from '../parseText';
|
import ParseText from '../parseText';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
|
import { LightConeDetail } from '@/types';
|
||||||
|
|
||||||
interface LightconeCardProps {
|
interface LightconeCardProps {
|
||||||
data: LightConeBasic
|
data: LightConeDetail
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function LightconeCard({ data }: LightconeCardProps) {
|
export default function LightconeCard({ data }: LightconeCardProps) {
|
||||||
|
|
||||||
const { locale } = useLocaleStore();
|
const { locale } = useLocaleStore();
|
||||||
const text = getLocaleName(locale, data)
|
const text = getLocaleName(locale, data.Name)
|
||||||
return (
|
return (
|
||||||
<li className="z-10 flex flex-col items-center rounded-md shadow-lg
|
<li className="z-10 flex flex-col items-center rounded-md shadow-lg
|
||||||
bg-linear-to-b from-customStart to-customEnd transform transition-transform duration-300
|
bg-linear-to-b from-customStart to-customEnd transform transition-transform duration-300
|
||||||
hover:scale-105 cursor-pointer min-h-55"
|
hover:scale-105 cursor-pointer min-h-55"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`w-full rounded-md bg-linear-to-br ${data.rank === "CombatPowerLightconeRarity5"
|
className={`w-full rounded-md bg-linear-to-br ${data.Rarity === "CombatPowerLightconeRarity5"
|
||||||
? "from-yellow-400 via-yellow-600/70 to-yellow-800/50"
|
? "from-yellow-400 via-yellow-600/70 to-yellow-800/50"
|
||||||
: data.rank === "CombatPowerLightconeRarity4" ? "from-purple-400 via-purple-600/70 to-purple-800/50" :
|
: data.Rarity === "CombatPowerLightconeRarity4" ? "from-purple-400 via-purple-600/70 to-purple-800/50" :
|
||||||
"from-blue-400 via-blue-600/70 to-blue-800/50"
|
"from-blue-400 via-blue-600/70 to-blue-800/50"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@@ -30,7 +30,7 @@ export default function LightconeCard({ data }: LightconeCardProps) {
|
|||||||
<div className="relative w-full h-full">
|
<div className="relative w-full h-full">
|
||||||
<Image
|
<Image
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
src={`${process.env.CDN_URL}/${data.thumbnail}`}
|
src={`${process.env.CDN_URL}/${data?.Image?.ThumbnailPath}`}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
width={348}
|
width={348}
|
||||||
|
|||||||
@@ -3,14 +3,15 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { AvatarProfileCardType } from '@/types';
|
import { AvatarProfileCardType } from '@/types';
|
||||||
import useLocaleStore from '@/stores/localeStore';
|
import useLocaleStore from '@/stores/localeStore';
|
||||||
import useLightconeStore from '@/stores/lightconeStore';
|
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import ParseText from '../parseText';
|
import ParseText from '../parseText';
|
||||||
|
import useDetailDataStore from '@/stores/detailDataStore';
|
||||||
|
import { getLocaleName } from '@/helper/getName';
|
||||||
|
|
||||||
|
|
||||||
export default function ProfileCard({ profile, selectedProfile, onProfileToggle }: { profile: AvatarProfileCardType, selectedProfile: AvatarProfileCardType[], onProfileToggle: (profileId: AvatarProfileCardType) => void }) {
|
export default function ProfileCard({ profile, selectedProfile, onProfileToggle }: { profile: AvatarProfileCardType, selectedProfile: AvatarProfileCardType[], onProfileToggle: (profileId: AvatarProfileCardType) => void }) {
|
||||||
const isSelected = selectedProfile.some((selectedProfile) => selectedProfile.key === profile.key);
|
const isSelected = selectedProfile.some((selectedProfile) => selectedProfile.key === profile.key);
|
||||||
const { mapLightconeInfo } = useLightconeStore();
|
const { mapLightCone } = useDetailDataStore();
|
||||||
const { locale } = useLocaleStore();
|
const { locale } = useLocaleStore();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -30,7 +31,7 @@ export default function ProfileCard({ profile, selectedProfile, onProfileToggle
|
|||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/spriteoutput/lightconemaxfigures/${profile.lightcone.item_id}.png`}
|
src={`${process.env.CDN_URL}/spriteoutput/lightconemaxfigures/${profile.lightcone.item_id}.png`}
|
||||||
alt={mapLightconeInfo[profile.lightcone.item_id.toString()]?.Name}
|
alt={getLocaleName(locale, mapLightCone[profile.lightcone.item_id.toString()]?.Name)}
|
||||||
width={348}
|
width={348}
|
||||||
height={408}
|
height={408}
|
||||||
className="w-full h-full object-contain rounded-lg"
|
className="w-full h-full object-contain rounded-lg"
|
||||||
@@ -41,7 +42,7 @@ export default function ProfileCard({ profile, selectedProfile, onProfileToggle
|
|||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<div className="text-lg font-bold">
|
<div className="text-lg font-bold">
|
||||||
<ParseText
|
<ParseText
|
||||||
text={mapLightconeInfo[profile.lightcone.item_id.toString()]?.Name}
|
text={getLocaleName(locale, mapLightCone[profile.lightcone.item_id.toString()]?.Name)}
|
||||||
locale={locale}
|
locale={locale}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -59,7 +60,7 @@ export default function ProfileCard({ profile, selectedProfile, onProfileToggle
|
|||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${relic.relic_set_id}_${relic.relic_id.toString()[relic.relic_id.toString().length - 1]}.webp`}
|
src={`${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${relic.relic_set_id}_${relic.relic_id.toString()[relic.relic_id.toString().length - 1]}.png`}
|
||||||
alt="Relic"
|
alt="Relic"
|
||||||
width={124}
|
width={124}
|
||||||
height={124}
|
height={124}
|
||||||
|
|||||||
@@ -1,29 +1,56 @@
|
|||||||
"use client";
|
"use client"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
useFetchASData,
|
useFetchASGroupData,
|
||||||
useFetchAvatarData,
|
useFetchAvatarData,
|
||||||
useFetchChangelog,
|
useFetchChangelog,
|
||||||
useFetchConfigData,
|
useFetchConfigData,
|
||||||
useFetchLightconeData,
|
useFetchLightconeData,
|
||||||
useFetchMOCData,
|
useFetchMOCGroupData,
|
||||||
useFetchMonsterData,
|
useFetchMonsterData,
|
||||||
useFetchPEAKData,
|
useFetchPeakGroupData,
|
||||||
useFetchPFData,
|
useFetchPFGroupData,
|
||||||
useFetchRelicData
|
useFetchRelicSetData
|
||||||
} from "@/lib/hooks";
|
} from "@/lib/hooks"
|
||||||
|
|
||||||
export default function ClientDataFetcher() {
|
export default function ClientDataFetcher({
|
||||||
useFetchConfigData();
|
children
|
||||||
useFetchAvatarData();
|
}: {
|
||||||
useFetchLightconeData();
|
children: React.ReactNode
|
||||||
useFetchRelicData();
|
}) {
|
||||||
useFetchMonsterData();
|
const q1 = useFetchConfigData()
|
||||||
useFetchPFData();
|
const q2 = useFetchAvatarData()
|
||||||
useFetchMOCData();
|
const q3 = useFetchLightconeData()
|
||||||
useFetchASData();
|
const q4 = useFetchRelicSetData()
|
||||||
useFetchPEAKData();
|
const q5 = useFetchMonsterData()
|
||||||
useFetchChangelog();
|
const q6 = useFetchPFGroupData()
|
||||||
|
const q7 = useFetchMOCGroupData()
|
||||||
|
const q8 = useFetchASGroupData()
|
||||||
|
const q9 = useFetchPeakGroupData()
|
||||||
|
const q10 = useFetchChangelog()
|
||||||
|
|
||||||
return null;
|
const queries = [q1,q2,q3,q4,q5,q6,q7,q8,q9,q10]
|
||||||
}
|
|
||||||
|
const loading = queries.some(q => q.isLoading)
|
||||||
|
|
||||||
|
const progress =
|
||||||
|
(queries.filter(q => q.isSuccess).length / queries.length) * 100
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div className="flex h-screen flex-col items-center justify-center gap-4">
|
||||||
|
<div className="text-lg font-semibold">Loading data...</div>
|
||||||
|
|
||||||
|
<progress
|
||||||
|
className="progress progress-primary w-56"
|
||||||
|
value={progress}
|
||||||
|
max="100"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div>{Math.floor(progress)}%</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return <>{children}</>
|
||||||
|
}
|
||||||
@@ -1,29 +1,28 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
|
||||||
"use client"
|
"use client"
|
||||||
import { replaceByParam } from "@/helper";
|
import { replaceByParam, getLocaleName } from '@/helper';
|
||||||
import useListAvatarStore from "@/stores/avatarStore";
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import ParseText from "../parseText";
|
import ParseText from "../parseText";
|
||||||
import useLocaleStore from "@/stores/localeStore";
|
import useLocaleStore from "@/stores/localeStore";
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
|
import useCurrentDataStore from "@/stores/currentDataStore";
|
||||||
|
|
||||||
|
|
||||||
export default function EidolonsInfo() {
|
export default function EidolonsInfo() {
|
||||||
const { avatarSelected, mapAvatarInfo } = useListAvatarStore()
|
const { avatarSelected } = useCurrentDataStore()
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
const { setAvatars, avatars } = useUserDataStore()
|
const { setAvatars, avatars } = useUserDataStore()
|
||||||
|
|
||||||
const charRank = useMemo(() => {
|
const charRank = useMemo(() => {
|
||||||
if (!avatarSelected) return null;
|
if (!avatarSelected) return null;
|
||||||
const avatar = avatars[avatarSelected.id];
|
const avatar = avatars[avatarSelected.ID];
|
||||||
if (avatar?.enhanced != "") {
|
if (avatar?.enhanced != "") {
|
||||||
return mapAvatarInfo[avatarSelected.id]?.Enhanced[avatar?.enhanced].Ranks
|
return avatarSelected?.Enhanced?.[avatar?.enhanced]?.Ranks
|
||||||
}
|
}
|
||||||
return mapAvatarInfo[avatarSelected.id]?.Ranks
|
return avatarSelected?.Ranks
|
||||||
}, [avatarSelected, avatars, locale, mapAvatarInfo]);
|
}, [avatarSelected, avatars]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-base-100 rounded-xl p-6 shadow-lg">
|
<div className="bg-base-100 rounded-xl p-6 shadow-lg">
|
||||||
@@ -32,22 +31,22 @@ export default function EidolonsInfo() {
|
|||||||
{transI18n("eidolons")}
|
{transI18n("eidolons")}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="grid grid-cols-1 m-4 p-4 font-bold gap-4 w-fit max-h-[77vh] min-h-[50vh] overflow-y-scroll overflow-x-hidden">
|
<div className="grid grid-cols-1 m-4 p-4 font-bold gap-4 w-fit max-h-[77vh] min-h-[50vh] overflow-y-scroll overflow-x-hidden">
|
||||||
{charRank && avatars[avatarSelected?.id || ""] && (
|
{charRank && avatars[avatarSelected?.ID || ""] && (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
{Object.entries(charRank || {}).map(([key, rank]) => (
|
{Object.entries(charRank || {}).map(([key, rank]) => (
|
||||||
<div key={key}
|
<div key={key}
|
||||||
className="flex flex-col items-center cursor-pointer hover:scale-105"
|
className="flex flex-col items-center cursor-pointer hover:scale-105"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
let newRank = Number(key)
|
let newRank = Number(key)
|
||||||
if (avatars[avatarSelected?.id || ""]?.data?.rank == Number(key)) {
|
if (avatars[avatarSelected?.ID || ""]?.data?.rank == Number(key)) {
|
||||||
newRank = Number(key) - 1
|
newRank = Number(key) - 1
|
||||||
}
|
}
|
||||||
setAvatars({ ...avatars, [avatarSelected?.id || ""]: { ...avatars[avatarSelected?.id || ""], data: { ...avatars[avatarSelected?.id || ""].data, rank: newRank } } })
|
setAvatars({ ...avatars, [avatarSelected?.ID || ""]: { ...avatars[avatarSelected?.ID || ""], data: { ...avatars[avatarSelected?.ID || ""].data, rank: newRank } } })
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
className={`w-60 object-contain mb-2 ${Number(key) <= avatars[avatarSelected?.id || ""]?.data?.rank ? "" : "grayscale"}`}
|
className={`w-60 object-contain mb-2 ${Number(key) <= avatars[avatarSelected?.ID.toString() || ""]?.data?.rank ? "" : "grayscale"}`}
|
||||||
src={`${process.env.CDN_URL}/ui/ui3d/rank/_dependencies/textures/${avatarSelected?.id}/${avatarSelected?.id}_Rank_${key}.png`}
|
src={`${process.env.CDN_URL}/ui/ui3d/rank/_dependencies/textures/${avatarSelected?.ID}/${avatarSelected?.ID}_Rank_${key}.png`}
|
||||||
alt={`Rank ${key}`}
|
alt={`Rank ${key}`}
|
||||||
priority
|
priority
|
||||||
unoptimized
|
unoptimized
|
||||||
@@ -60,12 +59,12 @@ export default function EidolonsInfo() {
|
|||||||
<span className="inline-block text-indigo-500">{key}.</span>
|
<span className="inline-block text-indigo-500">{key}.</span>
|
||||||
<ParseText
|
<ParseText
|
||||||
locale={locale}
|
locale={locale}
|
||||||
text={rank.Name}
|
text={getLocaleName(locale, rank.Name)}
|
||||||
className="text-center text-base font-normal leading-tight"
|
className="text-center text-base font-normal leading-tight"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-sm font-normal">
|
<div className="text-sm font-normal">
|
||||||
<div dangerouslySetInnerHTML={{ __html: replaceByParam(rank.Desc, rank.ParamList) }} />
|
<div dangerouslySetInnerHTML={{ __html: replaceByParam(getLocaleName(locale, rank.Desc), rank.Param) }} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -3,34 +3,28 @@ import { motion } from "framer-motion"
|
|||||||
import { EyeOff, Eye, Hammer, RefreshCw, ShieldBan, User, Swords, SkipForward, BowArrow, Info, RouteIcon, Search } from "lucide-react"
|
import { EyeOff, Eye, Hammer, RefreshCw, ShieldBan, User, Swords, SkipForward, BowArrow, Info, RouteIcon, Search } from "lucide-react"
|
||||||
import useGlobalStore from '@/stores/globalStore'
|
import useGlobalStore from '@/stores/globalStore'
|
||||||
import { useTranslations } from "next-intl"
|
import { useTranslations } from "next-intl"
|
||||||
import useEventStore from "@/stores/eventStore"
|
|
||||||
import { getLocaleName, getNameChar } from "@/helper"
|
import { getLocaleName, getNameChar } from "@/helper"
|
||||||
import useLocaleStore from "@/stores/localeStore"
|
import useLocaleStore from "@/stores/localeStore"
|
||||||
import useAvatarStore from "@/stores/avatarStore"
|
|
||||||
import SelectCustomImage from "../select/customSelectImage"
|
import SelectCustomImage from "../select/customSelectImage"
|
||||||
import { useMemo, useState } from "react"
|
import { useMemo, useState } from "react"
|
||||||
import useMazeStore from "@/stores/mazeStore"
|
import useDetailDataStore from "@/stores/detailDataStore"
|
||||||
|
|
||||||
export default function ExtraSettingBar() {
|
export default function ExtraSettingBar() {
|
||||||
const { extraData, setExtraData } = useGlobalStore()
|
const { extraData, setExtraData } = useGlobalStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
const { PEAKEvent } = useEventStore()
|
const { mapAvatar, mapPeak, stage, baseType } = useDetailDataStore()
|
||||||
const { listAvatar } = useAvatarStore()
|
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
const [showSearchStage, setShowSearchStage] = useState(false)
|
const [showSearchStage, setShowSearchStage] = useState(false)
|
||||||
const [isChildClick, setIsChildClick] = useState(false)
|
const [isChildClick, setIsChildClick] = useState(false)
|
||||||
const [stageSearchTerm, setStageSearchTerm] = useState("")
|
const [stageSearchTerm, setStageSearchTerm] = useState("")
|
||||||
const [stagePage, setStagePage] = useState(1)
|
const [stagePage, setStagePage] = useState(1)
|
||||||
const { Stage } = useMazeStore()
|
|
||||||
const pageSize = 30
|
const pageSize = 30
|
||||||
const stageList = useMemo(() => Object.values(Stage).map((stage) => ({
|
const stageList = useMemo(() => Object.values(stage), [stage])
|
||||||
id: stage.stage_id.toString(),
|
|
||||||
name: `${stage.stage_type} (${stage.stage_id})`,
|
|
||||||
})), [Stage])
|
|
||||||
|
|
||||||
const filteredStages = useMemo(() => stageList.filter((s) =>
|
const filteredStages = useMemo(() => stageList.filter((s) =>
|
||||||
s.name.toLowerCase().includes(stageSearchTerm.toLowerCase())
|
getLocaleName(locale, s.Name).toLowerCase().includes(stageSearchTerm.toLowerCase())
|
||||||
), [stageList, stageSearchTerm])
|
), [stageList, stageSearchTerm, locale])
|
||||||
|
|
||||||
const paginatedStages = useMemo(() => filteredStages.slice(
|
const paginatedStages = useMemo(() => filteredStages.slice(
|
||||||
(stagePage - 1) * pageSize,
|
(stagePage - 1) * pageSize,
|
||||||
@@ -122,7 +116,7 @@ export default function ExtraSettingBar() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Search className="w-6 h-6" />
|
<Search className="w-6 h-6" />
|
||||||
<span className="text-left"> {transI18n("stage")}: {stageList.find((s) => s.id === extraData?.theory_craft?.stage_id?.toString())?.name || transI18n("selectStage")}</span>
|
<span className="text-left"> {transI18n("stage")}: {getLocaleName(locale, stageList.find((s) => s.ID.toString() === extraData?.theory_craft?.stage_id?.toString())?.Name) || transI18n("selectStage")}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{showSearchStage && (
|
{showSearchStage && (
|
||||||
@@ -158,17 +152,17 @@ export default function ExtraSettingBar() {
|
|||||||
<>
|
<>
|
||||||
{paginatedStages.map((stage) => (
|
{paginatedStages.map((stage) => (
|
||||||
<div
|
<div
|
||||||
key={stage.id}
|
key={stage.ID}
|
||||||
className="p-2 hover:bg-primary/20 rounded cursor-pointer"
|
className="p-2 hover:bg-primary/20 rounded cursor-pointer"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
setIsChildClick(true)
|
setIsChildClick(true)
|
||||||
|
|
||||||
if (extraData?.theory_craft?.stage_id !== Number(stage.id)) {
|
if (extraData?.theory_craft?.stage_id !== Number(stage.ID)) {
|
||||||
setExtraData({
|
setExtraData({
|
||||||
...extraData,
|
...extraData,
|
||||||
theory_craft: {
|
theory_craft: {
|
||||||
stage_id: Number(stage.id),
|
stage_id: Number(stage.ID),
|
||||||
cycle_count: extraData?.theory_craft?.cycle_count || 1,
|
cycle_count: extraData?.theory_craft?.cycle_count || 1,
|
||||||
mode: extraData?.theory_craft?.mode || false,
|
mode: extraData?.theory_craft?.mode || false,
|
||||||
hp: extraData?.theory_craft?.hp || {}
|
hp: extraData?.theory_craft?.hp || {}
|
||||||
@@ -179,7 +173,7 @@ export default function ExtraSettingBar() {
|
|||||||
onChangeSearch("")
|
onChangeSearch("")
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{stage.name}
|
{getLocaleName(locale, stage.Name)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
@@ -238,10 +232,10 @@ export default function ExtraSettingBar() {
|
|||||||
<User className="text-warning" size={20} />
|
<User className="text-warning" size={20} />
|
||||||
<span className="label-text font-semibold">{transI18n("mainPath")}</span>
|
<span className="label-text font-semibold">{transI18n("mainPath")}</span>
|
||||||
<SelectCustomImage
|
<SelectCustomImage
|
||||||
customSet={listAvatar.filter((it) => extraData?.multi_path?.multi_path_main?.includes(Number(it.id))).map((it) => ({
|
customSet={Object.values(mapAvatar).filter((it) => extraData?.multi_path?.multi_path_main?.includes(Number(it.ID))).map((it) => ({
|
||||||
value: it.id,
|
value: it.ID.toString(),
|
||||||
label: getNameChar(locale, transI18n, it),
|
label: getNameChar(locale, transI18n, it),
|
||||||
imageUrl: `/icon/${it.baseType.toLowerCase()}.webp`
|
imageUrl: `${process.env.CDN_URL}/${baseType?.[it.BaseType].Icon}`
|
||||||
}))}
|
}))}
|
||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={extraData?.multi_path?.main?.toString() || ""}
|
selectedCustomSet={extraData?.multi_path?.main?.toString() || ""}
|
||||||
@@ -268,10 +262,10 @@ export default function ExtraSettingBar() {
|
|||||||
<BowArrow className="text-info" size={20} />
|
<BowArrow className="text-info" size={20} />
|
||||||
<span className="label-text font-semibold">{transI18n("march7Path")}</span>
|
<span className="label-text font-semibold">{transI18n("march7Path")}</span>
|
||||||
<SelectCustomImage
|
<SelectCustomImage
|
||||||
customSet={listAvatar.filter((it) => extraData?.multi_path?.multi_path_march_7?.includes(Number(it.id))).map((it) => ({
|
customSet={Object.values(mapAvatar).filter((it) => extraData?.multi_path?.multi_path_march_7?.includes(Number(it.ID))).map((it) => ({
|
||||||
value: it.id,
|
value: it.ID.toString(),
|
||||||
label: getNameChar(locale, transI18n, it),
|
label: getNameChar(locale, transI18n, it),
|
||||||
imageUrl: `/icon/${it.baseType.toLowerCase()}.webp`
|
imageUrl: `${process.env.CDN_URL}/${baseType?.[it.BaseType].Icon}`
|
||||||
}))}
|
}))}
|
||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={extraData?.multi_path?.march_7?.toString() || ""}
|
selectedCustomSet={extraData?.multi_path?.march_7?.toString() || ""}
|
||||||
@@ -318,9 +312,9 @@ export default function ExtraSettingBar() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{PEAKEvent.filter(event => extraData?.challenge?.challenge_peak_group_id_list?.includes(Number(event.id))).map(event => (
|
{Object.values(mapPeak).filter(event => extraData?.challenge?.challenge_peak_group_id_list?.includes(Number(event.ID))).map(event => (
|
||||||
<option key={event.id} value={event.id}>
|
<option key={event.ID} value={event.ID}>
|
||||||
{getLocaleName(locale, event)} ({event.id})
|
{getLocaleName(locale, event.Name)} ({event.ID})
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import useLocaleStore from "@/stores/localeStore";
|
|||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import Link from "next/link";
|
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import EnkaImport from "../importBar/enka";
|
import EnkaImport from "../importBar/enka";
|
||||||
@@ -564,19 +563,6 @@ export default function Header() {
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* GitHub Link */}
|
|
||||||
<Link
|
|
||||||
className='flex btn btn-ghost btn-sm btn-circle bg-white/20 hover:bg-white transition-all duration-200 items-center justify-center tooltip tooltip-bottom'
|
|
||||||
href={"https://github.com/AzenKain/Firefly-Srtools"}
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
data-tip="Github"
|
|
||||||
>
|
|
||||||
<svg className="w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512">
|
|
||||||
<path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z" />
|
|
||||||
</svg>
|
|
||||||
</Link>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{modalConfigs?.map(({ id, title, onClose, content }) => (
|
{modalConfigs?.map(({ id, title, onClose, content }) => (
|
||||||
|
|||||||
@@ -1,27 +1,26 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import { useEffect, useState } from "react";
|
import { useState, useMemo } from "react";
|
||||||
import useCopyProfileStore from "@/stores/copyProfile";
|
import useCopyProfileStore from "@/stores/copyProfile";
|
||||||
import ProfileCard from "../card/profileCard";
|
import ProfileCard from "../card/profileCard";
|
||||||
import { AvatarProfileCardType, AvatarProfileStore } from "@/types";
|
import { AvatarProfileCardType, AvatarProfileStore } from "@/types";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import useListAvatarStore from "@/stores/avatarStore";
|
import { getNameChar, calcRarity } from "@/helper";
|
||||||
import { getNameChar } from "@/helper";
|
|
||||||
import useLocaleStore from "@/stores/localeStore";
|
import useLocaleStore from "@/stores/localeStore";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import SelectCustomImage from "../select/customSelectImage";
|
import SelectCustomImage from "../select/customSelectImage";
|
||||||
|
import useCurrentDataStore from "@/stores/currentDataStore";
|
||||||
|
import useDetailDataStore from "@/stores/detailDataStore";
|
||||||
|
|
||||||
export default function CopyImport() {
|
export default function CopyImport() {
|
||||||
const { avatars, setAvatar } = useUserDataStore();
|
const { avatars, setAvatar } = useUserDataStore();
|
||||||
const { avatarSelected } = useListAvatarStore()
|
const { avatarSelected } = useCurrentDataStore()
|
||||||
|
const { mapAvatar, baseType, damageType } = useDetailDataStore()
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
const {
|
const {
|
||||||
selectedProfiles,
|
selectedProfiles,
|
||||||
listCopyAvatar,
|
|
||||||
avatarCopySelected,
|
avatarCopySelected,
|
||||||
setSelectedProfiles,
|
setSelectedProfiles,
|
||||||
filterCopy,
|
|
||||||
setFilterCopy,
|
|
||||||
setAvatarCopySelected,
|
setAvatarCopySelected,
|
||||||
listElement,
|
listElement,
|
||||||
listPath,
|
listPath,
|
||||||
@@ -37,6 +36,22 @@ export default function CopyImport() {
|
|||||||
text: ""
|
text: ""
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const listAvatar = useMemo(() => {
|
||||||
|
if (!mapAvatar || !locale || !transI18n) return []
|
||||||
|
let list = Object.values(mapAvatar);
|
||||||
|
const allElementFalse = !Object.values(listElement).some(v => v)
|
||||||
|
const allPathFalse = !Object.values(listPath).some(v => v)
|
||||||
|
const allRarityFalse = !Object.values(listRank).some(v => v)
|
||||||
|
list = list.filter(item => (allElementFalse || listElement[item.DamageType]) && (allPathFalse || listPath[item.BaseType]) && (allRarityFalse || listRank[calcRarity(item.Rarity)]))
|
||||||
|
list.sort((a, b) => {
|
||||||
|
const r = calcRarity(b.Rarity) - calcRarity(a.Rarity)
|
||||||
|
if (r !== 0) return r
|
||||||
|
return a.ID - b.ID
|
||||||
|
})
|
||||||
|
return list
|
||||||
|
}, [mapAvatar, listElement, listPath, listRank, locale, transI18n])
|
||||||
|
|
||||||
|
|
||||||
const handleProfileToggle = (profile: AvatarProfileCardType) => {
|
const handleProfileToggle = (profile: AvatarProfileCardType) => {
|
||||||
if (selectedProfiles.some((selectedProfile) => selectedProfile.key === profile.key)) {
|
if (selectedProfiles.some((selectedProfile) => selectedProfile.key === profile.key)) {
|
||||||
setSelectedProfiles(selectedProfiles.filter((selectedProfile) => selectedProfile.key !== profile.key));
|
setSelectedProfiles(selectedProfiles.filter((selectedProfile) => selectedProfile.key !== profile.key));
|
||||||
@@ -45,17 +60,6 @@ export default function CopyImport() {
|
|||||||
setSelectedProfiles([...selectedProfiles, profile]);
|
setSelectedProfiles([...selectedProfiles, profile]);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setFilterCopy({
|
|
||||||
...filterCopy,
|
|
||||||
locale: locale,
|
|
||||||
path: Object.keys(listPath).filter((key) => listPath[key]),
|
|
||||||
element: Object.keys(listElement).filter((key) => listElement[key]),
|
|
||||||
rarity: Object.keys(listRank).filter((key) => listRank[key])
|
|
||||||
})
|
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [listPath, listRank, listElement, locale, setFilterCopy])
|
|
||||||
|
|
||||||
const clearSelection = () => {
|
const clearSelection = () => {
|
||||||
setSelectedProfiles([]);
|
setSelectedProfiles([]);
|
||||||
@@ -67,7 +71,7 @@ export default function CopyImport() {
|
|||||||
|
|
||||||
const selectAll = () => {
|
const selectAll = () => {
|
||||||
if (avatarCopySelected) {
|
if (avatarCopySelected) {
|
||||||
setSelectedProfiles(avatars[avatarCopySelected?.id.toString()].profileList.map((profile, index) => {
|
setSelectedProfiles(avatars[avatarCopySelected?.ID.toString()].profileList.map((profile, index) => {
|
||||||
if (!profile.lightcone?.item_id && Object.keys(profile.relics).length == 0) {
|
if (!profile.lightcone?.item_id && Object.keys(profile.relics).length == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -106,20 +110,20 @@ export default function CopyImport() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const newListProfile = avatars[avatarCopySelected.id.toString()].profileList.map((profile) => {
|
const newListProfile = avatars[avatarCopySelected.ID.toString()].profileList.map((profile) => {
|
||||||
if (!profile.lightcone?.item_id && Object.keys(profile.relics).length == 0) {
|
if (!profile.lightcone?.item_id && Object.keys(profile.relics).length == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
...profile,
|
...profile,
|
||||||
profile_name: profile.profile_name + ` - Copy: ${avatarCopySelected?.id}`,
|
profile_name: profile.profile_name + ` - Copy: ${avatarCopySelected?.ID}`,
|
||||||
} as AvatarProfileStore
|
} as AvatarProfileStore
|
||||||
}).filter((profile) => profile !== null);
|
}).filter((profile) => profile !== null);
|
||||||
|
|
||||||
const newAvatar = {
|
const newAvatar = {
|
||||||
...avatars[avatarSelected.id.toString()],
|
...avatars[avatarSelected?.ID?.toString()],
|
||||||
profileList: avatars[avatarSelected.id.toString()].profileList.concat(newListProfile),
|
profileList: avatars[avatarSelected?.ID?.toString()].profileList.concat(newListProfile),
|
||||||
profileSelect: avatars[avatarSelected.id.toString()].profileList.length - 1,
|
profileSelect: avatars[avatarSelected?.ID?.toString()].profileList.length - 1,
|
||||||
}
|
}
|
||||||
setAvatar(newAvatar);
|
setAvatar(newAvatar);
|
||||||
setSelectedProfiles([]);
|
setSelectedProfiles([]);
|
||||||
@@ -143,9 +147,9 @@ export default function CopyImport() {
|
|||||||
{/* Path */}
|
{/* Path */}
|
||||||
<div>
|
<div>
|
||||||
<div className="flex flex-wrap gap-2 justify-start items-center">
|
<div className="flex flex-wrap gap-2 justify-start items-center">
|
||||||
{Object.entries(listPath).map(([key], index) => (
|
{Object.entries(baseType).map(([key, value]) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={key}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setListPath({ ...listPath, [key]: !listPath[key] })
|
setListPath({ ...listPath, [key]: !listPath[key] })
|
||||||
}}
|
}}
|
||||||
@@ -156,7 +160,7 @@ export default function CopyImport() {
|
|||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`/icon/${key}.webp`}
|
src={`${process.env.CDN_URL}/${value.Icon}`}
|
||||||
alt={key}
|
alt={key}
|
||||||
className="h-8 w-8 object-contain rounded-md"
|
className="h-8 w-8 object-contain rounded-md"
|
||||||
width={200}
|
width={200}
|
||||||
@@ -170,9 +174,9 @@ export default function CopyImport() {
|
|||||||
{/* Element */}
|
{/* Element */}
|
||||||
<div>
|
<div>
|
||||||
<div className="flex flex-wrap gap-2 justify-start items-center">
|
<div className="flex flex-wrap gap-2 justify-start items-center">
|
||||||
{Object.entries(listElement).map(([key], index) => (
|
{Object.entries(damageType).map(([key, value]) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={key}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setListElement({ ...listElement, [key]: !listElement[key] })
|
setListElement({ ...listElement, [key]: !listElement[key] })
|
||||||
}}
|
}}
|
||||||
@@ -183,7 +187,7 @@ export default function CopyImport() {
|
|||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`/icon/${key}.webp`}
|
src={`${process.env.CDN_URL}/${value.Icon}`}
|
||||||
alt={key}
|
alt={key}
|
||||||
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md"
|
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md"
|
||||||
width={200}
|
width={200}
|
||||||
@@ -217,19 +221,19 @@ export default function CopyImport() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 gap-2">
|
<div className="grid grid-cols-1 gap-2">
|
||||||
|
|
||||||
{listCopyAvatar.length > 0 && (
|
{listAvatar.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
<div>{transI18n("characterName")}</div>
|
<div>{transI18n("characterName")}</div>
|
||||||
<SelectCustomImage
|
<SelectCustomImage
|
||||||
customSet={listCopyAvatar.map((avatar) => ({
|
customSet={listAvatar.map((avatar) => ({
|
||||||
value: avatar.id.toString(),
|
value: avatar.ID.toString(),
|
||||||
label: getNameChar(locale, transI18n, avatar),
|
label: getNameChar(locale, transI18n, avatar),
|
||||||
imageUrl: `${process.env.CDN_URL}/spriteoutput/avatarshopicon/avatar/${avatar.id}.png`
|
imageUrl: `${process.env.CDN_URL}/${avatar.Image.AvatarIconPath}`
|
||||||
}))}
|
}))}
|
||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={avatarCopySelected?.id.toString() || ""}
|
selectedCustomSet={avatarCopySelected?.ID.toString() || ""}
|
||||||
placeholder="Character Select"
|
placeholder="Character Select"
|
||||||
setSelectedCustomSet={(value) => setAvatarCopySelected(listCopyAvatar.find((avatar) => avatar.id.toString() === value) || null)}
|
setSelectedCustomSet={(value) => setAvatarCopySelected(mapAvatar[value] || null)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -270,7 +274,7 @@ export default function CopyImport() {
|
|||||||
|
|
||||||
{/* Character Grid */}
|
{/* Character Grid */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||||
{avatarCopySelected && avatars[avatarCopySelected?.id.toString()]?.profileList.map((profile, index) => {
|
{avatarCopySelected && avatars[avatarCopySelected?.ID.toString()]?.profileList.map((profile, index) => {
|
||||||
if (!profile.lightcone?.item_id && Object.keys(profile.relics).length == 0) {
|
if (!profile.lightcone?.item_id && Object.keys(profile.relics).length == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,58 +1,59 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useEffect } from "react"
|
import { useMemo } from "react"
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import useLocaleStore from "@/stores/localeStore"
|
import useLocaleStore from "@/stores/localeStore"
|
||||||
import useLightconeStore from "@/stores/lightconeStore";
|
|
||||||
import LightconeCard from "../card/lightconeCard";
|
import LightconeCard from "../card/lightconeCard";
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import useAvatarStore from "@/stores/avatarStore";
|
|
||||||
import useModelStore from "@/stores/modelStore";
|
import useModelStore from "@/stores/modelStore";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
|
import useCurrentDataStore from "@/stores/currentDataStore";
|
||||||
|
import useDetailDataStore from "@/stores/detailDataStore";
|
||||||
|
import { calcRarity, getLocaleName } from "@/helper";
|
||||||
|
|
||||||
export default function LightconeBar() {
|
export default function LightconeBar() {
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
const {
|
const {
|
||||||
listLightcone,
|
avatarSelected,
|
||||||
filter,
|
mapLightconePathActive,
|
||||||
setFilter,
|
mapLightconeRankActive,
|
||||||
defaultFilter,
|
setMapLightconePathActive,
|
||||||
listPath,
|
setMapLightconeRankActive,
|
||||||
listRank,
|
lightconeSearch,
|
||||||
setListPath,
|
setLightconeSearch
|
||||||
setListRank
|
} = useCurrentDataStore()
|
||||||
} = useLightconeStore()
|
|
||||||
const { setAvatar, avatars } = useUserDataStore()
|
const { setAvatar, avatars } = useUserDataStore()
|
||||||
const { avatarSelected } = useAvatarStore()
|
|
||||||
const { setIsOpenLightcone } = useModelStore()
|
const { setIsOpenLightcone } = useModelStore()
|
||||||
|
const { mapLightCone, baseType } = useDetailDataStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
|
|
||||||
useEffect(() => {
|
const listLightcone = useMemo(() => {
|
||||||
const newListPath: Record<string, boolean> = { "knight": false, "mage": false, "priest": false, "rogue": false, "shaman": false, "warlock": false, "warrior": false, "memory": false, "elation": false }
|
if (!mapLightCone || !locale) return []
|
||||||
const newListRank: Record<string, boolean> = { "3": false, "4": false, "5": false }
|
|
||||||
for (const path of defaultFilter.path) {
|
|
||||||
if (path in newListPath) {
|
|
||||||
newListPath[path] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const rarity of defaultFilter.rarity) {
|
|
||||||
if (rarity in newListRank) {
|
|
||||||
newListRank[rarity] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setListPath(newListPath)
|
|
||||||
setListRank(newListRank)
|
|
||||||
}, [defaultFilter, setListPath, setListRank])
|
|
||||||
|
|
||||||
useEffect(() => {
|
let list = Object.values(mapLightCone)
|
||||||
setFilter({
|
|
||||||
...filter,
|
if (lightconeSearch) {
|
||||||
locale: locale,
|
list = list.filter(item => getLocaleName(locale, item.Name).toLowerCase().includes(lightconeSearch.toLowerCase()))
|
||||||
path: Object.keys(listPath).filter((key) => listPath[key]),
|
}
|
||||||
rarity: Object.keys(listRank).filter((key) => listRank[key])
|
|
||||||
|
const allRankFalse = !Object.values(mapLightconeRankActive).some(v => v)
|
||||||
|
const allPathFalse = !Object.values(mapLightconePathActive).some(v => v)
|
||||||
|
|
||||||
|
list = list.filter(item =>
|
||||||
|
(allRankFalse || mapLightconeRankActive[item.Rarity]) &&
|
||||||
|
(allPathFalse || mapLightconePathActive[item.BaseType])
|
||||||
|
)
|
||||||
|
|
||||||
|
list.sort((a, b) => {
|
||||||
|
const r = calcRarity(b.Rarity) - calcRarity(a.Rarity)
|
||||||
|
if (r !== 0) return r
|
||||||
|
return a.ID - b.ID
|
||||||
})
|
})
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, [listPath, listRank, locale])
|
return list
|
||||||
|
}, [mapLightCone, mapLightconePathActive, mapLightconeRankActive, lightconeSearch, locale])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -65,8 +66,8 @@ export default function LightconeBar() {
|
|||||||
<div className="flex items-start flex-col gap-2">
|
<div className="flex items-start flex-col gap-2">
|
||||||
<div>Search</div>
|
<div>Search</div>
|
||||||
<input
|
<input
|
||||||
value={filter.name}
|
value={lightconeSearch}
|
||||||
onChange={(e) => setFilter({ ...filter, name: e.target.value, locale: locale })}
|
onChange={(e) => setLightconeSearch(e.target.value)}
|
||||||
type="text" placeholder="LightCone Name" className="input input-accent mt-1 w-full"
|
type="text" placeholder="LightCone Name" className="input input-accent mt-1 w-full"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -74,21 +75,21 @@ export default function LightconeBar() {
|
|||||||
<div>Filter</div>
|
<div>Filter</div>
|
||||||
<div className="flex flex-row flex-wrap justify-between mt-1 w-full">
|
<div className="flex flex-row flex-wrap justify-between mt-1 w-full">
|
||||||
<div className="flex flex-wrap mb-1 mx-1 gap-2">
|
<div className="flex flex-wrap mb-1 mx-1 gap-2">
|
||||||
{Object.keys(listPath).map((key, index) => (
|
{Object.entries(baseType).map(([key, value]) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={key}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setListPath({ ...listPath, [key]: !listPath[key] })
|
setMapLightconePathActive({ ...mapLightconePathActive, [key]: !mapLightconePathActive[key] })
|
||||||
}}
|
}}
|
||||||
className="h-9.5 w-9.5 md:h-12.5 md:w-12.5 hover:bg-gray-600 grid place-items-center rounded-md shadow-lg cursor-pointer"
|
className="h-9.5 w-9.5 md:h-12.5 md:w-12.5 hover:bg-gray-600 grid place-items-center rounded-md shadow-lg cursor-pointer"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: listPath[key] ? "#374151" : "#6B7280"
|
backgroundColor: mapLightconePathActive[key] ? "#374151" : "#6B7280"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`/icon/${key}.webp`}
|
src={`${process.env.CDN_URL}/${value.Icon}`}
|
||||||
alt={key}
|
alt={key}
|
||||||
className="h-7 w-7 md:h-8 md:w-8 object-contain rounded-md"
|
className="h-7 w-7 md:h-8 md:w-8 object-contain rounded-md"
|
||||||
width={200}
|
width={200}
|
||||||
@@ -99,15 +100,15 @@ export default function LightconeBar() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-wrap mb-1 mx-1 gap-2">
|
<div className="flex flex-wrap mb-1 mx-1 gap-2">
|
||||||
{Object.keys(listRank).map((key, index) => (
|
{Object.keys(mapLightconeRankActive).map((key, index) => (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setListRank({ ...listRank, [key]: !listRank[key] })
|
setMapLightconeRankActive({ ...mapLightconeRankActive, [key]: !mapLightconeRankActive[key] })
|
||||||
}}
|
}}
|
||||||
className="h-9.5 w-9.5 md:h-12.5 md:w-12.5 hover:bg-gray-600 grid place-items-center rounded-md shadow-lg cursor-pointer"
|
className="h-9.5 w-9.5 md:h-12.5 md:w-12.5 hover:bg-gray-600 grid place-items-center rounded-md shadow-lg cursor-pointer"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: listRank[key] ? "#374151" : "#6B7280"
|
backgroundColor: mapLightconeRankActive[key] ? "#374151" : "#6B7280"
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="font-bold text-white h-8 w-8 text-center flex items-center justify-center">
|
<div className="font-bold text-white h-8 w-8 text-center flex items-center justify-center">
|
||||||
@@ -124,10 +125,10 @@ export default function LightconeBar() {
|
|||||||
{listLightcone.map((item, index) => (
|
{listLightcone.map((item, index) => (
|
||||||
<div key={index} onClick={() => {
|
<div key={index} onClick={() => {
|
||||||
if (avatarSelected) {
|
if (avatarSelected) {
|
||||||
const avatar = avatars[avatarSelected.id]
|
const avatar = avatars[avatarSelected?.ID?.toString()]
|
||||||
avatar.profileList[avatar.profileSelect].lightcone = {
|
avatar.profileList[avatar.profileSelect].lightcone = {
|
||||||
level: 80,
|
level: 80,
|
||||||
item_id: Number(item.id),
|
item_id: item.ID,
|
||||||
rank: 1,
|
rank: 1,
|
||||||
promotion: 6
|
promotion: 6
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,60 +1,50 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useEffect, useMemo } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
import SelectCustomText from "../select/customSelectText";
|
import SelectCustomText from "../select/customSelectText";
|
||||||
import useEventStore from "@/stores/eventStore";
|
import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper";
|
||||||
import { getLocaleName, replaceByParam } from "@/helper";
|
|
||||||
import useLocaleStore from "@/stores/localeStore";
|
import useLocaleStore from "@/stores/localeStore";
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import useMonsterStore from "@/stores/monsterStore";
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { MonsterStore } from "@/types";
|
import { MonsterStore } from "@/types";
|
||||||
import useMazeStore from "@/stores/mazeStore";
|
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
|
import useDetailDataStore from "@/stores/detailDataStore";
|
||||||
|
|
||||||
export default function AsBar() {
|
export default function AsBar() {
|
||||||
const { ASEvent, mapASInfo } = useEventStore()
|
|
||||||
const { mapMonster } = useMonsterStore()
|
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
const {
|
const {
|
||||||
as_config,
|
as_config,
|
||||||
setAsConfig
|
setAsConfig
|
||||||
} = useUserDataStore()
|
} = useUserDataStore()
|
||||||
const { AS } = useMazeStore()
|
const { mapMonster, mapAS, damageType, hardLevelConfig, eliteConfig } = useDetailDataStore()
|
||||||
|
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
|
|
||||||
const challengeSelected = useMemo(() => {
|
const challengeSelected = useMemo(() => {
|
||||||
return mapASInfo[as_config.event_id.toString()]?.Level.find((as) => as.Id === as_config.challenge_id)
|
return mapAS[as_config.event_id.toString()]?.Level.find((as) => as.ID === as_config.challenge_id)
|
||||||
}, [as_config, mapASInfo])
|
}, [as_config, mapAS])
|
||||||
|
|
||||||
const eventSelected = useMemo(() => {
|
const eventSelected = useMemo(() => {
|
||||||
return mapASInfo[as_config.event_id.toString()]
|
return mapAS[as_config.event_id.toString()]
|
||||||
}, [as_config, mapASInfo])
|
}, [as_config, mapAS])
|
||||||
|
|
||||||
const buffList = useMemo(() => {
|
const buffList = useMemo(() => {
|
||||||
const challenge = AS[as_config.event_id.toString()];
|
if (!eventSelected) return [];
|
||||||
if (!challenge) return { buffList: [], buffId: [] };
|
|
||||||
|
|
||||||
if (as_config.floor_side === "Upper" || as_config.floor_side === "Upper -> Lower") {
|
if (as_config.floor_side === "Upper" || as_config.floor_side === "Upper -> Lower") {
|
||||||
return {
|
return eventSelected?.BuffList1 ?? [];
|
||||||
buffList: eventSelected?.BuffList1 ?? [],
|
|
||||||
buffId: challenge.buff_1 ?? [],
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (as_config.floor_side === "Lower" || as_config.floor_side === "Lower -> Upper") {
|
if (as_config.floor_side === "Lower" || as_config.floor_side === "Lower -> Upper") {
|
||||||
return {
|
return eventSelected?.BuffList2 ?? [];
|
||||||
buffList: eventSelected?.BuffList2 ?? [],
|
|
||||||
buffId: challenge.buff_2 ?? [],
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return { buffList: [], buffId: [] };
|
return [];
|
||||||
}, [AS, as_config.event_id, as_config.floor_side, eventSelected?.BuffList1, eventSelected?.BuffList2]);
|
}, [as_config.floor_side, eventSelected]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!challengeSelected || as_config.event_id === 0 || as_config.challenge_id === 0) return
|
if (!challengeSelected || as_config.event_id === 0 || as_config.challenge_id === 0) return
|
||||||
const newBattleConfig = structuredClone(as_config)
|
const newBattleConfig = structuredClone(as_config)
|
||||||
newBattleConfig.cycle_count = 0
|
newBattleConfig.cycle_count = challengeSelected.TurnLimit
|
||||||
|
|
||||||
newBattleConfig.blessings = []
|
newBattleConfig.blessings = []
|
||||||
if (as_config.buff_id !== 0) {
|
if (as_config.buff_id !== 0) {
|
||||||
@@ -63,24 +53,27 @@ export default function AsBar() {
|
|||||||
level: 1
|
level: 1
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (AS[as_config.challenge_id.toString()]) {
|
|
||||||
newBattleConfig.blessings.push({
|
if (challengeSelected) {
|
||||||
id: Number(AS[as_config.challenge_id.toString()].maze_buff),
|
challengeSelected.MazeBuff.map((item) => {
|
||||||
level: 1
|
newBattleConfig.blessings.push({
|
||||||
|
id: item.ID,
|
||||||
|
level: 1
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
newBattleConfig.monsters = []
|
newBattleConfig.monsters = []
|
||||||
newBattleConfig.stage_id = 0
|
newBattleConfig.stage_id = 0
|
||||||
if ((as_config.floor_side === "Upper" || as_config.floor_side === "Upper -> Lower")
|
if ((as_config.floor_side === "Upper" || as_config.floor_side === "Upper -> Lower")
|
||||||
&& challengeSelected.EventIDList1.length > 0) {
|
&& challengeSelected.EventList1.length > 0) {
|
||||||
newBattleConfig.stage_id = challengeSelected.EventIDList1[0].StageID
|
newBattleConfig.stage_id = challengeSelected.EventList1[0].ID
|
||||||
for (const wave of challengeSelected.EventIDList1[0].MonsterList) {
|
for (const wave of challengeSelected.EventList1[0].MonsterList) {
|
||||||
const newWave: MonsterStore[] = []
|
const newWave: MonsterStore[] = []
|
||||||
for (const value of Object.values(wave)) {
|
for (const value of Object.values(wave)) {
|
||||||
newWave.push({
|
newWave.push({
|
||||||
monster_id: Number(value),
|
monster_id: value,
|
||||||
level: challengeSelected.EventIDList1[0].Level,
|
level: challengeSelected.EventList1[0].Level,
|
||||||
amount: 1,
|
amount: 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -88,14 +81,14 @@ export default function AsBar() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((as_config.floor_side === "Lower" || as_config.floor_side === "Lower -> Upper")
|
if ((as_config.floor_side === "Lower" || as_config.floor_side === "Lower -> Upper")
|
||||||
&& challengeSelected.EventIDList2.length > 0) {
|
&& challengeSelected.EventList2.length > 0) {
|
||||||
newBattleConfig.stage_id = challengeSelected.EventIDList2[0].StageID
|
newBattleConfig.stage_id = challengeSelected.EventList2[0].ID
|
||||||
for (const wave of challengeSelected.EventIDList2[0].MonsterList) {
|
for (const wave of challengeSelected.EventList2[0].MonsterList) {
|
||||||
const newWave: MonsterStore[] = []
|
const newWave: MonsterStore[] = []
|
||||||
for (const value of Object.values(wave)) {
|
for (const value of Object.values(wave)) {
|
||||||
newWave.push({
|
newWave.push({
|
||||||
monster_id: Number(value),
|
monster_id: value,
|
||||||
level: challengeSelected.EventIDList2[0].Level,
|
level: challengeSelected.EventList2[0].Level,
|
||||||
amount: 1,
|
amount: 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -103,26 +96,26 @@ export default function AsBar() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (as_config.floor_side === "Lower -> Upper"
|
if (as_config.floor_side === "Lower -> Upper"
|
||||||
&& challengeSelected.EventIDList1.length > 0) {
|
&& challengeSelected.EventList1.length > 0) {
|
||||||
for (const wave of challengeSelected.EventIDList1[0].MonsterList) {
|
for (const wave of challengeSelected.EventList1[0].MonsterList) {
|
||||||
const newWave: MonsterStore[] = []
|
const newWave: MonsterStore[] = []
|
||||||
for (const value of Object.values(wave)) {
|
for (const value of Object.values(wave)) {
|
||||||
newWave.push({
|
newWave.push({
|
||||||
monster_id: Number(value),
|
monster_id: value,
|
||||||
level: challengeSelected.EventIDList1[0].Level,
|
level: challengeSelected.EventList1[0].Level,
|
||||||
amount: 1,
|
amount: 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
newBattleConfig.monsters.push(newWave)
|
newBattleConfig.monsters.push(newWave)
|
||||||
}
|
}
|
||||||
} else if (as_config.floor_side === "Upper -> Lower"
|
} else if (as_config.floor_side === "Upper -> Lower"
|
||||||
&& challengeSelected.EventIDList2.length > 0) {
|
&& challengeSelected.EventList2.length > 0) {
|
||||||
for (const wave of challengeSelected.EventIDList2[0].MonsterList) {
|
for (const wave of challengeSelected.EventList2[0].MonsterList) {
|
||||||
const newWave: MonsterStore[] = []
|
const newWave: MonsterStore[] = []
|
||||||
for (const value of Object.values(wave)) {
|
for (const value of Object.values(wave)) {
|
||||||
newWave.push({
|
newWave.push({
|
||||||
monster_id: Number(value),
|
monster_id: value,
|
||||||
level: challengeSelected.EventIDList2[0].Level,
|
level: challengeSelected.EventList2[0].Level,
|
||||||
amount: 1,
|
amount: 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -130,19 +123,17 @@ export default function AsBar() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
setAsConfig(newBattleConfig)
|
setAsConfig(newBattleConfig)
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [
|
}, [
|
||||||
challengeSelected,
|
challengeSelected,
|
||||||
|
mapAS,
|
||||||
as_config.event_id,
|
as_config.event_id,
|
||||||
as_config.challenge_id,
|
as_config.challenge_id,
|
||||||
as_config.floor_side,
|
as_config.floor_side,
|
||||||
as_config.buff_id,
|
as_config.buff_id,
|
||||||
mapASInfo,
|
|
||||||
AS,
|
|
||||||
])
|
])
|
||||||
|
|
||||||
if (!ASEvent) return null
|
if (!mapAS) return null
|
||||||
return (
|
return (
|
||||||
<div className="py-8 relative">
|
<div className="py-8 relative">
|
||||||
|
|
||||||
@@ -150,10 +141,10 @@ export default function AsBar() {
|
|||||||
<div className="rounded-xl p-4 mb-2 border border-warning">
|
<div className="rounded-xl p-4 mb-2 border border-warning">
|
||||||
<div className="mb-4 w-full">
|
<div className="mb-4 w-full">
|
||||||
<SelectCustomText
|
<SelectCustomText
|
||||||
customSet={ASEvent.map((as) => ({
|
customSet={Object.values(mapAS).sort((a, b) => b.ID - a.ID).map((as) => ({
|
||||||
id: as.id,
|
id: as.ID.toString(),
|
||||||
name: getLocaleName(locale, as),
|
name: getLocaleName(locale, as.Name),
|
||||||
time: `${as.begin} - ${as.end}`,
|
time: `${as.BeginTime} - ${as.EndTime}`,
|
||||||
}))}
|
}))}
|
||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={as_config.event_id.toString()}
|
selectedCustomSet={as_config.event_id.toString()}
|
||||||
@@ -161,7 +152,7 @@ export default function AsBar() {
|
|||||||
setSelectedCustomSet={(id) => setAsConfig({
|
setSelectedCustomSet={(id) => setAsConfig({
|
||||||
...as_config,
|
...as_config,
|
||||||
event_id: Number(id),
|
event_id: Number(id),
|
||||||
challenge_id: mapASInfo[Number(id)]?.Level.slice(-1)[0]?.Id || 0,
|
challenge_id: mapAS[Number(id)]?.Level.at(-1)?.ID || 0,
|
||||||
buff_id: 0
|
buff_id: 0
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
@@ -179,8 +170,8 @@ export default function AsBar() {
|
|||||||
onChange={(e) => setAsConfig({ ...as_config, challenge_id: Number(e.target.value) })}
|
onChange={(e) => setAsConfig({ ...as_config, challenge_id: Number(e.target.value) })}
|
||||||
>
|
>
|
||||||
<option value={0} disabled={true}>{transI18n("selectFloor")}</option>
|
<option value={0} disabled={true}>{transI18n("selectFloor")}</option>
|
||||||
{mapASInfo[as_config.event_id.toString()]?.Level.map((as) => (
|
{eventSelected?.Level.map((as) => (
|
||||||
<option key={as.Id} value={as.Id}>{as.Id % 10}</option>
|
<option key={as.ID} value={as.ID}>{getLocaleName(locale, as.Name)}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@@ -205,15 +196,11 @@ export default function AsBar() {
|
|||||||
{eventSelected && (
|
{eventSelected && (
|
||||||
<div className="mb-4 w-full">
|
<div className="mb-4 w-full">
|
||||||
<SelectCustomText
|
<SelectCustomText
|
||||||
customSet={
|
customSet={buffList.map((buff) => ({
|
||||||
Array.isArray(buffList?.buffList) && Array.isArray(buffList?.buffId)
|
id: buff.ID?.toString() || "",
|
||||||
? buffList.buffList.map((buff, index) => ({
|
name: getLocaleName(locale, buff?.Name) || "",
|
||||||
id: buffList.buffId?.[index]?.toString() || "",
|
description: replaceByParam(getLocaleName(locale, buff?.Desc) || "", buff?.Param || []),
|
||||||
name: buff?.Name || "",
|
}))}
|
||||||
description: replaceByParam(buff?.Desc || "", buff?.Param || []),
|
|
||||||
}))
|
|
||||||
: []
|
|
||||||
}
|
|
||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={as_config?.buff_id?.toString()}
|
selectedCustomSet={as_config?.buff_id?.toString()}
|
||||||
placeholder={transI18n("selectBuff")}
|
placeholder={transI18n("selectBuff")}
|
||||||
@@ -224,16 +211,19 @@ export default function AsBar() {
|
|||||||
{/* Turbulence Buff */}
|
{/* Turbulence Buff */}
|
||||||
<div className="bg-base-200/20 rounded-lg p-4 border border-purple-500/20">
|
<div className="bg-base-200/20 rounded-lg p-4 border border-purple-500/20">
|
||||||
<h2 className="text-2xl font-bold mb-2 text-info">{transI18n("turbulenceBuff")}</h2>
|
<h2 className="text-2xl font-bold mb-2 text-info">{transI18n("turbulenceBuff")}</h2>
|
||||||
{eventSelected && eventSelected.Buff?.Name ? (
|
{challengeSelected ? (
|
||||||
<div
|
challengeSelected.MazeBuff.map((buff, i) => (
|
||||||
className="text-base"
|
<div
|
||||||
dangerouslySetInnerHTML={{
|
key={i}
|
||||||
__html: replaceByParam(
|
className="text-base"
|
||||||
eventSelected.Buff?.Desc || "",
|
dangerouslySetInnerHTML={{
|
||||||
eventSelected.Buff?.Param || []
|
__html: replaceByParam(
|
||||||
)
|
getLocaleName(locale, buff?.Desc) || "",
|
||||||
}}
|
buff?.Param || []
|
||||||
/>
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))
|
||||||
) : (
|
) : (
|
||||||
<div className="text-base">{transI18n("noTurbulenceBuff")}</div>
|
<div className="text-base">{transI18n("noTurbulenceBuff")}</div>
|
||||||
)}
|
)}
|
||||||
@@ -247,48 +237,85 @@ export default function AsBar() {
|
|||||||
<div className="rounded-xl p-4 mt-2 border border-warning">
|
<div className="rounded-xl p-4 mt-2 border border-warning">
|
||||||
<h2 className="text-2xl font-bold mb-6 text-info">{transI18n("firstHalfEnemies")}</h2>
|
<h2 className="text-2xl font-bold mb-6 text-info">{transI18n("firstHalfEnemies")}</h2>
|
||||||
|
|
||||||
{challengeSelected && challengeSelected?.EventIDList1?.length > 0 && challengeSelected?.EventIDList1[0].MonsterList.map((wave, waveIndex) => (
|
{challengeSelected && challengeSelected?.EventList1?.length > 0 && challengeSelected?.EventList1?.[0]?.MonsterList?.map((wave, waveIndex) => (
|
||||||
<div key={waveIndex} className="mb-6">
|
<div key={waveIndex} className="mb-6">
|
||||||
<h3 className="text-lg font-semibold mb-t">{transI18n("wave")} {waveIndex + 1}</h3>
|
<h3 className="text-lg font-semibold">{transI18n("wave")} {waveIndex + 1}</h3>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div className="flex flex-wrap gap-2 mt-2">
|
||||||
{Object.values(wave).map((waveValue, enemyIndex) => (
|
{Object.values(wave).map((waveValue, enemyIndex) => {
|
||||||
<div
|
const monsterStats = calcMonsterStats(
|
||||||
key={enemyIndex}
|
mapMonster?.[waveValue.toString()],
|
||||||
className="rounded-xl p-2 border border-white/10 shadow-md hover:border-white/20 hover:shadow-lg transition"
|
challengeSelected?.EventList1?.[0]?.EliteGroup,
|
||||||
>
|
challengeSelected?.EventList1?.[0]?.HardLevelGroup,
|
||||||
<div className="flex items-center space-x-3">
|
challengeSelected?.EventList1?.[0]?.Level,
|
||||||
<div className="relative w-20 h-20 rounded-full overflow-hidden shrink-0 border border-white/10 shadow-sm">
|
hardLevelConfig,
|
||||||
{mapMonster?.[waveValue.toString()]?.icon && <Image
|
eliteConfig
|
||||||
unoptimized
|
);
|
||||||
crossOrigin="anonymous"
|
return (
|
||||||
src={`${process.env.CDN_URL}/${mapMonster?.[waveValue.toString()]?.icon}`}
|
<div
|
||||||
alt="Enemy Icon"
|
key={enemyIndex}
|
||||||
width={376}
|
className="group relative flex flex-col w-40 bg-base-100 rounded-2xl border border-base-300 shadow-md"
|
||||||
height={512}
|
>
|
||||||
className="w-full h-full object-cover"
|
<div className="badge badge-warning badge-sm font-bold absolute top-2 right-2 z-10 shadow-sm">
|
||||||
/>}
|
Lv. {challengeSelected?.EventList1[0].Level}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col">
|
<div className="relative w-full h-20 bg-base-200 flex items-center justify-center p-4 rounded-t-2xl">
|
||||||
<div className="text-sm font-semibold">Lv. {challengeSelected?.EventIDList1[0].Level}</div>
|
{mapMonster?.[waveValue.toString()]?.Image?.IconPath && (
|
||||||
<div className="flex items-center space-x-1 mt-1">
|
<div className="relative w-16 h-16 rounded-full border-2 border-base-300 shadow-md overflow-hidden group-hover:scale-110 transition-transform duration-300 bg-base-100">
|
||||||
{mapMonster?.[waveValue.toString()]?.weak?.map((icon, iconIndex) => (
|
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`/icon/${icon.toLowerCase()}.webp`}
|
src={`${process.env.CDN_URL}/${mapMonster?.[waveValue.toString()]?.Image?.IconPath}`}
|
||||||
alt={icon}
|
alt="Enemy Icon"
|
||||||
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
|
width={150}
|
||||||
width={200}
|
height={150}
|
||||||
height={200}
|
className="w-full h-full object-cover"
|
||||||
key={iconIndex}
|
|
||||||
/>
|
/>
|
||||||
))}
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col px-1 pb-2 pt-2">
|
||||||
|
<div className="flex flex-col space-y-1.5">
|
||||||
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
|
<span className="text-xs font-semibold text-error">HP</span>
|
||||||
|
<span className="text-sm font-bold text-base-content">{monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
|
<span className="text-xs font-semibold text-info">Speed</span>
|
||||||
|
<span className="text-sm font-bold text-base-content">{monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
|
<span className="text-xs font-semibold text-base-content/70">Toughness</span>
|
||||||
|
<span className="text-sm font-bold text-base-content">{monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-3 pt-2 border-t border-base-300 flex flex-col items-center">
|
||||||
|
<span className="text-[10px] text-base-content/60 font-bold uppercase tracking-widest mb-1.5">
|
||||||
|
Weakness
|
||||||
|
</span>
|
||||||
|
<div className="flex items-center justify-center gap-1.5 flex-wrap">
|
||||||
|
{mapMonster?.[waveValue.toString()]?.StanceWeakList?.map((icon, iconIndex) => (
|
||||||
|
<Image
|
||||||
|
key={iconIndex}
|
||||||
|
unoptimized
|
||||||
|
crossOrigin="anonymous"
|
||||||
|
src={`${process.env.CDN_URL}/${damageType[icon]?.Icon}`}
|
||||||
|
alt={icon}
|
||||||
|
width={40}
|
||||||
|
height={40}
|
||||||
|
className="h-6 w-6 object-contain rounded-full bg-base-300 border border-base-content/10 p-0.5 shadow-sm hover:scale-110 transition-transform"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)
|
||||||
))}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -298,49 +325,85 @@ export default function AsBar() {
|
|||||||
<div className="rounded-xl p-4 mt-2 border border-warning">
|
<div className="rounded-xl p-4 mt-2 border border-warning">
|
||||||
<h2 className="text-2xl font-bold mb-6 text-info">{transI18n("secondHalfEnemies")}</h2>
|
<h2 className="text-2xl font-bold mb-6 text-info">{transI18n("secondHalfEnemies")}</h2>
|
||||||
|
|
||||||
{challengeSelected && challengeSelected?.EventIDList2?.length > 0 && challengeSelected?.EventIDList2[0].MonsterList.map((wave, waveIndex) => (
|
{challengeSelected && challengeSelected?.EventList2?.length > 0 && challengeSelected?.EventList2?.[0]?.MonsterList?.map((wave, waveIndex) => (
|
||||||
<div key={waveIndex} className="mb-6">
|
<div key={waveIndex} className="mb-6">
|
||||||
<h3 className="text-lg font-semibold mb-t">{transI18n("wave")} {waveIndex + 1}</h3>
|
<h3 className="text-lg font-semibold mb-t">{transI18n("wave")} {waveIndex + 1}</h3>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div className="flex flex-wrap gap-2 mt-2">
|
||||||
{Object.values(wave).map((waveValue, enemyIndex) => (
|
{Object.values(wave).map((waveValue, enemyIndex) => {
|
||||||
<div
|
const monsterStats = calcMonsterStats(
|
||||||
key={enemyIndex}
|
mapMonster?.[waveValue.toString()],
|
||||||
className="rounded-xl p-2 border border-white/10 shadow-md hover:border-white/20 hover:shadow-lg transition"
|
challengeSelected?.EventList2?.[0]?.EliteGroup,
|
||||||
>
|
challengeSelected?.EventList2?.[0]?.HardLevelGroup,
|
||||||
|
challengeSelected?.EventList2?.[0]?.Level,
|
||||||
<div className="flex items-center space-x-3">
|
hardLevelConfig,
|
||||||
<div className="relative w-20 h-20 rounded-full overflow-hidden shrink-0 border border-white/10 shadow-sm">
|
eliteConfig
|
||||||
{mapMonster?.[waveValue.toString()]?.icon && <Image
|
);
|
||||||
unoptimized
|
return (
|
||||||
crossOrigin="anonymous"
|
<div
|
||||||
src={`${process.env.CDN_URL}/${mapMonster?.[waveValue.toString()]?.icon}`}
|
key={enemyIndex}
|
||||||
alt="Enemy Icon"
|
className="group relative flex flex-col w-40 bg-base-100 rounded-2xl border border-base-300 shadow-md"
|
||||||
width={376}
|
>
|
||||||
height={512}
|
<div className="badge badge-warning badge-sm font-bold absolute top-2 right-2 z-10 shadow-sm">
|
||||||
className="w-full h-full object-cover"
|
Lv. {challengeSelected?.EventList2[0].Level}
|
||||||
/>}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col">
|
<div className="relative w-full h-20 bg-base-200 flex items-center justify-center p-4 rounded-t-2xl">
|
||||||
<div className="text-sm font-semibold">Lv. {challengeSelected?.EventIDList1[0].Level}</div>
|
{mapMonster?.[waveValue.toString()]?.Image?.IconPath && (
|
||||||
<div className="flex items-center space-x-1 mt-1">
|
<div className="relative w-16 h-16 rounded-full border-2 border-base-300 shadow-md overflow-hidden group-hover:scale-110 transition-transform duration-300 bg-base-100">
|
||||||
{mapMonster?.[waveValue.toString()].weak?.map((icon, iconIndex) => (
|
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`/icon/${icon.toLowerCase()}.webp`}
|
src={`${process.env.CDN_URL}/${mapMonster?.[waveValue.toString()]?.Image?.IconPath}`}
|
||||||
alt={icon}
|
alt="Enemy Icon"
|
||||||
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
|
width={150}
|
||||||
width={200}
|
height={150}
|
||||||
height={200}
|
className="w-full h-full object-cover"
|
||||||
key={iconIndex}
|
|
||||||
/>
|
/>
|
||||||
))}
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col px-1 pb-2 pt-2">
|
||||||
|
<div className="flex flex-col space-y-1.5">
|
||||||
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
|
<span className="text-xs font-semibold text-error">HP</span>
|
||||||
|
<span className="text-sm font-bold text-base-content">{monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
|
<span className="text-xs font-semibold text-info">Speed</span>
|
||||||
|
<span className="text-sm font-bold text-base-content">{monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
|
<span className="text-xs font-semibold text-base-content/70">Toughness</span>
|
||||||
|
<span className="text-sm font-bold text-base-content">{monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-2 pt-2 border-t border-base-300 flex flex-col items-center">
|
||||||
|
<span className="text-[10px] text-base-content/60 font-bold uppercase tracking-widest mb-1.5">
|
||||||
|
Weakness
|
||||||
|
</span>
|
||||||
|
<div className="flex items-center justify-center gap-1.5 flex-wrap">
|
||||||
|
{mapMonster?.[waveValue.toString()]?.StanceWeakList?.map((icon, iconIndex) => (
|
||||||
|
<Image
|
||||||
|
key={iconIndex}
|
||||||
|
unoptimized
|
||||||
|
crossOrigin="anonymous"
|
||||||
|
src={`${process.env.CDN_URL}/${damageType[icon]?.Icon}`}
|
||||||
|
alt={icon}
|
||||||
|
width={40}
|
||||||
|
height={40}
|
||||||
|
className="h-6 w-6 object-contain rounded-full bg-base-300 border border-base-content/10 p-0.5 shadow-sm hover:scale-110 transition-transform"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)
|
||||||
))}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -9,23 +9,21 @@ import {
|
|||||||
CopyPlus,
|
CopyPlus,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
||||||
import useMazeStore from "@/stores/mazeStore";
|
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import useMonsterStore from "@/stores/monsterStore";
|
|
||||||
import useLocaleStore from "@/stores/localeStore";
|
import useLocaleStore from "@/stores/localeStore";
|
||||||
import { getLocaleName } from "@/helper";
|
import { getLocaleName } from "@/helper";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { MonsterBasic } from "@/types";
|
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import useGlobalStore from "@/stores/globalStore";
|
import useGlobalStore from "@/stores/globalStore";
|
||||||
|
import useDetailDataStore from "@/stores/detailDataStore";
|
||||||
|
import { MonsterDetail } from "@/types";
|
||||||
|
|
||||||
|
|
||||||
export default function CeBar() {
|
export default function CeBar() {
|
||||||
const [searchTerm, setSearchTerm] = useState("");
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
const [showSearchWaveId, setShowSearchWaveId] = useState<number | null>(null);
|
const [showSearchWaveId, setShowSearchWaveId] = useState<number | null>(null);
|
||||||
const { Stage } = useMazeStore()
|
|
||||||
const { ce_config, setCeConfig } = useUserDataStore()
|
const { ce_config, setCeConfig } = useUserDataStore()
|
||||||
const { listMonster, mapMonster } = useMonsterStore()
|
const { mapMonster, stage, damageType } = useDetailDataStore()
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
const [showSearchStage, setShowSearchStage] = useState(false)
|
const [showSearchStage, setShowSearchStage] = useState(false)
|
||||||
@@ -39,17 +37,17 @@ export default function CeBar() {
|
|||||||
const [monsterPage, setMonsterPage] = useState(1)
|
const [monsterPage, setMonsterPage] = useState(1)
|
||||||
|
|
||||||
const filteredMonsters = useMemo(() => {
|
const filteredMonsters = useMemo(() => {
|
||||||
const newlistMonster = new Set<MonsterBasic>()
|
const newlistMonster = new Set<MonsterDetail>()
|
||||||
for (const monster of listMonster) {
|
for (const monster of Object.values(mapMonster)) {
|
||||||
if (getLocaleName(locale, monster).toLowerCase().includes(searchTerm.toLowerCase())) {
|
if (getLocaleName(locale, monster.Name).toLowerCase().includes(searchTerm.toLowerCase())) {
|
||||||
newlistMonster.add(monster)
|
newlistMonster.add(monster)
|
||||||
}
|
}
|
||||||
if (monster.id.toLowerCase().includes(searchTerm.toLowerCase())) {
|
if (monster.ID.toString().includes(searchTerm.toLowerCase())) {
|
||||||
newlistMonster.add(monster)
|
newlistMonster.add(monster)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Array.from(newlistMonster)
|
return Array.from(newlistMonster)
|
||||||
}, [listMonster, locale, searchTerm]);
|
}, [locale, searchTerm, mapMonster]);
|
||||||
|
|
||||||
const paginatedMonsters = useMemo(() =>
|
const paginatedMonsters = useMemo(() =>
|
||||||
filteredMonsters.slice((monsterPage - 1) * pageSizeMonsters, monsterPage * pageSizeMonsters),
|
filteredMonsters.slice((monsterPage - 1) * pageSizeMonsters, monsterPage * pageSizeMonsters),
|
||||||
@@ -65,10 +63,10 @@ export default function CeBar() {
|
|||||||
setMonsterPage(1)
|
setMonsterPage(1)
|
||||||
}, [searchTerm])
|
}, [searchTerm])
|
||||||
|
|
||||||
const stageList = useMemo(() => Object.values(Stage).map((stage) => ({
|
const stageList = useMemo(() => Object.values(stage).map((item) => ({
|
||||||
id: stage.stage_id.toString(),
|
id: item.ID.toString(),
|
||||||
name: `${stage.stage_type} (${stage.stage_id})`,
|
name: `${getLocaleName(locale, item.Name)} (${item.ID})`,
|
||||||
})), [Stage])
|
})), [stage, locale])
|
||||||
|
|
||||||
const filteredStages = useMemo(() => stageList.filter((s) =>
|
const filteredStages = useMemo(() => stageList.filter((s) =>
|
||||||
s.name.toLowerCase().includes(stageSearchTerm.toLowerCase())
|
s.name.toLowerCase().includes(stageSearchTerm.toLowerCase())
|
||||||
@@ -312,10 +310,10 @@ export default function CeBar() {
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
{mapMonster?.[member.monster_id.toString()]?.icon && <Image
|
{mapMonster?.[member.monster_id.toString()]?.Image?.IconPath && <Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/${mapMonster?.[member.monster_id.toString()]?.icon}`}
|
src={`${process.env.CDN_URL}/${mapMonster?.[member.monster_id.toString()]?.Image?.IconPath}`}
|
||||||
alt="Enemy Icon"
|
alt="Enemy Icon"
|
||||||
width={376}
|
width={376}
|
||||||
height={512}
|
height={512}
|
||||||
@@ -324,11 +322,11 @@ export default function CeBar() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-wrap justify-center gap-1 mb-2">
|
<div className="flex flex-wrap justify-center gap-1 mb-2">
|
||||||
{mapMonster?.[member.monster_id.toString()]?.weak?.map((icon, iconIndex) => (
|
{mapMonster?.[member.monster_id.toString()]?.StanceWeakList?.map((icon, iconIndex) => (
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`/icon/${icon.toLowerCase()}.webp`}
|
src={`${process.env.CDN_URL}/${damageType[icon]?.Icon}`}
|
||||||
alt={icon}
|
alt={icon}
|
||||||
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
|
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
|
||||||
width={200}
|
width={200}
|
||||||
@@ -339,7 +337,7 @@ export default function CeBar() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="text-center flex flex-col items-center justify-center">
|
<div className="text-center flex flex-col items-center justify-center">
|
||||||
<div className="text-sm font-medium">
|
<div className="text-sm font-medium">
|
||||||
{getLocaleName(locale, mapMonster?.[member.monster_id.toString()])} {`(${member.monster_id})`}
|
{getLocaleName(locale, mapMonster?.[member.monster_id.toString()]?.Name)} {`(${member.monster_id})`}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1 mt-1 mx-2">
|
<div className="flex items-center gap-1 mt-1 mx-2">
|
||||||
<span className="text-sm">LV.</span>
|
<span className="text-sm">LV.</span>
|
||||||
@@ -444,12 +442,12 @@ export default function CeBar() {
|
|||||||
{paginatedMonsters.length > 0 ? (
|
{paginatedMonsters.length > 0 ? (
|
||||||
paginatedMonsters.map((monster) => (
|
paginatedMonsters.map((monster) => (
|
||||||
<div
|
<div
|
||||||
key={monster.id}
|
key={monster.ID}
|
||||||
className="flex items-center gap-2 p-2 hover:bg-success/40 rounded cursor-pointer"
|
className="flex items-center gap-2 p-2 hover:bg-success/40 rounded cursor-pointer"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const newCeConfig = structuredClone(ce_config)
|
const newCeConfig = structuredClone(ce_config)
|
||||||
newCeConfig.monsters[waveIndex].push({
|
newCeConfig.monsters[waveIndex].push({
|
||||||
monster_id: Number(monster.id),
|
monster_id: monster.ID,
|
||||||
level: 95,
|
level: 95,
|
||||||
amount: 1,
|
amount: 1,
|
||||||
})
|
})
|
||||||
@@ -458,11 +456,11 @@ export default function CeBar() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="relative w-8 h-8 rounded-full overflow-hidden shrink-0 border border-white/10 shadow-sm">
|
<div className="relative w-8 h-8 rounded-full overflow-hidden shrink-0 border border-white/10 shadow-sm">
|
||||||
{mapMonster?.[monster.id.toString()]?.icon && (
|
{mapMonster?.[monster.ID.toString()]?.Image?.IconPath && (
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/${mapMonster?.[monster.id.toString()]?.icon}`}
|
src={`${process.env.CDN_URL}/${mapMonster?.[monster.ID.toString()]?.Image?.IconPath}`}
|
||||||
alt="Enemy Icon"
|
alt="Enemy Icon"
|
||||||
width={376}
|
width={376}
|
||||||
height={512}
|
height={512}
|
||||||
@@ -470,7 +468,7 @@ export default function CeBar() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<span>{getLocaleName(locale, monster)} {`(${monster.id})`}</span>
|
<span>{getLocaleName(locale, monster.Name)} {`(${monster.ID})`}</span>
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -2,115 +2,117 @@
|
|||||||
|
|
||||||
import { useEffect, useMemo } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
import SelectCustomText from "../select/customSelectText";
|
import SelectCustomText from "../select/customSelectText";
|
||||||
import useEventStore from "@/stores/eventStore";
|
import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper";
|
||||||
import { getLocaleName, replaceByParam } from "@/helper";
|
|
||||||
import useLocaleStore from "@/stores/localeStore";
|
import useLocaleStore from "@/stores/localeStore";
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import useMonsterStore from "@/stores/monsterStore";
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import useMazeStore from "@/stores/mazeStore";
|
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { MonsterStore } from "@/types";
|
import { MonsterStore } from "@/types";
|
||||||
|
import useDetailDataStore from "@/stores/detailDataStore";
|
||||||
|
|
||||||
export default function MocBar() {
|
export default function MocBar() {
|
||||||
const { MOCEvent, mapMOCInfo } = useEventStore()
|
|
||||||
const { mapMonster } = useMonsterStore()
|
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
const {
|
const {
|
||||||
moc_config,
|
moc_config,
|
||||||
setMocConfig
|
setMocConfig
|
||||||
} = useUserDataStore()
|
} = useUserDataStore()
|
||||||
const { MOC } = useMazeStore()
|
const { mapMonster, mapMoc, damageType, hardLevelConfig, eliteConfig } = useDetailDataStore()
|
||||||
|
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
|
|
||||||
const challengeSelected = useMemo(() => {
|
const challengeSelected = useMemo(() => {
|
||||||
return mapMOCInfo[moc_config.event_id.toString()]?.find((moc) => moc.Id === moc_config.challenge_id)
|
return mapMoc[moc_config.event_id.toString()]?.Level.find((moc) => moc.ID === moc_config.challenge_id)
|
||||||
}, [moc_config, mapMOCInfo])
|
}, [moc_config, mapMoc])
|
||||||
|
|
||||||
|
const eventSelected = useMemo(() => {
|
||||||
|
return mapMoc[moc_config.event_id.toString()]
|
||||||
|
}, [moc_config, mapMoc])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const challenge = mapMOCInfo[moc_config.event_id.toString()]?.find((moc) => moc.Id === moc_config.challenge_id)
|
if (!challengeSelected || moc_config.event_id === 0 || moc_config.challenge_id === 0) return
|
||||||
if (moc_config.event_id !== 0 && moc_config.challenge_id !== 0 && challenge) {
|
|
||||||
const newBattleConfig = structuredClone(moc_config)
|
const newBattleConfig = structuredClone(moc_config)
|
||||||
newBattleConfig.cycle_count = 0
|
newBattleConfig.cycle_count = 0
|
||||||
if (moc_config.use_cycle_count) {
|
if (moc_config.use_cycle_count) {
|
||||||
newBattleConfig.cycle_count = challenge.Countdown
|
newBattleConfig.cycle_count = challengeSelected.TurnLimit
|
||||||
}
|
}
|
||||||
newBattleConfig.blessings = []
|
newBattleConfig.blessings = []
|
||||||
if (moc_config.use_turbulence_buff && MOC[moc_config.challenge_id.toString()]) {
|
if (moc_config.use_turbulence_buff && challengeSelected) {
|
||||||
|
challengeSelected.MazeBuff.map((item) => {
|
||||||
newBattleConfig.blessings.push({
|
newBattleConfig.blessings.push({
|
||||||
id: Number(MOC[moc_config.challenge_id.toString()].maze_buff),
|
id: item.ID,
|
||||||
level: 1
|
level: 1
|
||||||
})
|
})
|
||||||
}
|
})
|
||||||
newBattleConfig.monsters = []
|
|
||||||
newBattleConfig.stage_id = 0
|
|
||||||
if ((moc_config.floor_side === "Upper" || moc_config.floor_side === "Upper -> Lower") && challenge.EventIDList1.length > 0) {
|
|
||||||
newBattleConfig.stage_id = challenge.EventIDList1[0].StageID
|
|
||||||
for (const wave of challenge.EventIDList1[0].MonsterList) {
|
|
||||||
const newWave: MonsterStore[] = []
|
|
||||||
for (const value of Object.values(wave)) {
|
|
||||||
newWave.push({
|
|
||||||
monster_id: Number(value),
|
|
||||||
level: challenge.EventIDList1[0].Level,
|
|
||||||
amount: 1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
newBattleConfig.monsters.push(newWave)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((moc_config.floor_side === "Lower" || moc_config.floor_side === "Lower -> Upper") && challenge.EventIDList2.length > 0) {
|
|
||||||
newBattleConfig.stage_id = challenge.EventIDList2[0].StageID
|
|
||||||
for (const wave of challenge.EventIDList2[0].MonsterList) {
|
|
||||||
const newWave: MonsterStore[] = []
|
|
||||||
for (const value of Object.values(wave)) {
|
|
||||||
newWave.push({
|
|
||||||
monster_id: Number(value),
|
|
||||||
level: challenge.EventIDList2[0].Level,
|
|
||||||
amount: 1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
newBattleConfig.monsters.push(newWave)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (moc_config.floor_side === "Lower -> Upper" && challenge.EventIDList1.length > 0) {
|
|
||||||
for (const wave of challenge.EventIDList1[0].MonsterList) {
|
|
||||||
const newWave: MonsterStore[] = []
|
|
||||||
for (const value of Object.values(wave)) {
|
|
||||||
newWave.push({
|
|
||||||
monster_id: Number(value),
|
|
||||||
level: challenge.EventIDList1[0].Level,
|
|
||||||
amount: 1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
newBattleConfig.monsters.push(newWave)
|
|
||||||
}
|
|
||||||
} else if (moc_config.floor_side === "Upper -> Lower" && challenge.EventIDList2.length > 0) {
|
|
||||||
for (const wave of challenge.EventIDList2[0].MonsterList) {
|
|
||||||
const newWave: MonsterStore[] = []
|
|
||||||
for (const value of Object.values(wave)) {
|
|
||||||
newWave.push({
|
|
||||||
monster_id: Number(value),
|
|
||||||
level: challenge.EventIDList2[0].Level,
|
|
||||||
amount: 1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
newBattleConfig.monsters.push(newWave)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setMocConfig(newBattleConfig)
|
|
||||||
}
|
}
|
||||||
|
newBattleConfig.monsters = []
|
||||||
|
newBattleConfig.stage_id = 0
|
||||||
|
if ((moc_config.floor_side === "Upper" || moc_config.floor_side === "Upper -> Lower") && challengeSelected.EventList1.length > 0) {
|
||||||
|
newBattleConfig.stage_id = challengeSelected.EventList1[0].ID
|
||||||
|
for (const wave of challengeSelected.EventList1[0].MonsterList) {
|
||||||
|
const newWave: MonsterStore[] = []
|
||||||
|
for (const value of Object.values(wave)) {
|
||||||
|
newWave.push({
|
||||||
|
monster_id: value,
|
||||||
|
level: challengeSelected.EventList1[0].Level,
|
||||||
|
amount: 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
newBattleConfig.monsters.push(newWave)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((moc_config.floor_side === "Lower" || moc_config.floor_side === "Lower -> Upper") && challengeSelected.EventList2.length > 0) {
|
||||||
|
newBattleConfig.stage_id = challengeSelected.EventList2[0].ID
|
||||||
|
for (const wave of challengeSelected.EventList2[0].MonsterList) {
|
||||||
|
const newWave: MonsterStore[] = []
|
||||||
|
for (const value of Object.values(wave)) {
|
||||||
|
newWave.push({
|
||||||
|
monster_id: value,
|
||||||
|
level: challengeSelected.EventList2[0].Level,
|
||||||
|
amount: 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
newBattleConfig.monsters.push(newWave)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (moc_config.floor_side === "Lower -> Upper" && challengeSelected.EventList1.length > 0) {
|
||||||
|
for (const wave of challengeSelected.EventList1[0].MonsterList) {
|
||||||
|
const newWave: MonsterStore[] = []
|
||||||
|
for (const value of Object.values(wave)) {
|
||||||
|
newWave.push({
|
||||||
|
monster_id: value,
|
||||||
|
level: challengeSelected.EventList1[0].Level,
|
||||||
|
amount: 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
newBattleConfig.monsters.push(newWave)
|
||||||
|
}
|
||||||
|
} else if (moc_config.floor_side === "Upper -> Lower" && challengeSelected.EventList2.length > 0) {
|
||||||
|
for (const wave of challengeSelected.EventList2[0].MonsterList) {
|
||||||
|
const newWave: MonsterStore[] = []
|
||||||
|
for (const value of Object.values(wave)) {
|
||||||
|
newWave.push({
|
||||||
|
monster_id: value,
|
||||||
|
level: challengeSelected.EventList2[0].Level,
|
||||||
|
amount: 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
newBattleConfig.monsters.push(newWave)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setMocConfig(newBattleConfig)
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [
|
}, [
|
||||||
moc_config.event_id,
|
moc_config.event_id,
|
||||||
moc_config.challenge_id,
|
moc_config.challenge_id,
|
||||||
moc_config.floor_side,
|
moc_config.floor_side,
|
||||||
mapMOCInfo,
|
|
||||||
MOC,
|
|
||||||
moc_config.use_cycle_count,
|
moc_config.use_cycle_count,
|
||||||
moc_config.use_turbulence_buff,
|
moc_config.use_turbulence_buff,
|
||||||
|
mapMoc,
|
||||||
])
|
])
|
||||||
if (!MOCEvent) return null
|
|
||||||
|
if (!mapMoc) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="py-8 relative">
|
<div className="py-8 relative">
|
||||||
|
|
||||||
@@ -118,10 +120,10 @@ export default function MocBar() {
|
|||||||
<div className="rounded-xl p-4 mb-2 border border-warning">
|
<div className="rounded-xl p-4 mb-2 border border-warning">
|
||||||
<div className="mb-4 w-full">
|
<div className="mb-4 w-full">
|
||||||
<SelectCustomText
|
<SelectCustomText
|
||||||
customSet={MOCEvent.map((moc) => ({
|
customSet={Object.values(mapMoc).sort((a, b) => b.ID - a.ID).map((moc) => ({
|
||||||
id: moc.id,
|
id: moc.ID.toString(),
|
||||||
name: getLocaleName(locale, moc),
|
name: getLocaleName(locale, moc.Name),
|
||||||
time: `${moc.begin} - ${moc.end}`,
|
time: `${moc.BeginTime} - ${moc.EndTime}`,
|
||||||
}))}
|
}))}
|
||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={moc_config.event_id.toString()}
|
selectedCustomSet={moc_config.event_id.toString()}
|
||||||
@@ -129,7 +131,7 @@ export default function MocBar() {
|
|||||||
setSelectedCustomSet={(id) => setMocConfig({
|
setSelectedCustomSet={(id) => setMocConfig({
|
||||||
...moc_config,
|
...moc_config,
|
||||||
event_id: Number(id),
|
event_id: Number(id),
|
||||||
challenge_id: mapMOCInfo[Number(id)]?.slice(-1)[0]?.Id || 0,
|
challenge_id: mapMoc[Number(id)]?.Level.at(-1)?.ID || 0,
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -149,8 +151,8 @@ export default function MocBar() {
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<option value={0} disabled={true}>Select a Floor</option>
|
<option value={0} disabled={true}>Select a Floor</option>
|
||||||
{mapMOCInfo[moc_config.event_id.toString()]?.map((moc) => (
|
{eventSelected?.Level?.map((moc) => (
|
||||||
<option key={moc.Id} value={moc.Id}>{moc.Id % 100}</option>
|
<option key={moc.ID} value={moc.ID}>{getLocaleName(locale, moc.Name)}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@@ -205,16 +207,19 @@ export default function MocBar() {
|
|||||||
{transI18n("useTurbulenceBuff")}
|
{transI18n("useTurbulenceBuff")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{challengeSelected && challengeSelected?.Desc ? (
|
{challengeSelected ? (
|
||||||
<div
|
challengeSelected.MazeBuff.map((buff, i) => (
|
||||||
className="text-base"
|
<div
|
||||||
dangerouslySetInnerHTML={{
|
key={i}
|
||||||
__html: replaceByParam(
|
className="text-base"
|
||||||
challengeSelected?.Desc || "",
|
dangerouslySetInnerHTML={{
|
||||||
challengeSelected?.Param || []
|
__html: replaceByParam(
|
||||||
)
|
getLocaleName(locale, buff?.Desc) || "",
|
||||||
}}
|
buff?.Param || []
|
||||||
/>
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))
|
||||||
) : (
|
) : (
|
||||||
<div className="text-base">{transI18n("noTurbulenceBuff")}</div>
|
<div className="text-base">{transI18n("noTurbulenceBuff")}</div>
|
||||||
)}
|
)}
|
||||||
@@ -228,48 +233,85 @@ export default function MocBar() {
|
|||||||
<div className="rounded-xl p-4 mt-2 border border-warning">
|
<div className="rounded-xl p-4 mt-2 border border-warning">
|
||||||
<h2 className="text-2xl font-bold mb-6 text-info">{transI18n("firstHalfEnemies")}</h2>
|
<h2 className="text-2xl font-bold mb-6 text-info">{transI18n("firstHalfEnemies")}</h2>
|
||||||
|
|
||||||
{challengeSelected && challengeSelected?.EventIDList1?.length > 0 && challengeSelected?.EventIDList1[0].MonsterList.map((wave, waveIndex) => (
|
{challengeSelected && challengeSelected?.EventList1?.length > 0 && challengeSelected?.EventList1?.[0]?.MonsterList?.map((wave, waveIndex) => (
|
||||||
<div key={waveIndex} className="mb-6">
|
<div key={waveIndex} className="mb-6">
|
||||||
<h3 className="text-lg font-semibold mb-t">{transI18n("wave")} {waveIndex + 1}</h3>
|
<h3 className="text-lg font-semibold">{transI18n("wave")} {waveIndex + 1}</h3>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div className="flex flex-wrap gap-2 mt-2">
|
||||||
{Object.values(wave).map((waveValue, enemyIndex) => (
|
{Object.values(wave).map((waveValue, enemyIndex) => {
|
||||||
<div
|
const monsterStats = calcMonsterStats(
|
||||||
key={enemyIndex}
|
mapMonster?.[waveValue.toString()],
|
||||||
className="rounded-xl p-2 border border-white/10 shadow-md hover:border-white/20 hover:shadow-lg transition"
|
challengeSelected?.EventList1?.[0]?.EliteGroup,
|
||||||
>
|
challengeSelected?.EventList1?.[0]?.HardLevelGroup,
|
||||||
<div className="flex items-center space-x-3">
|
challengeSelected?.EventList1?.[0]?.Level,
|
||||||
<div className="relative w-20 h-20 rounded-full overflow-hidden shrink-0 border border-white/10 shadow-sm">
|
hardLevelConfig,
|
||||||
{mapMonster?.[waveValue.toString()]?.icon && <Image
|
eliteConfig
|
||||||
unoptimized
|
);
|
||||||
crossOrigin="anonymous"
|
return (
|
||||||
src={`${process.env.CDN_URL}/${mapMonster?.[waveValue.toString()]?.icon}`}
|
<div
|
||||||
alt="Enemy Icon"
|
key={enemyIndex}
|
||||||
width={376}
|
className="group relative flex flex-col w-40 bg-base-100 rounded-2xl border border-base-300 shadow-md"
|
||||||
height={512}
|
>
|
||||||
className="w-full h-full object-cover"
|
<div className="badge badge-warning badge-sm font-bold absolute top-2 right-2 z-10 shadow-sm">
|
||||||
/>}
|
Lv. {challengeSelected?.EventList1[0].Level}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col">
|
<div className="relative w-full h-20 bg-base-200 flex items-center justify-center p-4 rounded-t-2xl">
|
||||||
<div className="text-sm font-semibold">Lv. {challengeSelected?.EventIDList1[0].Level}</div>
|
{mapMonster?.[waveValue.toString()]?.Image?.IconPath && (
|
||||||
<div className="flex items-center space-x-1 mt-1">
|
<div className="relative w-16 h-16 rounded-full border-2 border-base-300 shadow-md overflow-hidden group-hover:scale-110 transition-transform duration-300 bg-base-100">
|
||||||
{mapMonster?.[waveValue.toString()]?.weak?.map((icon, iconIndex) => (
|
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`/icon/${icon.toLowerCase()}.webp`}
|
src={`${process.env.CDN_URL}/${mapMonster?.[waveValue.toString()]?.Image?.IconPath}`}
|
||||||
alt={icon}
|
alt="Enemy Icon"
|
||||||
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
|
width={150}
|
||||||
width={200}
|
height={150}
|
||||||
height={200}
|
className="w-full h-full object-cover"
|
||||||
key={iconIndex}
|
|
||||||
/>
|
/>
|
||||||
))}
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col px-1 pb-2 pt-2">
|
||||||
|
<div className="flex flex-col space-y-1.5">
|
||||||
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
|
<span className="text-xs font-semibold text-error">HP</span>
|
||||||
|
<span className="text-sm font-bold text-base-content">{monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
|
<span className="text-xs font-semibold text-info">Speed</span>
|
||||||
|
<span className="text-sm font-bold text-base-content">{monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
|
<span className="text-xs font-semibold text-base-content/70">Toughness</span>
|
||||||
|
<span className="text-sm font-bold text-base-content">{monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-2 pt-2 border-t border-base-300 flex flex-col items-center">
|
||||||
|
<span className="text-[10px] text-base-content/60 font-bold uppercase tracking-widest mb-1.5">
|
||||||
|
Weakness
|
||||||
|
</span>
|
||||||
|
<div className="flex items-center justify-center gap-1.5 flex-wrap">
|
||||||
|
{mapMonster?.[waveValue.toString()]?.StanceWeakList?.map((icon, iconIndex) => (
|
||||||
|
<Image
|
||||||
|
key={iconIndex}
|
||||||
|
unoptimized
|
||||||
|
crossOrigin="anonymous"
|
||||||
|
src={`${process.env.CDN_URL}/${damageType[icon]?.Icon}`}
|
||||||
|
alt={icon}
|
||||||
|
width={40}
|
||||||
|
height={40}
|
||||||
|
className="h-6 w-6 object-contain rounded-full bg-base-300 border border-base-content/10 p-0.5 shadow-sm hover:scale-110 transition-transform"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)
|
||||||
))}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -279,49 +321,85 @@ export default function MocBar() {
|
|||||||
<div className="rounded-xl p-4 mt-2 border border-warning">
|
<div className="rounded-xl p-4 mt-2 border border-warning">
|
||||||
<h2 className="text-2xl font-bold mb-6 text-info">{transI18n("secondHalfEnemies")}</h2>
|
<h2 className="text-2xl font-bold mb-6 text-info">{transI18n("secondHalfEnemies")}</h2>
|
||||||
|
|
||||||
{challengeSelected && challengeSelected?.EventIDList2?.length > 0 && challengeSelected?.EventIDList2[0].MonsterList.map((wave, waveIndex) => (
|
{challengeSelected && challengeSelected?.EventList2?.length > 0 && challengeSelected?.EventList2?.[0]?.MonsterList?.map((wave, waveIndex) => (
|
||||||
<div key={waveIndex} className="mb-6">
|
<div key={waveIndex} className="mb-6">
|
||||||
<h3 className="text-lg font-semibold mb-t">{transI18n("wave")} {waveIndex + 1}</h3>
|
<h3 className="text-lg font-semibold">{transI18n("wave")} {waveIndex + 1}</h3>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div className="flex flex-wrap gap-2 mt-2">
|
||||||
{Object.values(wave).map((waveValue, enemyIndex) => (
|
{Object.values(wave).map((waveValue, enemyIndex) => {
|
||||||
<div
|
const monsterStats = calcMonsterStats(
|
||||||
key={enemyIndex}
|
mapMonster?.[waveValue.toString()],
|
||||||
className="rounded-xl p-2 border border-white/10 shadow-md hover:border-white/20 hover:shadow-lg transition"
|
challengeSelected?.EventList2?.[0]?.EliteGroup,
|
||||||
>
|
challengeSelected?.EventList2?.[0]?.HardLevelGroup,
|
||||||
|
challengeSelected?.EventList2?.[0]?.Level,
|
||||||
<div className="flex items-center space-x-3">
|
hardLevelConfig,
|
||||||
<div className="relative w-20 h-20 rounded-full overflow-hidden shrink-0 border border-white/10 shadow-sm">
|
eliteConfig
|
||||||
{mapMonster?.[waveValue.toString()]?.icon && <Image
|
);
|
||||||
unoptimized
|
return (
|
||||||
crossOrigin="anonymous"
|
<div
|
||||||
src={`${process.env.CDN_URL}/${mapMonster?.[waveValue.toString()]?.icon}`}
|
key={enemyIndex}
|
||||||
alt="Enemy Icon"
|
className="group relative flex flex-col w-40 bg-base-100 rounded-2xl border border-base-300 shadow-md"
|
||||||
width={376}
|
>
|
||||||
height={512}
|
<div className="badge badge-warning badge-sm font-bold absolute top-2 right-2 z-10 shadow-sm">
|
||||||
className="w-full h-full object-cover"
|
Lv. {challengeSelected?.EventList2[0].Level}
|
||||||
/>}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col">
|
<div className="relative w-full h-20 bg-base-200 flex items-center justify-center p-4 rounded-t-2xl">
|
||||||
<div className="text-sm font-semibold">Lv. {challengeSelected?.EventIDList1[0].Level}</div>
|
{mapMonster?.[waveValue.toString()]?.Image?.IconPath && (
|
||||||
<div className="flex items-center space-x-1 mt-1">
|
<div className="relative w-16 h-16 rounded-full border-2 border-base-300 shadow-md overflow-hidden group-hover:scale-110 transition-transform duration-300 bg-base-100">
|
||||||
{mapMonster?.[waveValue.toString()]?.weak?.map((icon, iconIndex) => (
|
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`/icon/${icon.toLowerCase()}.webp`}
|
src={`${process.env.CDN_URL}/${mapMonster?.[waveValue.toString()]?.Image?.IconPath}`}
|
||||||
alt={icon}
|
alt="Enemy Icon"
|
||||||
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
|
width={150}
|
||||||
width={200}
|
height={150}
|
||||||
height={200}
|
className="w-full h-full object-cover"
|
||||||
key={iconIndex}
|
|
||||||
/>
|
/>
|
||||||
))}
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col px-1 pb-2 pt-2">
|
||||||
|
<div className="flex flex-col space-y-1.5">
|
||||||
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
|
<span className="text-xs font-semibold text-error">HP</span>
|
||||||
|
<span className="text-sm font-bold text-base-content">{monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
|
<span className="text-xs font-semibold text-info">Speed</span>
|
||||||
|
<span className="text-sm font-bold text-base-content">{monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
|
<span className="text-xs font-semibold text-base-content/70">Toughness</span>
|
||||||
|
<span className="text-sm font-bold text-base-content">{monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-2 pt-2 border-t border-base-300 flex flex-col items-center">
|
||||||
|
<span className="text-[10px] text-base-content/60 font-bold uppercase tracking-widest mb-1.5">
|
||||||
|
Weakness
|
||||||
|
</span>
|
||||||
|
<div className="flex items-center justify-center gap-1.5 flex-wrap">
|
||||||
|
{mapMonster?.[waveValue.toString()]?.StanceWeakList?.map((icon, iconIndex) => (
|
||||||
|
<Image
|
||||||
|
key={iconIndex}
|
||||||
|
unoptimized
|
||||||
|
crossOrigin="anonymous"
|
||||||
|
src={`${process.env.CDN_URL}/${damageType[icon]?.Icon}`}
|
||||||
|
alt={icon}
|
||||||
|
width={40}
|
||||||
|
height={40}
|
||||||
|
className="h-6 w-6 object-contain rounded-full bg-base-300 border border-base-content/10 p-0.5 shadow-sm hover:scale-110 transition-transform"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)
|
||||||
))}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,57 +1,49 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useEffect, useMemo } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
import SelectCustomText from "../select/customSelectText";
|
import SelectCustomText from "../select/customSelectText";
|
||||||
import useEventStore from "@/stores/eventStore";
|
import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper";
|
||||||
import { getLocaleName, replaceByParam } from "@/helper";
|
|
||||||
import useLocaleStore from "@/stores/localeStore";
|
import useLocaleStore from "@/stores/localeStore";
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";;
|
||||||
import useMonsterStore from "@/stores/monsterStore";
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { MonsterStore } from "@/types";
|
import { MonsterStore } from "@/types";
|
||||||
|
import useDetailDataStore from "@/stores/detailDataStore";
|
||||||
|
|
||||||
export default function PeakBar() {
|
export default function PeakBar() {
|
||||||
const { PEAKEvent, mapPEAKInfo } = useEventStore()
|
|
||||||
const { mapMonster } = useMonsterStore()
|
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
const {
|
const {
|
||||||
peak_config,
|
peak_config,
|
||||||
setPeakConfig
|
setPeakConfig
|
||||||
} = useUserDataStore()
|
} = useUserDataStore()
|
||||||
|
const { mapMonster, mapPeak, damageType, eliteConfig, hardLevelConfig } = useDetailDataStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
|
|
||||||
const listFloor = useMemo(() => {
|
const listFloor = useMemo(() => {
|
||||||
if (!mapPEAKInfo?.[peak_config?.event_id?.toString()]) return []
|
const peak = mapPeak?.[peak_config?.event_id?.toString()]
|
||||||
return [
|
if (!peak) return []
|
||||||
...mapPEAKInfo[peak_config?.event_id?.toString()]?.PreLevel,
|
|
||||||
mapPEAKInfo[peak_config?.event_id?.toString()]?.BossLevel,
|
|
||||||
]
|
|
||||||
}, [peak_config, mapPEAKInfo])
|
|
||||||
|
|
||||||
|
return [...peak.PreLevel, peak.BossLevel].filter(it => it != null)
|
||||||
|
}, [peak_config, mapPeak])
|
||||||
const eventSelected = useMemo(() => {
|
const eventSelected = useMemo(() => {
|
||||||
return mapPEAKInfo?.[peak_config?.event_id?.toString()]
|
return mapPeak?.[peak_config?.event_id?.toString()]
|
||||||
}, [peak_config, mapPEAKInfo])
|
}, [peak_config, mapPeak])
|
||||||
|
|
||||||
const bossConfig = useMemo(() => {
|
const bossConfig = useMemo(() => {
|
||||||
return mapPEAKInfo?.[peak_config?.event_id?.toString()]?.BossConfig;
|
return mapPeak?.[peak_config?.event_id?.toString()]?.BossConfig;
|
||||||
}, [peak_config, mapPEAKInfo])
|
}, [peak_config, mapPeak])
|
||||||
|
|
||||||
const challengeSelected = useMemo(() => {
|
const challengeSelected = useMemo(() => {
|
||||||
const challenge = structuredClone(listFloor.find((peak) => peak.Id === peak_config.challenge_id))
|
const challenge = structuredClone(listFloor?.find((peak) => peak?.ID === peak_config.challenge_id))
|
||||||
if (
|
if (
|
||||||
challenge
|
challenge
|
||||||
&& challenge.Id === mapPEAKInfo?.[peak_config?.event_id?.toString()]?.BossLevel?.Id
|
&& challenge.ID === mapPeak?.[peak_config?.event_id?.toString()]?.BossLevel?.ID
|
||||||
&& bossConfig
|
&& bossConfig
|
||||||
&& peak_config?.boss_mode === "Hard"
|
&& peak_config?.boss_mode === "Hard"
|
||||||
) {
|
) {
|
||||||
challenge.Name = bossConfig.HardName
|
return bossConfig
|
||||||
challenge.EventIDList = bossConfig.EventIDList
|
|
||||||
challenge.InfiniteList = bossConfig.InfiniteList
|
|
||||||
challenge.TagList = bossConfig.TagList
|
|
||||||
}
|
}
|
||||||
return challenge
|
return challenge
|
||||||
}, [peak_config, listFloor, mapPEAKInfo, bossConfig])
|
}, [peak_config, listFloor, mapPeak, bossConfig])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!challengeSelected) return
|
if (!challengeSelected) return
|
||||||
@@ -59,9 +51,9 @@ export default function PeakBar() {
|
|||||||
const newBattleConfig = structuredClone(peak_config)
|
const newBattleConfig = structuredClone(peak_config)
|
||||||
newBattleConfig.cycle_count = 6
|
newBattleConfig.cycle_count = 6
|
||||||
newBattleConfig.blessings = []
|
newBattleConfig.blessings = []
|
||||||
for (const value of challengeSelected.TagList) {
|
for (const value of challengeSelected.MazeBuff) {
|
||||||
newBattleConfig.blessings.push({
|
newBattleConfig.blessings.push({
|
||||||
id: Number(value.Id),
|
id: value.ID,
|
||||||
level: 1
|
level: 1
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -72,15 +64,15 @@ export default function PeakBar() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
newBattleConfig.monsters = []
|
newBattleConfig.monsters = []
|
||||||
newBattleConfig.stage_id = challengeSelected.EventIDList[0].StageID
|
newBattleConfig.stage_id = challengeSelected.EventList[0].ID
|
||||||
for (const wave of challengeSelected.EventIDList[0].MonsterList) {
|
for (const wave of challengeSelected.EventList[0].MonsterList) {
|
||||||
if (!wave) continue
|
if (!wave) continue
|
||||||
const newWave: MonsterStore[] = []
|
const newWave: MonsterStore[] = []
|
||||||
for (const value of Object.values(wave)) {
|
for (const value of Object.values(wave)) {
|
||||||
if (!value) continue
|
if (!value) continue
|
||||||
newWave.push({
|
newWave.push({
|
||||||
monster_id: Number(value),
|
monster_id: value,
|
||||||
level: challengeSelected.EventIDList[0].Level,
|
level: challengeSelected.EventList[0].Level,
|
||||||
amount: 1,
|
amount: 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -94,10 +86,11 @@ export default function PeakBar() {
|
|||||||
peak_config.event_id,
|
peak_config.event_id,
|
||||||
peak_config.challenge_id,
|
peak_config.challenge_id,
|
||||||
peak_config.buff_id,
|
peak_config.buff_id,
|
||||||
mapPEAKInfo,
|
peak_config.boss_mode,
|
||||||
|
mapPeak,
|
||||||
])
|
])
|
||||||
|
|
||||||
if (!PEAKEvent) return null
|
if (!mapPeak) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="py-8 relative">
|
<div className="py-8 relative">
|
||||||
@@ -106,9 +99,9 @@ export default function PeakBar() {
|
|||||||
<div className="rounded-xl p-4 mb-2 border border-warning">
|
<div className="rounded-xl p-4 mb-2 border border-warning">
|
||||||
<div className="mb-4 w-full">
|
<div className="mb-4 w-full">
|
||||||
<SelectCustomText
|
<SelectCustomText
|
||||||
customSet={PEAKEvent.map((peak) => ({
|
customSet={Object.values(mapPeak).sort((a, b) => b.ID - a.ID).map((peak) => ({
|
||||||
id: peak.id,
|
id: peak.ID.toString(),
|
||||||
name: `${getLocaleName(locale, peak)} (${peak.id})`,
|
name: `${getLocaleName(locale, peak.Name)} (${peak.ID})`,
|
||||||
}))}
|
}))}
|
||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={peak_config.event_id.toString()}
|
selectedCustomSet={peak_config.event_id.toString()}
|
||||||
@@ -119,7 +112,7 @@ export default function PeakBar() {
|
|||||||
{/* Settings */}
|
{/* Settings */}
|
||||||
<div className={
|
<div className={
|
||||||
`grid grid-cols-1
|
`grid grid-cols-1
|
||||||
${eventSelected && eventSelected.BossLevel.Id === peak_config.challenge_id ? "md:grid-cols-2" : ""}
|
${eventSelected && eventSelected.BossLevel?.ID === peak_config.challenge_id ? "md:grid-cols-2" : ""}
|
||||||
gap-4 mb-4 justify-items-center items-center w-full`}
|
gap-4 mb-4 justify-items-center items-center w-full`}
|
||||||
>
|
>
|
||||||
|
|
||||||
@@ -134,11 +127,11 @@ export default function PeakBar() {
|
|||||||
>
|
>
|
||||||
<option value={0} disabled={true}>{transI18n("selectFloor")}</option>
|
<option value={0} disabled={true}>{transI18n("selectFloor")}</option>
|
||||||
{listFloor.map((peak) => (
|
{listFloor.map((peak) => (
|
||||||
<option key={peak.Id} value={peak.Id}>{peak.Name}</option>
|
<option key={peak.ID} value={peak.ID}>{getLocaleName(locale, peak.Name)}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
{eventSelected && eventSelected.BossLevel.Id === peak_config.challenge_id && (
|
{eventSelected && eventSelected.BossLevel?.ID === peak_config.challenge_id && (
|
||||||
<div className="flex items-center gap-2 w-full">
|
<div className="flex items-center gap-2 w-full">
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-bold text-success">{transI18n("mode")}:{" "}</span>
|
<span className="label-text font-bold text-success">{transI18n("mode")}:{" "}</span>
|
||||||
@@ -158,20 +151,17 @@ export default function PeakBar() {
|
|||||||
</div>
|
</div>
|
||||||
{
|
{
|
||||||
eventSelected
|
eventSelected
|
||||||
&& eventSelected.BossLevel.Id === peak_config.challenge_id
|
&& eventSelected.BossLevel?.ID === peak_config.challenge_id
|
||||||
&& bossConfig
|
&& bossConfig
|
||||||
&& bossConfig.BuffList
|
|
||||||
&& (
|
&& (
|
||||||
<div className="mb-4 w-full">
|
<div className="mb-4 w-full">
|
||||||
<SelectCustomText
|
<SelectCustomText
|
||||||
customSet={
|
customSet={
|
||||||
Array.isArray(bossConfig.BuffList)
|
bossConfig.BuffList.map((buff) => ({
|
||||||
? bossConfig.BuffList.map((buff) => ({
|
id: buff.ID.toString(),
|
||||||
id: buff.Id.toString(),
|
name: getLocaleName(locale, buff?.Name || ""),
|
||||||
name: buff?.Name || "",
|
description: replaceByParam(getLocaleName(locale, buff?.Desc || ""), buff?.Param || []),
|
||||||
description: replaceByParam(buff?.Desc || "", buff?.Param || []),
|
}))
|
||||||
}))
|
|
||||||
: []
|
|
||||||
}
|
}
|
||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={peak_config?.buff_id?.toString()}
|
selectedCustomSet={peak_config?.buff_id?.toString()}
|
||||||
@@ -189,19 +179,19 @@ export default function PeakBar() {
|
|||||||
{transI18n("turbulenceBuff")}
|
{transI18n("turbulenceBuff")}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
{challengeSelected && challengeSelected?.TagList?.length > 0 ? (
|
{challengeSelected && challengeSelected?.MazeBuff?.length > 0 ? (
|
||||||
challengeSelected.TagList.map((subOption, index) => (
|
challengeSelected.MazeBuff.map((subOption, index) => (
|
||||||
<div key={index}>
|
<div key={index}>
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-bold text-success">
|
<span className="label-text font-bold text-success">
|
||||||
{index + 1}. {subOption.Name}
|
{index + 1}. {getLocaleName(locale, subOption.Name)}
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
<div
|
<div
|
||||||
className="text-base"
|
className="text-base"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: replaceByParam(
|
__html: replaceByParam(
|
||||||
subOption.Desc,
|
getLocaleName(locale, subOption.Desc),
|
||||||
subOption.Param || []
|
subOption.Param || []
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
@@ -220,51 +210,87 @@ export default function PeakBar() {
|
|||||||
<div className="grid grid-cols-1 gap-4">
|
<div className="grid grid-cols-1 gap-4">
|
||||||
|
|
||||||
<div className="rounded-xl p-4 mt-2 border border-warning">
|
<div className="rounded-xl p-4 mt-2 border border-warning">
|
||||||
<h2 className="text-2xl font-bold mb-6 text-info">{challengeSelected?.Name}</h2>
|
<h2 className="text-2xl font-bold mb-6 text-info">{getLocaleName(locale, challengeSelected?.Name)}</h2>
|
||||||
|
|
||||||
{challengeSelected && Object.values(challengeSelected.InfiniteList).map((waveValue, waveIndex) => (
|
{challengeSelected && Object.values(challengeSelected?.EventList?.[0]?.Infinite || []).map((waveValue, waveIndex) => (
|
||||||
<div key={waveIndex} className="mb-6">
|
<div key={waveIndex} className="mb-6">
|
||||||
<h3 className="text-lg font-semibold mb-t">{transI18n("wave")} {waveIndex + 1}</h3>
|
<h3 className="text-lg font-semibold">{transI18n("wave")} {waveIndex + 1}</h3>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div className="flex flex-wrap gap-2 mt-2">
|
||||||
{Array.from(new Set(waveValue.MonsterGroupIDList)).map((monsterId, enemyIndex) => (
|
{Array.from(new Set(waveValue.MonsterList)).map((monsterId, enemyIndex) => {
|
||||||
<div
|
const monsterStats = calcMonsterStats(
|
||||||
key={enemyIndex}
|
mapMonster?.[monsterId.toString()],
|
||||||
className="rounded-xl p-2 border border-white/10 shadow-md hover:border-white/20 hover:shadow-lg transition"
|
waveValue.EliteGroup,
|
||||||
>
|
challengeSelected?.EventList?.[0]?.HardLevelGroup,
|
||||||
|
challengeSelected?.EventList?.[0]?.Level,
|
||||||
<div className="flex items-center space-x-3">
|
hardLevelConfig,
|
||||||
<div className="relative w-20 h-20 rounded-full overflow-hidden shrink-0 border border-white/10 shadow-sm">
|
eliteConfig
|
||||||
{mapMonster?.[monsterId.toString()]?.icon && <Image
|
);
|
||||||
unoptimized
|
return (
|
||||||
crossOrigin="anonymous"
|
<div
|
||||||
src={`${process.env.CDN_URL}/${mapMonster?.[monsterId.toString()]?.icon}`}
|
key={enemyIndex}
|
||||||
alt="Enemy Icon"
|
className="group relative flex flex-col w-40 bg-base-100 rounded-2xl border border-base-300 shadow-md"
|
||||||
width={376}
|
>
|
||||||
height={512}
|
<div className="badge badge-warning badge-sm font-bold absolute top-2 right-2 z-10 shadow-sm">
|
||||||
className="w-full h-full object-cover"
|
Lv. {challengeSelected?.EventList[0].Level}
|
||||||
/>}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col">
|
<div className="relative w-full h-20 bg-base-200 flex items-center justify-center p-4 rounded-t-2xl">
|
||||||
<div className="text-sm font-semibold">Lv. {challengeSelected?.EventIDList[0].Level}</div>
|
{mapMonster?.[monsterId.toString()]?.Image?.IconPath && (
|
||||||
<div className="flex items-center space-x-1 mt-1">
|
<div className="relative w-16 h-16 rounded-full border-2 border-base-300 shadow-md overflow-hidden group-hover:scale-110 transition-transform duration-300 bg-base-100">
|
||||||
{mapMonster?.[monsterId.toString()]?.weak?.map((icon, iconIndex) => (
|
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`/icon/${icon.toLowerCase()}.webp`}
|
src={`${process.env.CDN_URL}/${mapMonster?.[monsterId.toString()]?.Image?.IconPath}`}
|
||||||
alt={icon}
|
alt="Enemy Icon"
|
||||||
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
|
width={150}
|
||||||
width={200}
|
height={150}
|
||||||
height={200}
|
className="w-full h-full object-cover"
|
||||||
key={iconIndex}
|
|
||||||
/>
|
/>
|
||||||
))}
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col px-1 pb-2 pt-2">
|
||||||
|
<div className="flex flex-col space-y-1.5">
|
||||||
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
|
<span className="text-xs font-semibold text-error">HP</span>
|
||||||
|
<span className="text-sm font-bold text-base-content">{monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
|
<span className="text-xs font-semibold text-info">Speed</span>
|
||||||
|
<span className="text-sm font-bold text-base-content">{monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
|
<span className="text-xs font-semibold text-base-content/70">Toughness</span>
|
||||||
|
<span className="text-sm font-bold text-base-content">{monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-2 pt-2 border-t border-base-300 flex flex-col items-center">
|
||||||
|
<span className="text-[10px] text-base-content/60 font-bold uppercase tracking-widest mb-1.5">
|
||||||
|
Weakness
|
||||||
|
</span>
|
||||||
|
<div className="flex items-center justify-center gap-1.5 flex-wrap">
|
||||||
|
{mapMonster?.[monsterId.toString()]?.StanceWeakList?.map((icon, iconIndex) => (
|
||||||
|
<Image
|
||||||
|
key={iconIndex}
|
||||||
|
unoptimized
|
||||||
|
crossOrigin="anonymous"
|
||||||
|
src={`${process.env.CDN_URL}/${damageType[icon]?.Icon}`}
|
||||||
|
alt={icon}
|
||||||
|
width={40}
|
||||||
|
height={40}
|
||||||
|
className="h-6 w-6 object-contain rounded-full bg-base-300 border border-base-content/10 p-0.5 shadow-sm hover:scale-110 transition-transform"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)
|
||||||
))}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,41 +1,38 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useEffect, useMemo } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
import SelectCustomText from "../select/customSelectText";
|
import SelectCustomText from "../select/customSelectText";
|
||||||
import useEventStore from "@/stores/eventStore";
|
import { calcMonsterStats, getLocaleName, replaceByParam } from "@/helper";
|
||||||
import { getLocaleName, replaceByParam } from "@/helper";
|
|
||||||
import useLocaleStore from "@/stores/localeStore";
|
import useLocaleStore from "@/stores/localeStore";
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import useMonsterStore from "@/stores/monsterStore";
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { MonsterStore } from "@/types";
|
import { MonsterStore } from "@/types";
|
||||||
import useMazeStore from "@/stores/mazeStore";
|
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
|
import useDetailDataStore from "@/stores/detailDataStore";
|
||||||
|
|
||||||
export default function PfBar() {
|
export default function PfBar() {
|
||||||
const { PFEvent, mapPFInfo } = useEventStore()
|
|
||||||
const { mapMonster } = useMonsterStore()
|
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
const {
|
const {
|
||||||
pf_config,
|
pf_config,
|
||||||
setPfConfig
|
setPfConfig
|
||||||
} = useUserDataStore()
|
} = useUserDataStore()
|
||||||
const { PF } = useMazeStore()
|
const { mapMonster, mapPF, damageType, hardLevelConfig, eliteConfig } = useDetailDataStore()
|
||||||
|
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
const challengeSelected = useMemo(() => {
|
const challengeSelected = useMemo(() => {
|
||||||
return mapPFInfo[pf_config.event_id.toString()]?.Level.find((pf) => pf.Id === pf_config.challenge_id)
|
return mapPF[pf_config.event_id.toString()]?.Level.find((pf) => pf.ID === pf_config.challenge_id)
|
||||||
}, [pf_config, mapPFInfo])
|
}, [pf_config, mapPF])
|
||||||
|
|
||||||
const eventSelected = useMemo(() => {
|
const eventSelected = useMemo(() => {
|
||||||
return mapPFInfo[pf_config.event_id.toString()]
|
return mapPF[pf_config.event_id.toString()]
|
||||||
}, [pf_config, mapPFInfo])
|
}, [pf_config, mapPF])
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!challengeSelected || pf_config.event_id === 0 || pf_config.challenge_id === 0) {
|
if (!challengeSelected || pf_config.event_id === 0 || pf_config.challenge_id === 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const newBattleConfig = structuredClone(pf_config)
|
const newBattleConfig = structuredClone(pf_config)
|
||||||
newBattleConfig.cycle_count = 4
|
newBattleConfig.cycle_count = challengeSelected.TurnLimit
|
||||||
newBattleConfig.blessings = []
|
newBattleConfig.blessings = []
|
||||||
if (pf_config.buff_id !== 0) {
|
if (pf_config.buff_id !== 0) {
|
||||||
newBattleConfig.blessings.push({
|
newBattleConfig.blessings.push({
|
||||||
@@ -43,62 +40,63 @@ export default function PfBar() {
|
|||||||
level: 1
|
level: 1
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (PF[pf_config.challenge_id.toString()]) {
|
if (challengeSelected) {
|
||||||
newBattleConfig.blessings.push({
|
challengeSelected.MazeBuff.map((item) => {
|
||||||
id: Number(PF[pf_config.challenge_id.toString()].maze_buff),
|
newBattleConfig.blessings.push({
|
||||||
level: 1
|
id: item.ID,
|
||||||
|
level: 1
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
newBattleConfig.monsters = []
|
newBattleConfig.monsters = []
|
||||||
newBattleConfig.stage_id = 0
|
newBattleConfig.stage_id = 0
|
||||||
if ((pf_config.floor_side === "Upper" || pf_config.floor_side === "Upper -> Lower") && challengeSelected.EventIDList1.length > 0) {
|
if ((pf_config.floor_side === "Upper" || pf_config.floor_side === "Upper -> Lower") && challengeSelected.EventList1.length > 0) {
|
||||||
newBattleConfig.stage_id = challengeSelected.EventIDList1[0].StageID
|
newBattleConfig.stage_id = challengeSelected.EventList1[0].ID
|
||||||
for (const wave of challengeSelected.EventIDList1[0].MonsterList) {
|
for (const wave of challengeSelected.EventList1[0].MonsterList) {
|
||||||
const newWave: MonsterStore[] = []
|
const newWave: MonsterStore[] = []
|
||||||
for (const value of Object.values(wave)) {
|
for (const value of Object.values(wave)) {
|
||||||
newWave.push({
|
newWave.push({
|
||||||
monster_id: Number(value),
|
monster_id: value,
|
||||||
level: challengeSelected.EventIDList1[0].Level,
|
level: challengeSelected.EventList1[0].Level,
|
||||||
amount: 1,
|
amount: 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
newBattleConfig.monsters.push(newWave)
|
newBattleConfig.monsters.push(newWave)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((pf_config.floor_side === "Lower" || pf_config.floor_side === "Lower -> Upper") && challengeSelected.EventIDList2.length > 0) {
|
if ((pf_config.floor_side === "Lower" || pf_config.floor_side === "Lower -> Upper") && challengeSelected.EventList2.length > 0) {
|
||||||
newBattleConfig.stage_id = challengeSelected.EventIDList2[0].StageID
|
newBattleConfig.stage_id = challengeSelected.EventList2[0].ID
|
||||||
for (const wave of challengeSelected.EventIDList2[0].MonsterList) {
|
for (const wave of challengeSelected.EventList2[0].MonsterList) {
|
||||||
const newWave: MonsterStore[] = []
|
const newWave: MonsterStore[] = []
|
||||||
for (const value of Object.values(wave)) {
|
for (const value of Object.values(wave)) {
|
||||||
newWave.push({
|
newWave.push({
|
||||||
monster_id: Number(value),
|
monster_id: value,
|
||||||
level: challengeSelected.EventIDList2[0].Level,
|
level: challengeSelected.EventList2[0].Level,
|
||||||
amount: 1,
|
amount: 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
newBattleConfig.monsters.push(newWave)
|
newBattleConfig.monsters.push(newWave)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (pf_config.floor_side === "Lower -> Upper" && challengeSelected.EventIDList1.length > 0) {
|
if (pf_config.floor_side === "Lower -> Upper" && challengeSelected.EventList1.length > 0) {
|
||||||
for (const wave of challengeSelected.EventIDList1[0].MonsterList) {
|
for (const wave of challengeSelected.EventList1[0].MonsterList) {
|
||||||
const newWave: MonsterStore[] = []
|
const newWave: MonsterStore[] = []
|
||||||
for (const value of Object.values(wave)) {
|
for (const value of Object.values(wave)) {
|
||||||
newWave.push({
|
newWave.push({
|
||||||
monster_id: Number(value),
|
monster_id: value,
|
||||||
level: challengeSelected.EventIDList1[0].Level,
|
level: challengeSelected.EventList1[0].Level,
|
||||||
amount: 1,
|
amount: 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
newBattleConfig.monsters.push(newWave)
|
newBattleConfig.monsters.push(newWave)
|
||||||
}
|
}
|
||||||
} else if (pf_config.floor_side === "Upper -> Lower" && challengeSelected.EventIDList2.length > 0) {
|
} else if (pf_config.floor_side === "Upper -> Lower" && challengeSelected.EventList2.length > 0) {
|
||||||
for (const wave of challengeSelected.EventIDList2[0].MonsterList) {
|
for (const wave of challengeSelected.EventList2[0].MonsterList) {
|
||||||
const newWave: MonsterStore[] = []
|
const newWave: MonsterStore[] = []
|
||||||
for (const value of Object.values(wave)) {
|
for (const value of Object.values(wave)) {
|
||||||
newWave.push({
|
newWave.push({
|
||||||
monster_id: Number(value),
|
monster_id: value,
|
||||||
level: challengeSelected.EventIDList2[0].Level,
|
level: challengeSelected.EventList2[0].Level,
|
||||||
amount: 1,
|
amount: 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -113,10 +111,9 @@ export default function PfBar() {
|
|||||||
pf_config.challenge_id,
|
pf_config.challenge_id,
|
||||||
pf_config.floor_side,
|
pf_config.floor_side,
|
||||||
pf_config.buff_id,
|
pf_config.buff_id,
|
||||||
mapPFInfo,
|
mapPF,
|
||||||
PF,
|
|
||||||
])
|
])
|
||||||
if (!PFEvent) return null
|
if (!mapPF) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="py-8 relative">
|
<div className="py-8 relative">
|
||||||
@@ -125,10 +122,10 @@ export default function PfBar() {
|
|||||||
<div className="rounded-xl p-4 mb-2 border border-warning">
|
<div className="rounded-xl p-4 mb-2 border border-warning">
|
||||||
<div className="mb-4 w-full">
|
<div className="mb-4 w-full">
|
||||||
<SelectCustomText
|
<SelectCustomText
|
||||||
customSet={PFEvent.map((pf) => ({
|
customSet={Object.values(mapPF).sort((a, b) => b.ID - a.ID).map((pf) => ({
|
||||||
id: pf.id,
|
id: pf.ID.toString(),
|
||||||
name: getLocaleName(locale, pf),
|
name: getLocaleName(locale, pf.Name),
|
||||||
time: `${pf.begin} - ${pf.end}`,
|
time: `${pf.BeginTime} - ${pf.EndTime}`,
|
||||||
}))}
|
}))}
|
||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={pf_config.event_id.toString()}
|
selectedCustomSet={pf_config.event_id.toString()}
|
||||||
@@ -136,7 +133,7 @@ export default function PfBar() {
|
|||||||
setSelectedCustomSet={(id) => setPfConfig({
|
setSelectedCustomSet={(id) => setPfConfig({
|
||||||
...pf_config,
|
...pf_config,
|
||||||
event_id: Number(id),
|
event_id: Number(id),
|
||||||
challenge_id: mapPFInfo[Number(id)]?.Level.slice(-1)[0]?.Id || 0,
|
challenge_id: mapPF[Number(id)]?.Level.slice(-1)[0]?.ID || 0,
|
||||||
buff_id: 0
|
buff_id: 0
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
@@ -154,8 +151,8 @@ export default function PfBar() {
|
|||||||
onChange={(e) => setPfConfig({ ...pf_config, challenge_id: Number(e.target.value) })}
|
onChange={(e) => setPfConfig({ ...pf_config, challenge_id: Number(e.target.value) })}
|
||||||
>
|
>
|
||||||
<option value={0} disabled={true}>{transI18n("selectFloor")}</option>
|
<option value={0} disabled={true}>{transI18n("selectFloor")}</option>
|
||||||
{mapPFInfo[pf_config.event_id.toString()]?.Level.map((pf) => (
|
{eventSelected?.Level.map((pf) => (
|
||||||
<option key={pf.Id} value={pf.Id}>{pf.Id % 10}</option>
|
<option key={pf.ID} value={pf.ID}>{getLocaleName(locale, pf.Name)}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@@ -180,10 +177,10 @@ export default function PfBar() {
|
|||||||
{eventSelected && (
|
{eventSelected && (
|
||||||
<div className="mb-4 w-full">
|
<div className="mb-4 w-full">
|
||||||
<SelectCustomText
|
<SelectCustomText
|
||||||
customSet={eventSelected.Option.map((buff, index) => ({
|
customSet={eventSelected.Option.map((buff) => ({
|
||||||
id: PF[eventSelected.Id.toString()]?.buff[index]?.toString(),
|
id: buff.ID?.toString() || "",
|
||||||
name: buff.Name,
|
name: getLocaleName(locale, buff?.Name) || "",
|
||||||
description: replaceByParam(buff.Desc, buff.Param || []),
|
description: replaceByParam(getLocaleName(locale, buff?.Desc) || "", buff?.Param || []),
|
||||||
}))}
|
}))}
|
||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={pf_config?.buff_id?.toString()}
|
selectedCustomSet={pf_config?.buff_id?.toString()}
|
||||||
@@ -199,29 +196,32 @@ export default function PfBar() {
|
|||||||
eventSelected.SubOption.map((subOption, index) => (
|
eventSelected.SubOption.map((subOption, index) => (
|
||||||
<div key={index}>
|
<div key={index}>
|
||||||
<label className="label">
|
<label className="label">
|
||||||
<span className="label-text font-bold text-success">{index + 1}. {subOption.Name}</span>
|
<span className="label-text font-bold text-success">{index + 1}. {getLocaleName(locale, subOption.Name)}</span>
|
||||||
</label>
|
</label>
|
||||||
<div
|
<div
|
||||||
className="text-base"
|
className="text-base"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: replaceByParam(
|
__html: replaceByParam(
|
||||||
subOption.Desc,
|
getLocaleName(locale, subOption.Desc) || "",
|
||||||
subOption.Param || []
|
subOption.Param || []
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
) : eventSelected && eventSelected.SubOption.length === 0 ? (
|
) : eventSelected && challengeSelected && eventSelected.SubOption.length === 0 ? (
|
||||||
<div
|
challengeSelected?.MazeBuff?.map((buff, i) => (
|
||||||
className="text-base"
|
<div
|
||||||
dangerouslySetInnerHTML={{
|
key={i}
|
||||||
__html: replaceByParam(
|
className="text-base"
|
||||||
eventSelected.Buff?.Desc || "",
|
dangerouslySetInnerHTML={{
|
||||||
eventSelected.Buff?.Param || []
|
__html: replaceByParam(
|
||||||
)
|
getLocaleName(locale, buff?.Desc) || "",
|
||||||
}}
|
buff?.Param || []
|
||||||
/>
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))
|
||||||
) : (
|
) : (
|
||||||
<div className="text-base">{transI18n("noTurbulenceBuff")}</div>
|
<div className="text-base">{transI18n("noTurbulenceBuff")}</div>
|
||||||
)}
|
)}
|
||||||
@@ -236,49 +236,85 @@ export default function PfBar() {
|
|||||||
<div className="rounded-xl p-4 mt-2 border border-warning">
|
<div className="rounded-xl p-4 mt-2 border border-warning">
|
||||||
<h2 className="text-2xl font-bold mb-6 text-info">{transI18n("firstHalfEnemies")}</h2>
|
<h2 className="text-2xl font-bold mb-6 text-info">{transI18n("firstHalfEnemies")}</h2>
|
||||||
|
|
||||||
{challengeSelected && Object.values(challengeSelected.InfiniteList1).map((waveValue, waveIndex) => (
|
{challengeSelected && Object.values(challengeSelected.EventList1?.[0]?.Infinite || []).map((waveValue, waveIndex) => (
|
||||||
<div key={waveIndex} className="mb-6">
|
<div key={waveIndex} className="mb-6">
|
||||||
<h3 className="text-lg font-semibold mb-t">{transI18n("wave")} {waveIndex + 1}</h3>
|
<h3 className="text-lg font-semibold">{transI18n("wave")} {waveIndex + 1}</h3>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div className="flex flex-wrap gap-2 mt-2">
|
||||||
{Array.from(new Set(waveValue.MonsterGroupIDList)).map((monsterId, enemyIndex) => (
|
{Array.from(new Set(waveValue.MonsterList)).map((monsterId, enemyIndex) => {
|
||||||
|
const monsterStats = calcMonsterStats(
|
||||||
<div
|
mapMonster?.[monsterId.toString()],
|
||||||
key={enemyIndex}
|
waveValue.EliteGroup,
|
||||||
className="rounded-xl p-2 border border-white/10 shadow-md hover:border-white/20 hover:shadow-lg transition"
|
challengeSelected?.EventList1?.[0]?.HardLevelGroup,
|
||||||
>
|
challengeSelected?.EventList1?.[0]?.Level,
|
||||||
<div className="flex items-center space-x-3">
|
hardLevelConfig,
|
||||||
<div className="relative w-20 h-20 rounded-full overflow-hidden shrink-0 border border-white/10 shadow-sm">
|
eliteConfig
|
||||||
{mapMonster?.[monsterId.toString()]?.icon && <Image
|
);
|
||||||
unoptimized
|
return (
|
||||||
crossOrigin="anonymous"
|
<div
|
||||||
src={`${process.env.CDN_URL}/${mapMonster?.[monsterId.toString()]?.icon}`}
|
key={enemyIndex}
|
||||||
alt="Enemy Icon"
|
className="group relative flex flex-col w-40 bg-base-100 rounded-2xl border border-base-300 shadow-md"
|
||||||
width={376}
|
>
|
||||||
height={512}
|
<div className="badge badge-warning badge-sm font-bold absolute top-2 right-2 z-10 shadow-sm">
|
||||||
className="w-full h-full object-cover"
|
Lv. {challengeSelected?.EventList1[0].Level}
|
||||||
/>}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col">
|
<div className="relative w-full h-20 bg-base-200 flex items-center justify-center p-4 rounded-t-2xl">
|
||||||
<div className="text-sm font-semibold">{mapMonster?.[monsterId.toString()]?.id} | Lv. {challengeSelected?.EventIDList1[0].Level}</div>
|
{mapMonster?.[monsterId.toString()]?.Image?.IconPath && (
|
||||||
<div className="flex items-center space-x-1 mt-1">
|
<div className="relative w-16 h-16 rounded-full border-2 border-base-300 shadow-md overflow-hidden group-hover:scale-110 transition-transform duration-300 bg-base-100">
|
||||||
{mapMonster?.[monsterId.toString()]?.weak?.map((icon, iconIndex) => (
|
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`/icon/${icon.toLowerCase()}.webp`}
|
src={`${process.env.CDN_URL}/${mapMonster?.[monsterId.toString()]?.Image?.IconPath}`}
|
||||||
alt={icon}
|
alt="Enemy Icon"
|
||||||
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
|
width={150}
|
||||||
width={200}
|
height={150}
|
||||||
height={200}
|
className="w-full h-full object-cover"
|
||||||
key={iconIndex}
|
|
||||||
/>
|
/>
|
||||||
))}
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col px-1 pb-2 pt-2">
|
||||||
|
<div className="flex flex-col space-y-1.5">
|
||||||
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
|
<span className="text-xs font-semibold text-error">HP</span>
|
||||||
|
<span className="text-sm font-bold text-base-content">{monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
|
<span className="text-xs font-semibold text-info">Speed</span>
|
||||||
|
<span className="text-sm font-bold text-base-content">{monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
|
<span className="text-xs font-semibold text-base-content/70">Toughness</span>
|
||||||
|
<span className="text-sm font-bold text-base-content">{monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-2 pt-2 border-t border-base-300 flex flex-col items-center">
|
||||||
|
<span className="text-[10px] text-base-content/60 font-bold uppercase tracking-widest mb-1.5">
|
||||||
|
Weakness
|
||||||
|
</span>
|
||||||
|
<div className="flex items-center justify-center gap-1.5 flex-wrap">
|
||||||
|
{mapMonster?.[monsterId.toString()]?.StanceWeakList?.map((icon, iconIndex) => (
|
||||||
|
<Image
|
||||||
|
key={iconIndex}
|
||||||
|
unoptimized
|
||||||
|
crossOrigin="anonymous"
|
||||||
|
src={`${process.env.CDN_URL}/${damageType[icon]?.Icon}`}
|
||||||
|
alt={icon}
|
||||||
|
width={40}
|
||||||
|
height={40}
|
||||||
|
className="h-6 w-6 object-contain rounded-full bg-base-300 border border-base-content/10 p-0.5 shadow-sm hover:scale-110 transition-transform"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)
|
||||||
))}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -288,49 +324,85 @@ export default function PfBar() {
|
|||||||
<div className="rounded-xl p-4 mt-2 border border-warning">
|
<div className="rounded-xl p-4 mt-2 border border-warning">
|
||||||
<h2 className="text-2xl font-bold mb-6 text-info">{transI18n("secondHalfEnemies")}</h2>
|
<h2 className="text-2xl font-bold mb-6 text-info">{transI18n("secondHalfEnemies")}</h2>
|
||||||
|
|
||||||
{challengeSelected && Object.values(challengeSelected?.InfiniteList2).map((waveValue, waveIndex) => (
|
{challengeSelected && Object.values(challengeSelected?.EventList2[0]?.Infinite || []).map((waveValue, waveIndex) => (
|
||||||
<div key={waveIndex} className="mb-6">
|
<div key={waveIndex} className="mb-6">
|
||||||
<h3 className="text-lg font-semibold mb-t">{transI18n("wave")} {waveIndex + 1}</h3>
|
<h3 className="text-lg font-semibold">{transI18n("wave")} {waveIndex + 1}</h3>
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
<div className="flex flex-wrap gap-2 mt-2">
|
||||||
{Array.from(new Set(waveValue.MonsterGroupIDList)).map((monsterId, enemyIndex) => (
|
{Array.from(new Set(waveValue.MonsterList)).map((monsterId, enemyIndex) => {
|
||||||
<div
|
const monsterStats = calcMonsterStats(
|
||||||
key={enemyIndex}
|
mapMonster?.[monsterId.toString()],
|
||||||
className="rounded-xl p-2 border border-white/10 shadow-md hover:border-white/20 hover:shadow-lg transition"
|
waveValue.EliteGroup,
|
||||||
>
|
challengeSelected?.EventList2?.[0]?.HardLevelGroup,
|
||||||
|
challengeSelected?.EventList2?.[0]?.Level,
|
||||||
<div className="flex items-center space-x-3">
|
hardLevelConfig,
|
||||||
<div className="relative w-20 h-20 rounded-full overflow-hidden shrink-0 border border-white/10 shadow-sm">
|
eliteConfig
|
||||||
{mapMonster?.[monsterId.toString()]?.icon && <Image
|
);
|
||||||
unoptimized
|
return (
|
||||||
crossOrigin="anonymous"
|
<div
|
||||||
src={`${process.env.CDN_URL}/${mapMonster?.[monsterId.toString()]?.icon}`}
|
key={enemyIndex}
|
||||||
alt="Enemy Icon"
|
className="group relative flex flex-col w-40 bg-base-100 rounded-2xl border border-base-300 shadow-md"
|
||||||
width={400}
|
>
|
||||||
height={300}
|
<div className="badge badge-warning badge-sm font-bold absolute top-2 right-2 z-10 shadow-sm">
|
||||||
className="w-full h-full object-cover"
|
Lv. {challengeSelected?.EventList2[0].Level}
|
||||||
/>}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col">
|
<div className="relative w-full h-20 bg-base-200 flex items-center justify-center p-4 rounded-t-2xl">
|
||||||
<div className="text-sm font-semibold">Lv. {challengeSelected?.EventIDList1[0].Level}</div>
|
{mapMonster?.[monsterId.toString()]?.Image?.IconPath && (
|
||||||
<div className="flex items-center space-x-1 mt-1">
|
<div className="relative w-16 h-16 rounded-full border-2 border-base-300 shadow-md overflow-hidden group-hover:scale-110 transition-transform duration-300 bg-base-100">
|
||||||
{mapMonster?.[monsterId.toString()]?.weak?.map((icon, iconIndex) => (
|
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`/icon/${icon.toLowerCase()}.webp`}
|
src={`${process.env.CDN_URL}/${mapMonster?.[monsterId.toString()]?.Image?.IconPath}`}
|
||||||
alt={icon}
|
alt="Enemy Icon"
|
||||||
className="h-7 w-7 2xl:h-10 2xl:w-10 object-contain rounded-md border border-white/20 shadow-sm"
|
width={150}
|
||||||
width={200}
|
height={150}
|
||||||
height={200}
|
className="w-full h-full object-cover"
|
||||||
key={iconIndex}
|
|
||||||
/>
|
/>
|
||||||
))}
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col px-1 pb-2 pt-2">
|
||||||
|
<div className="flex flex-col space-y-1.5">
|
||||||
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
|
<span className="text-xs font-semibold text-error">HP</span>
|
||||||
|
<span className="text-sm font-bold text-base-content">{monsterStats.hp.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
|
<span className="text-xs font-semibold text-info">Speed</span>
|
||||||
|
<span className="text-sm font-bold text-base-content">{monsterStats.spd.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex justify-between items-center bg-base-200 px-2.5 py-1.5 rounded-lg">
|
||||||
|
<span className="text-xs font-semibold text-base-content/70">Toughness</span>
|
||||||
|
<span className="text-sm font-bold text-base-content">{monsterStats.stance.toLocaleString(undefined, { maximumFractionDigits: 0 })}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-2 pt-2 border-t border-base-300 flex flex-col items-center">
|
||||||
|
<span className="text-[10px] text-base-content/60 font-bold uppercase tracking-widest mb-1.5">
|
||||||
|
Weakness
|
||||||
|
</span>
|
||||||
|
<div className="flex items-center justify-center gap-1.5 flex-wrap">
|
||||||
|
{mapMonster?.[monsterId.toString()]?.StanceWeakList?.map((icon, iconIndex) => (
|
||||||
|
<Image
|
||||||
|
key={iconIndex}
|
||||||
|
unoptimized
|
||||||
|
crossOrigin="anonymous"
|
||||||
|
src={`${process.env.CDN_URL}/${damageType[icon]?.Icon}`}
|
||||||
|
alt={icon}
|
||||||
|
width={40}
|
||||||
|
height={40}
|
||||||
|
className="h-6 w-6 object-contain rounded-full bg-base-300 border border-base-content/10 p-0.5 shadow-sm hover:scale-110 transition-transform"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)
|
||||||
))}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,39 +1,34 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import NextImage from "next/image"
|
import NextImage from "next/image"
|
||||||
import useLightconeStore from "@/stores/lightconeStore";
|
|
||||||
import useAffixStore from "@/stores/affixStore";
|
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import useRelicStore from "@/stores/relicStore";
|
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import useAvatarStore from "@/stores/avatarStore";
|
import { calcAffixBonus, calcBaseStatRaw, calcBonusStatRaw, calcMainAffixBonus, calcMainAffixBonusRaw, calcPromotion, calcSubAffixBonusRaw, getLocaleName, replaceByParam } from "@/helper";
|
||||||
import { calcAffixBonus, calcBaseStatRaw, calcBonusStatRaw, calcMainAffixBonus, calcMainAffixBonusRaw, calcPromotion, calcSubAffixBonusRaw, replaceByParam } from "@/helper";
|
|
||||||
import { mappingStats } from "@/constant/constant";
|
import { mappingStats } from "@/constant/constant";
|
||||||
import RelicShowcase from "../showcaseCard/relicShowcase";
|
import RelicShowcase from "../showcaseCard/relicShowcase";
|
||||||
|
import useLocaleStore from "@/stores/localeStore";
|
||||||
|
import useDetailDataStore from "@/stores/detailDataStore";
|
||||||
|
import useCurrentDataStore from "@/stores/currentDataStore";
|
||||||
|
|
||||||
export default function QuickView() {
|
export default function QuickView() {
|
||||||
const { avatarSelected, mapAvatarInfo } = useAvatarStore()
|
|
||||||
const { mapLightconeInfo } = useLightconeStore()
|
|
||||||
const { mapMainAffix, mapSubAffix } = useAffixStore()
|
|
||||||
const { avatars } = useUserDataStore()
|
const { avatars } = useUserDataStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
const { mapRelicInfo } = useRelicStore()
|
const { locale } = useLocaleStore()
|
||||||
|
const { avatarSelected, } = useCurrentDataStore()
|
||||||
const avatarInfo = useMemo(() => {
|
const { mainAffix, subAffix, mapRelicSet, mapLightCone, mapAvatar } = useDetailDataStore()
|
||||||
if (!avatarSelected) return
|
|
||||||
return mapAvatarInfo[avatarSelected.id]
|
|
||||||
}, [avatarSelected, mapAvatarInfo])
|
|
||||||
|
|
||||||
|
|
||||||
const avatarSkillTree = useMemo(() => {
|
const avatarSkillTree = useMemo(() => {
|
||||||
if (!avatarSelected || !avatars[avatarSelected.id]) return {}
|
if (!avatarSelected || !avatars[avatarSelected?.ID?.toString()]) return {}
|
||||||
if (avatars[avatarSelected.id].enhanced) {
|
if (avatars[avatarSelected?.ID?.toString()].enhanced) {
|
||||||
return avatarInfo?.Enhanced[avatars[avatarSelected.id].enhanced.toString()].SkillTrees || {}
|
return avatarSelected?.Enhanced?.[avatars[avatarSelected?.ID?.toString()].enhanced.toString()].SkillTrees || {}
|
||||||
}
|
}
|
||||||
return avatarInfo?.SkillTrees || {}
|
return avatarSelected?.SkillTrees || {}
|
||||||
}, [avatarSelected, avatarInfo, avatars])
|
}, [avatarSelected, avatars])
|
||||||
|
|
||||||
const avatarData = useMemo(() => {
|
const avatarData = useMemo(() => {
|
||||||
if (!avatarSelected) return
|
if (!avatarSelected) return
|
||||||
return avatars[avatarSelected.id]
|
return avatars[avatarSelected?.ID?.toString()]
|
||||||
}, [avatarSelected, avatars])
|
}, [avatarSelected, avatars])
|
||||||
|
|
||||||
const avatarProfile = useMemo(() => {
|
const avatarProfile = useMemo(() => {
|
||||||
@@ -42,7 +37,7 @@ export default function QuickView() {
|
|||||||
}, [avatarSelected, avatarData])
|
}, [avatarSelected, avatarData])
|
||||||
|
|
||||||
const relicEffects = useMemo(() => {
|
const relicEffects = useMemo(() => {
|
||||||
const avatar = avatars[avatarSelected?.id || ""];
|
const avatar = avatars[avatarSelected?.ID?.toString() || ""];
|
||||||
const relicCount: { [key: string]: number } = {};
|
const relicCount: { [key: string]: number } = {};
|
||||||
if (avatar) {
|
if (avatar) {
|
||||||
for (const relic of Object.values(avatar.profileList[avatar.profileSelect].relics)) {
|
for (const relic of Object.values(avatar.profileList[avatar.profileSelect].relics)) {
|
||||||
@@ -63,32 +58,32 @@ export default function QuickView() {
|
|||||||
}, [avatars, avatarSelected]);
|
}, [avatars, avatarSelected]);
|
||||||
|
|
||||||
const relicStats = useMemo(() => {
|
const relicStats = useMemo(() => {
|
||||||
if (!avatarSelected || !avatarProfile?.relics || !mapMainAffix || !mapSubAffix) return
|
if (!avatarSelected || !avatarProfile?.relics || !mainAffix || !subAffix) return
|
||||||
|
|
||||||
return Object.entries(avatarProfile?.relics).map(([key, value]) => {
|
return Object.entries(avatarProfile?.relics).map(([key, value]) => {
|
||||||
const mainAffixMap = mapMainAffix["5" + key]
|
const mainAffixMap = mainAffix["5" + key]
|
||||||
const subAffixMap = mapSubAffix["5"]
|
const subAffixMap = subAffix["5"]
|
||||||
if (!mainAffixMap || !subAffixMap) return
|
if (!mainAffixMap || !subAffixMap) return
|
||||||
return {
|
return {
|
||||||
img: `${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${value.relic_set_id}_${key}.png`,
|
img: `${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${value.relic_set_id}_${key}.png`,
|
||||||
mainAffix: {
|
mainAffix: {
|
||||||
property: mainAffixMap?.[value?.main_affix_id]?.property,
|
property: mainAffixMap?.[value?.main_affix_id]?.Property,
|
||||||
level: value?.level,
|
level: value?.level,
|
||||||
valueAffix: calcMainAffixBonus(mainAffixMap?.[value?.main_affix_id], value?.level),
|
valueAffix: calcMainAffixBonus(mainAffixMap?.[value?.main_affix_id], value?.level),
|
||||||
detail: mappingStats?.[mainAffixMap?.[value?.main_affix_id]?.property]
|
detail: mappingStats?.[mainAffixMap?.[value?.main_affix_id]?.Property]
|
||||||
},
|
},
|
||||||
subAffix: value?.sub_affixes?.map((subValue) => {
|
subAffix: value?.sub_affixes?.map((subValue) => {
|
||||||
return {
|
return {
|
||||||
property: subAffixMap?.[subValue?.sub_affix_id]?.property,
|
property: subAffixMap?.[subValue?.sub_affix_id]?.Property,
|
||||||
valueAffix: calcAffixBonus(subAffixMap?.[subValue?.sub_affix_id], subValue?.step, subValue?.count),
|
valueAffix: calcAffixBonus(subAffixMap?.[subValue?.sub_affix_id], subValue?.step, subValue?.count),
|
||||||
detail: mappingStats?.[subAffixMap?.[subValue?.sub_affix_id]?.property],
|
detail: mappingStats?.[subAffixMap?.[subValue?.sub_affix_id]?.Property],
|
||||||
step: subValue?.step,
|
step: subValue?.step,
|
||||||
count: subValue?.count
|
count: subValue?.count
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, [avatarSelected, avatarProfile, mapMainAffix, mapSubAffix])
|
}, [avatarSelected, avatarProfile, mainAffix, subAffix])
|
||||||
|
|
||||||
const characterStats = useMemo(() => {
|
const characterStats = useMemo(() => {
|
||||||
if (!avatarSelected || !avatarData) return
|
if (!avatarSelected || !avatarData) return
|
||||||
@@ -104,73 +99,73 @@ export default function QuickView() {
|
|||||||
}> = {
|
}> = {
|
||||||
HP: {
|
HP: {
|
||||||
value: calcBaseStatRaw(
|
value: calcBaseStatRaw(
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.HPBase,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPBase,
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.HPAdd,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPAdd,
|
||||||
avatarData.level
|
avatarData.level
|
||||||
),
|
),
|
||||||
base: calcBaseStatRaw(
|
base: calcBaseStatRaw(
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.HPBase,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPBase,
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.HPAdd,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPAdd,
|
||||||
avatarData.level
|
avatarData.level
|
||||||
),
|
),
|
||||||
name: "HP",
|
name: "HP",
|
||||||
icon: "/icon/hp.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconMaxHP.png",
|
||||||
unit: "",
|
unit: "",
|
||||||
round: 0
|
round: 0
|
||||||
},
|
},
|
||||||
ATK: {
|
ATK: {
|
||||||
value: calcBaseStatRaw(
|
value: calcBaseStatRaw(
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.AttackBase,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackBase,
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.AttackAdd,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackAdd,
|
||||||
avatarData.level
|
avatarData.level
|
||||||
),
|
),
|
||||||
base: calcBaseStatRaw(
|
base: calcBaseStatRaw(
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.AttackBase,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackBase,
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.AttackAdd,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackAdd,
|
||||||
avatarData.level
|
avatarData.level
|
||||||
),
|
),
|
||||||
name: "ATK",
|
name: "ATK",
|
||||||
icon: "/icon/attack.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconAttack.png",
|
||||||
unit: "",
|
unit: "",
|
||||||
round: 0
|
round: 0
|
||||||
},
|
},
|
||||||
DEF: {
|
DEF: {
|
||||||
value: calcBaseStatRaw(
|
value: calcBaseStatRaw(
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.DefenceBase,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceBase,
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.DefenceAdd,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceAdd,
|
||||||
avatarData.level
|
avatarData.level
|
||||||
),
|
),
|
||||||
base: calcBaseStatRaw(
|
base: calcBaseStatRaw(
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.DefenceBase,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceBase,
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.DefenceAdd,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceAdd,
|
||||||
avatarData.level
|
avatarData.level
|
||||||
),
|
),
|
||||||
name: "DEF",
|
name: "DEF",
|
||||||
icon: "/icon/defence.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconDefence.png",
|
||||||
unit: "",
|
unit: "",
|
||||||
round: 0
|
round: 0
|
||||||
},
|
},
|
||||||
SPD: {
|
SPD: {
|
||||||
value: mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.SpeedBase || 0,
|
value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.SpeedBase || 0,
|
||||||
base: mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.SpeedBase || 0,
|
base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.SpeedBase || 0,
|
||||||
name: "SPD",
|
name: "SPD",
|
||||||
icon: "/icon/speed.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconSpeed.png",
|
||||||
unit: "",
|
unit: "",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
CRITRate: {
|
CRITRate: {
|
||||||
value: mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.CriticalChance || 0,
|
value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalChance || 0,
|
||||||
base: mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.CriticalChance || 0,
|
base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalChance || 0,
|
||||||
name: "CRIT Rate",
|
name: "CRIT Rate",
|
||||||
icon: "/icon/crit-rate.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconCriticalChance.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
CRITDmg: {
|
CRITDmg: {
|
||||||
value: mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.CriticalDamage || 0,
|
value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalDamage || 0,
|
||||||
base: mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.CriticalDamage || 0,
|
base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalDamage || 0,
|
||||||
name: "CRIT DMG",
|
name: "CRIT DMG",
|
||||||
icon: "/icon/crit-damage.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconCriticalDamage.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -178,7 +173,7 @@ export default function QuickView() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Break Effect",
|
name: "Break Effect",
|
||||||
icon: "/icon/break-effect.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconBreakUp.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -186,7 +181,7 @@ export default function QuickView() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Effect RES",
|
name: "Effect RES",
|
||||||
icon: "/icon/effect-res.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconStatusResistance.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -194,7 +189,7 @@ export default function QuickView() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Energy Rate",
|
name: "Energy Rate",
|
||||||
icon: "/icon/energy-rate.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconEnergyRecovery.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -202,7 +197,7 @@ export default function QuickView() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Effect Hit Rate",
|
name: "Effect Hit Rate",
|
||||||
icon: "/icon/effect-hit-rate.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconStatusProbability.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -210,7 +205,7 @@ export default function QuickView() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Healing Boost",
|
name: "Healing Boost",
|
||||||
icon: "/icon/healing-boost.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconHealRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -218,7 +213,7 @@ export default function QuickView() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Physical Boost",
|
name: "Physical Boost",
|
||||||
icon: "/icon/physical-add.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconPhysicalAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -226,7 +221,7 @@ export default function QuickView() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Fire Boost",
|
name: "Fire Boost",
|
||||||
icon: "/icon/fire-add.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconFireAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -234,7 +229,7 @@ export default function QuickView() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Ice Boost",
|
name: "Ice Boost",
|
||||||
icon: "/icon/ice-add.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconIceAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -242,7 +237,7 @@ export default function QuickView() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Thunder Boost",
|
name: "Thunder Boost",
|
||||||
icon: "/icon/thunder-add.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconThunderAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -250,7 +245,7 @@ export default function QuickView() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Wind Boost",
|
name: "Wind Boost",
|
||||||
icon: "/icon/wind-add.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconWindAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -258,7 +253,7 @@ export default function QuickView() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Quantum Boost",
|
name: "Quantum Boost",
|
||||||
icon: "/icon/quantum-add.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconQuantumAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -266,7 +261,7 @@ export default function QuickView() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Imaginary Boost",
|
name: "Imaginary Boost",
|
||||||
icon: "/icon/imaginary-add.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconImaginaryAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -274,57 +269,57 @@ export default function QuickView() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Elation Boost",
|
name: "Elation Boost",
|
||||||
icon: "/icon/IconJoy.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconJoy.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avatarProfile?.lightcone && mapLightconeInfo[avatarProfile?.lightcone?.item_id]) {
|
if (avatarProfile?.lightcone && mapLightCone[avatarProfile?.lightcone?.item_id]) {
|
||||||
const lightconePromotion = calcPromotion(avatarProfile?.lightcone?.level)
|
const lightconePromotion = calcPromotion(avatarProfile?.lightcone?.level)
|
||||||
statsData.HP.value += calcBaseStatRaw(
|
statsData.HP.value += calcBaseStatRaw(
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHP,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHP,
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHPAdd,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHPAdd,
|
||||||
avatarProfile?.lightcone?.level
|
avatarProfile?.lightcone?.level
|
||||||
)
|
)
|
||||||
statsData.HP.base += calcBaseStatRaw(
|
statsData.HP.base += calcBaseStatRaw(
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHP,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHP,
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHPAdd,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHPAdd,
|
||||||
avatarProfile?.lightcone?.level
|
avatarProfile?.lightcone?.level
|
||||||
)
|
)
|
||||||
statsData.ATK.value += calcBaseStatRaw(
|
statsData.ATK.value += calcBaseStatRaw(
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttack,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttack,
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttackAdd,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttackAdd,
|
||||||
avatarProfile?.lightcone?.level
|
avatarProfile?.lightcone?.level
|
||||||
)
|
)
|
||||||
statsData.ATK.base += calcBaseStatRaw(
|
statsData.ATK.base += calcBaseStatRaw(
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttack,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttack,
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttackAdd,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttackAdd,
|
||||||
avatarProfile?.lightcone?.level
|
avatarProfile?.lightcone?.level
|
||||||
)
|
)
|
||||||
statsData.DEF.value += calcBaseStatRaw(
|
statsData.DEF.value += calcBaseStatRaw(
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefence,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefence,
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefenceAdd,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefenceAdd,
|
||||||
avatarProfile?.lightcone?.level
|
avatarProfile?.lightcone?.level
|
||||||
)
|
)
|
||||||
statsData.DEF.base += calcBaseStatRaw(
|
statsData.DEF.base += calcBaseStatRaw(
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefence,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefence,
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefenceAdd,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefenceAdd,
|
||||||
avatarProfile?.lightcone?.level
|
avatarProfile?.lightcone?.level
|
||||||
)
|
)
|
||||||
|
|
||||||
const bonusData = mapLightconeInfo[avatarProfile?.lightcone?.item_id].Bonus?.[avatarProfile?.lightcone.rank - 1]
|
const bonusData = mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Skills?.Level?.[avatarProfile?.lightcone.rank]?.Bonus
|
||||||
if (bonusData && bonusData.length > 0) {
|
if (bonusData && bonusData.length > 0) {
|
||||||
const bonusSpd = bonusData.filter((bonus) => bonus.type === "BaseSpeed")
|
const bonusSpd = bonusData.filter((bonus) => bonus.PropertyType === "BaseSpeed")
|
||||||
const bonusOther = bonusData.filter((bonus) => bonus.type !== "BaseSpeed")
|
const bonusOther = bonusData.filter((bonus) => bonus.PropertyType !== "BaseSpeed")
|
||||||
bonusSpd.forEach((bonus) => {
|
bonusSpd.forEach((bonus) => {
|
||||||
statsData.SPD.value += bonus.value
|
statsData.SPD.value += bonus.Value
|
||||||
statsData.SPD.base += bonus.value
|
statsData.SPD.base += bonus.Value
|
||||||
})
|
})
|
||||||
bonusOther.forEach((bonus) => {
|
bonusOther.forEach((bonus) => {
|
||||||
const statsBase = mappingStats?.[bonus.type]?.baseStat
|
const statsBase = mappingStats?.[bonus.PropertyType]?.baseStat
|
||||||
if (statsBase && statsData[statsBase]) {
|
if (statsBase && statsData[statsBase]) {
|
||||||
statsData[statsBase].value += calcBonusStatRaw(bonus.type, statsData[statsBase].base, bonus.value)
|
statsData[statsBase].value += calcBonusStatRaw(bonus.PropertyType, statsData[statsBase].base, bonus.Value)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -349,17 +344,17 @@ export default function QuickView() {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (avatarProfile?.relics && mapMainAffix && mapSubAffix) {
|
if (avatarProfile?.relics && mainAffix && subAffix) {
|
||||||
Object.entries(avatarProfile?.relics).forEach(([key, value]) => {
|
Object.entries(avatarProfile?.relics).forEach(([key, value]) => {
|
||||||
const mainAffixMap = mapMainAffix["5" + key]
|
const mainAffixMap = mainAffix["5" + key]
|
||||||
const subAffixMap = mapSubAffix["5"]
|
const subAffixMap = subAffix["5"]
|
||||||
if (!mainAffixMap || !subAffixMap) return
|
if (!mainAffixMap || !subAffixMap) return
|
||||||
const mainStats = mappingStats?.[mainAffixMap?.[value.main_affix_id]?.property]?.baseStat
|
const mainStats = mappingStats?.[mainAffixMap?.[value.main_affix_id]?.Property]?.baseStat
|
||||||
if (mainStats && statsData[mainStats]) {
|
if (mainStats && statsData[mainStats]) {
|
||||||
statsData[mainStats].value += calcMainAffixBonusRaw(mainAffixMap?.[value.main_affix_id], value.level, statsData[mainStats].base)
|
statsData[mainStats].value += calcMainAffixBonusRaw(mainAffixMap?.[value.main_affix_id], value.level, statsData[mainStats].base)
|
||||||
}
|
}
|
||||||
value?.sub_affixes.forEach((subValue) => {
|
value?.sub_affixes.forEach((subValue) => {
|
||||||
const subStats = mappingStats?.[subAffixMap?.[subValue.sub_affix_id]?.property]?.baseStat
|
const subStats = mappingStats?.[subAffixMap?.[subValue.sub_affix_id]?.Property]?.baseStat
|
||||||
if (subStats && statsData[subStats]) {
|
if (subStats && statsData[subStats]) {
|
||||||
statsData[subStats].value += calcSubAffixBonusRaw(subAffixMap?.[subValue.sub_affix_id], subValue.step, subValue.count, statsData[subStats].base)
|
statsData[subStats].value += calcSubAffixBonusRaw(subAffixMap?.[subValue.sub_affix_id], subValue.step, subValue.count, statsData[subStats].base)
|
||||||
}
|
}
|
||||||
@@ -369,14 +364,14 @@ export default function QuickView() {
|
|||||||
|
|
||||||
if (relicEffects && relicEffects.length > 0) {
|
if (relicEffects && relicEffects.length > 0) {
|
||||||
relicEffects.forEach((relic) => {
|
relicEffects.forEach((relic) => {
|
||||||
const dataBonus = mapRelicInfo?.[relic.key]?.Bonus
|
const dataBonus = mapRelicSet?.[relic.key]?.Skills
|
||||||
if (!dataBonus || Object.keys(dataBonus).length === 0) return
|
if (!dataBonus || Object.keys(dataBonus).length === 0) return
|
||||||
Object.entries(dataBonus || {}).forEach(([key, value]) => {
|
Object.entries(dataBonus || {}).forEach(([key, value]) => {
|
||||||
if (relic.count < Number(key)) return
|
if (relic.count < Number(key)) return
|
||||||
value.forEach((bonus) => {
|
value.Bonus.forEach((bonus) => {
|
||||||
const statsBase = mappingStats?.[bonus.type]?.baseStat
|
const statsBase = mappingStats?.[bonus.PropertyType]?.baseStat
|
||||||
if (statsBase && statsData[statsBase]) {
|
if (statsBase && statsData[statsBase]) {
|
||||||
statsData[statsBase].value += calcBonusStatRaw(bonus.type, statsData[statsBase].base, bonus.value)
|
statsData[statsBase].value += calcBonusStatRaw(bonus.PropertyType, statsData[statsBase].base, bonus.Value)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -388,14 +383,14 @@ export default function QuickView() {
|
|||||||
}, [
|
}, [
|
||||||
avatarSelected,
|
avatarSelected,
|
||||||
avatarData,
|
avatarData,
|
||||||
mapAvatarInfo,
|
mapAvatar,
|
||||||
avatarProfile?.lightcone,
|
avatarProfile?.lightcone,
|
||||||
avatarProfile?.relics,
|
avatarProfile?.relics,
|
||||||
mapLightconeInfo,
|
mapLightCone,
|
||||||
mapMainAffix,
|
mainAffix,
|
||||||
mapSubAffix,
|
subAffix,
|
||||||
relicEffects,
|
relicEffects,
|
||||||
mapRelicInfo,
|
mapRelicSet,
|
||||||
avatarSkillTree
|
avatarSkillTree
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -409,13 +404,13 @@ export default function QuickView() {
|
|||||||
<div key={index} className="flex flex-row items-center justify-between">
|
<div key={index} className="flex flex-row items-center justify-between">
|
||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center">
|
||||||
<NextImage
|
<NextImage
|
||||||
src={stat?.icon || ""}
|
src={stat?.icon || ""}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
alt="Stat Icon"
|
alt="Stat Icon"
|
||||||
width={40}
|
width={40}
|
||||||
height={40}
|
height={40}
|
||||||
className="h-auto w-10 p-1 mx-1 bg-black/20 rounded-full"
|
className="h-10 w-10 p-1 mx-1 bg-black/20 rounded-full"
|
||||||
/>
|
/>
|
||||||
<div className="font-bold">{stat.name}</div>
|
<div className="font-bold">{stat.name}</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -431,7 +426,7 @@ export default function QuickView() {
|
|||||||
|
|
||||||
<div className="flex flex-col items-center gap-1 w-full my-2">
|
<div className="flex flex-col items-center gap-1 w-full my-2">
|
||||||
{relicEffects.map((setEffect, index) => {
|
{relicEffects.map((setEffect, index) => {
|
||||||
const relicInfo = mapRelicInfo[setEffect.key];
|
const relicInfo = mapRelicSet[setEffect.key];
|
||||||
if (!relicInfo) return null;
|
if (!relicInfo) return null;
|
||||||
return (
|
return (
|
||||||
<div key={index} className="flex w-full flex-row justify-between text-left">
|
<div key={index} className="flex w-full flex-row justify-between text-left">
|
||||||
@@ -442,7 +437,7 @@ export default function QuickView() {
|
|||||||
}}
|
}}
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: replaceByParam(
|
__html: replaceByParam(
|
||||||
relicInfo.Name,
|
getLocaleName(locale, relicInfo.Name),
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
@@ -459,9 +454,9 @@ export default function QuickView() {
|
|||||||
<div className="grid grid-cols-1 gap-2 justify-between py-3 text-lg">
|
<div className="grid grid-cols-1 gap-2 justify-between py-3 text-lg">
|
||||||
|
|
||||||
{relicStats?.map((relic, index) => {
|
{relicStats?.map((relic, index) => {
|
||||||
if (!relic || !avatarInfo) return null
|
if (!relic || !avatarSelected) return null
|
||||||
return (
|
return (
|
||||||
<RelicShowcase key={index} relic={relic} avatarInfo={avatarInfo} />
|
<RelicShowcase key={index} relic={relic} avatarInfo={avatarSelected} />
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +1,27 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import useRelicStore from '@/stores/relicStore';
|
|
||||||
import useUserDataStore from '@/stores/userDataStore';
|
import useUserDataStore from '@/stores/userDataStore';
|
||||||
import { AffixDetail, RelicDetail } from '@/types';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import React, { useEffect, useMemo, useState } from 'react';
|
|
||||||
import SelectCustomImage from '../select/customSelectImage';
|
import SelectCustomImage from '../select/customSelectImage';
|
||||||
import { calcAffixBonus, calcMainAffixBonus, randomPartition, randomStep, replaceByParam } from '@/helper';
|
import { calcAffixBonus, calcMainAffixBonus, randomPartition, randomStep, replaceByParam, getLocaleName } from '@/helper';
|
||||||
import useAffixStore from '@/stores/affixStore';
|
|
||||||
import { mappingStats } from '@/constant/constant';
|
import { mappingStats } from '@/constant/constant';
|
||||||
import useAvatarStore from '@/stores/avatarStore';
|
|
||||||
import useModelStore from '@/stores/modelStore';
|
import useModelStore from '@/stores/modelStore';
|
||||||
import useRelicMakerStore from '@/stores/relicMakerStore';
|
import useRelicMakerStore from '@/stores/relicMakerStore';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { useTranslations } from 'next-intl'
|
import { useTranslations } from 'next-intl'
|
||||||
import { ChevronDown, ChevronUp } from 'lucide-react';
|
import { ChevronDown, ChevronUp } from 'lucide-react';
|
||||||
import { AnimatePresence, motion } from 'framer-motion';
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
|
import useDetailDataStore from '@/stores/detailDataStore';
|
||||||
|
import useCurrentDataStore from '@/stores/currentDataStore';
|
||||||
|
import { RelicSetDetail, SubAffixData } from '@/types';
|
||||||
|
import useLocaleStore from '@/stores/localeStore';
|
||||||
|
import { mappingRelicSlot } from "@/constant/constant";
|
||||||
|
|
||||||
export default function RelicMaker() {
|
export default function RelicMaker() {
|
||||||
const { avatars, setAvatars } = useUserDataStore()
|
const { avatars, setAvatars } = useUserDataStore()
|
||||||
const { avatarSelected } = useAvatarStore()
|
const { avatarSelected } = useCurrentDataStore()
|
||||||
const { setIsOpenRelic } = useModelStore()
|
const { setIsOpenRelic } = useModelStore()
|
||||||
const { mapRelicInfo } = useRelicStore()
|
const { mainAffix, subAffix, mapRelicSet } = useDetailDataStore()
|
||||||
const { mapMainAffix, mapSubAffix } = useAffixStore()
|
const { locale } = useLocaleStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
const {
|
const {
|
||||||
selectedRelicSlot,
|
selectedRelicSlot,
|
||||||
@@ -40,11 +41,11 @@ export default function RelicMaker() {
|
|||||||
const [error, setError] = useState<string>("");
|
const [error, setError] = useState<string>("");
|
||||||
|
|
||||||
const relicSets = useMemo(() => {
|
const relicSets = useMemo(() => {
|
||||||
const listSet: Record<string, RelicDetail> = {};
|
const listSet: Record<string, RelicSetDetail> = {};
|
||||||
for (const [key, value] of Object.entries(mapRelicInfo || {})) {
|
for (const [key, value] of Object.entries(mapRelicSet || {})) {
|
||||||
let isOk = false;
|
let isOk = false;
|
||||||
for (const key2 of Object.keys(value.Parts)) {
|
for (const key2 of Object.keys(value.Parts)) {
|
||||||
if (key2.endsWith(selectedRelicSlot)) {
|
if (key2 == mappingRelicSlot?.[selectedRelicSlot]) {
|
||||||
isOk = true;
|
isOk = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -54,30 +55,30 @@ export default function RelicMaker() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return listSet;
|
return listSet;
|
||||||
}, [mapRelicInfo, selectedRelicSlot]);
|
}, [mapRelicSet , selectedRelicSlot]);
|
||||||
|
|
||||||
const subAffixOptions = useMemo(() => {
|
const subAffixOptions = useMemo(() => {
|
||||||
const listSet: Record<string, AffixDetail> = {};
|
const listSet: Record<string, SubAffixData> = {};
|
||||||
const subAffixMap = mapSubAffix["5"];
|
const subAffixMap = subAffix["5"];
|
||||||
const mainAffixMap = mapMainAffix["5" + selectedRelicSlot]
|
const mainAffixMap = mainAffix["5" + selectedRelicSlot]
|
||||||
|
|
||||||
if (Object.keys(subAffixMap || {}).length === 0 || Object.keys(mainAffixMap || {}).length === 0) return listSet;
|
if (Object.keys(subAffixMap || {}).length === 0 || Object.keys(mainAffixMap || {}).length === 0) return listSet;
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(subAffixMap)) {
|
for (const [key, value] of Object.entries(subAffixMap)) {
|
||||||
if (value.property !== mainAffixMap[selectedMainStat]?.property) {
|
if (value.Property !== mainAffixMap[selectedMainStat]?.Property) {
|
||||||
listSet[key] = value;
|
listSet[key] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return listSet;
|
return listSet;
|
||||||
}, [mapSubAffix, mapMainAffix, selectedRelicSlot, selectedMainStat]);
|
}, [subAffix, mainAffix, selectedRelicSlot, selectedMainStat]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const subAffixMap = mapSubAffix["5"];
|
const subAffixMap = subAffix["5"];
|
||||||
const mainAffixMap = mapMainAffix["5" + selectedRelicSlot];
|
const mainAffixMap = mainAffix["5" + selectedRelicSlot];
|
||||||
|
|
||||||
if (!subAffixMap || !mainAffixMap) return;
|
if (!subAffixMap || !mainAffixMap) return;
|
||||||
|
|
||||||
const mainProp = mainAffixMap[selectedMainStat]?.property;
|
const mainProp = mainAffixMap[selectedMainStat]?.Property;
|
||||||
if (!mainProp) return;
|
if (!mainProp) return;
|
||||||
|
|
||||||
const newSubAffixes = structuredClone(listSelectedSubStats);
|
const newSubAffixes = structuredClone(listSelectedSubStats);
|
||||||
@@ -95,33 +96,33 @@ export default function RelicMaker() {
|
|||||||
|
|
||||||
if (updated) setListSelectedSubStats(newSubAffixes);
|
if (updated) setListSelectedSubStats(newSubAffixes);
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [selectedMainStat, mapSubAffix, mapMainAffix, selectedRelicSlot]);
|
}, [selectedMainStat, subAffix, mainAffix, selectedRelicSlot]);
|
||||||
|
|
||||||
const exSubAffixOptions = useMemo(() => {
|
const exSubAffixOptions = useMemo(() => {
|
||||||
const listSet: Record<string, AffixDetail> = {};
|
const listSet: Record<string, SubAffixData> = {};
|
||||||
const subAffixMap = mapSubAffix["5"];
|
const subAffixMap = subAffix["5"];
|
||||||
const mainAffixMap = mapMainAffix["5" + selectedRelicSlot];
|
const mainAffixMap = mainAffix["5" + selectedRelicSlot];
|
||||||
|
|
||||||
if (!subAffixMap || !mainAffixMap) return listSet;
|
if (!subAffixMap || !mainAffixMap) return listSet;
|
||||||
|
|
||||||
for (const [key, value] of Object.entries(subAffixMap)) {
|
for (const [key, value] of Object.entries(subAffixMap)) {
|
||||||
const subAffix = listSelectedSubStats.find((item) => item.property === value.property);
|
const subAffix = listSelectedSubStats.find((item) => item.property === value.Property);
|
||||||
if (subAffix && value.property !== mainAffixMap[selectedMainStat]?.property) {
|
if (subAffix && value.Property !== mainAffixMap[selectedMainStat]?.Property) {
|
||||||
listSet[key] = value;
|
listSet[key] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return listSet;
|
return listSet;
|
||||||
}, [mapSubAffix, listSelectedSubStats, mapMainAffix, selectedRelicSlot, selectedMainStat]);
|
}, [subAffix, listSelectedSubStats, mainAffix, selectedRelicSlot, selectedMainStat]);
|
||||||
|
|
||||||
const effectBonus = useMemo(() => {
|
const effectBonus = useMemo(() => {
|
||||||
const affixSet = mapMainAffix?.["5" + selectedRelicSlot];
|
const affixSet = mainAffix?.["5" + selectedRelicSlot];
|
||||||
if (!affixSet) return 0;
|
if (!affixSet) return 0;
|
||||||
|
|
||||||
const data = affixSet[selectedMainStat];
|
const data = affixSet[selectedMainStat];
|
||||||
if (!data) return 0;
|
if (!data) return 0;
|
||||||
|
|
||||||
return calcMainAffixBonus(data, selectedRelicLevel);
|
return calcMainAffixBonus(data, selectedRelicLevel);
|
||||||
}, [mapMainAffix, selectedRelicSlot, selectedMainStat, selectedRelicLevel]);
|
}, [mainAffix, selectedRelicSlot, selectedMainStat, selectedRelicLevel]);
|
||||||
|
|
||||||
const handleSubStatChange = (key: string, index: number, rollCount: number, stepCount: number) => {
|
const handleSubStatChange = (key: string, index: number, rollCount: number, stepCount: number) => {
|
||||||
setError("");
|
setError("");
|
||||||
@@ -136,7 +137,7 @@ export default function RelicMaker() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
newSubAffixes[index].affixId = key;
|
newSubAffixes[index].affixId = key;
|
||||||
newSubAffixes[index].property = subAffixOptions[key].property;
|
newSubAffixes[index].property = subAffixOptions[key].Property;
|
||||||
newSubAffixes[index].rollCount = rollCount;
|
newSubAffixes[index].rollCount = rollCount;
|
||||||
newSubAffixes[index].stepCount = stepCount;
|
newSubAffixes[index].stepCount = stepCount;
|
||||||
setListSelectedSubStats(newSubAffixes);
|
setListSelectedSubStats(newSubAffixes);
|
||||||
@@ -179,7 +180,7 @@ export default function RelicMaker() {
|
|||||||
exKeys.push(randomKey);
|
exKeys.push(randomKey);
|
||||||
const randomValue = subAffixOptions[randomKey];
|
const randomValue = subAffixOptions[randomKey];
|
||||||
newSubAffixes[i].affixId = randomKey;
|
newSubAffixes[i].affixId = randomKey;
|
||||||
newSubAffixes[i].property = randomValue.property;
|
newSubAffixes[i].property = randomValue.Property;
|
||||||
newSubAffixes[i].rollCount = 0;
|
newSubAffixes[i].rollCount = 0;
|
||||||
newSubAffixes[i].stepCount = 0;
|
newSubAffixes[i].stepCount = 0;
|
||||||
}
|
}
|
||||||
@@ -205,7 +206,7 @@ export default function RelicMaker() {
|
|||||||
|
|
||||||
const handlerSaveRelic = () => {
|
const handlerSaveRelic = () => {
|
||||||
setError("");
|
setError("");
|
||||||
const avatar = avatars[avatarSelected?.id || ""];
|
const avatar = avatars[avatarSelected?.ID?.toString() || ""];
|
||||||
if (!selectedRelicSet || !selectedMainStat || !selectedRelicLevel || !selectedRelicSlot) {
|
if (!selectedRelicSet || !selectedMainStat || !selectedRelicLevel || !selectedRelicSlot) {
|
||||||
setError(transI18n("pleaseSelectAllOptions"));
|
setError(transI18n("pleaseSelectAllOptions"));
|
||||||
return;
|
return;
|
||||||
@@ -258,10 +259,10 @@ export default function RelicMaker() {
|
|||||||
<div>
|
<div>
|
||||||
<label className="block text-lg font-medium mb-2">{transI18n("mainStat")}</label>
|
<label className="block text-lg font-medium mb-2">{transI18n("mainStat")}</label>
|
||||||
<SelectCustomImage
|
<SelectCustomImage
|
||||||
customSet={Object.entries(mapMainAffix["5" + selectedRelicSlot] || {}).map(([key, value]) => ({
|
customSet={Object.entries(mainAffix["5" + selectedRelicSlot] || {}).map(([key, value]) => ({
|
||||||
value: key,
|
value: key,
|
||||||
label: mappingStats[value.property].name + " " + mappingStats[value.property].unit,
|
label: mappingStats[value.Property].name + " " + mappingStats[value.Property].unit,
|
||||||
imageUrl: mappingStats[value.property].icon
|
imageUrl: `${process.env.CDN_URL}/${mappingStats[value.Property].icon}`
|
||||||
}))}
|
}))}
|
||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={selectedMainStat}
|
selectedCustomSet={selectedMainStat}
|
||||||
@@ -275,8 +276,8 @@ export default function RelicMaker() {
|
|||||||
<SelectCustomImage
|
<SelectCustomImage
|
||||||
customSet={Object.entries(relicSets).map(([key, value]) => ({
|
customSet={Object.entries(relicSets).map(([key, value]) => ({
|
||||||
value: key,
|
value: key,
|
||||||
label: value.Name,
|
label: getLocaleName(locale, value.Name),
|
||||||
imageUrl: `${process.env.CDN_URL}/spriteoutput/itemfigures/${value.Icon.match(/\d+/)?.[0]}.png`
|
imageUrl: `${process.env.CDN_URL}/${value.Image.SetIconPath}`
|
||||||
}))}
|
}))}
|
||||||
excludeSet={[]}
|
excludeSet={[]}
|
||||||
selectedCustomSet={selectedRelicSet}
|
selectedCustomSet={selectedRelicSet}
|
||||||
@@ -288,15 +289,15 @@ export default function RelicMaker() {
|
|||||||
|
|
||||||
{/* Set Bonus Display */}
|
{/* Set Bonus Display */}
|
||||||
<div className="mb-6 py-4 bg-base-100 rounded-lg">
|
<div className="mb-6 py-4 bg-base-100 rounded-lg">
|
||||||
{selectedRelicSet !== "" ? Object.entries(mapRelicInfo[selectedRelicSet].RequireNum).map(([key, value]) => (
|
{selectedRelicSet !== "" ? Object.entries(mapRelicSet[selectedRelicSet].Skills).map(([key, value]) => (
|
||||||
<div key={key} className="text-blue-300 text-sm mb-1">
|
<div key={key} className="text-blue-300 text-sm mb-1">
|
||||||
<span className="text-info font-bold">{key}-Pc:
|
<span className="text-info font-bold">{key}-Pc:
|
||||||
<div
|
<div
|
||||||
className="text-warning leading-relaxed font-bold"
|
className="text-warning leading-relaxed font-bold"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: replaceByParam(
|
__html: replaceByParam(
|
||||||
value.Desc,
|
getLocaleName(locale, value.Desc),
|
||||||
value.ParamList || []
|
value.Param || []
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/> </span>
|
/> </span>
|
||||||
@@ -384,13 +385,13 @@ export default function RelicMaker() {
|
|||||||
<SelectCustomImage
|
<SelectCustomImage
|
||||||
customSet={Object.entries(subAffixOptions).map(([key, value]) => ({
|
customSet={Object.entries(subAffixOptions).map(([key, value]) => ({
|
||||||
value: key,
|
value: key,
|
||||||
label: mappingStats[value.property].name + " " + mappingStats[value.property].unit,
|
label: mappingStats[value.Property].name + " " + mappingStats[value.Property].unit,
|
||||||
imageUrl: mappingStats[value.property].icon
|
imageUrl: `${process.env.CDN_URL}/${mappingStats[value.Property].icon}`
|
||||||
}))}
|
}))}
|
||||||
excludeSet={Object.entries(exSubAffixOptions).map(([key, value]) => ({
|
excludeSet={Object.entries(exSubAffixOptions).map(([key, value]) => ({
|
||||||
value: key,
|
value: key,
|
||||||
label: mappingStats[value.property].name + " " + mappingStats[value.property].unit,
|
label: mappingStats[value.Property].name + " " + mappingStats[value.Property].unit,
|
||||||
imageUrl: mappingStats[value.property].icon
|
imageUrl: `${process.env.CDN_URL}/${mappingStats[value.Property].icon}`
|
||||||
}))}
|
}))}
|
||||||
selectedCustomSet={v.affixId}
|
selectedCustomSet={v.affixId}
|
||||||
placeholder={transI18n("selectASubStat")}
|
placeholder={transI18n("selectASubStat")}
|
||||||
@@ -401,7 +402,7 @@ export default function RelicMaker() {
|
|||||||
{/* Current Value */}
|
{/* Current Value */}
|
||||||
<div className="col-span-4 text-center flex items-center justify-center gap-2">
|
<div className="col-span-4 text-center flex items-center justify-center gap-2">
|
||||||
<span className="text-2xl font-mono">+{ }</span>
|
<span className="text-2xl font-mono">+{ }</span>
|
||||||
<div className="text-xl font-bold text-info">{calcAffixBonus(subAffixOptions[v.affixId], v.stepCount, v.rollCount)}{mappingStats?.[subAffixOptions[v.affixId]?.property]?.unit || ""}</div>
|
<div className="text-xl font-bold text-info">{calcAffixBonus(subAffixOptions[v.affixId], v.stepCount, v.rollCount)}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Up Roll Values */}
|
{/* Up Roll Values */}
|
||||||
@@ -415,19 +416,19 @@ export default function RelicMaker() {
|
|||||||
onClick={() => handleSubStatChange(v.affixId, index, v.rollCount + 1, v.stepCount + 0)}
|
onClick={() => handleSubStatChange(v.affixId, index, v.rollCount + 1, v.stepCount + 0)}
|
||||||
className="btn btn-sm btn-info border-0"
|
className="btn btn-sm btn-info border-0"
|
||||||
>
|
>
|
||||||
{calcAffixBonus(subAffixOptions[v.affixId], 0, v.rollCount + 1)}{mappingStats?.[subAffixOptions[v.affixId]?.property]?.unit || ""}
|
{calcAffixBonus(subAffixOptions[v.affixId], 0, v.rollCount + 1)}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleSubStatChange(v.affixId, index, v.rollCount + 1, v.stepCount + 1)}
|
onClick={() => handleSubStatChange(v.affixId, index, v.rollCount + 1, v.stepCount + 1)}
|
||||||
className="btn btn-sm btn-info border-0"
|
className="btn btn-sm btn-info border-0"
|
||||||
>
|
>
|
||||||
{calcAffixBonus(subAffixOptions[v.affixId], v.stepCount + 1, v.rollCount + 1)}{mappingStats?.[subAffixOptions[v.affixId]?.property]?.unit || ""}
|
{calcAffixBonus(subAffixOptions[v.affixId], v.stepCount + 1, v.rollCount + 1)}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleSubStatChange(v.affixId, index, v.rollCount + 1, v.stepCount + 2)}
|
onClick={() => handleSubStatChange(v.affixId, index, v.rollCount + 1, v.stepCount + 2)}
|
||||||
className="btn btn-sm btn-info border-0"
|
className="btn btn-sm btn-info border-0"
|
||||||
>
|
>
|
||||||
{calcAffixBonus(subAffixOptions[v.affixId], v.stepCount + 2, v.rollCount + 1)}{mappingStats?.[subAffixOptions[v.affixId]?.property]?.unit || ""}
|
{calcAffixBonus(subAffixOptions[v.affixId], v.stepCount + 2, v.rollCount + 1)}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -443,19 +444,19 @@ export default function RelicMaker() {
|
|||||||
onClick={() => handleSubStatChange(v.affixId, index, Math.max(v.rollCount - 1, 0), Math.max(v.stepCount, 0))}
|
onClick={() => handleSubStatChange(v.affixId, index, Math.max(v.rollCount - 1, 0), Math.max(v.stepCount, 0))}
|
||||||
className="btn btn-sm btn-info border-0"
|
className="btn btn-sm btn-info border-0"
|
||||||
>
|
>
|
||||||
{calcAffixBonus(subAffixOptions[v.affixId], 0, Math.max(v.rollCount - 1, 0))}{mappingStats?.[subAffixOptions[v.affixId]?.property]?.unit || ""}
|
{calcAffixBonus(subAffixOptions[v.affixId], 0, Math.max(v.rollCount - 1, 0))}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleSubStatChange(v.affixId, index, Math.max(v.rollCount - 1, 0), Math.max(v.stepCount - 1, 0))}
|
onClick={() => handleSubStatChange(v.affixId, index, Math.max(v.rollCount - 1, 0), Math.max(v.stepCount - 1, 0))}
|
||||||
className="btn btn-sm btn-info border-0"
|
className="btn btn-sm btn-info border-0"
|
||||||
>
|
>
|
||||||
{calcAffixBonus(subAffixOptions[v.affixId], Math.max(v.stepCount - 1, 0), Math.max(v.rollCount - 1, 0))}{mappingStats?.[subAffixOptions[v.affixId]?.property]?.unit || ""}
|
{calcAffixBonus(subAffixOptions[v.affixId], Math.max(v.stepCount - 1, 0), Math.max(v.rollCount - 1, 0))}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => handleSubStatChange(v.affixId, index, Math.max(v.rollCount - 1, 0), Math.max(v.stepCount - 2, 0))}
|
onClick={() => handleSubStatChange(v.affixId, index, Math.max(v.rollCount - 1, 0), Math.max(v.stepCount - 2, 0))}
|
||||||
className="btn btn-sm btn-info border-0"
|
className="btn btn-sm btn-info border-0"
|
||||||
>
|
>
|
||||||
{calcAffixBonus(subAffixOptions[v.affixId], Math.max(v.stepCount - 2, 0), Math.max(v.rollCount - 1, 0))}{mappingStats?.[subAffixOptions[v.affixId]?.property]?.unit || ""}
|
{calcAffixBonus(subAffixOptions[v.affixId], Math.max(v.stepCount - 2, 0), Math.max(v.rollCount - 1, 0))}{mappingStats?.[subAffixOptions[v.affixId]?.Property]?.unit || ""}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,18 +6,18 @@ import { motion } from "framer-motion";
|
|||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import RelicCard from "../card/relicCard";
|
import RelicCard from "../card/relicCard";
|
||||||
import useAvatarStore from "@/stores/avatarStore";
|
|
||||||
import useRelicStore from "@/stores/relicStore";
|
|
||||||
import useModelStore from '@/stores/modelStore';
|
import useModelStore from '@/stores/modelStore';
|
||||||
import { replaceByParam } from '@/helper';
|
import { replaceByParam } from '@/helper';
|
||||||
import useRelicMakerStore from '@/stores/relicMakerStore';
|
import useRelicMakerStore from '@/stores/relicMakerStore';
|
||||||
import useAffixStore from '@/stores/affixStore';
|
|
||||||
import QuickView from "../quickView";
|
import QuickView from "../quickView";
|
||||||
import { ModalConfig } from "@/types";
|
import { ModalConfig } from "@/types";
|
||||||
|
import useCurrentDataStore from "@/stores/currentDataStore";
|
||||||
|
import useDetailDataStore from "@/stores/detailDataStore";
|
||||||
|
import { getLocaleName } from '@/helper';
|
||||||
|
import useLocaleStore from "@/stores/localeStore";
|
||||||
|
|
||||||
export default function RelicsInfo() {
|
export default function RelicsInfo() {
|
||||||
const { avatars, setAvatars } = useUserDataStore()
|
const { avatars, setAvatars } = useUserDataStore()
|
||||||
const { avatarSelected } = useAvatarStore()
|
|
||||||
const {
|
const {
|
||||||
setSelectedRelicSlot,
|
setSelectedRelicSlot,
|
||||||
selectedRelicSlot,
|
selectedRelicSlot,
|
||||||
@@ -29,7 +29,7 @@ export default function RelicsInfo() {
|
|||||||
resetSubStat,
|
resetSubStat,
|
||||||
listSelectedSubStats,
|
listSelectedSubStats,
|
||||||
} = useRelicMakerStore()
|
} = useRelicMakerStore()
|
||||||
const { mapSubAffix } = useAffixStore()
|
|
||||||
const {
|
const {
|
||||||
isOpenRelic,
|
isOpenRelic,
|
||||||
setIsOpenRelic,
|
setIsOpenRelic,
|
||||||
@@ -37,9 +37,10 @@ export default function RelicsInfo() {
|
|||||||
setIsOpenQuickView
|
setIsOpenQuickView
|
||||||
} = useModelStore()
|
} = useModelStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
|
const { avatarSelected } = useCurrentDataStore()
|
||||||
const { mapRelicInfo } = useRelicStore()
|
const { mapRelicSet, subAffix } = useDetailDataStore()
|
||||||
|
const { locale } = useLocaleStore()
|
||||||
|
|
||||||
const handleShow = (modalId: string) => {
|
const handleShow = (modalId: string) => {
|
||||||
const modal = document.getElementById(modalId) as HTMLDialogElement | null;
|
const modal = document.getElementById(modalId) as HTMLDialogElement | null;
|
||||||
if (modal) {
|
if (modal) {
|
||||||
@@ -56,8 +57,8 @@ export default function RelicsInfo() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const getRelic = useCallback((slot: string) => {
|
const getRelic = useCallback((slot: string) => {
|
||||||
const avatar = avatars[avatarSelected?.id || ""];
|
const avatar = avatars[avatarSelected?.ID.toString() || ""];
|
||||||
if (avatar) {
|
if (avatar) {
|
||||||
return avatar.profileList[avatar.profileSelect]?.relics[slot] || null;
|
return avatar.profileList[avatar.profileSelect]?.relics[slot] || null;
|
||||||
}
|
}
|
||||||
@@ -65,7 +66,7 @@ export default function RelicsInfo() {
|
|||||||
}, [avatars, avatarSelected]);
|
}, [avatars, avatarSelected]);
|
||||||
|
|
||||||
const handlerDeleteRelic = (slot: string) => {
|
const handlerDeleteRelic = (slot: string) => {
|
||||||
const avatar = avatars[avatarSelected?.id || ""];
|
const avatar = avatars[avatarSelected?.ID.toString() || ""];
|
||||||
if (avatar) {
|
if (avatar) {
|
||||||
delete avatar.profileList[avatar.profileSelect].relics[slot]
|
delete avatar.profileList[avatar.profileSelect].relics[slot]
|
||||||
setAvatars({ ...avatars });
|
setAvatars({ ...avatars });
|
||||||
@@ -84,7 +85,7 @@ export default function RelicsInfo() {
|
|||||||
const newSubAffixes: { affixId: string, property: string, rollCount: number, stepCount: number }[] = [...listSelectedSubStats];
|
const newSubAffixes: { affixId: string, property: string, rollCount: number, stepCount: number }[] = [...listSelectedSubStats];
|
||||||
relic.sub_affixes.forEach((item, index) => {
|
relic.sub_affixes.forEach((item, index) => {
|
||||||
newSubAffixes[index].affixId = item.sub_affix_id.toString();
|
newSubAffixes[index].affixId = item.sub_affix_id.toString();
|
||||||
newSubAffixes[index].property = mapSubAffix["5"][item.sub_affix_id.toString()]?.property || "";
|
newSubAffixes[index].property = subAffix["5"][item.sub_affix_id.toString()]?.Property || "";
|
||||||
newSubAffixes[index].rollCount = item.count || 0;
|
newSubAffixes[index].rollCount = item.count || 0;
|
||||||
newSubAffixes[index].stepCount = item.step || 0;
|
newSubAffixes[index].stepCount = item.step || 0;
|
||||||
})
|
})
|
||||||
@@ -108,7 +109,7 @@ export default function RelicsInfo() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const relicEffects = useMemo(() => {
|
const relicEffects = useMemo(() => {
|
||||||
const avatar = avatars[avatarSelected?.id || ""];
|
const avatar = avatars[avatarSelected?.ID.toString() || ""];
|
||||||
const relicCount: { [key: string]: number } = {};
|
const relicCount: { [key: string]: number } = {};
|
||||||
if (avatar) {
|
if (avatar) {
|
||||||
for (const relic of Object.values(avatar.profileList[avatar.profileSelect].relics)) {
|
for (const relic of Object.values(avatar.profileList[avatar.profileSelect].relics)) {
|
||||||
@@ -199,7 +200,7 @@ export default function RelicsInfo() {
|
|||||||
>
|
>
|
||||||
<RelicCard
|
<RelicCard
|
||||||
slot={item}
|
slot={item}
|
||||||
avatarId={avatarSelected?.id || ""}
|
avatarId={avatarSelected?.ID.toString() || ""}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -284,7 +285,7 @@ export default function RelicsInfo() {
|
|||||||
|
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
{relicEffects.map((setEffect, index) => {
|
{relicEffects.map((setEffect, index) => {
|
||||||
const relicInfo = mapRelicInfo[setEffect.key];
|
const relicInfo = mapRelicSet[setEffect.key];
|
||||||
if (!relicInfo) return null;
|
if (!relicInfo) return null;
|
||||||
return (
|
return (
|
||||||
<div key={index} className="space-y-3">
|
<div key={index} className="space-y-3">
|
||||||
@@ -293,7 +294,7 @@ export default function RelicsInfo() {
|
|||||||
className="font-bold text-warning"
|
className="font-bold text-warning"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: replaceByParam(
|
__html: replaceByParam(
|
||||||
relicInfo.Name,
|
getLocaleName(locale, relicInfo.Name),
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
@@ -306,7 +307,7 @@ export default function RelicsInfo() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2 pl-4">
|
<div className="space-y-2 pl-4">
|
||||||
{Object.entries(relicInfo.RequireNum).map(([requireNum, value]) => {
|
{Object.entries(relicInfo.Skills).map(([requireNum, value]) => {
|
||||||
if (Number(requireNum) > Number(setEffect.count)) return null;
|
if (Number(requireNum) > Number(setEffect.count)) return null;
|
||||||
return (
|
return (
|
||||||
<div key={requireNum} className="space-y-1">
|
<div key={requireNum} className="space-y-1">
|
||||||
@@ -317,8 +318,8 @@ export default function RelicsInfo() {
|
|||||||
className="text-sm text-base-content/80 leading-relaxed pl-4"
|
className="text-sm text-base-content/80 leading-relaxed pl-4"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: replaceByParam(
|
__html: replaceByParam(
|
||||||
value.Desc,
|
getLocaleName(locale, value.Desc),
|
||||||
value.ParamList || []
|
value.Param || []
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { useEffect, useState, useRef, useMemo, useCallback } from 'react';
|
import { useEffect, useState, useRef, useMemo, useCallback } from 'react';
|
||||||
import useAvatarStore from "@/stores/avatarStore";
|
|
||||||
import { FastAverageColor } from 'fast-average-color';
|
import { FastAverageColor } from 'fast-average-color';
|
||||||
import NextImage from 'next/image';
|
import NextImage from 'next/image';
|
||||||
import ParseText from '../parseText';
|
import ParseText from '../parseText';
|
||||||
@@ -8,27 +7,24 @@ import useLocaleStore from '@/stores/localeStore';
|
|||||||
import { calcAffixBonus, calcBaseStat, calcBaseStatRaw, calcBonusStatRaw, calcMainAffixBonus, calcMainAffixBonusRaw, calcPromotion, calcSubAffixBonusRaw, convertToRoman, getNameChar, replaceByParam } from '@/helper';
|
import { calcAffixBonus, calcBaseStat, calcBaseStatRaw, calcBonusStatRaw, calcMainAffixBonus, calcMainAffixBonusRaw, calcPromotion, calcSubAffixBonusRaw, convertToRoman, getNameChar, replaceByParam } from '@/helper';
|
||||||
import useUserDataStore from '@/stores/userDataStore';
|
import useUserDataStore from '@/stores/userDataStore';
|
||||||
import { traceShowCaseMap } from '@/constant/traceConstant';
|
import { traceShowCaseMap } from '@/constant/traceConstant';
|
||||||
import { StatusAddType } from '@/types';
|
|
||||||
import { mappingStats } from '@/constant/constant';
|
import { mappingStats } from '@/constant/constant';
|
||||||
import useLightconeStore from '@/stores/lightconeStore';
|
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import useAffixStore from '@/stores/affixStore';
|
|
||||||
import useRelicStore from '@/stores/relicStore';
|
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import RelicShowcase from './relicShowcase';
|
import RelicShowcase from './relicShowcase';
|
||||||
|
import useDetailDataStore from '@/stores/detailDataStore';
|
||||||
|
import useCurrentDataStore from '@/stores/currentDataStore';
|
||||||
|
import { getLocaleName } from '@/helper';
|
||||||
|
|
||||||
|
|
||||||
export default function ShowCaseInfo() {
|
export default function ShowCaseInfo() {
|
||||||
const { avatarSelected, mapAvatarInfo } = useAvatarStore()
|
const { avatarSelected } = useCurrentDataStore()
|
||||||
const { mapLightconeInfo } = useLightconeStore()
|
const { mainAffix, subAffix, mapRelicSet, mapAvatar, mapLightCone, baseType, damageType } = useDetailDataStore()
|
||||||
const { mapMainAffix, mapSubAffix } = useAffixStore()
|
|
||||||
const { avatars } = useUserDataStore()
|
const { avatars } = useUserDataStore()
|
||||||
const [avgColor, setAvgColor] = useState('#222');
|
const [avgColor, setAvgColor] = useState('#222');
|
||||||
const imgRef = useRef(null);
|
const imgRef = useRef(null);
|
||||||
const cardRef = useRef(null)
|
const cardRef = useRef(null)
|
||||||
const { locale } = useLocaleStore()
|
const { locale } = useLocaleStore()
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
const { mapRelicInfo } = useRelicStore()
|
|
||||||
|
|
||||||
const handleSaveImage = useCallback(() => {
|
const handleSaveImage = useCallback(() => {
|
||||||
if (cardRef.current === null || !avatarSelected) {
|
if (cardRef.current === null || !avatarSelected) {
|
||||||
@@ -52,25 +48,24 @@ export default function ShowCaseInfo() {
|
|||||||
link.click();
|
link.click();
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
console.log(e)
|
|
||||||
toast.error("Error generating showcase card!");
|
toast.error("Error generating showcase card!");
|
||||||
});
|
});
|
||||||
}, [avatarSelected, locale, transI18n]);
|
}, [avatarSelected, locale, transI18n]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!avatarSelected?.id) return;
|
if (!avatarSelected) return;
|
||||||
const fac = new FastAverageColor();
|
const fac = new FastAverageColor();
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
|
|
||||||
img.crossOrigin = 'anonymous';
|
img.crossOrigin = 'anonymous';
|
||||||
img.src = `${process.env.CDN_URL}/spriteoutput/avatardrawcard/${avatarSelected?.id}.png`;
|
img.src = `${process.env.CDN_URL}/${avatarSelected?.Image?.AvatarCutinFrontImgPath}`;
|
||||||
|
|
||||||
img.onload = () => {
|
img.onload = () => {
|
||||||
fac.getColorAsync(img)
|
fac.getColorAsync(img)
|
||||||
.then((color) => {
|
.then((color) => {
|
||||||
setAvgColor(color.hex);
|
setAvgColor(color.hex);
|
||||||
})
|
})
|
||||||
.catch(e => console.error("Vẫn lỗi CORS:", e));
|
.catch(e => console.error("Error:", e));
|
||||||
};
|
};
|
||||||
return () => {
|
return () => {
|
||||||
fac.destroy();
|
fac.destroy();
|
||||||
@@ -78,22 +73,17 @@ export default function ShowCaseInfo() {
|
|||||||
};
|
};
|
||||||
}, [avatarSelected]);
|
}, [avatarSelected]);
|
||||||
|
|
||||||
const avatarInfo = useMemo(() => {
|
|
||||||
if (!avatarSelected) return
|
|
||||||
return mapAvatarInfo[avatarSelected.id]
|
|
||||||
}, [avatarSelected, mapAvatarInfo])
|
|
||||||
|
|
||||||
const avatarSkillTree = useMemo(() => {
|
const avatarSkillTree = useMemo(() => {
|
||||||
if (!avatarSelected || !avatars[avatarSelected.id]) return {}
|
if (!avatarSelected || !avatars[avatarSelected?.ID?.toString()]) return {}
|
||||||
if (avatars[avatarSelected.id].enhanced) {
|
if (avatars[avatarSelected?.ID?.toString()].enhanced) {
|
||||||
return avatarInfo?.Enhanced[avatars[avatarSelected.id].enhanced.toString()].SkillTrees || {}
|
return avatarSelected?.Enhanced?.[avatars[avatarSelected?.ID?.toString()].enhanced.toString()].SkillTrees || {}
|
||||||
}
|
}
|
||||||
return avatarInfo?.SkillTrees || {}
|
return avatarSelected?.SkillTrees || {}
|
||||||
}, [avatarSelected, avatarInfo, avatars])
|
}, [avatarSelected, avatars])
|
||||||
|
|
||||||
const avatarData = useMemo(() => {
|
const avatarData = useMemo(() => {
|
||||||
if (!avatarSelected) return
|
if (!avatarSelected) return
|
||||||
return avatars[avatarSelected.id]
|
return avatars[avatarSelected?.ID?.toString()]
|
||||||
}, [avatarSelected, avatars])
|
}, [avatarSelected, avatars])
|
||||||
|
|
||||||
const avatarProfile = useMemo(() => {
|
const avatarProfile = useMemo(() => {
|
||||||
@@ -102,23 +92,23 @@ export default function ShowCaseInfo() {
|
|||||||
}, [avatarSelected, avatarData])
|
}, [avatarSelected, avatarData])
|
||||||
|
|
||||||
const lightconeStats = useMemo(() => {
|
const lightconeStats = useMemo(() => {
|
||||||
if (!avatarSelected || !avatarProfile?.lightcone || !mapLightconeInfo[avatarProfile?.lightcone?.item_id]) return
|
if (!avatarSelected || !avatarProfile?.lightcone || !mapLightCone[avatarProfile?.lightcone?.item_id]) return
|
||||||
const promotion = calcPromotion(avatarProfile?.lightcone?.level)
|
const promotion = calcPromotion(avatarProfile?.lightcone?.level)
|
||||||
const atkStat = calcBaseStat(
|
const atkStat = calcBaseStat(
|
||||||
mapLightconeInfo[avatarProfile?.lightcone?.item_id].Stats[promotion].BaseAttack,
|
mapLightCone[avatarProfile?.lightcone?.item_id].Stats[promotion].BaseAttack,
|
||||||
mapLightconeInfo[avatarProfile?.lightcone?.item_id].Stats[promotion].BaseAttackAdd,
|
mapLightCone[avatarProfile?.lightcone?.item_id].Stats[promotion].BaseAttackAdd,
|
||||||
0,
|
0,
|
||||||
avatarProfile?.lightcone?.level
|
avatarProfile?.lightcone?.level
|
||||||
)
|
)
|
||||||
const hpStat = calcBaseStat(
|
const hpStat = calcBaseStat(
|
||||||
mapLightconeInfo[avatarProfile?.lightcone?.item_id].Stats[promotion].BaseHP,
|
mapLightCone[avatarProfile?.lightcone?.item_id].Stats[promotion].BaseHP,
|
||||||
mapLightconeInfo[avatarProfile?.lightcone?.item_id].Stats[promotion].BaseHPAdd,
|
mapLightCone[avatarProfile?.lightcone?.item_id].Stats[promotion].BaseHPAdd,
|
||||||
0,
|
0,
|
||||||
avatarProfile?.lightcone?.level
|
avatarProfile?.lightcone?.level
|
||||||
)
|
)
|
||||||
const defStat = calcBaseStat(
|
const defStat = calcBaseStat(
|
||||||
mapLightconeInfo[avatarProfile?.lightcone?.item_id].Stats[promotion].BaseDefence,
|
mapLightCone[avatarProfile?.lightcone?.item_id].Stats[promotion].BaseDefence,
|
||||||
mapLightconeInfo[avatarProfile?.lightcone?.item_id].Stats[promotion].BaseDefenceAdd,
|
mapLightCone[avatarProfile?.lightcone?.item_id].Stats[promotion].BaseDefenceAdd,
|
||||||
0,
|
0,
|
||||||
avatarProfile?.lightcone?.level
|
avatarProfile?.lightcone?.level
|
||||||
)
|
)
|
||||||
@@ -127,10 +117,10 @@ export default function ShowCaseInfo() {
|
|||||||
hp: hpStat,
|
hp: hpStat,
|
||||||
def: defStat,
|
def: defStat,
|
||||||
}
|
}
|
||||||
}, [avatarSelected, mapLightconeInfo, avatarProfile])
|
}, [avatarSelected, mapLightCone, avatarProfile])
|
||||||
|
|
||||||
const relicEffects = useMemo(() => {
|
const relicEffects = useMemo(() => {
|
||||||
const avatar = avatars[avatarSelected?.id || ""];
|
const avatar = avatars[avatarSelected?.ID?.toString() || ""];
|
||||||
const relicCount: { [key: string]: number } = {};
|
const relicCount: { [key: string]: number } = {};
|
||||||
if (avatar) {
|
if (avatar) {
|
||||||
for (const relic of Object.values(avatar.profileList[avatar.profileSelect].relics)) {
|
for (const relic of Object.values(avatar.profileList[avatar.profileSelect].relics)) {
|
||||||
@@ -151,45 +141,45 @@ export default function ShowCaseInfo() {
|
|||||||
}, [avatars, avatarSelected]);
|
}, [avatars, avatarSelected]);
|
||||||
|
|
||||||
const relicStats = useMemo(() => {
|
const relicStats = useMemo(() => {
|
||||||
if (!avatarSelected || !avatarProfile?.relics || !mapMainAffix || !mapSubAffix) return
|
if (!avatarSelected || !avatarProfile?.relics || !mainAffix || !subAffix) return
|
||||||
|
|
||||||
return Object.entries(avatarProfile?.relics).map(([key, value]) => {
|
return Object.entries(avatarProfile?.relics).map(([key, value]) => {
|
||||||
const mainAffixMap = mapMainAffix["5" + key]
|
const mainAffixMap = mainAffix["5" + key]
|
||||||
const subAffixMap = mapSubAffix["5"]
|
const subAffixMap = subAffix["5"]
|
||||||
if (!mainAffixMap || !subAffixMap) return
|
if (!mainAffixMap || !subAffixMap) return
|
||||||
return {
|
return {
|
||||||
img: `${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${value.relic_set_id}_${key}.png`,
|
img: `${process.env.CDN_URL}/spriteoutput/relicfigures/IconRelic_${value.relic_set_id}_${key}.png`,
|
||||||
mainAffix: {
|
mainAffix: {
|
||||||
property: mainAffixMap?.[value?.main_affix_id]?.property,
|
property: mainAffixMap?.[value?.main_affix_id]?.Property,
|
||||||
level: value?.level,
|
level: value?.level,
|
||||||
valueAffix: calcMainAffixBonus(mainAffixMap?.[value?.main_affix_id], value?.level),
|
valueAffix: calcMainAffixBonus(mainAffixMap?.[value?.main_affix_id], value?.level),
|
||||||
detail: mappingStats?.[mainAffixMap?.[value?.main_affix_id]?.property]
|
detail: mappingStats?.[mainAffixMap?.[value?.main_affix_id]?.Property]
|
||||||
},
|
},
|
||||||
subAffix: value?.sub_affixes?.map((subValue) => {
|
subAffix: value?.sub_affixes?.map((subValue) => {
|
||||||
return {
|
return {
|
||||||
property: subAffixMap?.[subValue?.sub_affix_id]?.property,
|
property: subAffixMap?.[subValue?.sub_affix_id]?.Property,
|
||||||
valueAffix: calcAffixBonus(subAffixMap?.[subValue?.sub_affix_id], subValue?.step, subValue?.count),
|
valueAffix: calcAffixBonus(subAffixMap?.[subValue?.sub_affix_id], subValue?.step, subValue?.count),
|
||||||
detail: mappingStats?.[subAffixMap?.[subValue?.sub_affix_id]?.property],
|
detail: mappingStats?.[subAffixMap?.[subValue?.sub_affix_id]?.Property],
|
||||||
step: subValue?.step,
|
step: subValue?.step,
|
||||||
count: subValue?.count
|
count: subValue?.count
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, [avatarSelected, avatarProfile, mapMainAffix, mapSubAffix])
|
}, [avatarSelected, avatarProfile, mainAffix, subAffix])
|
||||||
|
|
||||||
const totalSubStats = useMemo(() => {
|
const totalSubStats = useMemo(() => {
|
||||||
if (!relicStats?.length) return 0
|
if (!relicStats?.length) return 0
|
||||||
return (relicStats ?? []).reduce((acc, relic) => {
|
return (relicStats ?? []).reduce((acc, relic) => {
|
||||||
const subAffixList = relic?.subAffix ?? []
|
const subAffixList = relic?.subAffix ?? []
|
||||||
return acc + subAffixList.reduce((subAcc, subAffix) => {
|
return acc + subAffixList.reduce((subAcc, subAffix) => {
|
||||||
if (avatarInfo?.Relics?.SubAffixPropertyList.findIndex(it => it === subAffix.property) !== -1) {
|
if (avatarSelected?.Relics?.SubAffixPropertyList.findIndex(it => it === subAffix.property) !== -1) {
|
||||||
return subAcc + (subAffix?.count ?? 0)
|
return subAcc + (subAffix?.count ?? 0)
|
||||||
}
|
}
|
||||||
return subAcc
|
return subAcc
|
||||||
}, 0)
|
}, 0)
|
||||||
}, 0)
|
}, 0)
|
||||||
}, [relicStats, avatarInfo])
|
}, [relicStats, avatarSelected])
|
||||||
|
|
||||||
const characterStats = useMemo(() => {
|
const characterStats = useMemo(() => {
|
||||||
if (!avatarSelected || !avatarData) return
|
if (!avatarSelected || !avatarData) return
|
||||||
@@ -205,73 +195,73 @@ export default function ShowCaseInfo() {
|
|||||||
}> = {
|
}> = {
|
||||||
HP: {
|
HP: {
|
||||||
value: calcBaseStatRaw(
|
value: calcBaseStatRaw(
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.HPBase,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPBase,
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.HPAdd,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPAdd,
|
||||||
avatarData.level
|
avatarData.level
|
||||||
),
|
),
|
||||||
base: calcBaseStatRaw(
|
base: calcBaseStatRaw(
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.HPBase,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPBase,
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.HPAdd,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.HPAdd,
|
||||||
avatarData.level
|
avatarData.level
|
||||||
),
|
),
|
||||||
name: "HP",
|
name: "HP",
|
||||||
icon: "/icon/hp.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconMaxHP.png",
|
||||||
unit: "",
|
unit: "",
|
||||||
round: 0
|
round: 0
|
||||||
},
|
},
|
||||||
ATK: {
|
ATK: {
|
||||||
value: calcBaseStatRaw(
|
value: calcBaseStatRaw(
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.AttackBase,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackBase,
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.AttackAdd,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackAdd,
|
||||||
avatarData.level
|
avatarData.level
|
||||||
),
|
),
|
||||||
base: calcBaseStatRaw(
|
base: calcBaseStatRaw(
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.AttackBase,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackBase,
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.AttackAdd,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.AttackAdd,
|
||||||
avatarData.level
|
avatarData.level
|
||||||
),
|
),
|
||||||
name: "ATK",
|
name: "ATK",
|
||||||
icon: "/icon/attack.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconAttack.png",
|
||||||
unit: "",
|
unit: "",
|
||||||
round: 0
|
round: 0
|
||||||
},
|
},
|
||||||
DEF: {
|
DEF: {
|
||||||
value: calcBaseStatRaw(
|
value: calcBaseStatRaw(
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.DefenceBase,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceBase,
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.DefenceAdd,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceAdd,
|
||||||
avatarData.level
|
avatarData.level
|
||||||
),
|
),
|
||||||
base: calcBaseStatRaw(
|
base: calcBaseStatRaw(
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.DefenceBase,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceBase,
|
||||||
mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.DefenceAdd,
|
mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.DefenceAdd,
|
||||||
avatarData.level
|
avatarData.level
|
||||||
),
|
),
|
||||||
name: "DEF",
|
name: "DEF",
|
||||||
icon: "/icon/defence.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconDefence.png",
|
||||||
unit: "",
|
unit: "",
|
||||||
round: 0
|
round: 0
|
||||||
},
|
},
|
||||||
SPD: {
|
SPD: {
|
||||||
value: mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.SpeedBase || 0,
|
value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.SpeedBase || 0,
|
||||||
base: mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.SpeedBase || 0,
|
base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.SpeedBase || 0,
|
||||||
name: "SPD",
|
name: "SPD",
|
||||||
icon: "/icon/speed.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconSpeed.png",
|
||||||
unit: "",
|
unit: "",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
CRITRate: {
|
CRITRate: {
|
||||||
value: mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.CriticalChance || 0,
|
value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalChance || 0,
|
||||||
base: mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.CriticalChance || 0,
|
base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalChance || 0,
|
||||||
name: "CRIT Rate",
|
name: "CRIT Rate",
|
||||||
icon: "/icon/crit-rate.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconCriticalChance.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
CRITDmg: {
|
CRITDmg: {
|
||||||
value: mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.CriticalDamage || 0,
|
value: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalDamage || 0,
|
||||||
base: mapAvatarInfo?.[avatarSelected.id]?.Stats[charPromotion]?.CriticalDamage || 0,
|
base: mapAvatar?.[avatarSelected?.ID?.toString()]?.Stats[charPromotion]?.CriticalDamage || 0,
|
||||||
name: "CRIT DMG",
|
name: "CRIT DMG",
|
||||||
icon: "/icon/crit-damage.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconCriticalDamage.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -279,7 +269,7 @@ export default function ShowCaseInfo() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Break Effect",
|
name: "Break Effect",
|
||||||
icon: "/icon/break-effect.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconBreakUp.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -287,7 +277,7 @@ export default function ShowCaseInfo() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Effect RES",
|
name: "Effect RES",
|
||||||
icon: "/icon/effect-res.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconStatusResistance.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -295,7 +285,7 @@ export default function ShowCaseInfo() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Energy Rate",
|
name: "Energy Rate",
|
||||||
icon: "/icon/energy-rate.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconEnergyRecovery.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -303,7 +293,7 @@ export default function ShowCaseInfo() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Effect Hit Rate",
|
name: "Effect Hit Rate",
|
||||||
icon: "/icon/effect-hit-rate.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconStatusProbability.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -311,7 +301,7 @@ export default function ShowCaseInfo() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Healing Boost",
|
name: "Healing Boost",
|
||||||
icon: "/icon/healing-boost.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconHealRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -319,7 +309,7 @@ export default function ShowCaseInfo() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Physical Boost",
|
name: "Physical Boost",
|
||||||
icon: "/icon/physical-add.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconPhysicalAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -327,7 +317,7 @@ export default function ShowCaseInfo() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Fire Boost",
|
name: "Fire Boost",
|
||||||
icon: "/icon/fire-add.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconFireAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -335,7 +325,7 @@ export default function ShowCaseInfo() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Ice Boost",
|
name: "Ice Boost",
|
||||||
icon: "/icon/ice-add.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconIceAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -343,7 +333,7 @@ export default function ShowCaseInfo() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Thunder Boost",
|
name: "Thunder Boost",
|
||||||
icon: "/icon/thunder-add.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconThunderAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -351,7 +341,7 @@ export default function ShowCaseInfo() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Wind Boost",
|
name: "Wind Boost",
|
||||||
icon: "/icon/wind-add.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconWindAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -359,7 +349,7 @@ export default function ShowCaseInfo() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Quantum Boost",
|
name: "Quantum Boost",
|
||||||
icon: "/icon/quantum-add.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconQuantumAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -367,7 +357,7 @@ export default function ShowCaseInfo() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Imaginary Boost",
|
name: "Imaginary Boost",
|
||||||
icon: "/icon/imaginary-add.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconImaginaryAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
},
|
},
|
||||||
@@ -375,57 +365,57 @@ export default function ShowCaseInfo() {
|
|||||||
value: 0,
|
value: 0,
|
||||||
base: 0,
|
base: 0,
|
||||||
name: "Elation Boost",
|
name: "Elation Boost",
|
||||||
icon: "/icon/IconJoy.webp",
|
icon: "spriteoutput/ui/avatar/icon/IconJoy.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
round: 1
|
round: 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avatarProfile?.lightcone && mapLightconeInfo[avatarProfile?.lightcone?.item_id]) {
|
if (avatarProfile?.lightcone && mapLightCone[avatarProfile?.lightcone?.item_id]) {
|
||||||
const lightconePromotion = calcPromotion(avatarProfile?.lightcone?.level)
|
const lightconePromotion = calcPromotion(avatarProfile?.lightcone?.level)
|
||||||
statsData.HP.value += calcBaseStatRaw(
|
statsData.HP.value += calcBaseStatRaw(
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHP,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHP,
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHPAdd,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHPAdd,
|
||||||
avatarProfile?.lightcone?.level
|
avatarProfile?.lightcone?.level
|
||||||
)
|
)
|
||||||
statsData.HP.base += calcBaseStatRaw(
|
statsData.HP.base += calcBaseStatRaw(
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHP,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHP,
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHPAdd,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseHPAdd,
|
||||||
avatarProfile?.lightcone?.level
|
avatarProfile?.lightcone?.level
|
||||||
)
|
)
|
||||||
statsData.ATK.value += calcBaseStatRaw(
|
statsData.ATK.value += calcBaseStatRaw(
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttack,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttack,
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttackAdd,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttackAdd,
|
||||||
avatarProfile?.lightcone?.level
|
avatarProfile?.lightcone?.level
|
||||||
)
|
)
|
||||||
statsData.ATK.base += calcBaseStatRaw(
|
statsData.ATK.base += calcBaseStatRaw(
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttack,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttack,
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttackAdd,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseAttackAdd,
|
||||||
avatarProfile?.lightcone?.level
|
avatarProfile?.lightcone?.level
|
||||||
)
|
)
|
||||||
statsData.DEF.value += calcBaseStatRaw(
|
statsData.DEF.value += calcBaseStatRaw(
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefence,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefence,
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefenceAdd,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefenceAdd,
|
||||||
avatarProfile?.lightcone?.level
|
avatarProfile?.lightcone?.level
|
||||||
)
|
)
|
||||||
statsData.DEF.base += calcBaseStatRaw(
|
statsData.DEF.base += calcBaseStatRaw(
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefence,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefence,
|
||||||
mapLightconeInfo?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefenceAdd,
|
mapLightCone?.[avatarProfile?.lightcone?.item_id]?.Stats[lightconePromotion]?.BaseDefenceAdd,
|
||||||
avatarProfile?.lightcone?.level
|
avatarProfile?.lightcone?.level
|
||||||
)
|
)
|
||||||
|
|
||||||
const bonusData = mapLightconeInfo[avatarProfile?.lightcone?.item_id].Bonus?.[avatarProfile?.lightcone.rank - 1]
|
const bonusData = mapLightCone[avatarProfile?.lightcone?.item_id]?.Skills?.Level?.[avatarProfile?.lightcone.rank]?.Bonus
|
||||||
if (bonusData && bonusData.length > 0) {
|
if (bonusData && bonusData.length > 0) {
|
||||||
const bonusSpd = bonusData.filter((bonus) => bonus.type === "BaseSpeed")
|
const bonusSpd = bonusData.filter((bonus) => bonus.PropertyType === "BaseSpeed")
|
||||||
const bonusOther = bonusData.filter((bonus) => bonus.type !== "BaseSpeed")
|
const bonusOther = bonusData.filter((bonus) => bonus.PropertyType !== "BaseSpeed")
|
||||||
bonusSpd.forEach((bonus) => {
|
bonusSpd.forEach((bonus) => {
|
||||||
statsData.SPD.value += bonus.value
|
statsData.SPD.value += bonus.Value
|
||||||
statsData.SPD.base += bonus.value
|
statsData.SPD.base += bonus.Value
|
||||||
})
|
})
|
||||||
bonusOther.forEach((bonus) => {
|
bonusOther.forEach((bonus) => {
|
||||||
const statsBase = mappingStats?.[bonus.type]?.baseStat
|
const statsBase = mappingStats?.[bonus.PropertyType]?.baseStat
|
||||||
if (statsBase && statsData[statsBase]) {
|
if (statsBase && statsData[statsBase]) {
|
||||||
statsData[statsBase].value += calcBonusStatRaw(bonus.type, statsData[statsBase].base, bonus.value)
|
statsData[statsBase].value += calcBonusStatRaw(bonus.PropertyType, statsData[statsBase].base, bonus.Value)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -448,19 +438,17 @@ export default function ShowCaseInfo() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (avatarProfile?.relics && mainAffix && subAffix) {
|
||||||
|
|
||||||
if (avatarProfile?.relics && mapMainAffix && mapSubAffix) {
|
|
||||||
Object.entries(avatarProfile?.relics).forEach(([key, value]) => {
|
Object.entries(avatarProfile?.relics).forEach(([key, value]) => {
|
||||||
const mainAffixMap = mapMainAffix["5" + key]
|
const mainAffixMap = mainAffix["5" + key]
|
||||||
const subAffixMap = mapSubAffix["5"]
|
const subAffixMap = subAffix["5"]
|
||||||
if (!mainAffixMap || !subAffixMap) return
|
if (!mainAffixMap || !subAffixMap) return
|
||||||
const mainStats = mappingStats?.[mainAffixMap?.[value.main_affix_id]?.property]?.baseStat
|
const mainStats = mappingStats?.[mainAffixMap?.[value.main_affix_id]?.Property]?.baseStat
|
||||||
if (mainStats && statsData[mainStats]) {
|
if (mainStats && statsData[mainStats]) {
|
||||||
statsData[mainStats].value += calcMainAffixBonusRaw(mainAffixMap?.[value.main_affix_id], value.level, statsData[mainStats].base)
|
statsData[mainStats].value += calcMainAffixBonusRaw(mainAffixMap?.[value.main_affix_id], value.level, statsData[mainStats].base)
|
||||||
}
|
}
|
||||||
value?.sub_affixes.forEach((subValue) => {
|
value?.sub_affixes.forEach((subValue) => {
|
||||||
const subStats = mappingStats?.[subAffixMap?.[subValue.sub_affix_id]?.property]?.baseStat
|
const subStats = mappingStats?.[subAffixMap?.[subValue.sub_affix_id]?.Property]?.baseStat
|
||||||
if (subStats && statsData[subStats]) {
|
if (subStats && statsData[subStats]) {
|
||||||
statsData[subStats].value += calcSubAffixBonusRaw(subAffixMap?.[subValue.sub_affix_id], subValue.step, subValue.count, statsData[subStats].base)
|
statsData[subStats].value += calcSubAffixBonusRaw(subAffixMap?.[subValue.sub_affix_id], subValue.step, subValue.count, statsData[subStats].base)
|
||||||
}
|
}
|
||||||
@@ -470,14 +458,14 @@ export default function ShowCaseInfo() {
|
|||||||
|
|
||||||
if (relicEffects && relicEffects.length > 0) {
|
if (relicEffects && relicEffects.length > 0) {
|
||||||
relicEffects.forEach((relic) => {
|
relicEffects.forEach((relic) => {
|
||||||
const dataBonus = mapRelicInfo?.[relic.key]?.Bonus
|
const dataBonus = mapRelicSet?.[relic.key]?.Skills
|
||||||
if (!dataBonus || Object.keys(dataBonus).length === 0) return
|
if (!dataBonus || Object.keys(dataBonus).length === 0) return
|
||||||
Object.entries(dataBonus || {}).forEach(([key, value]) => {
|
Object.entries(dataBonus || {}).forEach(([key, value]) => {
|
||||||
if (relic.count < Number(key)) return
|
if (relic.count < Number(key)) return
|
||||||
value.forEach((bonus) => {
|
value.Bonus.forEach((bonus) => {
|
||||||
const statsBase = mappingStats?.[bonus.type]?.baseStat
|
const statsBase = mappingStats?.[bonus.PropertyType]?.baseStat
|
||||||
if (statsBase && statsData[statsBase]) {
|
if (statsBase && statsData[statsBase]) {
|
||||||
statsData[statsBase].value += calcBonusStatRaw(bonus.type, statsData[statsBase].base, bonus.value)
|
statsData[statsBase].value += calcBonusStatRaw(bonus.PropertyType, statsData[statsBase].base, bonus.Value)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -489,14 +477,14 @@ export default function ShowCaseInfo() {
|
|||||||
}, [
|
}, [
|
||||||
avatarSelected,
|
avatarSelected,
|
||||||
avatarData,
|
avatarData,
|
||||||
mapAvatarInfo,
|
mapAvatar,
|
||||||
avatarProfile?.lightcone,
|
avatarProfile?.lightcone,
|
||||||
avatarProfile?.relics,
|
avatarProfile?.relics,
|
||||||
mapLightconeInfo,
|
mapLightCone,
|
||||||
mapMainAffix,
|
mainAffix,
|
||||||
mapSubAffix,
|
subAffix,
|
||||||
relicEffects,
|
relicEffects,
|
||||||
mapRelicInfo,
|
mapRelicSet,
|
||||||
avatarSkillTree
|
avatarSkillTree
|
||||||
])
|
])
|
||||||
|
|
||||||
@@ -508,19 +496,6 @@ export default function ShowCaseInfo() {
|
|||||||
return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
|
return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const getImageSkill = useCallback((icon: string | undefined, status: StatusAddType | undefined) => {
|
|
||||||
if (!icon) return
|
|
||||||
if (icon.startsWith("SkillIcon")) {
|
|
||||||
return `${process.env.CDN_URL}/spriteoutput/skillicons/avatar/${avatarSelected?.id}/${icon}`
|
|
||||||
} else if (status && mappingStats[status.PropertyType]) {
|
|
||||||
return mappingStats[status.PropertyType].icon
|
|
||||||
}
|
|
||||||
else if (icon.startsWith("Icon")) {
|
|
||||||
return `${process.env.CDN_URL}/spriteoutput/trace/${icon}`
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}, [avatarSelected?.id])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col justify-start m-1 text-white">
|
<div className="flex flex-col justify-start m-1 text-white">
|
||||||
<div className="flex items-center justify-start mt-4 mb-4">
|
<div className="flex items-center justify-start mt-4 mb-4">
|
||||||
@@ -550,7 +525,7 @@ export default function ShowCaseInfo() {
|
|||||||
ref={imgRef}
|
ref={imgRef}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`${process.env.CDN_URL}/spriteoutput/avatardrawcard/${avatarSelected?.id}.png`}
|
src={`${process.env.CDN_URL}/${avatarSelected?.Image?.AvatarCutinFrontImgPath}`}
|
||||||
className="object-contain scale-[2] overflow-hidden"
|
className="object-contain scale-[2] overflow-hidden"
|
||||||
alt="Character Preview"
|
alt="Character Preview"
|
||||||
width={1024}
|
width={1024}
|
||||||
@@ -576,9 +551,9 @@ export default function ShowCaseInfo() {
|
|||||||
>
|
>
|
||||||
|
|
||||||
<div className="absolute top-4 left-3">
|
<div className="absolute top-4 left-3">
|
||||||
{avatarSelected && avatarInfo && avatarData?.data && typeof avatarData?.data?.rank === "number" && (
|
{avatarSelected && avatarSelected && avatarData?.data && typeof avatarData?.data?.rank === "number" && (
|
||||||
<div className="flex flex-col items-center gap-2 py-2">
|
<div className="flex flex-col items-center gap-2 py-2">
|
||||||
{avatarInfo?.RankIcon?.map((src, index) => {
|
{Object.values(avatarSelected?.Ranks || {})?.map((rank, index) => {
|
||||||
const isActive = avatarData?.data?.rank > index;
|
const isActive = avatarData?.data?.rank > index;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -621,7 +596,7 @@ export default function ShowCaseInfo() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<NextImage
|
<NextImage
|
||||||
src={src ?? null}
|
src={`${process.env.CDN_URL}/${rank.Icon}`}
|
||||||
alt="Rank Icon"
|
alt="Rank Icon"
|
||||||
width={125}
|
width={125}
|
||||||
height={125}
|
height={125}
|
||||||
@@ -664,20 +639,20 @@ export default function ShowCaseInfo() {
|
|||||||
<NextImage
|
<NextImage
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`/icon/${avatarSelected?.baseType.toLowerCase()}.webp`}
|
src={`${process.env.CDN_URL}/${baseType[avatarSelected?.BaseType]?.Icon}`}
|
||||||
alt="Path Icon"
|
alt="Path Icon"
|
||||||
width={32}
|
width={32}
|
||||||
height={32}
|
height={32}
|
||||||
className="h-auto w-8"
|
className="h-8 w-8"
|
||||||
/>
|
/>
|
||||||
<NextImage
|
<NextImage
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`/icon/${avatarSelected?.damageType.toLowerCase()}.webp`}
|
src={`${process.env.CDN_URL}/${damageType[avatarSelected?.DamageType]?.Icon}`}
|
||||||
alt="Element Icon"
|
alt="Element Icon"
|
||||||
width={32}
|
width={32}
|
||||||
height={32}
|
height={32}
|
||||||
className="h-auto w-8" />
|
className="h-8 w-8" />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -691,7 +666,7 @@ export default function ShowCaseInfo() {
|
|||||||
<NextImage
|
<NextImage
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`/icon/${avatarSelected?.baseType.toLowerCase()}.webp`}
|
src={`${process.env.CDN_URL}/${baseType[avatarSelected?.BaseType]?.Icon}`}
|
||||||
alt="Path Icon"
|
alt="Path Icon"
|
||||||
width={160}
|
width={160}
|
||||||
height={160}
|
height={160}
|
||||||
@@ -701,8 +676,8 @@ export default function ShowCaseInfo() {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
{avatarData && avatarInfo && avatarSkillTree && traceShowCaseMap[avatarSelected?.baseType || ""]
|
{avatarData && avatarSelected && avatarSkillTree && traceShowCaseMap[avatarSelected?.BaseType || ""]
|
||||||
&& Object.values(traceShowCaseMap[avatarSelected?.baseType || ""] || []).map((item, index) => {
|
&& Object.values(traceShowCaseMap[avatarSelected?.BaseType || ""] || []).map((item, index) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={`row-${index}`} className="flex flex-row items-center">
|
<div key={`row-${index}`} className="flex flex-row items-center">
|
||||||
@@ -742,26 +717,15 @@ export default function ShowCaseInfo() {
|
|||||||
${avatarData.data.skills[avatarSkillTree?.[btn.id]?.["1"]?.PointID] ? "" : "opacity-50"}
|
${avatarData.data.skills[avatarSkillTree?.[btn.id]?.["1"]?.PointID] ? "" : "opacity-50"}
|
||||||
`}
|
`}
|
||||||
>
|
>
|
||||||
{
|
<NextImage
|
||||||
(() => {
|
src={`${process.env.CDN_URL}/${avatarSelected?.SkillTrees?.[btn.id]?.["1"]?.Icon}`}
|
||||||
const skillImg = getImageSkill(
|
unoptimized
|
||||||
avatarInfo.SkillTrees?.[btn.id]?.["1"]?.Icon,
|
crossOrigin="anonymous"
|
||||||
avatarSkillTree?.[btn.id]?.["1"]?.StatusAddList[0]
|
alt={btn.id}
|
||||||
);
|
width={125}
|
||||||
|
height={125}
|
||||||
return skillImg ? (
|
className={`h-full ${imageSize} ${filterClass}`}
|
||||||
<NextImage
|
/>
|
||||||
src={skillImg}
|
|
||||||
unoptimized
|
|
||||||
crossOrigin="anonymous"
|
|
||||||
alt={btn.id}
|
|
||||||
width={125}
|
|
||||||
height={125}
|
|
||||||
className={`h-full ${imageSize} ${filterClass}`}
|
|
||||||
/>
|
|
||||||
) : null;
|
|
||||||
})()
|
|
||||||
}
|
|
||||||
|
|
||||||
{(isBig || isBigMemory) && (
|
{(isBig || isBigMemory) && (
|
||||||
<span className="absolute bottom-0 left-0 text-[12px] text-white bg-black/70 px-1 rounded-sm">
|
<span className="absolute bottom-0 left-0 text-[12px] text-white bg-black/70 px-1 rounded-sm">
|
||||||
@@ -872,8 +836,8 @@ export default function ShowCaseInfo() {
|
|||||||
>
|
>
|
||||||
{[...Array(
|
{[...Array(
|
||||||
Number(
|
Number(
|
||||||
mapLightconeInfo[avatarProfile?.lightcone?.item_id]?.Rarity?.[
|
mapLightCone[avatarProfile?.lightcone?.item_id]?.Rarity?.[
|
||||||
mapLightconeInfo[avatarProfile?.lightcone?.item_id]?.Rarity.length - 1
|
mapLightCone[avatarProfile?.lightcone?.item_id]?.Rarity.length - 1
|
||||||
] || 0
|
] || 0
|
||||||
)
|
)
|
||||||
)].map((_, i) => (
|
)].map((_, i) => (
|
||||||
@@ -890,7 +854,7 @@ export default function ShowCaseInfo() {
|
|||||||
<ParseText
|
<ParseText
|
||||||
className="text-lg font-semibold"
|
className="text-lg font-semibold"
|
||||||
locale={locale}
|
locale={locale}
|
||||||
text={mapLightconeInfo[avatarProfile?.lightcone?.item_id].Name}
|
text={getLocaleName(locale, mapLightCone[avatarProfile?.lightcone?.item_id].Name)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -910,7 +874,7 @@ export default function ShowCaseInfo() {
|
|||||||
<NextImage
|
<NextImage
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src="/icon/hp.webp"
|
src={`${process.env.CDN_URL}/spriteoutput/ui/avatar/icon/IconMaxHP.png`}
|
||||||
alt="HP"
|
alt="HP"
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
@@ -922,7 +886,7 @@ export default function ShowCaseInfo() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-1 rounded bg-black/30 px-1 w-fit py-1">
|
<div className="flex items-center gap-1 rounded bg-black/30 px-1 w-fit py-1">
|
||||||
<NextImage
|
<NextImage
|
||||||
src="/icon/attack.webp"
|
src={`${process.env.CDN_URL}/spriteoutput/ui/avatar/icon/IconAttack.png`}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
alt="ATK"
|
alt="ATK"
|
||||||
@@ -938,7 +902,7 @@ export default function ShowCaseInfo() {
|
|||||||
<NextImage
|
<NextImage
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src="/icon/defence.webp"
|
src={`${process.env.CDN_URL}/spriteoutput/ui/avatar/icon/IconDefence.png`}
|
||||||
alt="DEF"
|
alt="DEF"
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
@@ -964,13 +928,13 @@ export default function ShowCaseInfo() {
|
|||||||
return (
|
return (
|
||||||
<div key={index} className="flex flex-row items-center justify-between">
|
<div key={index} className="flex flex-row items-center justify-between">
|
||||||
<div className="flex flex-row items-center">
|
<div className="flex flex-row items-center">
|
||||||
<NextImage src={stat?.icon || ""}
|
<NextImage src={`${process.env.CDN_URL}/${stat?.icon}`}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
alt="Stat Icon"
|
alt="Stat Icon"
|
||||||
width={40}
|
width={40}
|
||||||
height={40}
|
height={40}
|
||||||
className="h-auto w-10 p-2"
|
className="h-10 w-10 p-2"
|
||||||
/>
|
/>
|
||||||
<span className="font-bold">{stat.name}</span>
|
<span className="font-bold">{stat.name}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -986,7 +950,7 @@ export default function ShowCaseInfo() {
|
|||||||
|
|
||||||
<div className="flex flex-col items-center gap-1 w-full my-2">
|
<div className="flex flex-col items-center gap-1 w-full my-2">
|
||||||
{relicEffects.map((setEffect, index) => {
|
{relicEffects.map((setEffect, index) => {
|
||||||
const relicInfo = mapRelicInfo[setEffect.key];
|
const relicInfo = mapRelicSet[setEffect.key];
|
||||||
if (!relicInfo) return null;
|
if (!relicInfo) return null;
|
||||||
return (
|
return (
|
||||||
<div key={index} className="flex w-full flex-row justify-between text-left">
|
<div key={index} className="flex w-full flex-row justify-between text-left">
|
||||||
@@ -997,7 +961,7 @@ export default function ShowCaseInfo() {
|
|||||||
}}
|
}}
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: replaceByParam(
|
__html: replaceByParam(
|
||||||
relicInfo.Name,
|
getLocaleName(locale, relicInfo.Name),
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
@@ -1015,9 +979,9 @@ export default function ShowCaseInfo() {
|
|||||||
<div className="flex h-162.5 flex-col justify-between py-3 mr-1 text-lg w-full" >
|
<div className="flex h-162.5 flex-col justify-between py-3 mr-1 text-lg w-full" >
|
||||||
|
|
||||||
{relicStats?.map((relic, index) => {
|
{relicStats?.map((relic, index) => {
|
||||||
if (!relic || !avatarInfo) return null
|
if (!relic || !avatarSelected) return null
|
||||||
return (
|
return (
|
||||||
<RelicShowcase key={index} relic={relic} avatarInfo={avatarInfo} />
|
<RelicShowcase key={index} relic={relic} avatarInfo={avatarSelected} />
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
|||||||
@@ -2,14 +2,14 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import NextImage from "next/image"
|
import NextImage from "next/image"
|
||||||
import { CharacterDetail, RelicShowcaseType } from "@/types";
|
import { AvatarDetail, RelicShowcaseType } from "@/types";
|
||||||
|
|
||||||
export default function RelicShowcase({
|
export default function RelicShowcase({
|
||||||
relic,
|
relic,
|
||||||
avatarInfo,
|
avatarInfo,
|
||||||
}: {
|
}: {
|
||||||
relic: RelicShowcaseType;
|
relic: RelicShowcaseType;
|
||||||
avatarInfo: CharacterDetail;
|
avatarInfo: AvatarDetail;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -28,7 +28,7 @@ export default function RelicShowcase({
|
|||||||
width={78}
|
width={78}
|
||||||
height={78}
|
height={78}
|
||||||
alt="Relic Icon"
|
alt="Relic Icon"
|
||||||
className="h-auto w-19.5 rounded-lg"
|
className="h-19.5 w-19.5 rounded-lg"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -48,13 +48,13 @@ export default function RelicShowcase({
|
|||||||
<div className="relative">
|
<div className="relative">
|
||||||
<div className="absolute inset-0 bg-yellow-500/15 rounded-full blur-md -z-10"></div>
|
<div className="absolute inset-0 bg-yellow-500/15 rounded-full blur-md -z-10"></div>
|
||||||
<NextImage
|
<NextImage
|
||||||
src={relic?.mainAffix?.detail?.icon || ""}
|
src={`${process.env.CDN_URL}/${relic?.mainAffix?.detail?.icon}` || ""}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
width={35}
|
width={35}
|
||||||
height={35}
|
height={35}
|
||||||
alt="Main Affix Icon"
|
alt="Main Affix Icon"
|
||||||
className="h-auto w-8.75"
|
className="h-8.75 w-8.75"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<span className="text-base text-yellow-400 font-semibold drop-shadow-[0_0_4px_rgba(251,191,36,0.5)]">
|
<span className="text-base text-yellow-400 font-semibold drop-shadow-[0_0_4px_rgba(251,191,36,0.5)]">
|
||||||
@@ -75,13 +75,13 @@ export default function RelicShowcase({
|
|||||||
<div className="relative flex flex-row items-center bg-black/20 backdrop-blur-sm rounded-md p-1 border border-white/5 min-w-0">
|
<div className="relative flex flex-row items-center bg-black/20 backdrop-blur-sm rounded-md p-1 border border-white/5 min-w-0">
|
||||||
{subAffix?.detail?.icon ? (
|
{subAffix?.detail?.icon ? (
|
||||||
<NextImage
|
<NextImage
|
||||||
src={subAffix?.detail?.icon || ""}
|
src={`${process.env.CDN_URL}/${subAffix?.detail?.icon}` || ""}
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
width={32}
|
width={32}
|
||||||
height={32}
|
height={32}
|
||||||
alt="Sub Affix Icon"
|
alt="Sub Affix Icon"
|
||||||
className="h-auto w-6 shrink-0"
|
className="h-6 w-6 shrink-0"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="h-6 w-6 bg-black/60 rounded flex items-center justify-center border border-white/10 shrink-0">
|
<div className="h-6 w-6 bg-black/60 rounded flex items-center justify-center border border-white/10 shrink-0">
|
||||||
|
|||||||
@@ -1,64 +1,47 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import useAvatarStore from "@/stores/avatarStore";
|
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { traceButtonsInfo, traceLink } from "@/constant/traceConstant";
|
import { traceButtonsInfo, traceLink } from "@/constant/traceConstant";
|
||||||
import useUserDataStore from "@/stores/userDataStore";
|
import useUserDataStore from "@/stores/userDataStore";
|
||||||
import useLocaleStore from "@/stores/localeStore";
|
import useLocaleStore from "@/stores/localeStore";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { replaceByParam } from "@/helper";
|
import { replaceByParam, getLocaleName } from '@/helper';
|
||||||
import { mappingStats } from "@/constant/constant";
|
import { mappingStats } from "@/constant/constant";
|
||||||
import { StatusAddType } from "@/types";
|
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
|
import useCurrentDataStore from "@/stores/currentDataStore";
|
||||||
|
import { StatusAdd } from '@/types/avatarDetail';
|
||||||
|
|
||||||
export default function SkillsInfo() {
|
export default function SkillsInfo() {
|
||||||
const transI18n = useTranslations("DataPage")
|
const transI18n = useTranslations("DataPage")
|
||||||
const { theme } = useLocaleStore()
|
const { theme } = useLocaleStore()
|
||||||
const { avatarSelected, mapAvatarInfo, skillSelected, setSkillSelected } = useAvatarStore()
|
const { avatarSelected, skillIDSelected, setSkillIDSelected } = useCurrentDataStore()
|
||||||
const { avatars, setAvatar } = useUserDataStore()
|
const { avatars, setAvatar } = useUserDataStore()
|
||||||
|
const { locale } = useLocaleStore()
|
||||||
const traceButtons = useMemo(() => {
|
const traceButtons = useMemo(() => {
|
||||||
if (!avatarSelected) return
|
if (!avatarSelected) return
|
||||||
return traceButtonsInfo[avatarSelected.baseType]
|
return traceButtonsInfo[avatarSelected.BaseType]
|
||||||
}, [avatarSelected])
|
}, [avatarSelected])
|
||||||
|
|
||||||
const avatarInfo = useMemo(() => {
|
|
||||||
if (!avatarSelected) return
|
|
||||||
return mapAvatarInfo[avatarSelected.id]
|
|
||||||
}, [avatarSelected, mapAvatarInfo])
|
|
||||||
|
|
||||||
const avatarData = useMemo(() => {
|
const avatarData = useMemo(() => {
|
||||||
if (!avatarSelected) return
|
if (!avatarSelected) return
|
||||||
return avatars[avatarSelected.id]
|
return avatars[avatarSelected?.ID?.toString()]
|
||||||
}, [avatarSelected, avatars])
|
}, [avatarSelected, avatars])
|
||||||
|
|
||||||
const avatarSkillTree = useMemo(() => {
|
const avatarSkillTree = useMemo(() => {
|
||||||
if (!avatarSelected || !avatars[avatarSelected.id]) return {}
|
if (!avatarSelected || !avatars[avatarSelected?.ID?.toString()]) return {}
|
||||||
if (avatars[avatarSelected.id].enhanced) {
|
if (avatars[avatarSelected?.ID?.toString()].enhanced) {
|
||||||
return avatarInfo?.Enhanced[avatars[avatarSelected.id].enhanced.toString()].SkillTrees || {}
|
return avatarSelected?.Enhanced?.[avatars[avatarSelected?.ID?.toString()].enhanced.toString()].SkillTrees || {}
|
||||||
}
|
}
|
||||||
return avatarInfo?.SkillTrees || {}
|
return avatarSelected?.SkillTrees || {}
|
||||||
}, [avatarSelected, avatarInfo, avatars])
|
}, [avatarSelected, avatars])
|
||||||
|
|
||||||
const skillInfo = useMemo(() => {
|
const skillInfo = useMemo(() => {
|
||||||
if (!avatarSelected || !skillSelected) return
|
if (!avatarSelected || !skillIDSelected) return
|
||||||
return avatarSkillTree?.[skillSelected || ""]?.["1"]
|
return avatarSkillTree?.[skillIDSelected || ""]?.["1"]
|
||||||
}, [avatarSelected, avatarSkillTree, skillSelected])
|
}, [avatarSelected, avatarSkillTree, skillIDSelected])
|
||||||
|
|
||||||
const getImageSkill = (icon: string | undefined, status: StatusAddType | undefined) => {
|
const getTraceBuffDisplay = (status: StatusAdd) => {
|
||||||
if (!icon) return
|
|
||||||
const urlPrefix = `${process.env.CDN_URL}/spriteoutput/skillicons/avatar/${avatarSelected?.id}/`;
|
|
||||||
if (icon.startsWith("SkillIcon")) {
|
|
||||||
return `${urlPrefix}${icon}`
|
|
||||||
} else if (status && mappingStats[status.PropertyType]) {
|
|
||||||
return mappingStats[status.PropertyType].icon
|
|
||||||
} else if (icon.startsWith("Icon")) {
|
|
||||||
return `${process.env.CDN_URL}/spriteoutput/trace/${icon}`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const getTraceBuffDisplay = (status: StatusAddType) => {
|
|
||||||
const dataDisplay = mappingStats[status.PropertyType]
|
const dataDisplay = mappingStats[status.PropertyType]
|
||||||
if (!dataDisplay) return ""
|
if (!dataDisplay) return ""
|
||||||
if (dataDisplay.unit === "%") {
|
if (dataDisplay.unit === "%") {
|
||||||
@@ -72,11 +55,12 @@ export default function SkillsInfo() {
|
|||||||
|
|
||||||
const dataLevelUpSkill = useMemo(() => {
|
const dataLevelUpSkill = useMemo(() => {
|
||||||
const skillIds: number[] = skillInfo?.LevelUpSkillID || []
|
const skillIds: number[] = skillInfo?.LevelUpSkillID || []
|
||||||
if (!avatarSelected || !avatarInfo || !avatarData) return
|
if (!avatarSelected || !avatarData) return undefined
|
||||||
let result = Object.values(avatarInfo.Skills || {})?.filter((skill) => skillIds.includes(skill.Id))
|
let result = Object.values(avatarSelected.Skills || {})?.filter((skill) => skillIds.includes(skill.ID))
|
||||||
if (avatarData.enhanced) {
|
if (avatarData.enhanced) {
|
||||||
result = Object.values(avatarInfo.Enhanced[avatarData.enhanced.toString()].Skills || {})?.filter((skill) => skillIds.includes(skill.Id))
|
result = Object.values(avatarSelected?.Enhanced?.[avatarData.enhanced.toString()]?.Skills || {})?.filter((skill) => skillIds.includes(skill.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result && result.length > 0) {
|
if (result && result.length > 0) {
|
||||||
return {
|
return {
|
||||||
isServant: false,
|
isServant: false,
|
||||||
@@ -84,25 +68,22 @@ export default function SkillsInfo() {
|
|||||||
servantData: null,
|
servantData: null,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const resultServant = Object.entries(avatarInfo.Memosprite?.Skills || {})
|
const resultServant = Object.values(avatarSelected?.Memosprite?.Skills || {})
|
||||||
?.filter(([skillId]) => skillIds.includes(Number(skillId)))
|
?.filter((skill) => skillIds.includes(skill.ID))
|
||||||
?.map(([skillId, skillData]) => ({
|
|
||||||
Id: Number(skillId),
|
|
||||||
...skillData,
|
|
||||||
}))
|
|
||||||
if (resultServant && resultServant.length > 0) {
|
if (resultServant && resultServant.length > 0) {
|
||||||
return {
|
return {
|
||||||
isServant: true,
|
isServant: true,
|
||||||
data: resultServant,
|
data: resultServant,
|
||||||
servantData: avatarInfo.Memosprite,
|
servantData: avatarSelected.Memosprite,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return undefined
|
return undefined
|
||||||
}, [skillInfo?.LevelUpSkillID, avatarSelected, avatarInfo, avatarData])
|
}, [skillInfo?.LevelUpSkillID, avatarSelected, avatarData])
|
||||||
|
|
||||||
|
|
||||||
const handlerMaxAll = () => {
|
const handlerMaxAll = () => {
|
||||||
if (!avatarInfo || !avatarData || !avatarSkillTree) {
|
if (!avatarData || !avatarSkillTree) {
|
||||||
toast.error(transI18n("maxAllFailed"))
|
toast.error(transI18n("maxAllFailed"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -123,8 +104,8 @@ export default function SkillsInfo() {
|
|||||||
const newData = structuredClone(avatarData)
|
const newData = structuredClone(avatarData)
|
||||||
newData.data.skills[skillInfo?.PointID] = status ? 1 : 0
|
newData.data.skills[skillInfo?.PointID] = status ? 1 : 0
|
||||||
|
|
||||||
if (!status && traceLink?.[avatarSelected?.baseType || ""]?.[skillSelected || ""]) {
|
if (!status && traceLink?.[avatarSelected?.BaseType || ""]?.[skillIDSelected || ""]) {
|
||||||
traceLink[avatarSelected?.baseType || ""][skillSelected || ""].forEach((pointId) => {
|
traceLink[avatarSelected?.BaseType || ""][skillIDSelected || ""].forEach((pointId) => {
|
||||||
if (avatarSkillTree?.[pointId]?.["1"]) {
|
if (avatarSkillTree?.[pointId]?.["1"]) {
|
||||||
newData.data.skills[avatarSkillTree?.[pointId]?.["1"].PointID] = 0
|
newData.data.skills[avatarSkillTree?.[pointId]?.["1"].PointID] = 0
|
||||||
}
|
}
|
||||||
@@ -143,12 +124,12 @@ export default function SkillsInfo() {
|
|||||||
</h2>
|
</h2>
|
||||||
<div className="flex flex-col items-center">
|
<div className="flex flex-col items-center">
|
||||||
<button className="btn btn-success" onClick={handlerMaxAll}>{transI18n("maxAll")}</button>
|
<button className="btn btn-success" onClick={handlerMaxAll}>{transI18n("maxAll")}</button>
|
||||||
{traceButtons && avatarInfo && (
|
{traceButtons && avatarSelected && (
|
||||||
<div className="grid col-span-4 relative w-full aspect-square">
|
<div className="grid col-span-4 relative w-full aspect-square">
|
||||||
<Image
|
<Image
|
||||||
unoptimized
|
unoptimized
|
||||||
crossOrigin="anonymous"
|
crossOrigin="anonymous"
|
||||||
src={`/skilltree/${avatarSelected?.baseType?.toUpperCase()}.webp`}
|
src={`/skilltree/${avatarSelected?.BaseType?.toUpperCase()}.webp`}
|
||||||
alt=""
|
alt=""
|
||||||
width={312}
|
width={312}
|
||||||
priority={true}
|
priority={true}
|
||||||
@@ -159,7 +140,7 @@ export default function SkillsInfo() {
|
|||||||
className={`w-full h-full object-cover rounded-xl`}
|
className={`w-full h-full object-cover rounded-xl`}
|
||||||
/>
|
/>
|
||||||
{traceButtons.map((btn, index) => {
|
{traceButtons.map((btn, index) => {
|
||||||
if (!avatarInfo?.SkillTrees?.[btn.id]) {
|
if (!avatarSelected?.SkillTrees?.[btn.id]) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
@@ -178,13 +159,13 @@ export default function SkillsInfo() {
|
|||||||
${btn.size === "special" ? "w-[9vw] h-[9vw] md:w-[3.5vw] md:h-[3.5vw] bg-white" : ""}
|
${btn.size === "special" ? "w-[9vw] h-[9vw] md:w-[3.5vw] md:h-[3.5vw] bg-white" : ""}
|
||||||
${btn.size === "memory" ? "w-[9vw] h-[9vw] md:w-[3.5vw] md:h-[3.5vw] bg-black" : ""}
|
${btn.size === "memory" ? "w-[9vw] h-[9vw] md:w-[3.5vw] md:h-[3.5vw] bg-black" : ""}
|
||||||
${btn.size === "elation" ? "w-[9vw] h-[9vw] md:w-[3.5vw] md:h-[3.5vw] bg-black" : ""}
|
${btn.size === "elation" ? "w-[9vw] h-[9vw] md:w-[3.5vw] md:h-[3.5vw] bg-black" : ""}
|
||||||
${skillSelected === btn.id ? "border-4 border-primary" : ""}
|
${skillIDSelected === btn.id ? "border-4 border-primary" : ""}
|
||||||
${!avatarData?.data.skills?.[avatarSkillTree?.[btn.id]?.["1"]?.PointID]
|
${!avatarData?.data.skills?.[avatarSkillTree?.[btn.id]?.["1"]?.PointID]
|
||||||
? "opacity-50 cursor-not-allowed"
|
? "opacity-50 cursor-not-allowed"
|
||||||
: ""}
|
: ""}
|
||||||
`}
|
`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSkillSelected(btn.id === skillSelected ? null : btn.id)
|
setSkillIDSelected(btn.id === skillIDSelected ? null : btn.id)
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
left: btn.left,
|
left: btn.left,
|
||||||
@@ -193,7 +174,7 @@ export default function SkillsInfo() {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Image
|
<Image
|
||||||
src={getImageSkill(avatarInfo?.SkillTrees?.[btn.id]?.["1"]?.Icon, avatarSkillTree?.[btn.id]?.["1"]?.StatusAddList[0]) || ""}
|
src={`${process.env.CDN_URL}/${avatarSelected?.SkillTrees?.[btn.id]?.["1"]?.Icon}`}
|
||||||
alt={btn.id.replaceAll("Point", "")}
|
alt={btn.id.replaceAll("Point", "")}
|
||||||
priority={true}
|
priority={true}
|
||||||
unoptimized
|
unoptimized
|
||||||
@@ -201,6 +182,8 @@ export default function SkillsInfo() {
|
|||||||
width={124}
|
width={124}
|
||||||
height={124}
|
height={124}
|
||||||
style={{
|
style={{
|
||||||
|
objectFit: "contain",
|
||||||
|
padding: "2px",
|
||||||
filter: (btn.size !== "big" && btn.size !== "memory" && btn.size !== "elation") ? "brightness(0%)" : ""
|
filter: (btn.size !== "big" && btn.size !== "memory" && btn.size !== "elation") ? "brightness(0%)" : ""
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -272,7 +255,7 @@ export default function SkillsInfo() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!traceButtons && avatarInfo && (
|
{!traceButtons && avatarSelected && (
|
||||||
<div className="flex flex-col relative w-full aspect-square">
|
<div className="flex flex-col relative w-full aspect-square">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -285,7 +268,7 @@ export default function SkillsInfo() {
|
|||||||
<div className="w-2 h-6 bg-linear-to-b from-primary to-primary/50 rounded-full"></div>
|
<div className="w-2 h-6 bg-linear-to-b from-primary to-primary/50 rounded-full"></div>
|
||||||
{transI18n("details")}
|
{transI18n("details")}
|
||||||
</h2>
|
</h2>
|
||||||
{skillSelected && avatarInfo?.SkillTrees && avatarData && (
|
{skillIDSelected && avatarSelected?.SkillTrees && avatarData && (
|
||||||
<div>
|
<div>
|
||||||
{skillInfo?.MaxLevel && skillInfo?.MaxLevel > 1 ? (
|
{skillInfo?.MaxLevel && skillInfo?.MaxLevel > 1 ? (
|
||||||
<div>
|
<div>
|
||||||
@@ -309,14 +292,14 @@ export default function SkillsInfo() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : skillInfo?.MaxLevel && skillInfo?.MaxLevel === 1 && traceButtons?.find((btn) => btn.id === skillSelected)?.size !== "big" ? (
|
) : skillInfo?.MaxLevel && skillInfo?.MaxLevel === 1 && traceButtons?.find((btn) => btn.id === skillIDSelected)?.size !== "big" ? (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={avatarData?.data.skills?.[skillInfo?.PointID] === 1}
|
checked={avatarData?.data.skills?.[skillInfo?.PointID] === 1}
|
||||||
className="toggle toggle-success"
|
className="toggle toggle-success"
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
if (traceButtons?.find((btn) => btn.id === skillSelected)?.size === "special") {
|
if (traceButtons?.find((btn) => btn.id === skillIDSelected)?.size === "special") {
|
||||||
if (e.target.checked) {
|
if (e.target.checked) {
|
||||||
const newData = structuredClone(avatarData)
|
const newData = structuredClone(avatarData)
|
||||||
newData.data.skills[skillInfo?.PointID] = 1
|
newData.data.skills[skillInfo?.PointID] = 1
|
||||||
@@ -343,7 +326,7 @@ export default function SkillsInfo() {
|
|||||||
(skillInfo?.PointName && skillInfo?.StatusAddList.length > 0))
|
(skillInfo?.PointName && skillInfo?.StatusAddList.length > 0))
|
||||||
&& (
|
&& (
|
||||||
<div className="text-xl font-bold flex items-center gap-2 mt-2">
|
<div className="text-xl font-bold flex items-center gap-2 mt-2">
|
||||||
{skillInfo.PointName}
|
{getLocaleName(locale, skillInfo.PointName)}
|
||||||
{skillInfo.StatusAddList.length > 0 && (
|
{skillInfo.StatusAddList.length > 0 && (
|
||||||
<div>
|
<div>
|
||||||
{skillInfo.StatusAddList.map((status, index) => (
|
{skillInfo.StatusAddList.map((status, index) => (
|
||||||
@@ -360,8 +343,8 @@ export default function SkillsInfo() {
|
|||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: replaceByParam(
|
__html: replaceByParam(
|
||||||
skillInfo?.PointDesc || "",
|
getLocaleName(locale, skillInfo?.PointDesc) || "",
|
||||||
skillInfo?.ParamList || []
|
skillInfo?.Param || []
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -377,19 +360,19 @@ export default function SkillsInfo() {
|
|||||||
<div key={index}>
|
<div key={index}>
|
||||||
|
|
||||||
<div className="text-xl font-bold text-primary">
|
<div className="text-xl font-bold text-primary">
|
||||||
{transI18n(dataLevelUpSkill.isServant ? `${skill?.Type ? "severaltalent" : "servantskill"}` : `${skill?.Type ? skill?.Type.toLowerCase() : "talent"}`)}
|
{transI18n(dataLevelUpSkill.isServant ? `${skill?.AttackType ? "severaltalent" : "servantskill"}` : `${skill?.AttackType ? skill?.AttackType.toLowerCase() : "talent"}`)}
|
||||||
{` (${transI18n(skill.Tag.toLowerCase())})`}
|
{` (${transI18n(skill?.SkillEffect?.toLowerCase())})`}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-lg font-bold" dangerouslySetInnerHTML={{ __html: replaceByParam(skill.Name, []) }}>
|
<div className="text-lg font-bold" dangerouslySetInnerHTML={{ __html: replaceByParam(getLocaleName(locale, skill.Name), []) }}>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: replaceByParam(
|
__html: replaceByParam(
|
||||||
skill.Desc || skill.SimpleDesc,
|
getLocaleName(locale, skill.Desc),
|
||||||
skill.Level[avatarData?.data.skills?.[skillInfo?.PointID]?.toString() || ""]?.ParamList || []
|
skill.Level[avatarData?.data.skills?.[skillInfo?.PointID]?.toString() || ""]?.Param || []
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -17,145 +17,145 @@ export const listCurrentLanguageApi : Record<string, string> = {
|
|||||||
export const mappingStats = <Record<string, {name: string, icon: string, unit: string, baseStat: string}> > {
|
export const mappingStats = <Record<string, {name: string, icon: string, unit: string, baseStat: string}> > {
|
||||||
"HPDelta": {
|
"HPDelta": {
|
||||||
name:"HP",
|
name:"HP",
|
||||||
icon:"/icon/hp.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconMaxHP.png",
|
||||||
unit: "",
|
unit: "",
|
||||||
baseStat: "HP"
|
baseStat: "HP"
|
||||||
},
|
},
|
||||||
"AttackDelta": {
|
"AttackDelta": {
|
||||||
name:"ATK",
|
name:"ATK",
|
||||||
icon:"/icon/attack.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconAttack.png",
|
||||||
unit: "",
|
unit: "",
|
||||||
baseStat: "ATK"
|
baseStat: "ATK"
|
||||||
},
|
},
|
||||||
"HPAddedRatio": {
|
"HPAddedRatio": {
|
||||||
name:"HP",
|
name:"HP",
|
||||||
icon:"/icon/hp.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconMaxHP.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "HP"
|
baseStat: "HP"
|
||||||
},
|
},
|
||||||
"AttackAddedRatio": {
|
"AttackAddedRatio": {
|
||||||
name:"ATK",
|
name:"ATK",
|
||||||
icon:"/icon/attack.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconAttack.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "ATK"
|
baseStat: "ATK"
|
||||||
},
|
},
|
||||||
"DefenceDelta": {
|
"DefenceDelta": {
|
||||||
name:"DEF",
|
name:"DEF",
|
||||||
icon:"/icon/defence.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconDefence.png",
|
||||||
unit: "",
|
unit: "",
|
||||||
baseStat: "DEF"
|
baseStat: "DEF"
|
||||||
},
|
},
|
||||||
"DefenceAddedRatio": {
|
"DefenceAddedRatio": {
|
||||||
name:"DEF",
|
name:"DEF",
|
||||||
icon:"/icon/defence.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconDefence.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "DEF"
|
baseStat: "DEF"
|
||||||
},
|
},
|
||||||
"SpeedAddedRatio": {
|
"SpeedAddedRatio": {
|
||||||
name:"SPD",
|
name:"SPD",
|
||||||
icon:"/icon/speed.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconSpeed.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "SPD"
|
baseStat: "SPD"
|
||||||
},
|
},
|
||||||
"BaseSpeed": {
|
"BaseSpeed": {
|
||||||
name:"SPD",
|
name:"SPD",
|
||||||
icon:"/icon/speed.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconSpeed.png",
|
||||||
unit: "",
|
unit: "",
|
||||||
baseStat: "SPD"
|
baseStat: "SPD"
|
||||||
},
|
},
|
||||||
"CriticalChanceBase": {
|
"CriticalChanceBase": {
|
||||||
name:"CRIT Rate",
|
name:"CRIT Rate",
|
||||||
icon:"/icon/crit-rate.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconCriticalChance.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "CRITRate"
|
baseStat: "CRITRate"
|
||||||
},
|
},
|
||||||
"CriticalDamageBase": {
|
"CriticalDamageBase": {
|
||||||
name:"CRIT DMG",
|
name:"CRIT DMG",
|
||||||
icon:"/icon/crit-damage.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconCriticalDamage.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "CRITDmg"
|
baseStat: "CRITDmg"
|
||||||
},
|
},
|
||||||
"HealRatioBase": {
|
"HealRatioBase": {
|
||||||
name:"Outgoing Healing Boost",
|
name:"Outgoing Healing Boost",
|
||||||
icon:"/icon/healing-boost.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconHealRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "HealBoost"
|
baseStat: "HealBoost"
|
||||||
},
|
},
|
||||||
"StatusProbabilityBase": {
|
"StatusProbabilityBase": {
|
||||||
name:"Effect Hit Rate",
|
name:"Effect Hit Rate",
|
||||||
icon:"/icon/effect-hit-rate.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconStatusProbability.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "EffectHitRate"
|
baseStat: "EffectHitRate"
|
||||||
},
|
},
|
||||||
"StatusResistanceBase": {
|
"StatusResistanceBase": {
|
||||||
name:"Effect RES",
|
name:"Effect RES",
|
||||||
icon:"/icon/effect-res.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconStatusResistance.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "EffectRES"
|
baseStat: "EffectRES"
|
||||||
},
|
},
|
||||||
"BreakDamageAddedRatioBase": {
|
"BreakDamageAddedRatioBase": {
|
||||||
name:"Break Effect",
|
name:"Break Effect",
|
||||||
icon:"/icon/break-effect.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconBreakUp.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "BreakEffect"
|
baseStat: "BreakEffect"
|
||||||
},
|
},
|
||||||
"SpeedDelta": {
|
"SpeedDelta": {
|
||||||
name:"SPD",
|
name:"SPD",
|
||||||
icon:"/icon/speed.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconSpeed.png",
|
||||||
unit: "",
|
unit: "",
|
||||||
baseStat: "SPD"
|
baseStat: "SPD"
|
||||||
},
|
},
|
||||||
"PhysicalAddedRatio": {
|
"PhysicalAddedRatio": {
|
||||||
name:"Physical DMG Boost",
|
name:"Physical DMG Boost",
|
||||||
icon:"/icon/physical-add.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconPhysicalAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "PhysicalAdd"
|
baseStat: "PhysicalAdd"
|
||||||
},
|
},
|
||||||
"FireAddedRatio": {
|
"FireAddedRatio": {
|
||||||
name:"Fire DMG Boost",
|
name:"Fire DMG Boost",
|
||||||
icon:"/icon/fire-add.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconFireAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "FireAdd"
|
baseStat: "FireAdd"
|
||||||
},
|
},
|
||||||
"IceAddedRatio": {
|
"IceAddedRatio": {
|
||||||
name:"Ice DMG Boost",
|
name:"Ice DMG Boost",
|
||||||
icon:"/icon/ice-add.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconIceAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "IceAdd"
|
baseStat: "IceAdd"
|
||||||
},
|
},
|
||||||
"ThunderAddedRatio": {
|
"ThunderAddedRatio": {
|
||||||
name:"Thunder DMG Boost",
|
name:"Thunder DMG Boost",
|
||||||
icon:"/icon/thunder-add.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconThunderAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "ThunderAdd"
|
baseStat: "ThunderAdd"
|
||||||
},
|
},
|
||||||
"WindAddedRatio": {
|
"WindAddedRatio": {
|
||||||
name:"Wind DMG Boost",
|
name:"Wind DMG Boost",
|
||||||
icon:"/icon/wind-add.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconWindAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "WindAdd"
|
baseStat: "WindAdd"
|
||||||
},
|
},
|
||||||
"QuantumAddedRatio": {
|
"QuantumAddedRatio": {
|
||||||
name:"Quantum DMG Boost",
|
name:"Quantum DMG Boost",
|
||||||
icon:"/icon/quantum-add.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconQuantumAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "QuantumAdd"
|
baseStat: "QuantumAdd"
|
||||||
},
|
},
|
||||||
"ImaginaryAddedRatio": {
|
"ImaginaryAddedRatio": {
|
||||||
name:"Imaginary DMG Boost",
|
name:"Imaginary DMG Boost",
|
||||||
icon:"/icon/imaginary-add.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconImaginaryAddedRatio.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "ImaginaryAdd"
|
baseStat: "ImaginaryAdd"
|
||||||
},
|
},
|
||||||
"ElationDamageAddedRatioBase": {
|
"ElationDamageAddedRatioBase": {
|
||||||
name:"Elation DMG Boost",
|
name:"Elation DMG Boost",
|
||||||
icon:"/icon/IconJoy.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconJoy.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "ElationAdd"
|
baseStat: "ElationAdd"
|
||||||
},
|
},
|
||||||
"SPRatioBase": {
|
"SPRatioBase": {
|
||||||
name:"Energy Regeneration Rate",
|
name:"Energy Regeneration Rate",
|
||||||
icon:"/icon/energy-rate.webp",
|
icon:"spriteoutput/ui/avatar/icon/IconEnergyRecovery.png",
|
||||||
unit: "%",
|
unit: "%",
|
||||||
baseStat: "EnergyRate"
|
baseStat: "EnergyRate"
|
||||||
}
|
}
|
||||||
@@ -169,7 +169,14 @@ export const ratioStats = [
|
|||||||
"SpeedAddedRatio",
|
"SpeedAddedRatio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
export const mappingRelicSlot: Record<string, string> = {
|
||||||
|
"1": "HEAD",
|
||||||
|
"2": "HAND",
|
||||||
|
"3": "BODY",
|
||||||
|
"4": "FOOT",
|
||||||
|
"5": "NECK",
|
||||||
|
"6": "OBJECT",
|
||||||
|
}
|
||||||
|
|
||||||
export const themeColors: Record<string, { bg: string; bgHover: string; text: string; border: string }> = {
|
export const themeColors: Record<string, { bg: string; bgHover: string; text: string; border: string }> = {
|
||||||
winter: {
|
winter: {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { mappingStats, ratioStats } from "@/constant/constant"
|
import { mappingStats, ratioStats } from "@/constant/constant"
|
||||||
import { AffixDetail } from "@/types"
|
import { EliteData, HardLevelData, MainAffixData, MonsterDetail, SubAffixData} from "@/types"
|
||||||
|
|
||||||
export function calcPromotion(level: number) {
|
export function calcPromotion(level: number) {
|
||||||
if (level < 20) {
|
if (level < 20) {
|
||||||
@@ -37,29 +37,29 @@ export function calcRarity(rarity: string) {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
export function calcMainAffixBonus(affix?: AffixDetail, level?: number) {
|
export function calcMainAffixBonus(affix?: MainAffixData, level?: number) {
|
||||||
if (!affix || typeof level !== "number") return "0"
|
if (!affix || typeof level !== "number") return "0"
|
||||||
const value = affix.base + affix.step * level;
|
const value = affix.BaseValue + affix.LevelAdd * level;
|
||||||
|
|
||||||
if (mappingStats?.[affix.property].unit === "%") {
|
if (mappingStats?.[affix.Property].unit === "%") {
|
||||||
return (value * 100).toFixed(1);
|
return (value * 100).toFixed(1);
|
||||||
}
|
}
|
||||||
if (mappingStats?.[affix.property].name === "SPD") {
|
if (mappingStats?.[affix.Property].name === "SPD") {
|
||||||
return value.toFixed(1);
|
return value.toFixed(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return value.toFixed(0);
|
return value.toFixed(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const calcAffixBonus = (affix?: AffixDetail, stepCount?: number, rollCount?: number) => {
|
export const calcAffixBonus = (affix?: SubAffixData, stepCount?: number, rollCount?: number) => {
|
||||||
if (!affix || typeof stepCount !== "number" || typeof rollCount !== "number") return "0"
|
if (!affix || typeof stepCount !== "number" || typeof rollCount !== "number") return "0"
|
||||||
if (mappingStats?.[affix.property].unit === "%") {
|
if (mappingStats?.[affix.Property].unit === "%") {
|
||||||
return ((affix.base * rollCount + affix.step * stepCount) * 100).toFixed(1);
|
return ((affix.BaseValue * rollCount + affix.StepValue * stepCount) * 100).toFixed(1);
|
||||||
}
|
}
|
||||||
if (mappingStats?.[affix.property].name === "SPD") {
|
if (mappingStats?.[affix.Property].name === "SPD") {
|
||||||
return (affix.base * rollCount + affix.step * stepCount).toFixed(1);
|
return (affix.BaseValue * rollCount + affix.StepValue * stepCount).toFixed(1);
|
||||||
}
|
}
|
||||||
return (affix.base * rollCount + affix.step * stepCount).toFixed(0);
|
return (affix.BaseValue * rollCount + affix.StepValue * stepCount).toFixed(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const calcBaseStat = (baseStat: number, stepStat: number, roundFixed: number, level: number) => {
|
export const calcBaseStat = (baseStat: number, stepStat: number, roundFixed: number, level: number) => {
|
||||||
@@ -72,19 +72,19 @@ export const calcBaseStatRaw = (baseStat?: number, stepStat?: number, level?: nu
|
|||||||
return baseStat + stepStat * (level-1);
|
return baseStat + stepStat * (level-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const calcSubAffixBonusRaw = (affix?: AffixDetail, stepCount?: number, rollCount?: number, baseStat?: number) => {
|
export const calcSubAffixBonusRaw = (affix?: SubAffixData, stepCount?: number, rollCount?: number, baseStat?: number) => {
|
||||||
if (!affix || typeof stepCount !== "number" || typeof rollCount !== "number" || typeof baseStat !== "number") return 0
|
if (!affix || typeof stepCount !== "number" || typeof rollCount !== "number" || typeof baseStat !== "number") return 0
|
||||||
if (ratioStats.includes(affix.property)) {
|
if (ratioStats.includes(affix.Property)) {
|
||||||
return (affix.base * rollCount + affix.step * stepCount) * baseStat;
|
return (affix.BaseValue * rollCount + affix.StepValue * stepCount) * baseStat;
|
||||||
}
|
}
|
||||||
return affix.base * rollCount + affix.step * stepCount;
|
return affix.BaseValue * rollCount + affix.StepValue * stepCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const calcMainAffixBonusRaw = (affix?: AffixDetail, level?: number, baseStat?: number) => {
|
export const calcMainAffixBonusRaw = (affix?: MainAffixData, level?: number, baseStat?: number) => {
|
||||||
if (!affix || typeof level !== "number" || typeof baseStat !== "number") return 0
|
if (!affix || typeof level !== "number" || typeof baseStat !== "number") return 0
|
||||||
const value = affix.base + affix.step * level;
|
const value = affix.BaseValue + affix.LevelAdd * level;
|
||||||
|
|
||||||
if (ratioStats.includes(affix.property)) {
|
if (ratioStats.includes(affix.Property)) {
|
||||||
return baseStat * value
|
return baseStat * value
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,4 +97,42 @@ export const calcBonusStatRaw = (affix?: string, baseStat?: number, bonusValue?:
|
|||||||
return baseStat * bonusValue
|
return baseStat * bonusValue
|
||||||
}
|
}
|
||||||
return bonusValue
|
return bonusValue
|
||||||
|
}
|
||||||
|
|
||||||
|
export const calcMonsterStats = (
|
||||||
|
monster: MonsterDetail,
|
||||||
|
eliteGroup: number,
|
||||||
|
hardLevelGroup: number,
|
||||||
|
level: number,
|
||||||
|
hardLevelConfig: Record<string, Record<string, HardLevelData>>,
|
||||||
|
eliteConfig: Record<string, EliteData>
|
||||||
|
) => {
|
||||||
|
let hardLevelRatio = {
|
||||||
|
AttackRatio: 1,
|
||||||
|
DefenceRatio:1,
|
||||||
|
HPRatio: 1,
|
||||||
|
SpeedRatio: 1,
|
||||||
|
StanceRatio: 1
|
||||||
|
}
|
||||||
|
if (hardLevelConfig?.[hardLevelGroup.toString()]?.[level.toString()]) {
|
||||||
|
hardLevelRatio = hardLevelConfig?.[hardLevelGroup.toString()]?.[level.toString()]
|
||||||
|
}
|
||||||
|
let eliteRatio = {
|
||||||
|
AttackRatio: 1,
|
||||||
|
DefenceRatio:1,
|
||||||
|
HPRatio: 1,
|
||||||
|
SpeedRatio: 1,
|
||||||
|
StanceRatio: 1
|
||||||
|
}
|
||||||
|
if (eliteConfig?.[eliteGroup.toString()]) {
|
||||||
|
eliteRatio = eliteConfig?.[eliteGroup.toString()]
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
atk: monster.Base.AttackBase * monster.Modify.AttackModifyRatio * hardLevelRatio.AttackRatio * eliteRatio.AttackRatio,
|
||||||
|
def: monster.Base.DefenceBase * monster.Modify.DefenceModifyRatio * hardLevelRatio.DefenceRatio * eliteRatio.DefenceRatio,
|
||||||
|
hp: monster.Base.HPBase * monster.Modify.HPModifyRatio * hardLevelRatio.HPRatio * eliteRatio.HPRatio,
|
||||||
|
spd: monster.Base.SpeedBase * monster.Modify.SpeedModifyRatio * hardLevelRatio.SpeedRatio * eliteRatio.SpeedRatio,
|
||||||
|
stance: (monster.Base.StanceBase * monster.Modify.StanceModifyRatio * hardLevelRatio.StanceRatio * eliteRatio.StanceRatio) / 3,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { AvatarEnkaDetail, AvatarProfileStore, AvatarStore, CharacterDetail, FreeSRJson, RelicStore } from "@/types";
|
import { AvatarEnkaDetail, AvatarProfileStore, AvatarStore, AvatarDetail, FreeSRJson, RelicStore } from "@/types";
|
||||||
|
|
||||||
function safeNumber(val: string | number | null, fallback = 0): number {
|
function safeNumber(val: string | number | null, fallback = 0): number {
|
||||||
if (!val) return fallback;
|
if (!val) return fallback;
|
||||||
@@ -6,7 +6,7 @@ function safeNumber(val: string | number | null, fallback = 0): number {
|
|||||||
return Number.isFinite(num) && num !== 0 ? num : fallback;
|
return Number.isFinite(num) && num !== 0 ? num : fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function converterToAvatarStore(data: Record<string, CharacterDetail>): { [key: string]: AvatarStore } {
|
export function converterToAvatarStore(data: Record<string, AvatarDetail>): { [key: string]: AvatarStore } {
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
Object.entries(data).map(([key, value]) => [
|
Object.entries(data).map(([key, value]) => [
|
||||||
key,
|
key,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import useMazeStore from "@/stores/mazeStore";
|
|
||||||
|
import useDetailDataStore from "@/stores/detailDataStore";
|
||||||
import { ASConfigStore, AvatarJson, AvatarStore, BattleConfigJson, CEConfigStore, FreeSRJson, LightconeJson, MOCConfigStore, PEAKConfigStore, PFConfigStore, RelicJson } from "@/types";
|
import { ASConfigStore, AvatarJson, AvatarStore, BattleConfigJson, CEConfigStore, FreeSRJson, LightconeJson, MOCConfigStore, PEAKConfigStore, PFConfigStore, RelicJson } from "@/types";
|
||||||
|
|
||||||
|
|
||||||
@@ -11,7 +12,7 @@ export function converterToFreeSRJson(
|
|||||||
ce_config: CEConfigStore,
|
ce_config: CEConfigStore,
|
||||||
peak_config: PEAKConfigStore,
|
peak_config: PEAKConfigStore,
|
||||||
): FreeSRJson {
|
): FreeSRJson {
|
||||||
const { SkillTree } = useMazeStore.getState()
|
const { skillConfig } = useDetailDataStore.getState()
|
||||||
const lightcones: LightconeJson[] = []
|
const lightcones: LightconeJson[] = []
|
||||||
const relics: RelicJson[] = []
|
const relics: RelicJson[] = []
|
||||||
let battleJson: BattleConfigJson
|
let battleJson: BattleConfigJson
|
||||||
@@ -84,8 +85,8 @@ export function converterToFreeSRJson(
|
|||||||
Object.entries(avatars).forEach(([avatarId, avatar]) => {
|
Object.entries(avatars).forEach(([avatarId, avatar]) => {
|
||||||
const skillsByAnchorType: Record<string, number> = {}
|
const skillsByAnchorType: Record<string, number> = {}
|
||||||
for (const [skillId, level] of Object.entries(avatar?.data?.skills || {})) {
|
for (const [skillId, level] of Object.entries(avatar?.data?.skills || {})) {
|
||||||
if (SkillTree?.[skillId]) {
|
if (skillConfig?.[skillId]) {
|
||||||
skillsByAnchorType[SkillTree[skillId].index_slot] = level > SkillTree[skillId].max_level ? SkillTree[skillId].max_level : level
|
skillsByAnchorType[skillConfig[skillId].IndexSlot] = level > skillConfig[skillId].MaxLevel ? skillConfig[skillId].MaxLevel : level
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
avatarsJson[avatarId] = {
|
avatarsJson[avatarId] = {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { listCurrentLanguage } from "@/constant/constant";
|
import { listCurrentLanguage } from "@/constant/constant";
|
||||||
import { CharacterBasic, EventBasic, LightConeBasic, MonsterBasic } from "@/types";
|
import { AvatarDetail } from "@/types";
|
||||||
import { useTranslations } from "next-intl"
|
import { useTranslations } from "next-intl"
|
||||||
|
|
||||||
type TFunc = ReturnType<typeof useTranslations>
|
type TFunc = ReturnType<typeof useTranslations>
|
||||||
@@ -7,7 +7,7 @@ type TFunc = ReturnType<typeof useTranslations>
|
|||||||
export function getNameChar(
|
export function getNameChar(
|
||||||
locale: string,
|
locale: string,
|
||||||
t: TFunc,
|
t: TFunc,
|
||||||
data: CharacterBasic | undefined
|
data: AvatarDetail | undefined
|
||||||
): string {
|
): string {
|
||||||
if (!data) return "";
|
if (!data) return "";
|
||||||
|
|
||||||
@@ -17,20 +17,20 @@ export function getNameChar(
|
|||||||
|
|
||||||
const langKey = listCurrentLanguage[locale as keyof typeof listCurrentLanguage].toLowerCase();
|
const langKey = listCurrentLanguage[locale as keyof typeof listCurrentLanguage].toLowerCase();
|
||||||
|
|
||||||
let text = data.lang[langKey] ?? "";
|
let text = data.Name[langKey] ?? "";
|
||||||
|
|
||||||
if (!text) {
|
if (!text) {
|
||||||
text = data.lang["en"] ?? "";
|
text = data.Name["en"] ?? "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Number(data.id) > 8000) {
|
if (data.ID > 8000) {
|
||||||
text = `${t("trailblazer")} • ${t(data?.baseType?.toLowerCase() ?? "")}`;
|
text = `${t("trailblazer")} • ${t(data?.BaseType?.toLowerCase() ?? "")}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLocaleName(locale: string, data: LightConeBasic | EventBasic | MonsterBasic | undefined): string {
|
export function getLocaleName(locale: string, data: Record<string, string> | undefined | null): string {
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -41,10 +41,10 @@ export function getLocaleName(locale: string, data: LightConeBasic | EventBasic
|
|||||||
const langKey = listCurrentLanguage[locale as keyof typeof listCurrentLanguage].toLowerCase();
|
const langKey = listCurrentLanguage[locale as keyof typeof listCurrentLanguage].toLowerCase();
|
||||||
|
|
||||||
|
|
||||||
let text = data.lang[langKey] ?? "";
|
let text = data[langKey] ?? "";
|
||||||
|
|
||||||
if (!text) {
|
if (!text) {
|
||||||
text = data.lang["en"] ?? "";
|
text = data["en"] ?? "";
|
||||||
}
|
}
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import useAvatarStore from "@/stores/avatarStore";
|
import { AvatarDetail } from "@/types";
|
||||||
|
|
||||||
export function getSkillTree(enhanced: string) {
|
|
||||||
const { avatarSelected, mapAvatarInfo } = useAvatarStore.getState()
|
|
||||||
|
|
||||||
|
export function getSkillTree(avatarSelected: AvatarDetail | null, enhanced: string) {
|
||||||
if (!avatarSelected) return null;
|
if (!avatarSelected) return null;
|
||||||
if (enhanced != "") return Object.values(mapAvatarInfo[avatarSelected.id || ""]?.Enhanced[enhanced].SkillTrees || {}).reduce((acc, dataPointEntry) => {
|
if (enhanced != "" && !!avatarSelected?.Enhanced?.[enhanced]?.SkillTrees) {
|
||||||
const firstEntry = Object.values(dataPointEntry)[0];
|
return Object.values(avatarSelected?.Enhanced?.[enhanced]?.SkillTrees).reduce((acc, dataPointEntry) => {
|
||||||
if (firstEntry) {
|
const firstEntry = Object.values(dataPointEntry)[0];
|
||||||
acc[firstEntry.PointID] = firstEntry.MaxLevel;
|
if (firstEntry) {
|
||||||
}
|
acc[firstEntry.PointID] = firstEntry.MaxLevel;
|
||||||
return acc;
|
}
|
||||||
}, {} as Record<string, number>)
|
return acc;
|
||||||
|
}, {} as Record<string, number>)
|
||||||
|
}
|
||||||
|
|
||||||
return Object.values(mapAvatarInfo[avatarSelected.id || ""]?.SkillTrees).reduce((acc, dataPointEntry) => {
|
return Object.values(avatarSelected?.SkillTrees).reduce((acc, dataPointEntry) => {
|
||||||
const firstEntry = Object.values(dataPointEntry)[0];
|
const firstEntry = Object.values(dataPointEntry)[0];
|
||||||
if (firstEntry) {
|
if (firstEntry) {
|
||||||
acc[firstEntry.PointID] = firstEntry.MaxLevel;
|
acc[firstEntry.PointID] = firstEntry.MaxLevel;
|
||||||
|
|||||||
@@ -1,84 +1,41 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import { AffixDetail, ASDetail, ChangelogItemType, CharacterBasic, CharacterDetail, ConfigMaze, EventBasic, FreeSRJson, LightConeBasic, LightConeDetail, MocDetail, MonsterBasic, PeakDetail, PFDetail, PSResponse, RelicDetail } from "@/types";
|
import { ASGroupDetail, ChangelogItemType, AvatarDetail, FreeSRJson, LightConeDetail, MOCGroupDetail, MonsterDetail, PeakGroupDetail, PFGroupDetail, PSResponse, RelicSetDetail } from "@/types";
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { psResponseSchema } from "@/zod";
|
import { psResponseSchema } from "@/zod";
|
||||||
import { ExtraData } from "@/types";
|
import { ExtraData, Metadata } from "@/types";
|
||||||
|
|
||||||
export async function getConfigMazeApi(): Promise<ConfigMaze> {
|
export async function getMetadataApi(): Promise<Metadata> {
|
||||||
try {
|
try {
|
||||||
const res = await axios.get<ConfigMaze>(`/data/config_maze.json`);
|
const res = await axios.get<Metadata>(`/api/data/metadata`);
|
||||||
return res.data as ConfigMaze;
|
return res.data as Metadata;
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
if (axios.isAxiosError(error)) {
|
console.error('Failed to fetch metadata:', error);
|
||||||
console.log(`Error: ${error.response?.status} - ${error.message}`);
|
|
||||||
} else {
|
|
||||||
console.log(`Unexpected error: ${String(error)}`);
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
Avatar: {},
|
BaseType: {},
|
||||||
MOC: {},
|
DamageType: {},
|
||||||
AS: {},
|
MainAffix: {},
|
||||||
PF: {},
|
SubAffix: {},
|
||||||
|
SkillConfig: {},
|
||||||
Stage: {},
|
Stage: {},
|
||||||
Skill: {}
|
HardLevelConfig: {},
|
||||||
|
EliteConfig: {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getMainAffixApi(): Promise<Record<string, Record<string, AffixDetail>>> {
|
export async function getAvatarListApi(): Promise<Record<string, AvatarDetail>> {
|
||||||
try {
|
try {
|
||||||
const res = await axios.get<Record<string, Record<string, AffixDetail>>>(`/data/main_affixes.json`);
|
const res = await axios.get<Record<string, AvatarDetail>>(`/api/data/avatar`);
|
||||||
return res.data as Record<string, Record<string, AffixDetail>>;
|
|
||||||
} catch (error: unknown) {
|
|
||||||
if (axios.isAxiosError(error)) {
|
|
||||||
console.log(`Error: ${error.response?.status} - ${error.message}`);
|
|
||||||
} else {
|
|
||||||
console.log(`Unexpected error: ${String(error)}`);
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getSubAffixApi(): Promise<Record<string, Record<string, AffixDetail>>> {
|
|
||||||
try {
|
|
||||||
const res = await axios.get<Record<string, Record<string, AffixDetail>>>(`/data/sub_affixes.json`);
|
|
||||||
|
|
||||||
return res.data as Record<string, Record<string, AffixDetail>>;
|
|
||||||
} catch (error: unknown) {
|
|
||||||
if (axios.isAxiosError(error)) {
|
|
||||||
console.log(`Error: ${error.response?.status} - ${error.message}`);
|
|
||||||
} else {
|
|
||||||
console.log(`Unexpected error: ${String(error)}`);
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function fetchCharactersApi(locale: string): Promise<Record<string, CharacterDetail>> {
|
|
||||||
try {
|
|
||||||
const res = await axios.get<Record<string, CharacterDetail>>(`/data/characters.${locale}.json`);
|
|
||||||
const resIcon = await axios.get<Record<string, string[]>>(`/data/rank_icon.json`);
|
|
||||||
for (const [key, char] of Object.entries(res.data)) {
|
|
||||||
if (resIcon.data[key]) {
|
|
||||||
char.RankIcon = resIcon.data[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res.data;
|
return res.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch characters:', error);
|
console.error('Failed to fetch Avatars:', error);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchLightconesApi(locale: string): Promise<Record<string, LightConeDetail>> {
|
export async function getLightconeListApi(): Promise<Record<string, LightConeDetail>> {
|
||||||
try {
|
try {
|
||||||
const res = await axios.get<Record<string, LightConeDetail>>(`/data/lightcones.${locale}.json`);
|
const res = await axios.get<Record<string, LightConeDetail>>(`/api/data/lightcone`);
|
||||||
const resBonus = await axios.get<Record<string, Record<string, { type: string, value: number }[]>>>('/data/lightcone_bonus.json');
|
|
||||||
for (const [key, relic] of Object.entries(res.data)) {
|
|
||||||
if (resBonus.data[key]) {
|
|
||||||
relic.Bonus = resBonus.data[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res.data;
|
return res.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch lightcones:', error);
|
console.error('Failed to fetch lightcones:', error);
|
||||||
@@ -86,15 +43,9 @@ export async function fetchLightconesApi(locale: string): Promise<Record<string,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchRelicsApi(locale: string): Promise<Record<string, RelicDetail>> {
|
export async function getRelicSetListApi(): Promise<Record<string, RelicSetDetail>> {
|
||||||
try {
|
try {
|
||||||
const res = await axios.get<Record<string, RelicDetail>>(`/data/relics.${locale}.json`);
|
const res = await axios.get<Record<string, RelicSetDetail>>(`/api/data/relic`);
|
||||||
const resBonus = await axios.get<Record<string, Record<string, { type: string, value: number }[]>>>('/data/relic_bonus.json');
|
|
||||||
for (const [key, relic] of Object.entries(res.data)) {
|
|
||||||
if (resBonus.data[key]) {
|
|
||||||
relic.Bonus = resBonus.data[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res.data;
|
return res.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch relics:', error);
|
console.error('Failed to fetch relics:', error);
|
||||||
@@ -102,130 +53,66 @@ export async function fetchRelicsApi(locale: string): Promise<Record<string, Rel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchASEventApi(locale: string): Promise<Record<string, ASDetail> | null> {
|
export async function getMonsterListApi(): Promise<Record<string, MonsterDetail>> {
|
||||||
try {
|
try {
|
||||||
const res = await axios.get<Record<string, ASDetail>>(`/data/as.${locale}.json`);
|
const res = await axios.get<Record<string, MonsterDetail>>(`/api/data/monster`);
|
||||||
return res.data;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to fetch AS:', error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function fetchPFEventApi(locale: string): Promise<Record<string, PFDetail> | null> {
|
|
||||||
try {
|
|
||||||
const res = await axios.get<Record<string, PFDetail>>(`/data/pf.${locale}.json`);
|
|
||||||
return res.data;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to fetch PF:', error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function fetchMOCEventApi(locale: string): Promise<Record<string, MocDetail[]> | null> {
|
|
||||||
try {
|
|
||||||
const res = await axios.get<Record<string, MocDetail[]>>(`/data/moc.${locale}.json`);
|
|
||||||
return res.data;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to fetch MOC:', error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function fetchPeakEventApi(locale: string): Promise<Record<string, PeakDetail> | null> {
|
|
||||||
try {
|
|
||||||
const res = await axios.get<Record<string, PeakDetail>>(`/data/peak.${locale}.json`);
|
|
||||||
return res.data;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to fetch peak:', error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function fetchChangelog(): Promise<ChangelogItemType[] | null> {
|
|
||||||
try {
|
|
||||||
const res = await axios.get<ChangelogItemType[]>(`/data/changelog.json`);
|
|
||||||
return res.data;
|
return res.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch monster:', error);
|
console.error('Failed to fetch monster:', error);
|
||||||
return null;
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getCharacterListApi(): Promise<CharacterBasic[]> {
|
export async function getASEventListApi(): Promise<Record<string, ASGroupDetail>> {
|
||||||
try {
|
try {
|
||||||
const res = await axios.get<CharacterBasic[]>('/data/character.json');
|
const res = await axios.get<Record<string, ASGroupDetail>>(`/api/data/as`);
|
||||||
return res.data;
|
return res.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch character list:', error);
|
console.error('Failed to fetch AS:', error);
|
||||||
return [];
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getPFEventListApi(): Promise<Record<string, PFGroupDetail>> {
|
||||||
export async function getLightconeListApi(): Promise<LightConeBasic[]> {
|
|
||||||
try {
|
try {
|
||||||
const res = await axios.get<LightConeBasic[]>('/data/lightcone.json');
|
const res = await axios.get<Record<string, PFGroupDetail>>(`/api/data/pf`);
|
||||||
return res.data
|
return res.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch lightcone list:', error);
|
console.error('Failed to fetch PF:', error);
|
||||||
return [];
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getMOCEventListApi(): Promise<Record<string, MOCGroupDetail>> {
|
||||||
export async function getMOCEventListApi(): Promise<EventBasic[]> {
|
|
||||||
try {
|
try {
|
||||||
const res = await axios.get<EventBasic[]>('/data/moc.json');
|
const res = await axios.get<Record<string, MOCGroupDetail>>(`/api/data/moc`);
|
||||||
return res.data
|
return res.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch moc list:', error);
|
console.error('Failed to fetch MOC:', error);
|
||||||
return [];
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getASEventListApi(): Promise<EventBasic[]> {
|
export async function getPeakEventListApi(): Promise<Record<string, PeakGroupDetail>> {
|
||||||
try {
|
try {
|
||||||
const res = await axios.get<EventBasic[]>('/data/as.json');
|
const res = await axios.get<Record<string, PeakGroupDetail>>(`/api/data/peak`);
|
||||||
return res.data
|
return res.data;
|
||||||
} catch (error: unknown) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch as list:', error);
|
console.error('Failed to fetch peak:', error);
|
||||||
return [];
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPFEventListApi(): Promise<EventBasic[]> {
|
export async function getChangelog(): Promise<ChangelogItemType[]> {
|
||||||
try {
|
try {
|
||||||
const res = await axios.get<EventBasic[]>('/data/pf.json');
|
const res = await axios.get<ChangelogItemType[]>(`/api/data/changelog`);
|
||||||
return res.data
|
return res.data;
|
||||||
} catch (error: unknown) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch pf list:', error);
|
console.error('Failed to fetch monster:', error);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPEAKEventListApi(): Promise<EventBasic[]> {
|
|
||||||
try {
|
|
||||||
const res = await axios.get<EventBasic[]>('/data/peak.json');
|
|
||||||
return res.data
|
|
||||||
} catch (error: unknown) {
|
|
||||||
console.error('Failed to fetch peak list:', error);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getMonsterListApi(): Promise<MonsterBasic[]> {
|
|
||||||
try {
|
|
||||||
const res = await axios.get<MonsterBasic[]>('/data/monster.json');
|
|
||||||
return res.data
|
|
||||||
} catch (error: unknown) {
|
|
||||||
console.error('Failed to fetch peak list:', error);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export async function SendDataToServer(
|
export async function SendDataToServer(
|
||||||
username: string,
|
username: string,
|
||||||
password: string,
|
password: string,
|
||||||
|
|||||||
38
src/lib/cache/cache.ts
vendored
Normal file
38
src/lib/cache/cache.ts
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { readFileSync, readdirSync } from "fs"
|
||||||
|
import path from "path"
|
||||||
|
|
||||||
|
type CacheItem = {
|
||||||
|
buf: Uint8Array
|
||||||
|
type: "json" | "br"
|
||||||
|
}
|
||||||
|
|
||||||
|
const cache = new Map<string, CacheItem>()
|
||||||
|
|
||||||
|
const dir = path.join(process.cwd(), "data")
|
||||||
|
|
||||||
|
for (const f of readdirSync(dir)) {
|
||||||
|
const file = path.join(dir, f)
|
||||||
|
|
||||||
|
if (f.endsWith(".json.br")) {
|
||||||
|
const name = f.replace(".json.br", "")
|
||||||
|
const buf = new Uint8Array(readFileSync(file))
|
||||||
|
cache.set(name, {
|
||||||
|
buf,
|
||||||
|
type: "br"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (f.endsWith(".json")) {
|
||||||
|
const name = f.replace(".json", "")
|
||||||
|
const buf = new Uint8Array(readFileSync(file))
|
||||||
|
|
||||||
|
cache.set(name, {
|
||||||
|
buf,
|
||||||
|
type: "json"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getDataCache(name: string) {
|
||||||
|
return cache.get(name)
|
||||||
|
}
|
||||||
1
src/lib/cache/index.ts
vendored
Normal file
1
src/lib/cache/index.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from "./cache"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export * from "./useFetchConfigData";
|
export * from "./useFetchMetaData";
|
||||||
export * from "./useFetchAvatarData";
|
export * from "./useFetchAvatarData";
|
||||||
export * from "./useFetchLightconeData";
|
export * from "./useFetchLightconeData";
|
||||||
export * from "./useFetchRelicData";
|
export * from "./useFetchRelicData";
|
||||||
|
|||||||
@@ -1,72 +1,26 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
||||||
"use client"
|
"use client"
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { fetchASEventApi, getASEventListApi } from '@/lib/api'
|
import { getASEventListApi } from '@/lib/api'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { listCurrentLanguageApi } from '@/constant/constant'
|
|
||||||
import useLocaleStore from '@/stores/localeStore'
|
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import useEventStore from '@/stores/eventStore'
|
import useDetailDataStore from '@/stores/detailDataStore'
|
||||||
import { EventStageDetail } from '@/types'
|
|
||||||
|
|
||||||
export const useFetchASData = () => {
|
export const useFetchASGroupData = () => {
|
||||||
const { setASEvent, setMapASInfo } = useEventStore()
|
const { setMapAS } = useDetailDataStore()
|
||||||
const { locale } = useLocaleStore()
|
|
||||||
const { data: dataAS, error: errorAS } = useQuery({
|
const query = useQuery({
|
||||||
queryKey: ['asData'],
|
queryKey: ['ASGroupData'],
|
||||||
queryFn: getASEventListApi,
|
queryFn: getASEventListApi,
|
||||||
select: (data) => data.sort((a, b) => Number(b.id) - Number(a.id)),
|
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: 1000 * 60 * 5,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: dataASInfo, error: errorASInfo } = useQuery({
|
|
||||||
queryKey: ['asInfoData', locale],
|
|
||||||
queryFn: () =>
|
|
||||||
fetchASEventApi(
|
|
||||||
listCurrentLanguageApi[locale.toLowerCase()]
|
|
||||||
),
|
|
||||||
staleTime: 1000 * 60 * 5,
|
|
||||||
enabled: !!dataAS,
|
|
||||||
select: (data) => {
|
|
||||||
const newData = { ...data }
|
|
||||||
for (const key in newData) {
|
|
||||||
for (const item of newData[key].Level) {
|
|
||||||
item.EventIDList1 = item.EventIDList1.map((event: EventStageDetail) => ({
|
|
||||||
...event,
|
|
||||||
|
|
||||||
MonsterList: event.MonsterList.map((monster) => {
|
|
||||||
const { $type, ...rest } = monster;
|
|
||||||
return rest;
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
item.EventIDList2 = item.EventIDList2.map((event: EventStageDetail) => ({
|
|
||||||
...event,
|
|
||||||
|
|
||||||
MonsterList: event.MonsterList.map((monster) => {
|
|
||||||
const { $type, ...rest } = monster;
|
|
||||||
return rest;
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return newData
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (dataAS && !errorAS) {
|
if (query.data) {
|
||||||
setASEvent(dataAS)
|
setMapAS(query.data)
|
||||||
} else if (errorAS) {
|
} else if (query.error) {
|
||||||
toast.error("Failed to load AS data")
|
toast.error("Failed to load ASGroup data")
|
||||||
}
|
}
|
||||||
}, [dataAS, errorAS, setASEvent])
|
}, [query.data, query.error, setMapAS])
|
||||||
|
|
||||||
useEffect(() => {
|
return query
|
||||||
if (dataASInfo && !errorASInfo) {
|
|
||||||
setMapASInfo(dataASInfo)
|
|
||||||
} else if (errorASInfo) {
|
|
||||||
toast.error("Failed to load AS info data")
|
|
||||||
}
|
|
||||||
|
|
||||||
}, [dataASInfo, errorASInfo, setMapASInfo])
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,63 +1,43 @@
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
"use client"
|
"use client"
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { fetchCharactersApi, getCharacterListApi } from '@/lib/api'
|
import { getAvatarListApi } from '@/lib/api'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import useAvatarStore from '@/stores/avatarStore'
|
import useDetailDataStore from '@/stores/detailDataStore'
|
||||||
import { listCurrentLanguageApi } from '@/constant/constant'
|
import useCurrentDataStore from '@/stores/currentDataStore'
|
||||||
import useLocaleStore from '@/stores/localeStore'
|
|
||||||
import useUserDataStore from '@/stores/userDataStore'
|
import useUserDataStore from '@/stores/userDataStore'
|
||||||
import { converterToAvatarStore } from '@/helper'
|
import { converterToAvatarStore } from '@/helper'
|
||||||
|
|
||||||
export const useFetchAvatarData = () => {
|
export const useFetchAvatarData = () => {
|
||||||
const { setAvatar, avatars } = useUserDataStore()
|
const { setMapAvatar, mapAvatar } = useDetailDataStore()
|
||||||
const { setListAvatar, setAllMapAvatarInfo, mapAvatarInfo, setAvatarSelected, avatarSelected } = useAvatarStore()
|
const { setAvatar, avatars } = useUserDataStore()
|
||||||
const { locale } = useLocaleStore()
|
const { avatarSelected, setAvatarSelected } = useCurrentDataStore()
|
||||||
const { data: dataAvatar, error: errorAvatar } = useQuery({
|
|
||||||
queryKey: ['avatarData'],
|
|
||||||
queryFn: getCharacterListApi,
|
|
||||||
staleTime: 1000 * 60 * 5,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
|
const query = useQuery({
|
||||||
|
queryKey: ['AvatarData'],
|
||||||
|
queryFn: getAvatarListApi,
|
||||||
|
staleTime: 1000 * 60 * 5,
|
||||||
|
})
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const listAvatarId = Object.keys(avatars)
|
const listAvatarId = Object.keys(avatars)
|
||||||
const listAvatarNotExist = Object.entries(mapAvatarInfo).filter(([avatarId]) => !listAvatarId.includes(avatarId))
|
const listAvatarNotExist = Object.entries(mapAvatar).filter(([avatarId]) => !listAvatarId.includes(avatarId))
|
||||||
const avatarStore = converterToAvatarStore(Object.fromEntries(listAvatarNotExist))
|
const avatarStore = converterToAvatarStore(Object.fromEntries(listAvatarNotExist))
|
||||||
if (Object.keys(avatarStore).length === 0) return
|
if (Object.keys(avatarStore).length === 0) return
|
||||||
for (const avatar of Object.values(avatarStore)) {
|
for (const avatar of Object.values(avatarStore)) {
|
||||||
setAvatar(avatar)
|
setAvatar(avatar)
|
||||||
}
|
}
|
||||||
}, [mapAvatarInfo])
|
}, [mapAvatar])
|
||||||
|
|
||||||
const { data: dataAvatarInfo, error: errorAvatarInfo } = useQuery({
|
|
||||||
queryKey: ['avatarInfoData', locale],
|
|
||||||
queryFn: () =>
|
|
||||||
fetchCharactersApi(
|
|
||||||
listCurrentLanguageApi[locale.toLowerCase()]
|
|
||||||
),
|
|
||||||
staleTime: 1000 * 60 * 5,
|
|
||||||
enabled: !!dataAvatar,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (dataAvatar && !errorAvatar) {
|
if (query.data) {
|
||||||
setListAvatar(dataAvatar)
|
setMapAvatar(query.data)
|
||||||
if (!avatarSelected) {
|
if (!avatarSelected) {
|
||||||
setAvatarSelected(dataAvatar[0])
|
setAvatarSelected(Object.values(query.data)[0])
|
||||||
}
|
|
||||||
|
|
||||||
} else if (errorAvatar) {
|
|
||||||
toast.error("Failed to load avatar data")
|
|
||||||
}
|
}
|
||||||
}, [dataAvatar, errorAvatar])
|
}
|
||||||
|
if (query.error) toast.error("Failed to load Avatar data")
|
||||||
|
}, [query.data, query.error, setMapAvatar, avatarSelected, setAvatarSelected])
|
||||||
|
|
||||||
useEffect(() => {
|
return query
|
||||||
if (dataAvatarInfo && !errorAvatarInfo) {
|
}
|
||||||
setAllMapAvatarInfo(dataAvatarInfo)
|
|
||||||
} else if (errorAvatarInfo) {
|
|
||||||
toast.error("Failed to load avatar info data")
|
|
||||||
}
|
|
||||||
}, [dataAvatarInfo, errorAvatarInfo])
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { fetchChangelog } from '@/lib/api'
|
import { getChangelog } from '@/lib/api'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import useModelStore from '@/stores/modelStore'
|
import useModelStore from '@/stores/modelStore'
|
||||||
@@ -9,23 +9,24 @@ import useLocaleStore from '@/stores/localeStore'
|
|||||||
export const useFetchChangelog = () => {
|
export const useFetchChangelog = () => {
|
||||||
const { currentVersion, setChangelog, setCurrentVersion } = useLocaleStore()
|
const { currentVersion, setChangelog, setCurrentVersion } = useLocaleStore()
|
||||||
const { setIsChangelog } = useModelStore()
|
const { setIsChangelog } = useModelStore()
|
||||||
const { data: dataChangelog, error: errorChangelog } = useQuery({
|
const query = useQuery({
|
||||||
queryKey: ['changelog'],
|
queryKey: ['changelog'],
|
||||||
queryFn: fetchChangelog,
|
queryFn: getChangelog,
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: 1000 * 60 * 5,
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (dataChangelog && !errorChangelog) {
|
if (query.data && !query.error) {
|
||||||
setChangelog(dataChangelog)
|
setChangelog(query.data)
|
||||||
if (dataChangelog?.[0] && dataChangelog[0].version != currentVersion) {
|
if (query.data?.[0] && query.data[0].version != currentVersion) {
|
||||||
setIsChangelog(true)
|
setIsChangelog(true)
|
||||||
setCurrentVersion(dataChangelog[0].version)
|
setCurrentVersion(query.data[0].version)
|
||||||
}
|
}
|
||||||
} else if (errorChangelog) {
|
} else if (query.error) {
|
||||||
toast.error("Failed to load changelog data")
|
toast.error("Failed to load changelog data")
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [dataChangelog, errorChangelog, setChangelog, setCurrentVersion, setIsChangelog])
|
}, [query.data, query.error, setChangelog, setCurrentVersion, setIsChangelog])
|
||||||
|
|
||||||
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
"use client"
|
|
||||||
import { useQuery } from '@tanstack/react-query'
|
|
||||||
import { getConfigMazeApi, getMainAffixApi, getSubAffixApi } from '@/lib/api'
|
|
||||||
import useAffixStore from '@/stores/affixStore'
|
|
||||||
import useMazeStore from '@/stores/mazeStore'
|
|
||||||
import { useEffect } from 'react'
|
|
||||||
import { toast } from 'react-toastify'
|
|
||||||
|
|
||||||
export const useFetchConfigData = () => {
|
|
||||||
|
|
||||||
const { setMapMainAffix, setMapSubAffix } = useAffixStore()
|
|
||||||
const { setAllMazeData } = useMazeStore()
|
|
||||||
|
|
||||||
const { data, error } = useQuery({
|
|
||||||
queryKey: ['initialConfigData'],
|
|
||||||
queryFn: async () => {
|
|
||||||
const [maze, main, sub] = await Promise.all([
|
|
||||||
getConfigMazeApi(),
|
|
||||||
getMainAffixApi(),
|
|
||||||
getSubAffixApi(),
|
|
||||||
])
|
|
||||||
return { maze, main, sub }
|
|
||||||
},
|
|
||||||
staleTime: 1000 * 60 * 5,
|
|
||||||
})
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (data && !error) {
|
|
||||||
setAllMazeData(data.maze)
|
|
||||||
setMapMainAffix(data.main)
|
|
||||||
setMapSubAffix(data.sub)
|
|
||||||
}
|
|
||||||
else if (error) {
|
|
||||||
toast.error("Failed to load initial config data")
|
|
||||||
}
|
|
||||||
}, [data, error, setAllMazeData, setMapMainAffix, setMapSubAffix])
|
|
||||||
}
|
|
||||||
@@ -1,46 +1,25 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { fetchLightconesApi, getLightconeListApi } from '@/lib/api'
|
import { getLightconeListApi } from '@/lib/api';
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import useLightconeStore from '@/stores/lightconeStore'
|
|
||||||
import { listCurrentLanguageApi } from '@/constant/constant'
|
|
||||||
import useLocaleStore from '@/stores/localeStore'
|
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
|
import useDetailDataStore from '@/stores/detailDataStore';
|
||||||
|
|
||||||
export const useFetchLightconeData = () => {
|
export const useFetchLightconeData = () => {
|
||||||
const { setListLightcone, setAllMapLightconeInfo } = useLightconeStore()
|
const { setMapLightCone } = useDetailDataStore()
|
||||||
const { locale } = useLocaleStore()
|
const query = useQuery({
|
||||||
const { data: dataLightcone, error: errorLightcone } = useQuery({
|
|
||||||
queryKey: ['lightconeData'],
|
queryKey: ['lightconeData'],
|
||||||
queryFn: getLightconeListApi,
|
queryFn: getLightconeListApi,
|
||||||
select: (data) => data.sort((a, b) => Number(b.id) - Number(a.id)),
|
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: 1000 * 60 * 5,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: dataLightconeInfo, error: errorLightconeInfo } = useQuery({
|
|
||||||
queryKey: ['lightconeInfoData', locale],
|
|
||||||
queryFn: () =>
|
|
||||||
fetchLightconesApi(
|
|
||||||
listCurrentLanguageApi[locale.toLowerCase()]
|
|
||||||
),
|
|
||||||
staleTime: 1000 * 60 * 5,
|
|
||||||
enabled: !!dataLightcone,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (dataLightcone && !errorLightcone) {
|
if (query.data && !query.error) {
|
||||||
setListLightcone(dataLightcone)
|
setMapLightCone(query.data)
|
||||||
} else if (errorLightcone) {
|
} else if (query.error) {
|
||||||
toast.error("Failed to load lightcone data")
|
toast.error("Failed to load lightcone data")
|
||||||
}
|
}
|
||||||
}, [dataLightcone, errorLightcone, setListLightcone])
|
}, [query.data, query.error, setMapLightCone])
|
||||||
|
|
||||||
useEffect(() => {
|
return query
|
||||||
if (dataLightconeInfo && !errorLightconeInfo) {
|
|
||||||
setAllMapLightconeInfo(dataLightconeInfo)
|
|
||||||
} else if (errorLightconeInfo) {
|
|
||||||
toast.error("Failed to load lightcone info data")
|
|
||||||
}
|
|
||||||
|
|
||||||
}, [dataLightconeInfo, errorLightconeInfo, setAllMapLightconeInfo])
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,66 +1,25 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
||||||
"use client"
|
"use client"
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { fetchMOCEventApi, getMOCEventListApi } from '@/lib/api'
|
import { getMOCEventListApi } from '@/lib/api'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { listCurrentLanguageApi } from '@/constant/constant'
|
import useDetailDataStore from '@/stores/detailDataStore'
|
||||||
import useLocaleStore from '@/stores/localeStore'
|
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import useEventStore from '@/stores/eventStore'
|
|
||||||
import { EventStageDetail, MocDetail } from '@/types'
|
|
||||||
|
|
||||||
export const useFetchMOCData = () => {
|
export const useFetchMOCGroupData = () => {
|
||||||
const { setMOCEvent, setMapMOCInfo } = useEventStore()
|
const { setMapMoc } = useDetailDataStore()
|
||||||
const { locale } = useLocaleStore()
|
const query = useQuery({
|
||||||
const { data: dataMOC, error: errorMOC } = useQuery({
|
queryKey: ['MOCGroupData'],
|
||||||
queryKey: ['mocData'],
|
|
||||||
queryFn: getMOCEventListApi,
|
queryFn: getMOCEventListApi,
|
||||||
select: (data) => data.sort((a, b) => Number(b.id) - Number(a.id)),
|
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: 1000 * 60 * 5,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: dataMOCInfo, error: errorMOCInfo } = useQuery({
|
|
||||||
queryKey: ['mocInfoData', locale],
|
|
||||||
queryFn: async () => {
|
|
||||||
const result = await fetchMOCEventApi(
|
|
||||||
listCurrentLanguageApi[locale.toLowerCase()]
|
|
||||||
);
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
staleTime: 1000 * 60 * 5,
|
|
||||||
select: (data) => {
|
|
||||||
const newData = { ...data }
|
|
||||||
for (const key in newData) {
|
|
||||||
for (const item of newData[key]) {
|
|
||||||
item.EventIDList1 = item.EventIDList1.map((event: EventStageDetail) => ({
|
|
||||||
...event,
|
|
||||||
MonsterList: event.MonsterList.map(({ $type, ...rest }) => rest)
|
|
||||||
}))
|
|
||||||
item.EventIDList2 = item.EventIDList2.map((event: EventStageDetail) => ({
|
|
||||||
...event,
|
|
||||||
MonsterList: event.MonsterList.map(({ $type, ...rest }) => rest)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return newData
|
|
||||||
},
|
|
||||||
enabled: !!dataMOC,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (dataMOC && !errorMOC) {
|
if (query.data && !query.error) {
|
||||||
setMOCEvent(dataMOC)
|
setMapMoc(query.data)
|
||||||
} else if (errorMOC) {
|
} else if (query.error) {
|
||||||
toast.error("Failed to load MOC data")
|
toast.error("Failed to load MOCGroup data")
|
||||||
}
|
}
|
||||||
}, [dataMOC, errorMOC, setMOCEvent])
|
}, [query.data, query.error, setMapMoc])
|
||||||
|
|
||||||
useEffect(() => {
|
return query
|
||||||
if (dataMOCInfo && !errorMOCInfo) {
|
|
||||||
setMapMOCInfo(dataMOCInfo as Record<string, MocDetail[]>)
|
|
||||||
} else if (errorMOCInfo) {
|
|
||||||
toast.error("Failed to load MOC info data")
|
|
||||||
}
|
|
||||||
|
|
||||||
}, [dataMOCInfo, errorMOCInfo, setMapMOCInfo])
|
|
||||||
}
|
}
|
||||||
|
|||||||
33
src/lib/hooks/useFetchMetaData.ts
Normal file
33
src/lib/hooks/useFetchMetaData.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
"use client"
|
||||||
|
import { useQuery } from '@tanstack/react-query'
|
||||||
|
import { useEffect } from 'react'
|
||||||
|
import { toast } from 'react-toastify'
|
||||||
|
import { getMetadataApi } from '@/lib/api'
|
||||||
|
import useCurrentDataStore from '@/stores/currentDataStore'
|
||||||
|
import useDetailDataStore from '@/stores/detailDataStore'
|
||||||
|
|
||||||
|
export const useFetchConfigData = () => {
|
||||||
|
const { setMetaData } = useDetailDataStore()
|
||||||
|
const { setSettingData } = useCurrentDataStore()
|
||||||
|
|
||||||
|
const query = useQuery({
|
||||||
|
queryKey: ['initialConfigData'],
|
||||||
|
queryFn: async () => {
|
||||||
|
const metaData = await getMetadataApi()
|
||||||
|
return { metaData }
|
||||||
|
},
|
||||||
|
staleTime: 1000 * 60 * 5,
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (query.data && !query.error) {
|
||||||
|
setSettingData(query.data.metaData)
|
||||||
|
setMetaData(query.data.metaData)
|
||||||
|
}
|
||||||
|
else if (query.error) {
|
||||||
|
toast.error("Failed to load initial config data")
|
||||||
|
}
|
||||||
|
}, [query.data, query.error, setMetaData, setSettingData])
|
||||||
|
|
||||||
|
return query
|
||||||
|
}
|
||||||
@@ -2,28 +2,24 @@
|
|||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { getMonsterListApi } from '@/lib/api'
|
import { getMonsterListApi } from '@/lib/api'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
|
import useDetailDataStore from '@/stores/detailDataStore'
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import useMonsterStore from '@/stores/monsterStore'
|
|
||||||
import { MonsterBasic } from '@/types'
|
|
||||||
|
|
||||||
export const useFetchMonsterData = () => {
|
export const useFetchMonsterData = () => {
|
||||||
const { setAllMapMonster, setListMonster } = useMonsterStore()
|
const { setMapMonster } = useDetailDataStore()
|
||||||
const { data: dataMonster, error: errorMonster } = useQuery({
|
const query = useQuery({
|
||||||
queryKey: ['monsterData'],
|
queryKey: ['MonsterData'],
|
||||||
queryFn: getMonsterListApi,
|
queryFn: getMonsterListApi,
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: 1000 * 60 * 5,
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (dataMonster && !errorMonster) {
|
if (query.data && !query.error) {
|
||||||
setListMonster(dataMonster.sort((a, b) => Number(b.id) - Number(a.id)))
|
setMapMonster(query.data)
|
||||||
const monsterMap = dataMonster.reduce<Record<string, MonsterBasic>>((acc, m) => {
|
} else if (query.error) {
|
||||||
acc[m.id] = m
|
toast.error("Failed to load Monster data")
|
||||||
return acc
|
|
||||||
}, {})
|
|
||||||
setAllMapMonster(monsterMap)
|
|
||||||
} else if (errorMonster) {
|
|
||||||
toast.error("Failed to load monster data")
|
|
||||||
}
|
}
|
||||||
}, [dataMonster, errorMonster, setAllMapMonster, setListMonster])
|
}, [query.data, query.error, setMapMonster])
|
||||||
|
|
||||||
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +1,25 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
||||||
"use client"
|
"use client"
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { fetchPeakEventApi, getPEAKEventListApi } from '@/lib/api'
|
import { getPeakEventListApi } from '@/lib/api'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { listCurrentLanguageApi } from '@/constant/constant'
|
import useDetailDataStore from '@/stores/detailDataStore'
|
||||||
import useLocaleStore from '@/stores/localeStore'
|
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import useEventStore from '@/stores/eventStore'
|
|
||||||
import { EventStageDetail, PeakDetail } from '@/types'
|
|
||||||
|
|
||||||
export const useFetchPEAKData = () => {
|
export const useFetchPeakGroupData = () => {
|
||||||
const { setPEAKEvent, setMapPEAKInfo } = useEventStore()
|
const { setMapPeak } = useDetailDataStore()
|
||||||
const { locale } = useLocaleStore()
|
const query = useQuery({
|
||||||
const { data: dataPEAK, error: errorPEAK } = useQuery({
|
queryKey: ['PeakGroupData'],
|
||||||
queryKey: ['peakData'],
|
queryFn: getPeakEventListApi,
|
||||||
queryFn: getPEAKEventListApi,
|
|
||||||
select: (data) => data.sort((a, b) => Number(b.id) - Number(a.id)),
|
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: 1000 * 60 * 5,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: dataPEAKInfo, error: errorPEAKInfo } = useQuery({
|
|
||||||
queryKey: ['peakInfoData', locale],
|
|
||||||
queryFn: () =>
|
|
||||||
fetchPeakEventApi(
|
|
||||||
listCurrentLanguageApi[locale.toLowerCase()]
|
|
||||||
),
|
|
||||||
staleTime: 1000 * 60 * 5,
|
|
||||||
select: (data) => {
|
|
||||||
const newData = { ...data }
|
|
||||||
for (const key in newData) {
|
|
||||||
for (const item of newData[key].PreLevel) {
|
|
||||||
item.EventIDList = item.EventIDList.map((event: EventStageDetail) => ({
|
|
||||||
...event,
|
|
||||||
MonsterList: event.MonsterList.map(({ $type, ...rest }) => rest)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
newData[key].BossLevel.EventIDList = newData[key].BossLevel.EventIDList.map((event: EventStageDetail) => ({
|
|
||||||
...event,
|
|
||||||
MonsterList: event.MonsterList.map(({ $type, ...rest }) => rest)
|
|
||||||
}))
|
|
||||||
newData[key].BossConfig.EventIDList = newData[key].BossConfig.EventIDList.map((event: EventStageDetail) => ({
|
|
||||||
...event,
|
|
||||||
MonsterList: event.MonsterList.map(({ $type, ...rest }) => rest)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
return newData
|
|
||||||
},
|
|
||||||
enabled: !!dataPEAK,
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (dataPEAK && !errorPEAK) {
|
if (query.data && !query.error) {
|
||||||
setPEAKEvent(dataPEAK)
|
setMapPeak(query.data)
|
||||||
} else if (errorPEAK) {
|
} else if (query.error) {
|
||||||
toast.error("Failed to load PEAK data")
|
toast.error("Failed to load PeakGroup data")
|
||||||
}
|
}
|
||||||
}, [dataPEAK, errorPEAK, setPEAKEvent])
|
}, [query.data, query.error, setMapPeak])
|
||||||
|
|
||||||
useEffect(() => {
|
return query
|
||||||
if (dataPEAKInfo && !errorPEAKInfo) {
|
|
||||||
setMapPEAKInfo(dataPEAKInfo as Record<string, PeakDetail>)
|
|
||||||
} else if (errorPEAKInfo) {
|
|
||||||
toast.error("Failed to load PEAK info data")
|
|
||||||
}
|
|
||||||
|
|
||||||
}, [dataPEAKInfo, errorPEAKInfo, setMapPEAKInfo])
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,64 +1,25 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
||||||
"use client"
|
"use client"
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { fetchPFEventApi, getPFEventListApi } from '@/lib/api'
|
import { getPFEventListApi } from '@/lib/api'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { listCurrentLanguageApi } from '@/constant/constant'
|
import useDetailDataStore from '@/stores/detailDataStore'
|
||||||
import useLocaleStore from '@/stores/localeStore'
|
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
import useEventStore from '@/stores/eventStore'
|
|
||||||
import { EventStageDetail } from '@/types'
|
|
||||||
|
|
||||||
export const useFetchPFData = () => {
|
export const useFetchPFGroupData = () => {
|
||||||
const { setPFEvent, setMapPFInfo } = useEventStore()
|
const { setMapPF } = useDetailDataStore()
|
||||||
const { locale } = useLocaleStore()
|
const query = useQuery({
|
||||||
const { data: dataPF, error: errorPF } = useQuery({
|
queryKey: ['PFGroupData'],
|
||||||
queryKey: ['pfData'],
|
|
||||||
queryFn: getPFEventListApi,
|
queryFn: getPFEventListApi,
|
||||||
select: (data) => data.sort((a, b) => Number(b.id) - Number(a.id)),
|
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: 1000 * 60 * 5,
|
||||||
})
|
})
|
||||||
|
|
||||||
const { data: dataPFInfo, error: errorPFInfo } = useQuery({
|
|
||||||
queryKey: ['pfInfoData', locale],
|
|
||||||
queryFn: () =>
|
|
||||||
fetchPFEventApi(
|
|
||||||
listCurrentLanguageApi[locale.toLowerCase()]
|
|
||||||
),
|
|
||||||
staleTime: 1000 * 60 * 5,
|
|
||||||
enabled: !!dataPF,
|
|
||||||
select: (data) => {
|
|
||||||
const newData = { ...data }
|
|
||||||
for (const key in newData) {
|
|
||||||
for (const item of newData[key].Level) {
|
|
||||||
item.EventIDList1 = item.EventIDList1.map((event: EventStageDetail) => ({
|
|
||||||
...event,
|
|
||||||
MonsterList: event.MonsterList.map(({ $type, ...rest }) => rest)
|
|
||||||
}))
|
|
||||||
item.EventIDList2 = item.EventIDList2.map((event: EventStageDetail) => ({
|
|
||||||
...event,
|
|
||||||
MonsterList: event.MonsterList.map(({ $type, ...rest }) => rest)
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return newData
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (dataPF && !errorPF) {
|
if (query.data && !query.error) {
|
||||||
setPFEvent(dataPF)
|
setMapPF(query.data)
|
||||||
} else if (errorPF) {
|
} else if (query.error) {
|
||||||
toast.error("Failed to load PF data")
|
toast.error("Failed to load PFGroup data")
|
||||||
}
|
}
|
||||||
}, [dataPF, errorPF, setPFEvent])
|
}, [query.data, query.error, setMapPF])
|
||||||
|
|
||||||
useEffect(() => {
|
return query
|
||||||
if (dataPFInfo && !errorPFInfo) {
|
|
||||||
setMapPFInfo(dataPFInfo)
|
|
||||||
} else if (errorPFInfo) {
|
|
||||||
toast.error("Failed to load PF info data")
|
|
||||||
}
|
|
||||||
|
|
||||||
}, [dataPFInfo, errorPFInfo, setMapPFInfo])
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,25 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { fetchRelicsApi } from '@/lib/api'
|
import { getRelicSetListApi } from '@/lib/api'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import useRelicStore from '@/stores/relicStore'
|
import useDetailDataStore from '@/stores/detailDataStore'
|
||||||
import { listCurrentLanguageApi } from '@/constant/constant'
|
|
||||||
import useLocaleStore from '@/stores/localeStore'
|
|
||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
export const useFetchRelicData = () => {
|
export const useFetchRelicSetData = () => {
|
||||||
const { setAllMapRelicInfo } = useRelicStore()
|
const { setMapRelicSet } = useDetailDataStore()
|
||||||
const { locale } = useLocaleStore()
|
const query = useQuery({
|
||||||
|
queryKey: ['RelicSetData'],
|
||||||
const { data: dataRelicInfo, error: errorRelicInfo } = useQuery({
|
queryFn: getRelicSetListApi,
|
||||||
queryKey: ['relicInfoData', locale],
|
|
||||||
queryFn: () =>
|
|
||||||
fetchRelicsApi(
|
|
||||||
listCurrentLanguageApi[locale.toLowerCase()]
|
|
||||||
),
|
|
||||||
staleTime: 1000 * 60 * 5,
|
staleTime: 1000 * 60 * 5,
|
||||||
});
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (dataRelicInfo && !errorRelicInfo) {
|
if (query.data && !query.error) {
|
||||||
setAllMapRelicInfo(dataRelicInfo)
|
setMapRelicSet(query.data)
|
||||||
} else if (errorRelicInfo) {
|
} else if (query.error) {
|
||||||
toast.error("Failed to load relic info data")
|
toast.error("Failed to load RelicSet data")
|
||||||
}
|
}
|
||||||
|
}, [query.data, query.error, setMapRelicSet])
|
||||||
}, [dataRelicInfo, errorRelicInfo, setAllMapRelicInfo])
|
|
||||||
|
return query
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
import { AffixDetail } from '@/types';
|
|
||||||
import { create } from 'zustand'
|
|
||||||
|
|
||||||
interface AffixState {
|
|
||||||
mapMainAffix: Record<string, Record<string, AffixDetail>>;
|
|
||||||
mapSubAffix: Record<string, Record<string, AffixDetail>>;
|
|
||||||
setMapMainAffix: (newMainAffix: Record<string, Record<string, AffixDetail>>) => void;
|
|
||||||
setMapSubAffix: (newSubAffix: Record<string, Record<string, AffixDetail>>) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const useAffixStore = create<AffixState>((set) => ({
|
|
||||||
mapMainAffix: {},
|
|
||||||
mapSubAffix: {},
|
|
||||||
setMapMainAffix: (newMainAffix: Record<string, Record<string, AffixDetail>>) => set({ mapMainAffix: newMainAffix }),
|
|
||||||
setMapSubAffix: (newSubAffix: Record<string, Record<string, AffixDetail>>) => set({ mapSubAffix: newSubAffix }),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export default useAffixStore;
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
import { CharacterBasic, CharacterDetail, FilterAvatarType } from '@/types';
|
|
||||||
import { create } from 'zustand'
|
|
||||||
|
|
||||||
|
|
||||||
interface AvatarState {
|
|
||||||
listAvatar: CharacterBasic[];
|
|
||||||
listRawAvatar: CharacterBasic[];
|
|
||||||
filter: FilterAvatarType;
|
|
||||||
avatarSelected: CharacterBasic | null;
|
|
||||||
mapAvatarInfo: Record<string, CharacterDetail>;
|
|
||||||
skillSelected: string | null;
|
|
||||||
listElement: Record<string, boolean>;
|
|
||||||
listPath: Record<string, boolean>;
|
|
||||||
setListElement: (newListElement: Record<string, boolean>) => void;
|
|
||||||
setListPath: (newListPath: Record<string, boolean>) => void;
|
|
||||||
setListAvatar: (newListAvatar: CharacterBasic[]) => void;
|
|
||||||
setAvatarSelected: (newAvatarSelected: CharacterBasic) => void;
|
|
||||||
setFilter: (newFilter: FilterAvatarType) => void;
|
|
||||||
setMapAvatarInfo: (avatarId: string, newCharacter: CharacterDetail) => void;
|
|
||||||
setAllMapAvatarInfo: (newCharacter: Record<string, CharacterDetail>) => void;
|
|
||||||
setSkillSelected: (newSkillSelected: string | null) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const useAvatarStore = create<AvatarState>((set, get) => ({
|
|
||||||
listAvatar: [],
|
|
||||||
listRawAvatar: [],
|
|
||||||
filter: {
|
|
||||||
name: "",
|
|
||||||
path: [],
|
|
||||||
element: [],
|
|
||||||
rarity: [],
|
|
||||||
locale: "",
|
|
||||||
},
|
|
||||||
avatarSelected: null,
|
|
||||||
skillSelected: null,
|
|
||||||
mapAvatarInfo: {},
|
|
||||||
listElement: { "fire": false, "ice": false, "imaginary": false, "physical": false, "quantum": false, "thunder": false, "wind": false },
|
|
||||||
listPath: { "knight": false, "mage": false, "priest": false, "rogue": false, "shaman": false, "warlock": false, "warrior": false, "memory": false, elation: false },
|
|
||||||
setListElement: (newListElement: Record<string, boolean>) => set({ listElement: newListElement }),
|
|
||||||
setListPath: (newListPath: Record<string, boolean>) => set({ listPath: newListPath }),
|
|
||||||
setSkillSelected: (newSkillSelected: string | null) => set({ skillSelected: newSkillSelected }),
|
|
||||||
setListAvatar: (newListAvatar: CharacterBasic[]) => set({ listAvatar: newListAvatar, listRawAvatar: newListAvatar }),
|
|
||||||
setAvatarSelected: (newAvatarSelected: CharacterBasic) => set({ avatarSelected: newAvatarSelected }),
|
|
||||||
setFilter: (newFilter: FilterAvatarType) => {
|
|
||||||
set({ filter: newFilter })
|
|
||||||
if (newFilter.locale === "") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let filteredList = get().listRawAvatar;
|
|
||||||
if (newFilter.name) {
|
|
||||||
filteredList = filteredList.filter((avatar) => {
|
|
||||||
return avatar.lang?.[newFilter.locale]?.toLowerCase().includes(newFilter.name.toLowerCase()) ?? false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (newFilter.path.length > 0) {
|
|
||||||
filteredList = filteredList.filter((avatar) => {
|
|
||||||
return newFilter.path.some((path) => avatar.baseType?.toLowerCase().includes(path.toLowerCase())) ?? false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (newFilter.element.length > 0) {
|
|
||||||
filteredList = filteredList.filter((avatar) => {
|
|
||||||
return newFilter.element.some((element) => avatar.damageType?.toLowerCase().includes(element.toLowerCase())) ?? false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (newFilter.rarity.length > 0) {
|
|
||||||
filteredList = filteredList.filter((avatar) => {
|
|
||||||
return newFilter.rarity.some((rarity) => avatar.rank?.toLowerCase().includes(rarity.toLowerCase())) ?? false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
set({ listAvatar: filteredList });
|
|
||||||
},
|
|
||||||
setMapAvatarInfo: (avatarId: string, newCharacter: CharacterDetail) => set((state) => ({ mapAvatarInfo: { ...state.mapAvatarInfo, [avatarId]: newCharacter } })),
|
|
||||||
setAllMapAvatarInfo: (newCharacter: Record<string, CharacterDetail>) => set((state) => ({ mapAvatarInfo: { ...state.mapAvatarInfo, ...newCharacter } })),
|
|
||||||
}));
|
|
||||||
|
|
||||||
export default useAvatarStore;
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user