feedback2 WI: a running 坚固维式重锤 不死 window stops a dollkeeper's switch to its 替身 (fatal priority −99, the lock stays at −100); the switch flag is trait.dollSwitching; the 替身's own max HP is stated as sourced (review of #44)
This commit is contained in:
1 parent
29784db4d9
commit
cfe85c6fee
8 files changed
+62
-31
No files matched your search
+1
-1
@@ -248,7 +248,7 @@ Base stats (from data): `maxHp, atk, def, res` (0–100), `aspd` (100 base), `ba
|
||||
| `hit` | `{ source, target, dmg }` | before mitigation; mutate `dmg` (DamageInfo) |
|
||||
| `damaged` | `{ source, target, amount, type, dmg }` | after application |
|
||||
| `heal` | `{ source, target, amount }` | before application (mutable amount) |
|
||||
| `fatal` | `{ unit, source, credit, dmg, amount, prevented }` | HP would reach 0 — fired by every HP loss of a unit without a boss pool (hits of any type, element bursts, 无来源 damage, `loseHp`); set `prevented`. Order: kits' own savers (10 … −60) → items' 不死 (坚固维式重锤, once per deployment) `PRIO_REVIVE` −100 → items' 复活 (M3茧甲) `PRIO_RESPAWN` −101 → 埃芒加德 −110; the two 复活 revive in place and open a new deployment for the lock (§21.21) |
|
||||
| `fatal` | `{ unit, source, credit, dmg, amount, prevented }` | HP would reach 0 — fired by every HP loss of a unit without a boss pool (hits of any type, element bursts, 无来源 damage, `loseHp`); set `prevented`. Order: kits' own savers (10 … −60) → items' 不死 (坚固维式重锤, once per deployment) `PRIO_REVIVE` −100 → items' 复活 (M3茧甲) `PRIO_RESPAWN` −101 → 埃芒加德 −110; the two 复活 revive in place and open a new deployment for the lock (§21.21); a running 坚固 window's hook sits at `PRIO_UNDYING_HELD` −99, before a 傀儡师's switch to its 替身 (−100, ahead of the lock — a 本体 holding 不死 does not switch, §22.11) |
|
||||
| `kill` | `{ killer, victim }` | victim hp reached 0; a `killed` ability that keeps the enemy alive (a knock-out into a 重生 / form change, §21.4) hides it from every later handler, the kill count, credit and the bounty |
|
||||
| `death` | `{ unit }` | unit removed (ops may redeploy later); the `die` event and the death hooks fire only on removal |
|
||||
| `skillStart` / `skillEnd` | `{ unit, skill }` | |
|
||||
|
||||
+2
-2
@@ -677,7 +677,7 @@ registration order. `battle.off(handle)` / `battle.off(name, fn)` / `battle.offO
|
||||
| `elementHit` | `{ source, target, dmg }` | before a gauge fill (`dmg.type === 'element'`); mutate `dmg.amount`/`dmg.mul`, set `dmg.cancel` |
|
||||
| `damaged` | `{ source, target, amount, type, dmg, credit }` | after application (`amount` may be 0 when shielded); element fills too (with their source); 无来源: `source` null, `credit` set |
|
||||
| `heal` | `{ source, target, amount, opts }` | mutable `amount` |
|
||||
| `fatal` | `{ unit, source, credit, dmg, amount, prevented }` | HP would reach 0 — set `prevented` (substitutes, kit savers, 不死 / 复活 items, 埃芒加德; 不屈 is a `death` hook). Fired by every HP loss of a unit without a boss pool — hits of any type, element bursts, 无来源 damage, `loseHp` 流失. Order: kits' own savers (10 … −60) → items' 不死 (坚固维式重锤 — once per deployment: `items/battle.js deploymentOf`, a key every deploy changes and an in-place 复活 changes too; one battle-level hook holds the running windows (`holdsUndying`), so a window outlasts a lend, DESIGN §21.21 — `PRIO_REVIVE` −100; substitutes −100) → items' 复活 (M3茧甲, `PRIO_RESPAWN` −101: PRTS "复活" acts on a knock-out, which a 不死 prevents) → 埃芒加德 (−110); both 复活 revive in place and call `revivedInPlace` (a new deployment for the lock) |
|
||||
| `fatal` | `{ unit, source, credit, dmg, amount, prevented }` | HP would reach 0 — set `prevented` (substitutes, kit savers, 不死 / 复活 items, 埃芒加德; 不屈 is a `death` hook). Fired by every HP loss of a unit without a boss pool — hits of any type, element bursts, 无来源 damage, `loseHp` 流失. Order: kits' own savers (10 … −60) → items' 不死 (坚固维式重锤 — once per deployment: `items/battle.js deploymentOf`, a key every deploy changes and an in-place 复活 changes too; one battle-level hook holds the running windows (`holdsUndying`), so a window outlasts a lend, DESIGN §21.21 — the lock `PRIO_REVIVE` −100 after the substitutes (−100, registered first), the running windows `PRIO_UNDYING_HELD` −99 before them: a 傀儡师 holding 不死 does not switch, PRTS 分支特性信息 傀儡师 "未持有不死的情况下", DESIGN §22.11) → items' 复活 (M3茧甲, `PRIO_RESPAWN` −101: PRTS "复活" acts on a knock-out, which a 不死 prevents) → 埃芒加德 (−110); both 复活 revive in place and call `revivedInPlace` (a new deployment for the lock) |
|
||||
| `dollSwitch` | `{ unit, reason, done }` | content switches a 傀儡师 to its <替身> now (归溟幽灵鲨 S2 "技能结束后立刻切换为<替身>": no lethal HP loss); its trait does it unless it already is one or is not on the field, and sets `done` |
|
||||
| `kill` | `{ killer, victim }` | victim HP reached 0 (a handler may revive by restoring HP) |
|
||||
| `death` | `{ unit, reason:'killed'|'leak'|'retreat'|'merchant'|'expired'|'forcedExit', killer }` | unit removed (`'forcedExit'`: an operator entering 联防 knocked out, §1.1) |
|
||||
@@ -1098,7 +1098,7 @@ table: `hitSleep` (targets and damages sleeping enemies — "可以攻击沉睡
|
||||
| agent / hookmaster | can hit FLY (ranged reach); hook displacement comes from skills (generic: `force`) |
|
||||
| bearer | block 0 while the skill is active |
|
||||
| alchemist | ranged lob, can hit FLY |
|
||||
| dollkeeper | fatal damage (no 不死) ⇒ a 1 s switch animation [ASSUMED length] (无敌, 不死, 阻回, 禁疗, 孤立, 缴械, 眩晕 / 冻结 / 睡眠 immune; ends the running skill and removes the statuses), then the <替身> for 20 s (bb duration): block 0 from the switch on, 阻回, HP = its own 替身 token's (风丸 纸偶), else its own max HP (归溟幽灵鲨 [ASSUMED]); then the switch back (the same animation) at full HP; dies if the 替身 dies. Kit flag `dollNoAttack` (归溟幽灵鲨): the 替身 makes no normal attack and casts no skill. Hook `dollSwitch` switches it at once (归溟幽灵鲨 S2's end). Model form `'doll'` (PRTS 分支特性信息 傀儡师, DESIGN §22.11) |
|
||||
| dollkeeper | fatal damage (no 不死 — a running 坚固维式重锤 window comes first) ⇒ a 1 s switch animation [ASSUMED length, also on a direct switch] (无敌, 不死, 阻回, 禁疗, 孤立, 缴械, 眩晕 / 冻结 / 睡眠 immune; ends the running skill and removes the statuses), then the <替身> for 20 s (bb duration): block 0 from the switch on, 阻回, HP = its own 替身 token's (风丸 纸偶), else its own max HP (归溟幽灵鲨: PRTS resets it "至最大值", the trait's 替身 HP bonus `max_hp` is 0, 风丸's 纸偶 has her HP); then the switch back (the same animation) at full HP; dies if the 替身 dies. Kit flag `dollNoAttack` (归溟幽灵鲨): the 替身 makes no normal attack and casts no skill. Hook `dollSwitch` switches it at once (归溟幽灵鲨 S2's end). Model form `'doll'` (PRTS 分支特性信息 傀儡师, DESIGN §22.11) |
|
||||
| geek | loses 1–3 % max HP per second (bb hp_ratio), never lethal on its own |
|
||||
| merchant | −3 DP every 3 s (bb cost/interval); retreats when DP runs out |
|
||||
| skywalker | can block FLY enemies (蒂比's kit: only while airborne — 起飞, flag `liftoff`, which also releases the ground enemies she blocked) |
|
||||
|
||||
@@ -20,8 +20,10 @@
|
||||
// either quality satisfies them; lent items count as carried. Both are evaluated live, at the moment of use.
|
||||
//
|
||||
// Hook priorities (non-default): 'fatal' — consumable death savers run LAST, so a skill's / talent's own undying (kits
|
||||
// use 10 … −60) never wastes a charge: 坚固维式重锤's lock (异常效果 不死, once per deployment — deploymentOf; its running
|
||||
// windows held by one battle-level hook) at PRIO_REVIVE −100, then the M3茧甲 revive at PRIO_RESPAWN −101 — PRTS
|
||||
// use 10 … −60) never wastes a charge: 坚固维式重锤's lock (异常效果 不死, once per deployment — deploymentOf) at
|
||||
// PRIO_REVIVE −100 and its running windows, held by one battle-level hook, at PRIO_UNDYING_HELD −99 — before a 傀儡师's
|
||||
// switch to its 替身 (professions.js, −100: PRTS 分支特性信息 傀儡师 "受到足以致命的伤害且未持有不死的情况下"), while the
|
||||
// lock itself still comes after that switch (as in 0.1.1); then the M3茧甲 revive at PRIO_RESPAWN −101 — PRTS
|
||||
// 卫戍协议:盟约 下半/PRTS盟约记录 备注 "“复活”的实现方式为:受益者因移动之外的原因退场时下次部署的再部署时间和费用归零": a
|
||||
// revive acts on a knock-out, which a 不死 prevents, so the lock always comes first whatever the equip order (player
|
||||
// report F1 after 0.1.0: with the 茧甲 equipped first the revive ran first and the first lethal hit showed no lock);
|
||||
@@ -65,6 +67,8 @@ const chance = (battle, p) => p > 0 && (p >= 1 || battle.rng() < p);
|
||||
const ENEMY_RECORD = /^enemy_/;
|
||||
/** 'fatal' priorities (see header). */
|
||||
export const PRIO_REVIVE = -100;
|
||||
/** A running 坚固 window (不死 held): before a 傀儡师's switch to its 替身 at −100 — see header. */
|
||||
export const PRIO_UNDYING_HELD = PRIO_REVIVE + 1;
|
||||
/** The items' 复活 (M3茧甲): acts on a knock-out, so after every 不死 (PRIO_REVIVE) — see header. */
|
||||
export const PRIO_RESPAWN = PRIO_REVIVE - 1;
|
||||
const PRIO_FREE_UNDYING = 20;
|
||||
@@ -277,9 +281,10 @@ function hammerAcquire(battle, rt, u) {
|
||||
const hs = hammerState(battle, rt, u);
|
||||
hs.refs++;
|
||||
if (hs.scope) return hs;
|
||||
// every running 坚固 window of the battle (holdsUndying): one hook that no grant owns, registered before any lock hook
|
||||
// every running 坚固 window of the battle (holdsUndying): one hook that no grant owns, ahead of every lock hook and of a
|
||||
// 傀儡师's switch to its 替身 (PRIO_UNDYING_HELD: a 本体 holding 不死 does not switch)
|
||||
if (!rt.undyingHook) {
|
||||
rt.undyingHook = battle.on('fatal', (c) => { if (!c.prevented && holdsUndying(battle, c.unit)) c.prevented = true; }, { priority: PRIO_REVIVE });
|
||||
rt.undyingHook = battle.on('fatal', (c) => { if (!c.prevented && holdsUndying(battle, c.unit)) c.prevented = true; }, { priority: PRIO_UNDYING_HELD });
|
||||
}
|
||||
const S = new Scope(battle, u, `item:hammer#${u.id}`);
|
||||
hs.scope = S;
|
||||
|
||||
@@ -1700,11 +1700,11 @@ const KITS = {
|
||||
{ install(battle, unit) { // 拥抱自我 (the 替身 fighting: not during a switch animation)
|
||||
const slow = num(t0.move_speed), scale = num(t0.atk_scale);
|
||||
whileOn(battle, unit, AURA_IV, () => {
|
||||
if (!unit.trait.doll || unit.trait.dollSwitch || !slow) return;
|
||||
if (!unit.trait.doll || unit.trait.dollSwitching || !slow) return;
|
||||
for (const e of battle.unitsInGrid(unit, aroundGrid, { side: 'enemy' })) if (!e.isFlying) battle.addBuff(e, { key: 'ghost2:embrace', duration: AURA_DUR, mods: { moveMul: Math.max(0, 1 + slow) } });
|
||||
});
|
||||
whileOn(battle, unit, 1, () => {
|
||||
if (!unit.trait.doll || unit.trait.dollSwitch || !(scale > 0)) return;
|
||||
if (!unit.trait.doll || unit.trait.dollSwitching || !(scale > 0)) return;
|
||||
for (const e of battle.unitsInGrid(unit, aroundGrid, { side: 'enemy' })) if (!e.isFlying) battle.dealDamage(unit, e, { amount: unit.s.atk * scale, type: 'arts', tags: ['talent', 'embrace'] });
|
||||
});
|
||||
} },
|
||||
|
||||
+19
-13
@@ -93,10 +93,12 @@ const installCharger = (battle, unit) => {
|
||||
|
||||
/**
|
||||
* 傀儡师 (dollkeeper) trait "受到致命伤时不撤退,切换成<替身>作战(替身阻挡数为0),持续20秒后自身再次替换<替身>" with the
|
||||
* branch rules of PRTS 分支特性信息 傀儡师: a lethal hit on the <本体> (one it survives with 不死 does not count) starts a
|
||||
* switch animation, after which the operator fights as its <替身> for the trait's 20 s (bb duration) holding 阻回; then
|
||||
* it switches back the same way. A switch animation clears the operator's Buffs and resets its HP to the max of the form
|
||||
* it switches to; during one the operator holds 不死, 无敌, 阻回, 禁疗, 孤立, 强制缴械 and is immune to 眩晕 / 冻结 / 睡眠.
|
||||
* branch rules of PRTS 分支特性信息 傀儡师: a lethal hit on the <本体> (one it survives with 不死 does not count: "受到足以致命的
|
||||
* 伤害且未持有不死的情况下" — a skill's / talent's own 不死 runs earlier, a running 坚固维式重锤 window just before this hook,
|
||||
* items/battle.js PRIO_UNDYING_HELD −99) starts a switch animation, after which the operator fights as its <替身> for the
|
||||
* trait's 20 s (bb duration) holding 阻回; then it switches back the same way. A switch animation clears the operator's
|
||||
* Buffs and resets its HP to the max of the form it switches to; during one the operator holds 不死, 无敌, 阻回, 禁疗, 孤立,
|
||||
* 强制缴械 and is immune to 眩晕 / 冻结 / 睡眠.
|
||||
* The 替身 blocks nothing from the start of the switch to it until the switch back starts; a lethal hit on it knocks the
|
||||
* operator out. Content may switch the operator at once (hook `dollSwitch` { unit, reason, done }: 归溟幽灵鲨 S2 "技能结束
|
||||
* 后立刻切换为<替身>"). A kit's `dollNoAttack` profile flag (归溟幽灵鲨, PRTS 特性备注 "<替身>不进行普通攻击") disarms the 替身
|
||||
@@ -104,10 +106,14 @@ const installCharger = (battle, unit) => {
|
||||
* 技能范围 — likewise); 风丸's 替身 attacks (PRTS: "<替身>状态下可对空").
|
||||
* The form is the unit's model state (`unit.form` 'doll', snapshot.js unitInfo; fx 'substitute' / 'swap' / 'dollEnd' with
|
||||
* `form`, shared/protocol.js fxForm): the client draws the 替身 on the skeleton's *_B clips (render/units.js FORMS).
|
||||
* The 替身's max HP is the operator's own (PRTS: HP "重设…至最大值"; the trait blackboard's 替身 HP bonus `max_hp` is 0 for
|
||||
* both 傀儡师 — the elite PUM-Y module's +20 % aside — and 风丸's 纸偶 token has her max HP at every level), or its 替身
|
||||
* token's (风丸).
|
||||
* [ASSUMED]: a switch animation lasts DOLL_SWITCH (the skeletons' 1 s Start_B — the 替身 appearing — and Start_2 — the
|
||||
* 本体 back); "清除自身一切Buff" ends the running skill and removes the statuses (buffs with a catalogue `status`) — the
|
||||
* remake's bond / item / talent effects are buffs too and stay; a dollkeeper without a 替身 token of its own (归溟幽灵鲨) keeps
|
||||
* its own max HP (PRTS: HP "重设…至最大值"; character_table has no 替身 of hers, and 风丸's 纸偶 token has her HP).
|
||||
* 本体 back), also on a direct switch (`dollSwitch`); "清除自身一切Buff" ends the running skill and removes the statuses
|
||||
* (buffs with a catalogue `status`) — every other buff stays, the remake's bond / item / talent effects (buffs too)
|
||||
* and other units' timed buffs and shields alike; 无敌 is damage immunity only (as for every ally 无敌 here — PRTS's
|
||||
* "无法被不同阵营选中" is not modelled for it).
|
||||
*/
|
||||
export const DOLL_SWITCH = 1;
|
||||
const DOLL_SWITCH_IMMUNE = new Set(['stun', 'freeze', 'sleep']);
|
||||
@@ -127,10 +133,10 @@ const installDollkeeper = (battle, unit) => {
|
||||
const sk = unit.skill;
|
||||
if (sk && sk.active && sk.kind !== 'passive') sk.end('substitute');
|
||||
for (const b of unit.buffs.slice()) if (b.status) battle.removeBuff(unit, b);
|
||||
unit.trait.dollSwitch = true;
|
||||
const done = () => { unit.trait.dollSwitch = false; };
|
||||
unit.trait.dollSwitching = true;
|
||||
const done = () => { unit.trait.dollSwitching = false; };
|
||||
battle.addBuff(unit, {
|
||||
key: 'trait:dollSwitch', duration: DOLL_SWITCH, onExpire: done, onRemove: done,
|
||||
key: 'trait:dollSwitching', duration: DOLL_SWITCH, onExpire: done, onRemove: done,
|
||||
flags: { invulnerable: true, noSp: true, noHeal: true, isolated: true, disarm: true },
|
||||
});
|
||||
};
|
||||
@@ -166,18 +172,18 @@ const installDollkeeper = (battle, unit) => {
|
||||
};
|
||||
battle.on('fatal', (ctx) => {
|
||||
if (ctx.unit !== unit || ctx.prevented) return;
|
||||
if (unit.trait.dollSwitch) { ctx.prevented = true; return; } // 不死 while switching (a 流失 ignores 无敌)
|
||||
if (unit.trait.dollSwitching) { ctx.prevented = true; return; } // 不死 while switching (a 流失 ignores 无敌)
|
||||
if (unit.trait.doll) return; // the 替身 is knocked out
|
||||
ctx.prevented = enter();
|
||||
}, { owner: unit, priority: -100 });
|
||||
battle.on('dollSwitch', (ctx) => { if (ctx.unit === unit && !ctx.done) ctx.done = enter(); }, { owner: unit });
|
||||
battle.on('beforeStatus', (ctx) => {
|
||||
if (ctx.target === unit && unit.trait.dollSwitch && DOLL_SWITCH_IMMUNE.has(ctx.status)) ctx.cancel = true;
|
||||
if (ctx.target === unit && unit.trait.dollSwitching && DOLL_SWITCH_IMMUNE.has(ctx.status)) ctx.cancel = true;
|
||||
}, { owner: unit });
|
||||
battle.on('death', (ctx) => {
|
||||
if (ctx.unit !== unit) return;
|
||||
unit.trait.doll = false;
|
||||
unit.trait.dollSwitch = false;
|
||||
unit.trait.dollSwitching = false;
|
||||
// knocked out as the 替身: its death clip has played (the 'die' event came first); the redeploy is the 本体 again
|
||||
if (unit.form) { unit.form = null; battle.fx('dollEnd', at({ form: null })); }
|
||||
}, { owner: unit });
|
||||
|
||||
@@ -9,6 +9,7 @@ import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { makeBattle, enemyRec, checkInvariants } from '../helpers/battleHarness.js';
|
||||
import { DOLL_SWITCH } from '../../server/sim/professions.js';
|
||||
import { holdsUndying } from '../../server/sim/content/items/battle.js';
|
||||
import { hasGeneratedData } from '../../server/sim/simdata.js';
|
||||
import { fxForm } from '../../shared/protocol.js';
|
||||
|
||||
@@ -18,11 +19,11 @@ const approx = (a, b, msg, eps = 1e-6) => assert.ok(Math.abs(a - b) <= eps, `${m
|
||||
const dummy = () => enemyRec({ key: 'enemy_dummy', hp: 1e9, atk: 0, speed: 0 });
|
||||
|
||||
/** 归溟幽灵鲨 on (9,5) facing right; a harmless parked enemy on (9,6) (in her range) unless `enemy` is false. */
|
||||
function setup({ id = G, skillIndex = 1, sp = 0, enemy = true, capture = false } = {}) {
|
||||
function setup({ id = G, skillIndex = 1, sp = 0, enemy = true, capture = false, items = undefined } = {}) {
|
||||
const h = makeBattle({
|
||||
seed: 4, timeLimit: 300, autoFinish: false, captureNoisy: capture,
|
||||
defs: { enemies: { enemy_dummy: dummy() } },
|
||||
units: [{ chessId: id, row: 9, col: 5, dir: 'RIGHT', skillIndex, carryState: { sp } }],
|
||||
units: [{ chessId: id, row: 9, col: 5, dir: 'RIGHT', skillIndex, carryState: { sp }, items }],
|
||||
enemies: enemy ? [{ key: 'enemy_dummy', pos: [9, 6] }] : [],
|
||||
});
|
||||
h.step();
|
||||
@@ -46,7 +47,7 @@ test('A: a lethal hit with no skill running ⇒ the 替身 (form, full HP, block
|
||||
lethal();
|
||||
assert.ok(g.alive && g.trait.doll && g.form === 'doll', `${id}/${skillIndex}: the 替身`);
|
||||
approx(g.hp, g.s.maxHp, 'full HP');
|
||||
approx(g.s.maxHp, full * (1 + (g.def.traitBb.max_hp || 0)), 'her own max HP (no 替身 token) [ASSUMED]');
|
||||
approx(g.s.maxHp, full * (1 + (g.def.traitBb.max_hp || 0)), 'her own max HP (no 替身 token; trait bb max_hp the module bonus)');
|
||||
assert.equal(g.s.blockCnt, 0);
|
||||
const [sub] = fx('substitute');
|
||||
assert.ok(sub && fxForm(sub) === 'doll', 'the fx puts the model in its 替身 form');
|
||||
@@ -58,7 +59,7 @@ test('A: a lethal hit with no skill running ⇒ the 替身 (form, full HP, block
|
||||
assert.ok(e.findBuff('ghost2:embrace'), '拥抱自我 slows');
|
||||
assert.ok(h.hooksOf('damaged').some((c) => c.source === g && c.target === e && (c.dmg.tags || []).includes('embrace')), '…and deals arts damage');
|
||||
h.run(1.05);
|
||||
assert.ok(!g.trait.doll && g.form === null && g.trait.dollSwitch, 'switching back after 1 s + 20 s');
|
||||
assert.ok(!g.trait.doll && g.form === null && g.trait.dollSwitching, 'switching back after 1 s + 20 s');
|
||||
assert.ok(fxForm(fx('swap')[0]) === null, 'the fx gives the 本体 its model back');
|
||||
assert.equal(g.s.blockCnt, 2);
|
||||
approx(g.hp, g.s.maxHp, 'full HP again');
|
||||
@@ -157,6 +158,25 @@ test('S1 / S3 running at a lethal hit end with the switch (清除自身一切Buf
|
||||
assert.equal(s2.h.b.emit('dollSwitch', { unit: s2.g, reason: 'test', done: false }).done, false, 'already the 替身');
|
||||
});
|
||||
|
||||
test('a 本体 holding 不死 (a running 坚固维式重锤 window) does not switch: held at 1 HP (PRTS "受到足以致命的伤害且未持有不死的情况下")', { skip }, () => {
|
||||
const { h, g, lethal } = setup({ items: ['chess_item_3_09_e_a'] });
|
||||
lethal();
|
||||
assert.ok(g.trait.doll && !holdsUndying(h.b, g), 'the first lethal hit: the switch, the hammer\'s lock unspent');
|
||||
h.run(15);
|
||||
lethal();
|
||||
assert.ok(g.alive && g.trait.doll && holdsUndying(h.b, g), 'the 替身\'s lethal hit sets off the lock: 8 s of 不死');
|
||||
approx(g.hp, 1, 'held at 1 HP');
|
||||
assert.ok(h.runUntil(() => !g.trait.doll && !g.trait.dollSwitching, 10), 'switched back');
|
||||
assert.ok(holdsUndying(h.b, g), 'the window still runs');
|
||||
lethal();
|
||||
assert.ok(g.alive && !g.trait.doll && g.form === null, 'no switch while it holds 不死');
|
||||
approx(g.hp, 1, 'held at 1 HP');
|
||||
assert.ok(h.runUntil(() => !holdsUndying(h.b, g), 10));
|
||||
lethal();
|
||||
assert.ok(g.alive && g.trait.doll, 'the window over: the switch');
|
||||
checkInvariants(h.b);
|
||||
});
|
||||
|
||||
test('风丸: her 替身 still attacks (PRTS "<替身>状态下可对空"); the same 替身 form, switch and 阻回', { skip }, () => {
|
||||
const id = 'chess_char_2_11_a';
|
||||
const { h, g, log, lethal } = setup({ id, skillIndex: 0 });
|
||||
|
||||
@@ -260,7 +260,7 @@ test('dollkeeper without a substitute token (归溟幽灵鲨) keeps its own max
|
||||
const full = u.s.maxHp;
|
||||
h.b.dealDamage(null, u, { amount: 1e7, type: 'true' });
|
||||
assert.ok(u.alive && u.hasBuff('trait:substitute'));
|
||||
// PRTS 分支特性信息 傀儡师 "重设自身生命至最大值"; no 替身 of hers in character_table [ASSUMED: her own max HP]
|
||||
// PRTS 分支特性信息 傀儡师 "重设自身生命至最大值"; the trait's 替身 HP bonus (bb max_hp) is 0 and 风丸's 纸偶 has her own HP
|
||||
approx(u.s.maxHp, full, 1e-6);
|
||||
approx(u.hp, full, 1e-6);
|
||||
});
|
||||
|
||||
@@ -166,7 +166,7 @@ test('dollkeeper: fatal damage ⇒ a 1 s switch, 20 s as the substitute (block 0
|
||||
h.b.dealDamage(null, u, { amount: 1e5, type: 'true' });
|
||||
assert.equal(u.alive, true, 'substitute instead of death');
|
||||
assert.equal(u.s.blockCnt, 0);
|
||||
assert.ok(u.trait.doll && u.trait.dollSwitch, 'the switch animation');
|
||||
assert.ok(u.trait.doll && u.trait.dollSwitching, 'the switch animation');
|
||||
assert.equal(u.form, 'doll');
|
||||
approx(u.hp, 2000); // no 替身 token: its own max HP
|
||||
// the switch animation (PRTS 分支特性信息 傀儡师): 无敌, 不死 (a 流失 stops at 1 HP), 阻回, 眩晕 immunity
|
||||
@@ -177,15 +177,15 @@ test('dollkeeper: fatal damage ⇒ a 1 s switch, 20 s as the substitute (block 0
|
||||
assert.ok(u.alive && u.trait.doll && u.hp >= 1, '不死');
|
||||
u.hp = 2000;
|
||||
h.run(1.05);
|
||||
assert.ok(u.trait.doll && !u.trait.dollSwitch, 'fighting as the substitute');
|
||||
assert.ok(u.trait.doll && !u.trait.dollSwitching, 'fighting as the substitute');
|
||||
h.run(19.9);
|
||||
assert.ok(u.trait.doll && u.s.flags.noSp, 'the 20 s form, 阻回');
|
||||
h.run(0.1);
|
||||
assert.ok(!u.trait.doll && u.trait.dollSwitch && u.form === null, 'switching back');
|
||||
assert.ok(!u.trait.doll && u.trait.dollSwitching && u.form === null, 'switching back');
|
||||
assert.equal(u.s.blockCnt, 2, 'blocks again from the start of the switch back');
|
||||
approx(u.hp, 2000);
|
||||
h.run(1.05);
|
||||
assert.ok(!u.trait.dollSwitch && !u.s.flags.noSp, 'the body again, no 阻回');
|
||||
assert.ok(!u.trait.dollSwitching && !u.s.flags.noSp, 'the body again, no 阻回');
|
||||
h.b.dealDamage(null, u, { amount: 1e5, type: 'true' });
|
||||
h.run(1.05);
|
||||
h.b.dealDamage(null, u, { amount: 1e5, type: 'true' });
|
||||
|
||||
Reference in new issue
Block a user