mirror of
https://github.com/Shu-Ying/noname-shuying.git
synced 2026-10-03 02:20:01 +08:00
162 lines
7.0 KiB
JavaScript
162 lines
7.0 KiB
JavaScript
// 只呈现已经选定的预测技能;不从手牌或原生技能猜测意图。
|
||
import { attackHitCount, previewAttackDamage } from "../battle/intent-damage.js";
|
||
import { isDeathBlowIntent } from "../battle/death-blow.js";
|
||
import { isStunIntent } from "../battle/stun-intent.js";
|
||
|
||
export function attackIconForDamage(damage) {
|
||
if (!Number.isInteger(damage) || damage < 0) {
|
||
throw new RangeError("预测伤害必须是非负整数");
|
||
}
|
||
if (damage < 5) return 1;
|
||
if (damage < 10) return 2;
|
||
if (damage < 20) return 3;
|
||
if (damage < 40) return 4;
|
||
return 5;
|
||
}
|
||
|
||
export function mountEnemyIntent(player, session, assetBase, document,
|
||
getTarget = () => null) {
|
||
const badge = document.createElement("div");
|
||
badge.className = "mengsan-intent-shuying";
|
||
badge.hidden = true;
|
||
badge.setAttribute("aria-live", "polite");
|
||
badge.setAttribute("aria-atomic", "true");
|
||
const damageLine = document.createElement("span");
|
||
damageLine.className = "mengsan-intent-damage-shuying";
|
||
const amount = document.createElement("span");
|
||
amount.className = "mengsan-intent-amount-shuying";
|
||
const icon = document.createElement("img");
|
||
icon.className = "mengsan-intent-icon-shuying";
|
||
icon.alt = "";
|
||
icon.decoding = "async";
|
||
damageLine.append(icon, amount);
|
||
const debuffLine = document.createElement("span");
|
||
debuffLine.className = "mengsan-intent-debuff-shuying";
|
||
const debuffIcon = document.createElement("img");
|
||
debuffIcon.className = "mengsan-intent-icon-shuying";
|
||
debuffIcon.alt = "";
|
||
debuffIcon.decoding = "async";
|
||
debuffIcon.src = `${assetBase}/assets/intent/Intent_debuff.png`;
|
||
const debuffAmount = document.createElement("span");
|
||
debuffLine.append(debuffIcon, debuffAmount);
|
||
const blockLine = document.createElement("span");
|
||
blockLine.className = "mengsan-intent-block-shuying";
|
||
const blockIcon = document.createElement("img");
|
||
blockIcon.className = "mengsan-intent-icon-shuying";
|
||
blockIcon.alt = "";
|
||
blockIcon.decoding = "async";
|
||
blockIcon.src = `${assetBase}/assets/intent/Intent_defend.png`;
|
||
const blockAmount = document.createElement("span");
|
||
blockLine.append(blockIcon, blockAmount);
|
||
const buffLine = document.createElement("span");
|
||
buffLine.className = "mengsan-intent-buff-shuying";
|
||
const buffIcon = document.createElement("img");
|
||
buffIcon.className = "mengsan-intent-icon-shuying";
|
||
buffIcon.alt = "";
|
||
buffIcon.decoding = "async";
|
||
buffIcon.src = `${assetBase}/assets/intent/Intent_buff.png`;
|
||
const buffAmount = document.createElement("span");
|
||
buffLine.append(buffIcon, buffAmount);
|
||
badge.append(damageLine, debuffLine, blockLine, buffLine);
|
||
player.appendChild(badge);
|
||
let currentIntent = null;
|
||
let timer;
|
||
session.ownResource(badge, () => {
|
||
clearInterval(timer);
|
||
badge.remove();
|
||
});
|
||
const refresh = () => {
|
||
const intent = currentIntent;
|
||
if (!intent) {
|
||
badge.hidden = true;
|
||
return;
|
||
}
|
||
if (isStunIntent(intent)) {
|
||
damageLine.hidden = false;
|
||
debuffLine.hidden = true;
|
||
blockLine.hidden = true;
|
||
buffLine.hidden = true;
|
||
amount.textContent = "";
|
||
const src = `${assetBase}/assets/intent/Intent_stun.png`;
|
||
if (icon.getAttribute("src") !== src) icon.src = src;
|
||
if (badge.getAttribute("aria-label") !== "Stunned") {
|
||
badge.setAttribute("aria-label", "Stunned");
|
||
}
|
||
badge.hidden = false;
|
||
return;
|
||
}
|
||
const hasDamage = intent.damage != null;
|
||
const deathBlow = isDeathBlowIntent(intent);
|
||
if (deathBlow && !hasDamage) {
|
||
throw new TypeError("濒死一击必须有伤害数值");
|
||
}
|
||
damageLine.hidden = !hasDamage;
|
||
const hasDebuff = typeof intent.debuff === "string" &&
|
||
Number.isInteger(intent.stacks) && intent.stacks > 0;
|
||
debuffLine.hidden = !hasDebuff;
|
||
if (hasDebuff) {
|
||
const debuffText = `×${intent.stacks}`;
|
||
if (debuffAmount.textContent !== debuffText) {
|
||
debuffAmount.textContent = debuffText;
|
||
}
|
||
}
|
||
const hasBlock = Number.isInteger(intent.block) && intent.block > 0;
|
||
const hasStrength = Number.isInteger(intent.strength) && intent.strength > 0;
|
||
blockLine.hidden = !hasBlock;
|
||
buffLine.hidden = !hasStrength;
|
||
if (hasBlock) blockAmount.textContent = String(intent.block);
|
||
if (hasStrength) {
|
||
buffAmount.textContent = `+${intent.strength}`;
|
||
}
|
||
const effects = [];
|
||
if (hasDamage) {
|
||
const hits = attackHitCount(intent);
|
||
if (deathBlow && hits !== 1) {
|
||
throw new RangeError("濒死一击只能造成一次伤害");
|
||
}
|
||
const target = getTarget();
|
||
const targets = Array.isArray(target) ? target : [target];
|
||
const damages = (targets.length ? targets : [null]).map(
|
||
current => previewAttackDamage(intent, player, current));
|
||
const damage = Math.max(...damages);
|
||
const minimum = Math.min(...damages);
|
||
const range = minimum === damage ? String(damage) :
|
||
`${minimum}~${damage}`;
|
||
const tier = deathBlow ? null :
|
||
attackIconForDamage(damage * hits);
|
||
const damageText = hits > 1 ? `${range}×${hits}` : range;
|
||
if (amount.textContent !== damageText) {
|
||
amount.textContent = damageText;
|
||
}
|
||
const src = deathBlow ?
|
||
`${assetBase}/assets/intent/Intent_death_blow.png` :
|
||
`${assetBase}/assets/intent/Intent_attack_${tier}.png`;
|
||
if (icon.getAttribute("src") !== src) icon.src = src;
|
||
effects.push(`${deathBlow ? "Death Blow" : "Attack"}: ${range} damage${hits > 1 ? ` × ${hits} hits` : ""}`);
|
||
}
|
||
if (hasDebuff) {
|
||
const label = intent.debuff === "脆弱" ? "Frail" :
|
||
intent.debuff === "易伤" ? "Vulnerable" : "Debuff";
|
||
effects.push(`${label} ×${intent.stacks}`);
|
||
}
|
||
if (hasBlock) effects.push(`Block ${intent.block}`);
|
||
if (hasStrength) effects.push(`Strength +${intent.strength}`);
|
||
if (!effects.length) {
|
||
damageLine.hidden = false;
|
||
amount.textContent = "";
|
||
const src = `${assetBase}/assets/intent/Intent_unknown.png`;
|
||
if (icon.getAttribute("src") !== src) icon.src = src;
|
||
effects.push("Unknown");
|
||
}
|
||
const label = effects.join(", ");
|
||
badge.title = "攻击与负面效果作用于所有存活敌对角色;伤害按各目标状态计算,护甲另行抵扣。";
|
||
if (badge.getAttribute("aria-label") !== label) badge.setAttribute("aria-label", label);
|
||
badge.hidden = false;
|
||
};
|
||
timer = setInterval(refresh, 100);
|
||
return intent => {
|
||
currentIntent = intent;
|
||
refresh();
|
||
};
|
||
}
|