feedback5: merge R19G — 蕾缪安's S3 waits with its bullets and locks when nothing is in her range instead of ending (skill text 「打完后结束」, PRTS 卫戍协议/帮助 「通常不会自动关闭技能」; community report); golden as R19G's commit (roster-018, boss-boss_3-pair, coop2-NORMAL-14-standins), the merged tree's golden equals the branch's
This commit is contained in:
commit
6b05295fab
11 files changed
+286
-103
No files matched your search
@@ -70,6 +70,7 @@
|
||||
- 自制投石机「索敌不受阻挡影响」:被站在排气格栅上(隐匿)的干员阻挡、范围内又没有别的干员时不会攻击,和原版一样;其他敌人仍会攻击阻挡自己的隐匿干员(社区反馈)。
|
||||
- 升级调度中心后,新增的商品栏位立即放上新卡,不用等下一次刷新(社区反馈;官方教程「升级后将出现更多的商品栏位」)。
|
||||
- 被冻结或晕眩的敌人仍会被接触到的干员阻挡:干员再部署到它身边、或阻挡数空出来时会立即挡住它,隐匿的敌人也随之破隐,不用再等冻结或晕眩结束;沉睡、浮空、恐惧和不可阻挡的敌人仍然不会被阻挡(GitHub #232,感谢 @ccyl、@kukiC)。
|
||||
- 蕾缪安三技能锁定时攻击范围里没有敌人,技能不会再自动结束:剩余子弹和已有的锁定都保留,下一个敌人进入范围就立即继续锁定,子弹打完才结束并开始轰炸,等待期间锁定标记一直显示(社区反馈;弹药类技能打完才结束,PRTS 卫戍协议帮助「通常不会自动关闭技能」)。
|
||||
|
||||
### 界面
|
||||
|
||||
|
||||
@@ -1980,3 +1980,50 @@ here, each reproduced first; the rest were answered (not a bug, already fixed in
|
||||
the parent), `test/match/playtest6-matchflow.test.js` #235 (2; the 联防 one fails on the parent: losses = the LP
|
||||
settled, no result ticker, gone in the next phase), SP_E2E `test/ui/resultbox.e2e.test.js` (3, mock variants
|
||||
`unite[,through][,helper|,dead]`). Docs: PLAYING.md §1 / §5, design/network.md, design/client.md, design/match.md.
|
||||
|
||||
#### 25.22.8 Community report 「蕾缪安3技能范围里没人好像会自动结束」 (relayed by the owner): 蕾缪安's S3 waits with its bullets while nothing is in her range — `kits/ops/chess_char_6_01-lemuen.js`, `render/fx/locks.js`
|
||||
|
||||
- **Seen** (a script on the parent 0255afd: S3 cast with 5 bullets, the locked enemy killed by someone else after 2
|
||||
locks, another enemy alive outside her range): the skill ended at the next lock tick
|
||||
(1.00 s, `skillEnd` reason `ammo`), 3 bullets thrown away, 2 shells; an enemy that came into her range 3 s later found
|
||||
it over. A locked walker leaving her range ended it the same way (4 locks, 1 bullet thrown away). In golden the elite
|
||||
蕾缪安 of roster-018 ended three of her five casts early (2 / 6, 5 / 9 and 6 / 9 bullets left).
|
||||
- **Official**: the skill text 「每0.5秒消耗1发子弹依次锁定敌人……攻击装有5发弹药,打完后结束(可随时停止技能)」: an ammo
|
||||
skill ends when its bullets are spent or when the player stops it. PRTS 卫戍协议/帮助 §作战阶段 技能操作: 「可执行的技能
|
||||
操作包括开启/关闭技能,通常不会自动关闭技能」 — the only automatic close is 玛露希尔's 召唤使魔; 蕾缪安's S3 is on the
|
||||
basic strategy. The client (charpack char_4193_lemuen, battle/buff_template_data.json): the S3 mode's lock is an attack
|
||||
that needs a target (`_minTargetNum` 1, `_allowNoTarget` 0, cooldown `aim_interval`); the skill buff `lemuen_s_3`
|
||||
switches to the S3_End mode (the bombardment) on `ON_BUFF_FINISH` only, and nothing ends it for want of a target. PRTS
|
||||
S3 note: a mark follows its target and stays where the target left.
|
||||
- **Cause**: the kit's lock tick ended the skill when `pickLock` found nobody and anyone had been locked —
|
||||
`if (!e) { if (m.lemLocks.length) skill.end('ammo'); return; }` (chess_char_6_01-lemuen.js:169 on the parent), there
|
||||
since the initial import with no decision behind it.
|
||||
- **Now**: with nothing to lock the skill waits, bullets and locks kept — the marks follow their enemies (out of her range
|
||||
too) or stay where they left — with its lock ready: the next enemy that comes into her range (or is 通缉) is locked on
|
||||
the next tick, then every `aim_interval` s. The skill ends when the bullets are spent and the shells fall on every mark
|
||||
in lock order, as before. While it waits she gains no SP (a running ammo skill holds it, as before); knocked out or
|
||||
withdrawn, no bombardment (unchanged); a battle that ends meanwhile ends without shells (nothing left to hit). The S3
|
||||
`lock` fx carry `hold`: render/fx/locks.js keeps such a reticle while the shooter's view has the skill bit (UF.SKILL)
|
||||
and counts its LOCK_T (5 game s) from the skill's end — without it the reticles faded 5 s into a wait. S2's aim lock
|
||||
(no `hold`) keeps its timeout.
|
||||
- Unchanged and noted: the client lock's 0.1 s pre-delay is not modelled (the lock lands on its tick, as before).
|
||||
- Seen on the way, not changed (S2, not this report): 归乡邀约 spends a bullet on every normal attack (5 of its 6 gone in
|
||||
8 s of attacks on an enemy that is not 通缉), while PRTS's S2 note says only the aimed snipe at a 通缉 target spends one
|
||||
(「若目标为被通缉状态,则攻击改为消耗1发弹药的瞄准狙击,否则不消耗弹药」) — left to the owner: fixed, S2 would hold its
|
||||
buff until 通缉 enemies come, a large change.
|
||||
- Golden: 3 scenarios — traced on both trees with an instrumented copy (SkillRuntime start / end logged, the round's
|
||||
fields and the carried SP); in each the first divergence is an S3 the parent ended with bullets left and nothing in her
|
||||
range:
|
||||
- roster-018: the elite 蕾缪安's first S3 (7.63 s) ended at 9.63 s with 2 of 6 bullets left; now the 6th goes at
|
||||
12.00 s (first differing snapshot window 0–10 s).
|
||||
- boss-boss_3-pair: her second S3 (27.8 s) ended at 30.3 s with 4 of 9 left; now all 9 by 34.43 s (window 30–40 s); the
|
||||
boss now falls at 199.53 s (before: `forced` at the 200 s cap with 145080 HP left).
|
||||
- coop2-NORMAL-14-standins: ai_0's 蕾缪安 (her own kit) — in round 11's battle her S3 ended at 17.2 s (6 of 8 left) and
|
||||
77.97 s (7 of 9); now all bullets are spent and the third cast does not come, so she enters the round's 联防 with
|
||||
25.96 SP instead of 10.27, casts S3 there and kills one more of h_0's leaked enemies: h_0 loses 1 LP less (rounds
|
||||
11–12 LP 18 → 19, 8 → 9; lpLost 30 → 29; ai_0 kills +1, dmgDealt +288).
|
||||
- bonds, standins and diy unchanged.
|
||||
- Tests: `test/sim/feedback5-lemuen-s3-wait.test.js` (3: the killed target and the walker who leaves fail on the parent —
|
||||
the skill ended —; the battle that ends while she waits passes on both), `test/render/fxproj.test.js` +1 (a held lock
|
||||
stays while the shooter's skill bit is on, then LOCK_T after; an unheld one times out — fails on the parent's
|
||||
renderer); `test/content/kits_t6.test.js`, `kits_alt_t6.test.js`, `test/sim/feedback3-ammo-bar.test.js` pass unchanged.
|
||||
@@ -18,7 +18,8 @@
|
||||
// blasts explosions (fireball, flash, shockwave + coloured ground ring, sparks, smoke; heavy ones add debris
|
||||
// and a scorch mark) for blast fx kinds and shell impacts
|
||||
// bombard 蕾缪安 S3: fx 'lock' keeps a reticle on the locked enemy until the 'bombard' of the shell fired at it
|
||||
// (or LOCK_T game s after the shooter's last lock / shell; the S2 aim lock until the snipe's 'crit'); 'bombardShell' = a shell fired now that
|
||||
// (or LOCK_T game s after the shooter's last lock / shell — a held S3 lock, `hold`, LOCK_T after her skill
|
||||
// ends: it waits with its bullets while nothing is in range; the S2 aim lock until the snipe's 'crit'); 'bombardShell' = a shell fired now that
|
||||
// lands on the spot after `t` game s — a launch streak leaves the shooter upwards and the shell drops out
|
||||
// of the sky onto the spot (a long flight climbs out of her first), a closing warning ring marks the spot
|
||||
// meanwhile; 'bombard' is the big explosion at the impact
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import { SHOT_HEIGHT, bodyZ } from './camera.js';
|
||||
import { FX_KINDS } from './kinds.js';
|
||||
import { easeOut } from './limits.js';
|
||||
import { UF } from '../../../../shared/constants.js';
|
||||
|
||||
/**
|
||||
* Lock reticle: how long (game s) a lock outlives the last sign of its shooter's S3 going on — its latest lock, shell or
|
||||
@@ -18,9 +19,11 @@ export class FxLocks {
|
||||
/**
|
||||
* fx 'lock': a reticle on the locked enemy (following its view; at the last spot once it is gone) until its shell
|
||||
* lands (mortar / _landed) or the aimed shot fires ('crit' from the same shooter); at most LOCK_T game seconds after
|
||||
* the shooter's last lock / shell / bombard (_touchLocks).
|
||||
* the shooter's last lock / shell / bombard (_touchLocks). A held lock (`hold`: 蕾缪安 S3, whose skill waits with its
|
||||
* bullets and locks while nothing is in range) waits as long as its shooter's skill runs (the view's UF.SKILL), its
|
||||
* LOCK_T counted from the skill's end.
|
||||
*/
|
||||
_lock(view, src, x, y, z) {
|
||||
_lock(view, src, x, y, z, hold = false) {
|
||||
let L = this.lockFree.pop();
|
||||
if (!L) {
|
||||
const P = this.P;
|
||||
@@ -34,7 +37,7 @@ export class FxLocks {
|
||||
L = { ring, core };
|
||||
}
|
||||
L.view = view; L.id = view ? view.id : null; L.src = src ?? null; L.x = x; L.y = y; L.z = z;
|
||||
L.t = 0; L.idle = 0; L.max = LOCK_T / this._ts(); L.out = -1; L.shell = false; L.sx = x; L.sy = y;
|
||||
L.t = 0; L.idle = 0; L.max = LOCK_T / this._ts(); L.out = -1; L.shell = false; L.sx = x; L.sy = y; L.hold = !!hold;
|
||||
L.ring.tint = L.core.tint = FX_KINDS.lock.c;
|
||||
L.ring.alpha = L.core.alpha = 0;
|
||||
L.ring.visible = L.core.visible = true;
|
||||
@@ -77,10 +80,12 @@ export class FxLocks {
|
||||
L.t += dt;
|
||||
L.idle += dt;
|
||||
if (L.out < 0 && L.idle >= L.max) L.out = 0;
|
||||
// the shooter knocked out / withdrawn: the sim fires no more shells — only a shell already in the air still lands
|
||||
// the shooter knocked out / withdrawn: the sim fires no more shells — only a shell already in the air still lands;
|
||||
// a held lock waits while its shooter's skill runs (蕾缪安 S3 with nothing in range: its bullets and locks wait)
|
||||
if (L.out < 0 && !L.shell && L.src != null && this.ctx.view) {
|
||||
const sv = this._viewOf(L.src);
|
||||
if (!sv || sv.alive === false) L.out = 0;
|
||||
else if (L.hold && (sv.flags & UF.SKILL)) L.idle = 0;
|
||||
}
|
||||
if (L.out >= 0) { L.out += dt; if (L.out >= LOCK_FADE) { this._freeLock(L); continue; } }
|
||||
const v = L.view;
|
||||
|
||||
@@ -50,7 +50,7 @@ export class FxSim {
|
||||
* counter, dp, coin, crate, down, beam, bolt, strike, volley, pillar, lp, chill, element, flame), except kinds whose
|
||||
* archetype is 'none' (hitCap: a leader hit cancelled by 限伤 draws nothing); unknown kinds get a generic sparkle. `extra` keys used: id (anchor unit — or the shooter of a `pt` kind), r | radius, dur | duration,
|
||||
* t (shell flight, game s), src / from / to / target / targets (unit ids), fx, fy / fromX, fromY / tx, ty (positions),
|
||||
* element, n, scale, kind, tiles.
|
||||
* element, n, scale, kind, tiles, hold ('lock': the reticle waits while the shooter's skill runs).
|
||||
*/
|
||||
simFx(kind, x, y, extra) {
|
||||
const ex = extra && typeof extra === 'object' ? extra : {};
|
||||
@@ -178,9 +178,10 @@ export class FxSim {
|
||||
case 'mark': case 'reticle': {
|
||||
if (kind === 'lock') {
|
||||
// `id` is always the locked enemy: the reticle sticks to its view even a little off the event's spot
|
||||
// (`hold`: 蕾缪安 S3 — the reticle waits while her skill runs, however long nothing is in range)
|
||||
const lv = at.v || this._viewOf(ex.id);
|
||||
this._touchLocks(ex.src ?? null);
|
||||
this._lock(lv, ex.src ?? null, lv ? lv.x : at.x, lv ? lv.y : at.y, lv ? bodyZ(cam, lv, SHOT_HEIGHT.aim) : at.z + 0.55);
|
||||
this._lock(lv, ex.src ?? null, lv ? lv.x : at.x, lv ? lv.y : at.y, lv ? bodyZ(cam, lv, SHOT_HEIGHT.aim) : at.z + 0.55, !!ex.hold);
|
||||
break;
|
||||
}
|
||||
const v = at.v;
|
||||
|
||||
@@ -152,6 +152,14 @@ function lemuen(bb, chess, def) {
|
||||
};
|
||||
return {
|
||||
skills,
|
||||
// S3 礼炮·强制追思, the lock phase: one lock every aim_interval s on an enemy in her range (the least locked first,
|
||||
// then the lowest DEF), one bullet each; the skill ends when the bullets are spent, then bombard. With nothing in
|
||||
// range it waits — bullets and locks kept (the marks follow their enemies / stay where they left) — and locks the
|
||||
// next enemy that comes at once: an ammo skill has no time limit ("攻击装有5发弹药,打完后结束(可随时停止技能)"; in
|
||||
// the client data her S3 lock needs a target and only the spent bullets end the skill buff lemuen_s_3) and the 卫戍协议
|
||||
// automation never stops a skill (PRTS 卫戍协议/帮助 技能操作: "通常不会自动关闭技能"). Until 0.2.0 the skill ended at
|
||||
// the first lock tick with nothing in range (community report: 「蕾缪安3技能范围里没人好像会自动结束」). Its fx 'lock'
|
||||
// carries `hold`: the renderer keeps those reticles up while her skill runs (render/fx/locks.js).
|
||||
skill: {
|
||||
kind: 'ammo',
|
||||
ammo: Math.max(1, Math.floor(num(bb['attack@trigger_time'], 5))),
|
||||
@@ -164,11 +172,11 @@ function lemuen(bb, chess, def) {
|
||||
if (!unit.canAct) return;
|
||||
m.lemAcc += dt;
|
||||
if (m.lemAcc + 1e-9 < aim) return;
|
||||
m.lemAcc -= aim;
|
||||
const e = pickLock(battle, unit, m.lemLocks);
|
||||
if (!e) { if (m.lemLocks.length) skill.end('ammo'); return; }
|
||||
if (!e) { m.lemAcc = aim; return; } // nothing to lock: wait, the next lock ready the moment an enemy comes
|
||||
m.lemAcc -= aim;
|
||||
m.lemLocks.push({ e, x: e.x, y: e.y });
|
||||
battle.fx('lock', { x: e.x, y: e.y, id: e.id, src: unit.id });
|
||||
battle.fx('lock', { x: e.x, y: e.y, id: e.id, src: unit.id, hold: 1 });
|
||||
skill.ammoLeft--;
|
||||
battle.emit('ammoUsed', { unit, left: skill.ammoLeft, skill });
|
||||
if (skill.active && skill.ammoLeft <= 0) skill.end('ammo');
|
||||
|
||||
+51
-53
@@ -224,104 +224,102 @@
|
||||
"round": 14,
|
||||
"seed": 1428555614,
|
||||
"waveId": "act1autochess_h07_03",
|
||||
"time": 200,
|
||||
"ticks": 6000,
|
||||
"reason": "forced",
|
||||
"time": 199.53,
|
||||
"ticks": 5986,
|
||||
"reason": "cleared",
|
||||
"killed": 21,
|
||||
"total": 21,
|
||||
"unspawned": 0,
|
||||
"bossHpLeft": 145080,
|
||||
"bossHpLeft": 0,
|
||||
"rngDraws": 1385,
|
||||
"errors": {"count":0,"kinds":[]},
|
||||
"players": {
|
||||
"p1": {"killed":21,"total":21,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":1679564,"bossDmg":1583484,"heal":2172,"deaths":0,"layerGains":{}},
|
||||
"p2": {"killed":0,"total":0,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":2359162,"bossDmg":2271437,"heal":16328,"deaths":0,"layerGains":{}}
|
||||
"p1": {"killed":21,"total":21,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":1881663,"bossDmg":1745302,"heal":2230,"deaths":0,"layerGains":{}},
|
||||
"p2": {"killed":0,"total":0,"perfect":true,"leaks":0,"leaksUncounted":0,"coins":0,"dmg":2302143,"bossDmg":2254698,"heal":16832,"deaths":0,"layerGains":{}}
|
||||
},
|
||||
"units": [
|
||||
["p1",1,"op","chess_char_6_03_b",2,"uniequip_002_yu",3430,1,7,0,0,0,605,0,1],
|
||||
["p1",1,"op","chess_char_6_03_b",2,"uniequip_002_yu",3430,1,7,0,0,0,638,0,1],
|
||||
["p1",2,"op","chess_char_6_06_b",2,"uniequip_002_pepe",2939,1,11,0,0,0,0,0,0],
|
||||
["p1",3,"op","chess_char_6_07_b",2,"uniequip_002_siege2",3101,1,9,0,0,0,0,0,0],
|
||||
["p1",4,"op","chess_char_6_01_b",2,"uniequip_002_lemuen",2131.5,1,13,1,877126,0,0,62,8],
|
||||
["p1",5,"op","chess_char_6_02_b",2,"uniequip_002_sbell2",1933,1,17,0,349841,28,2,63,3],
|
||||
["p1",6,"op","chess_char_6_04_b",2,"uniequip_002_skadi2",1214.37,1,19,1,35820,8433,1241,0,5],
|
||||
["p1",7,"op","chess_char_6_05_b",2,"uniequip_002_pasngr",2307.2,1,21,2,293209,1008,323,97,8],
|
||||
["p1",8,"op","chess_char_6_08_b",2,"uniequip_002_reed2",1668,1,5,1,3064,0,1,11,1],
|
||||
["p1",9,"op","chess_char_6_09_b",0,"uniequip_002_cello",1322,1,15,1,21050,0,0,8,8],
|
||||
["p1",10,"op","chess_char_6_11_b",2,"uniequip_002_mlyss",1893,1,23,0,89834,0,0,206,5],
|
||||
["p1",4,"op","chess_char_6_01_b",2,"uniequip_002_lemuen",2131.5,1,13,2,1071144,0,0,62,8],
|
||||
["p1",5,"op","chess_char_6_02_b",2,"uniequip_002_sbell2",1933,1,17,1,348232,165,10,63,3],
|
||||
["p1",6,"op","chess_char_6_04_b",2,"uniequip_002_skadi2",1214.37,1,19,1,35824,8763,1252,0,5],
|
||||
["p1",7,"op","chess_char_6_05_b",2,"uniequip_002_pasngr",2307.2,1,21,4,284196,1248,326,96,8],
|
||||
["p1",8,"op","chess_char_6_08_b",2,"uniequip_002_reed2",1668,1,5,0,13,0,3,6,1],
|
||||
["p1",9,"op","chess_char_6_09_b",0,"uniequip_002_cello",1322,1,15,1,10883,0,0,7,7],
|
||||
["p1",10,"op","chess_char_6_11_b",2,"uniequip_002_mlyss",1893,1,23,0,88919,0,0,206,5],
|
||||
["p1",11,"token","token_10017_skadi2_dedant",null,null,130.4,0,26,0,0,0,0,0,null],
|
||||
["p2",12,"op","chess_char_6_15_b",2,"uniequip_002_qiubai",3985.92,1,31,6,345376,3222,0,183,3],
|
||||
["p2",13,"op","chess_char_6_19_b",2,"uniequip_002_blkkgt",2767,1,12,1,27218,0,0,6,1],
|
||||
["p2",12,"op","chess_char_6_15_b",2,"uniequip_002_qiubai",3985.92,1,31,3,358064,3029,0,188,3],
|
||||
["p2",13,"op","chess_char_6_19_b",2,"uniequip_002_blkkgt",2767,1,12,0,5,0,0,0,1],
|
||||
["p2",14,"op","chess_char_5_01_b",1,"uniequip_002_excu2",2314,1,10,0,0,0,0,0,0],
|
||||
["p2",15,"op","chess_char_6_17_b",2,"uniequip_002_nearl2",6215.89,1,46,0,293226,0,0,85,4],
|
||||
["p2",16,"op","chess_char_6_12_b",1,"uniequip_002_rosmon",3117.74,1,16,2,189809,0,0,74,4],
|
||||
["p2",17,"op","chess_char_6_13_b",1,"uniequip_002_angel2",2392.2,1,20,3,1205701,5257,423,365,6],
|
||||
["p2",18,"op","chess_char_6_14_b",2,"uniequip_002_lumen",2050.92,1,22,0,0,0,2846,42,1],
|
||||
["p2",19,"op","chess_char_6_16_b",2,"uniequip_002_halo2",1554.12,1,25,0,87945,932,0,163,4],
|
||||
["p2",20,"op","chess_char_6_18_b",2,"uniequip_002_whitw2",1541,1,6,3,182528,0,0,13,3],
|
||||
["p2",21,"op","chess_char_6_20_b",2,"uniequip_002_agoat2",1631.88,1,18,0,0,0,13059,42,2],
|
||||
["p2",15,"op","chess_char_6_17_b",2,"uniequip_002_nearl2",6215.89,1,48,0,280519,0,0,81,4],
|
||||
["p2",16,"op","chess_char_6_12_b",1,"uniequip_002_rosmon",3117.74,1,16,2,186844,0,0,74,4],
|
||||
["p2",17,"op","chess_char_6_13_b",1,"uniequip_002_angel2",2392.2,1,20,4,1216961,5257,494,363,6],
|
||||
["p2",18,"op","chess_char_6_14_b",2,"uniequip_002_lumen",2050.92,1,22,0,0,0,2448,44,1],
|
||||
["p2",19,"op","chess_char_6_16_b",2,"uniequip_002_halo2",1554.12,1,25,0,86149,981,0,163,4],
|
||||
["p2",20,"op","chess_char_6_18_b",2,"uniequip_002_whitw2",1541,1,6,4,173602,0,0,5,3],
|
||||
["p2",21,"op","chess_char_6_20_b",2,"uniequip_002_agoat2",1631.88,1,18,0,0,0,13890,48,2],
|
||||
["-","~1","device","trap_1105_accrate",null,null,100,1,1,0,0,0,0,0,null],
|
||||
["-","~2","device","trap_1105_accrate",null,null,100,1,2,0,0,0,0,0,null],
|
||||
["-","~3","device","trap_1105_accrate",null,null,100,1,3,0,0,0,0,0,null],
|
||||
["-","~4","device","trap_1105_accrate",null,null,100,1,4,0,0,0,0,0,null],
|
||||
["p1","~1","token","token_10030_mlyss_wtrman",null,null,0,0,24,0,8947,1880,0,18,1],
|
||||
["p1","~2","token","token_10030_mlyss_wtrman",null,null,860.02,0,41,0,674,1020,0,1,null],
|
||||
["p1","~1","token","token_10017_skadi2_dedant",null,null,100,0,48,0,0,0,0,0,null],
|
||||
["p2","~1","token","token_10019_nearl2_sword",null,null,6480,0,50,0,9120,0,0,0,null],
|
||||
["p1","~1","token","token_10030_mlyss_wtrman",null,null,1557.36,1,24,0,42452,0,0,90,1],
|
||||
["p1","~2","token","token_10030_mlyss_wtrman",null,null,1880.37,0,41,0,0,0,0,0,null],
|
||||
["p1","~1","token","token_10017_skadi2_dedant",null,null,100,0,49,0,0,0,0,0,null],
|
||||
["p2","~1","token","token_10019_nearl2_sword",null,null,6480,0,50,0,0,0,0,0,null],
|
||||
["p1","~2","token","token_10017_skadi2_dedant",null,null,130.4,0,53,0,0,0,0,0,null],
|
||||
["p2","~2","token","token_10019_nearl2_sword",null,null,6480,0,55,0,9120,0,0,0,null],
|
||||
["p1","~3","token","token_10017_skadi2_dedant",null,null,100,0,57,0,0,0,0,0,null],
|
||||
["p2","~3","token","token_10019_nearl2_sword",null,null,6480,1,60,0,9120,0,0,0,null]
|
||||
["p2","~2","token","token_10019_nearl2_sword",null,null,6480,0,55,0,0,0,0,0,null],
|
||||
["p1","~3","token","token_10017_skadi2_dedant",null,null,100,0,58,0,0,0,0,0,null],
|
||||
["p2","~3","token","token_10019_nearl2_sword",null,null,6480,1,60,0,0,0,0,0,null]
|
||||
],
|
||||
"enemies": {
|
||||
"enemy_1197_sfshu_2": [2,2,0,0,28809],
|
||||
"enemy_1198_msfshu_2": [6,6,0,0,24],
|
||||
"enemy_1209_sfden_2": [2,2,0,2901,154793],
|
||||
"enemy_1209_sfden_2": [2,2,0,0,154793],
|
||||
"enemy_1210_msfden_2": [2,2,0,0,90],
|
||||
"enemy_9021_acduml": [1,0,0,0,3854920],
|
||||
"enemy_9023_acdums": [9,9,0,5690,90]
|
||||
"enemy_9021_acduml": [1,1,0,0,4000000],
|
||||
"enemy_9023_acdums": [9,9,0,6252,90]
|
||||
},
|
||||
"hooks": [1,58,6000,1444,1444,3043,3408,2091,22,32,68,62,368,46997,1655,3,22,0,1,22,0,2,0],
|
||||
"events": {"atk":1595,"deploy":32,"die":32,"dmg":3120,"engage":15,"fx":1016,"heal":279,"skill":130,"spawn":55,"status":506},
|
||||
"hooks": [1,58,5986,1498,1498,3074,3446,2102,22,32,67,60,370,46778,1625,2,22,0,1,21,0,2,0],
|
||||
"events": {"atk":1634,"deploy":32,"die":32,"dmg":3158,"engage":13,"fx":1025,"heal":282,"skill":127,"spawn":55,"status":511},
|
||||
"fx": {
|
||||
"airstrike": 121,
|
||||
"appear": 1,
|
||||
"airstrike": 130,
|
||||
"beam": 9,
|
||||
"blink": 3,
|
||||
"bombard": 65,
|
||||
"bombardShell": 65,
|
||||
"bombard": 69,
|
||||
"bombardShell": 69,
|
||||
"buff": 3,
|
||||
"burst": 2,
|
||||
"copy": 1,
|
||||
"disappear": 1,
|
||||
"displace": 3,
|
||||
"displace": 1,
|
||||
"dp": 5,
|
||||
"drone": 338,
|
||||
"droneLock": 16,
|
||||
"drone": 337,
|
||||
"droneLock": 17,
|
||||
"drones": 3,
|
||||
"explode": 132,
|
||||
"explode": 133,
|
||||
"finale": 1,
|
||||
"firewall": 1,
|
||||
"garrisonGrant": 1,
|
||||
"lightning": 73,
|
||||
"link": 6,
|
||||
"lock": 65,
|
||||
"lightning": 76,
|
||||
"link": 4,
|
||||
"lock": 69,
|
||||
"lure": 3,
|
||||
"phase": 18,
|
||||
"radiantSword": 3,
|
||||
"scorchBurst": 2,
|
||||
"slash": 11,
|
||||
"snow": 16,
|
||||
"scorchBurst": 1,
|
||||
"slash": 5,
|
||||
"snow": 15,
|
||||
"split": 1,
|
||||
"steal": 6,
|
||||
"storm": 8,
|
||||
"summon": 14,
|
||||
"sunBurst": 3,
|
||||
"talent": 4,
|
||||
"telegraph": 4,
|
||||
"telegraph": 3,
|
||||
"tide": 5,
|
||||
"wanted": 3
|
||||
"wanted": 2
|
||||
},
|
||||
"snaps": "4c79f145 ebcda26d 068f0e59 260d03ab 4d134b3f 74d79bd3 96b17d7f aa35887e 6f63c25c 204e2f98 9ab4f3f0 f9ac2510 13eb96e3 7db1e342 0bf684aa 45383cbf d2edb802 be365f88 86b51adc d438ceb4"
|
||||
"snaps": "4c79f145 ebcda26d 068f0e59 f77a47b8 1b3b0779 8e1af8f0 1007ce9e d942d746 2f1bb88d ece27d81 47c5042c 2a8ee6a5 39f34644 5c84574d dd568842 0b4a024a 5841ee36 0333ed5c df19d0bf fd9c01b0"
|
||||
},
|
||||
"boss-boss_3-solo": {
|
||||
"about": "假想敌:管 solo pool 2000000: chess_char_5_08_b chess_char_5_11_b chess_char_5_05_b chess_char_5_06_b chess_char_5_13_b chess_char_5_07_b chess_char_5_02_b chess_char_5_03_b chess_char_5_10_b chess_char_5_12_b",
|
||||
|
||||
@@ -1559,12 +1559,12 @@
|
||||
},
|
||||
{
|
||||
"round": 11,
|
||||
"h_0": [1,18,12,0,2,5,8,"def68119",93,"arcaneShip 1/15, kazimierzShip 0/8, miraShip 1/0, preciShip 1/26, siracusaShip 1/18, soloShip 1/0, suntShip 1/0, swiftShip 1/34, yanShip 0/13",21,25,4,102],
|
||||
"h_0": [1,19,12,0,2,5,8,"def68119",93,"arcaneShip 1/15, kazimierzShip 0/8, miraShip 1/0, preciShip 1/26, siracusaShip 1/18, soloShip 1/0, suntShip 1/0, swiftShip 1/34, yanShip 0/13",21,25,4,102],
|
||||
"ai_0": [1,23,13,0,0,5,8,"c0b11e3b",224,"arcaneShip 0/2, deputShip 1/30, kazimierzShip 0/8, lateranoShip 1/39, miraShip 1/4, preciShip 0/2, raidShip 0/2, sargonShip 0/2, siracusaShip 1/40, skillfulShip 1/52, suntShip 1/12, swiftShip 1/47",25,25,0,98]
|
||||
},
|
||||
{
|
||||
"round": 12,
|
||||
"h_0": [1,8,14,0,0,6,8,"13e7bef4",103,"arcaneShip 1/15, kazimierzShip 0/8, miraShip 1/0, preciShip 1/31, siracusaShip 1/18, suntShip 1/0, swiftShip 1/39, yanShip 0/13",18,31,13,117],
|
||||
"h_0": [1,9,14,0,0,6,8,"13e7bef4",103,"arcaneShip 1/15, kazimierzShip 0/8, miraShip 1/0, preciShip 1/31, siracusaShip 1/18, suntShip 1/0, swiftShip 1/39, yanShip 0/13",18,31,13,117],
|
||||
"ai_0": [1,22,12,0,1,6,8,"defd2806",262,"arcaneShip 0/2, deputShip 1/32, kazimierzShip 0/8, lateranoShip 1/57, miraShip 1/6, preciShip 0/2, raidShip 0/2, sargonShip 0/2, siracusaShip 1/42, skillfulShip 1/62, suntShip 1/14, swiftShip 1/49",30,31,1,112]
|
||||
},
|
||||
{
|
||||
@@ -1584,7 +1584,7 @@
|
||||
"title": {"id":"comment_6","name":"挥金如土","picId":"comment_icon_6","text":"消耗资金最多"},
|
||||
"trophies": 3,
|
||||
"reward": 280,
|
||||
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":2083752,"gold":129,"itemsEquipped":15,"kills":231,"leaks":63,"lpLost":30,"merges":4,"perfectRounds":8,"refreshes":22},
|
||||
"stats": {"activatedLayers":0,"bossDamage":0,"dmgDealt":2083752,"gold":129,"itemsEquipped":15,"kills":231,"leaks":63,"lpLost":29,"merges":4,"perfectRounds":8,"refreshes":22},
|
||||
"lineup": [],
|
||||
"bonds": ["arcaneShip 0/21","kazimierzShip 0/8","preciShip 0/42","raidShip 0/16","siracusaShip 0/30","swiftShip 0/44","yanShip 0/13"]
|
||||
},
|
||||
@@ -1598,7 +1598,7 @@
|
||||
"title": {"id":"comment_1","name":"卫戍之星","picId":"comment_icon_1","text":"对敌方领袖造成伤害最高(仅胜利时)"},
|
||||
"trophies": 4,
|
||||
"reward": 400,
|
||||
"stats": {"activatedLayers":336,"bossDamage":390000,"dmgDealt":2919224,"gold":141,"itemsEquipped":23,"kills":258,"leaks":41,"lpLost":11,"merges":8,"perfectRounds":11,"refreshes":27},
|
||||
"stats": {"activatedLayers":336,"bossDamage":390000,"dmgDealt":2919512,"gold":141,"itemsEquipped":23,"kills":259,"leaks":41,"lpLost":11,"merges":8,"perfectRounds":11,"refreshes":27},
|
||||
"lineup": ["4_21_a@12,3","5_20_a@12,7","3_21_b@11,3[1_05_e_b+3_01_e_b]","6_05_a@11,8","3_01_b@10,3[1_01_e_a+6_05_e_a]","3_18_b@10,7[4_02_e_a+4_11_e_a]","4_16_b@10,8[4_10_e_a+4_04_e_a]","6_01_a@9,8[2_01_e_a+1_02_e_a]"],
|
||||
"bonds": ["arcaneShip 0/2","deputShip 0/32","kazimierzShip 0/8","lateranoShip 1/83","miraShip 1/12","preciShip 1/2","raidShip 0/2","sargonShip 0/2","siracusaShip 1/84","skillfulShip 1/74","suntShip 1/14","swiftShip 1/67"]
|
||||
}
|
||||
|
||||
+34
-36
@@ -1129,14 +1129,14 @@
|
||||
"round": 13,
|
||||
"seed": 264387324,
|
||||
"waveId": "act1autochess_h06",
|
||||
"time": 195,
|
||||
"ticks": 5850,
|
||||
"time": 194.97,
|
||||
"ticks": 5849,
|
||||
"reason": "cleared",
|
||||
"killed": 120,
|
||||
"total": 120,
|
||||
"unspawned": 0,
|
||||
"bossHpLeft": null,
|
||||
"rngDraws": 549,
|
||||
"rngDraws": 490,
|
||||
"errors": {"count":0,"kinds":[]},
|
||||
"players": {
|
||||
"p1": {
|
||||
@@ -1146,64 +1146,62 @@
|
||||
"leaks": 0,
|
||||
"leaksUncounted": 0,
|
||||
"coins": 12,
|
||||
"dmg": 1634623,
|
||||
"dmg": 1565127,
|
||||
"bossDmg": 0,
|
||||
"heal": 33055,
|
||||
"heal": 31396,
|
||||
"deaths": 1,
|
||||
"layerGains": {"lateranoShip":8,"preciShip":8,"sargonShip":23}
|
||||
"layerGains": {"lateranoShip":12,"preciShip":12,"sargonShip":26}
|
||||
}
|
||||
},
|
||||
"units": [
|
||||
["p1",1,"op","chess_char_6_03_b",2,"uniequip_002_yu",3430,1,3,0,0,0,1317,0,1],
|
||||
["p1",2,"op","chess_char_6_06_b",2,"uniequip_002_pepe",2939,1,6,0,0,0,0,0,0],
|
||||
["p1",3,"op","chess_char_6_07_b",2,"uniequip_002_siege2",3287.06,1,9,0,6177,1792,0,2,1],
|
||||
["p1",4,"op","chess_char_6_20_a",2,null,1311.22,1,4,0,0,0,22720,45,1],
|
||||
["p1",5,"op","chess_char_6_01_b",2,"uniequip_002_lemuen",1558.2,1,7,12,177469,0,0,23,5],
|
||||
["p1",6,"op","chess_char_6_02_b",2,"uniequip_002_sbell2",3981.98,1,10,8,54371,1751,197,11,2],
|
||||
["p1",7,"op","chess_char_6_04_b",2,"uniequip_002_skadi2",1693.88,1,13,4,48553,8129,105,0,5],
|
||||
["p1",8,"op","chess_char_6_05_b",2,"uniequip_002_pasngr",0,0,15,20,243670,11371,3228,39,7],
|
||||
["p1",9,"op","chess_char_6_08_b",2,"uniequip_002_reed2",1768.08,1,5,2,78780,0,2839,18,2],
|
||||
["p1",10,"op","chess_char_6_09_b",0,"uniequip_002_cello",1401.32,1,8,2,28299,0,0,12,13],
|
||||
["p1",11,"op","chess_char_6_11_b",2,"uniequip_002_mlyss",2006.58,1,11,18,153886,2952,641,152,5],
|
||||
["p1",12,"op","chess_char_6_12_b",1,"uniequip_002_rosmon",4214.73,1,14,48,795747,7667,1111,70,4],
|
||||
["p1",1,"op","chess_char_6_03_b",2,"uniequip_002_yu",4026.82,1,3,0,0,0,0,0,0],
|
||||
["p1",2,"op","chess_char_6_06_b",2,"uniequip_002_pepe",3450.39,1,6,0,0,0,0,0,0],
|
||||
["p1",3,"op","chess_char_6_07_b",2,"uniequip_002_siege2",3640.57,1,9,0,0,0,0,0,0],
|
||||
["p1",4,"op","chess_char_6_20_a",2,null,1452.24,1,4,0,0,0,22245,41,2],
|
||||
["p1",5,"op","chess_char_6_01_b",2,"uniequip_002_lemuen",1725.78,1,7,9,194843,0,0,18,5],
|
||||
["p1",6,"op","chess_char_6_02_b",2,"uniequip_002_sbell2",4202.34,1,10,4,49828,1259,566,10,2],
|
||||
["p1",7,"op","chess_char_6_04_b",2,"uniequip_002_skadi2",1876.05,1,13,2,30324,9629,331,0,4],
|
||||
["p1",8,"op","chess_char_6_05_b",2,"uniequip_002_pasngr",0,0,15,18,231847,10065,3224,37,7],
|
||||
["p1",9,"op","chess_char_6_08_b",2,"uniequip_002_reed2",1958.23,1,5,4,57431,0,1066,8,1],
|
||||
["p1",10,"op","chess_char_6_09_b",0,"uniequip_002_cello",1552.03,1,8,0,9057,0,0,6,6],
|
||||
["p1",11,"op","chess_char_6_11_b",2,"uniequip_002_mlyss",2222.38,1,11,20,158083,1500,152,153,4],
|
||||
["p1",12,"op","chess_char_6_12_b",1,"uniequip_002_rosmon",4659.3,1,14,55,762444,7150,658,73,4],
|
||||
["p1",13,"token","token_10017_skadi2_dedant",null,null,100,0,16,0,0,0,0,0,null],
|
||||
["p1",14,"token","token_10030_mlyss_wtrman",null,null,0,0,12,3,14239,2620,0,38,1],
|
||||
["p1",14,"token","token_10030_mlyss_wtrman",null,null,2420.79,1,12,6,41576,2426,0,89,1],
|
||||
["-","~1","device","trap_1105_accrate",null,null,100,1,1,0,0,0,0,0,null],
|
||||
["-","~2","device","trap_1105_accrate",null,null,100,1,2,0,0,0,0,0,null],
|
||||
["p1","~1","token","token_10030_mlyss_wtrman",null,null,0,0,37,0,4916,2245,59,5,null],
|
||||
["p1","~2","token","token_10030_mlyss_wtrman",null,null,0,0,52,0,640,2810,389,5,null],
|
||||
["p1","~1","token","token_10030_mlyss_wtrman",null,null,0,0,37,0,5009,2915,729,10,null],
|
||||
["p1","~2","token","token_10030_mlyss_wtrman",null,null,0,0,52,0,512,2733,355,4,null],
|
||||
["p1","~1","token","token_10017_skadi2_dedant",null,null,117.4,0,60,0,0,0,0,0,null],
|
||||
["p1","~3","token","token_10030_mlyss_wtrman",null,null,0,0,69,0,1028,2421,0,3,null],
|
||||
["p1","~1","token","token_10040_siege2_vlion",null,null,4240,0,100,0,475,0,0,1,null],
|
||||
["p1","~3","token","token_10030_mlyss_wtrman",null,null,1313.01,0,71,1,9719,1878,770,12,null],
|
||||
["p1","~4","token","token_10030_mlyss_wtrman",null,null,1405.84,0,100,1,5828,1493,713,12,null],
|
||||
["p1","~2","token","token_10017_skadi2_dedant",null,null,100,0,102,0,0,0,0,0,null],
|
||||
["p1","~4","token","token_10030_mlyss_wtrman",null,null,2185.72,1,121,3,15883,0,0,30,1],
|
||||
["p1","~5","token","token_10030_mlyss_wtrman",null,null,2137.36,0,133,2,9943,498,449,12,null],
|
||||
["p1","~3","token","token_10017_skadi2_dedant",null,null,100,1,139,0,0,0,0,0,null],
|
||||
["p1","~6","token","token_10030_mlyss_wtrman",null,null,2062,1,150,0,548,0,0,1,null]
|
||||
["p1","~5","token","token_10030_mlyss_wtrman",null,null,1054.21,0,132,2,8626,1773,586,12,null],
|
||||
["p1","~3","token","token_10017_skadi2_dedant",null,null,117.4,1,138,0,0,0,0,0,null]
|
||||
],
|
||||
"enemies": {
|
||||
"enemy_1005_yokai": [1,1,0,0,1327],
|
||||
"enemy_1009_lurker": [1,1,0,0,3650],
|
||||
"enemy_1023_jmage": [1,1,0,697,3650],
|
||||
"enemy_1092_mdgint": [1,1,0,1156,165888],
|
||||
"enemy_1092_mdgint": [1,1,0,1397,165888],
|
||||
"enemy_1166_dusbr": [1,1,0,0,4977],
|
||||
"enemy_1197_sfshu_2": [1,1,0,0,5557],
|
||||
"enemy_1198_msfshu_2": [3,3,0,0,12],
|
||||
"enemy_1210_msfden_2": [1,1,0,0,45],
|
||||
"enemy_1229_darmy": [27,27,0,0,188117],
|
||||
"enemy_1229_darmy_2": [57,57,0,0,520059],
|
||||
"enemy_1275_dwlock_2": [21,21,0,18938,487711],
|
||||
"enemy_1283_sgkill": [1,1,0,2701,19907],
|
||||
"enemy_1364_spnaxe_2": [1,1,0,4260,135851],
|
||||
"enemy_1275_dwlock_2": [21,21,0,27266,487711],
|
||||
"enemy_1283_sgkill": [1,1,0,2984,19907],
|
||||
"enemy_1364_spnaxe_2": [1,1,0,1086,66355],
|
||||
"enemy_1367_dseed": [2,2,0,0,9953],
|
||||
"enemy_1439_dslntf": [1,1,0,0,49766],
|
||||
"enemy_2002_bearmi_2": [1,1,0,0,18248],
|
||||
"enemy_9008_acbunn": [1,1,0,9545,19907]
|
||||
"enemy_9008_acbunn": [1,1,0,2434,19907]
|
||||
},
|
||||
"hooks": [1,148,5850,530,530,1385,1542,3782,127,132,48,43,27,29676,402,6,122,0,1,127,8,6,0],
|
||||
"events": {"atk":636,"bounty":6,"deploy":24,"die":132,"dmg":1462,"engage":16,"fx":258,"heal":231,"layer":8,"skill":91,"spawn":148,"status":437},
|
||||
"fx": {"beam":5,"bombard":27,"bombardShell":27,"burst":6,"camouflage":2,"copy":2,"dp":5,"firewall":1,"garrison":6,"lightning":97,"link":1,"lock":27,"lure":2,"scorchBurst":11,"snow":12,"spGain":2,"split":5,"storm":7,"summon":3,"talent":1,"tide":5,"wanted":4},
|
||||
"snaps": "9cdb845f 1a3da81a b8f8fa18 18adb6a1 045ac790 92daeb67 b8964a05 72d66b06 7650d4e4 fac284c3 d14f07dd e93511be da2827c7 b87b3885 004babab e8850449 0fff5530 45bf7443 2e7fa788 2015799d"
|
||||
"hooks": [1,146,5849,541,541,1343,1500,4125,125,131,36,32,37,24823,332,7,122,0,1,125,11,8,0],
|
||||
"events": {"atk":632,"bounty":6,"deploy":22,"die":131,"dmg":1420,"engage":13,"fx":271,"heal":198,"layer":11,"skill":68,"spawn":146,"status":377},
|
||||
"fx": {"beam":6,"bombard":31,"bombardShell":31,"burst":8,"camouflage":2,"copy":1,"dp":4,"explode":1,"garrison":8,"lightning":98,"link":1,"lock":37,"lure":2,"scorchBurst":13,"snow":7,"split":5,"storm":7,"summon":2,"talent":1,"tide":4,"wanted":2},
|
||||
"snaps": "462bd251 0720b450 e6cc2e2b e9020690 49bdb529 f3e5da0b 9e37e4e7 1f15c3ae 4e3f15d4 2dfffd4f 241bcf3a f616e60d 30648a5c 32b08230 6b3af4f0 8f125452 d62f0b43 82439da4 0cca37a4 0eb827d6"
|
||||
},
|
||||
"roster-019": {
|
||||
"about": "pass 0 band 7: chess_char_6_15_b chess_char_6_19_b chess_char_5_01_b chess_char_5_05_b chess_char_6_17_b chess_char_6_13_b chess_char_6_14_b chess_char_6_16_b chess_char_6_18_b chess_char_6_20_b chess_char_5_02_b chess_char_5_03_b",
|
||||
|
||||
@@ -18,6 +18,7 @@ import * as SIM from '../../server/sim/constants.js';
|
||||
import { installFakePixi, fakeViewCtx } from './fakepixi.js';
|
||||
import { presetCamera } from '../../public/js/render/projection.js';
|
||||
import { PROJ, HIT_TINT } from '../../public/js/render/style.js';
|
||||
import { UF } from '../../shared/constants.js';
|
||||
|
||||
let fake, FX, T;
|
||||
before(async () => {
|
||||
@@ -354,6 +355,23 @@ describe('蕾缪安 S3: lock, bombardShell, bombard', () => {
|
||||
assert.equal(fx.locks.length, 0);
|
||||
});
|
||||
|
||||
test('a held S3 lock (fx `hold`) waits as long as her skill runs with nothing in range, then LOCK_T after it ends; an unheld lock still times out', () => {
|
||||
// 0.2.0 community report: S3 no longer ends by itself with nothing in her range — it waits with its bullets and locks
|
||||
const { fx, lem } = setup();
|
||||
lem.flags = UF.SKILL; // her S3 runs (the snapshot's skill bit)
|
||||
fx.simFx('lock', 8, 10, { id: 21, src: 1, hold: 1 });
|
||||
fx.simFx('lock', 9, 11.5, { id: 23, src: 1 }); // an S2 aim lock: no hold
|
||||
const [held, plain] = fx.locks;
|
||||
run(fx, 20 / 2); // 20 game s at 2×, nothing new from her
|
||||
assert.ok(fx.locks.includes(held) && held.out < 0, 'the held reticle stays while her skill waits');
|
||||
assert.ok(!fx.locks.includes(plain), 'the unheld one timed out after LOCK_T');
|
||||
lem.flags = 0; // the skill ended
|
||||
run(fx, 4 / 2);
|
||||
assert.ok(held.out < 0, 'LOCK_T counts from the skill end');
|
||||
run(fx, 1 / 2 + 0.3);
|
||||
assert.equal(fx.locks.length, 0, 'gone LOCK_T after the skill ended (no shell came)');
|
||||
});
|
||||
|
||||
test('a lock sticks to the enemy named in `id` even when its view is a little off the event spot', () => {
|
||||
const { fx, e1 } = setup();
|
||||
fx.simFx('lock', 9, 10, { id: 21, src: 1 }); // e1 drawn at (8, 10): a fast walker between snapshots
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
// Community report (0.2.0, relayed by the owner): 「蕾缪安3技能范围里没人好像会自动结束」 — 蕾缪安's S3 礼炮·强制追思 ended by
|
||||
// itself when nothing was left in her range. The kit ended the skill at the first lock tick with no enemy to lock (once
|
||||
// it had locked one), so the bombardment fell at once and the bullets left were thrown away. Official: an ammo skill
|
||||
// ends when its bullets are spent ("攻击装有5发弹药,打完后结束(可随时停止技能)"); in the client data her S3 lock needs a
|
||||
// target and only the spent bullets end the skill buff (lemuen_s_3 → the S3_End bombardment mode); the 卫戍协议
|
||||
// automation never stops a skill (PRTS 卫戍协议/帮助 技能操作: "通常不会自动关闭技能"). Now the skill waits — bullets and
|
||||
// locks kept, the marks following their enemies — and locks the next enemy that comes into her range at once.
|
||||
//
|
||||
// Flat stage: 蕾缪安 on (10,3) faces right — her 3-9 range covers cols 3–7 on her row, 3–6 one row off, 3–5 two rows off.
|
||||
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { makeBattle, enemyRec, checkInvariants } from '../helpers/battleHarness.js';
|
||||
|
||||
const LEM = 'chess_char_6_01_a';
|
||||
const dummy = (key) => enemyRec({ key, hp: 1e7, speed: 0 });
|
||||
const DEFS = {
|
||||
enemies: {
|
||||
enemy_dummy: dummy('enemy_dummy'), enemy_far: dummy('enemy_far'), enemy_late: dummy('enemy_late'),
|
||||
enemy_walker: enemyRec({ key: 'enemy_walker', hp: 1e7, speed: 1.5 }),
|
||||
},
|
||||
};
|
||||
|
||||
/** 蕾缪安 with a full S3 at once (the harness has no battle-start operation cooldown); h.dmg / h.locks / h.shells logs. */
|
||||
function lemuen(enemies, opts = {}) {
|
||||
const dmg = [], ends = [];
|
||||
const h = makeBattle({
|
||||
defs: DEFS, seed: 7, timeLimit: 200, units: [{ chessId: LEM, row: 10, col: 3, carryState: { sp: 99 } }], enemies,
|
||||
hooks: ['skillStart', 'skillEnd', 'ammoUsed'], ...opts,
|
||||
setup(b) {
|
||||
b.on('damaged', (c) => dmg.push({ src: c.source?.id ?? null, tgt: c.target.id, tags: c.dmg?.tags ?? [], t: b.time }), { priority: -999 });
|
||||
b.on('skillEnd', ({ unit, reason }) => { if (unit.defId === LEM) ends.push({ reason, t: b.time }); });
|
||||
},
|
||||
});
|
||||
const u = h.unit(LEM);
|
||||
const fxOf = (kind) => h.eventsOf('fx').filter((e) => e[1] === kind).map((e) => ({ x: e[2], y: e[3], t: e[4]?.t, ...e[4] }));
|
||||
return { h, u, dmg, ends, locks: () => fxOf('lock'), shells: () => fxOf('bombardShell'), bombs: () => dmg.filter((d) => d.src === u.id && d.tags.includes('bombard')) };
|
||||
}
|
||||
const inRange = (h, u) => h.b.enemiesInKeys(u.rangeKeys, u, u.profile);
|
||||
|
||||
test('the locked enemy dies and nothing else is in her range: S3 keeps running with its bullets and locks — no early bombardment', () => {
|
||||
const { h, u, ends, locks, shells, bombs } = lemuen([{ key: 'enemy_dummy', pos: [10, 5] }, { key: 'enemy_far', pos: [10, 9] }]);
|
||||
assert.ok(h.runUntil(() => u.skill.active, 5), 'S3 cast');
|
||||
const full = u.skill.ammoMax;
|
||||
assert.ok(h.runUntil(() => u.skill.ammoLeft === full - 2, 5), 'two locks');
|
||||
h.b.kill(h.enemy('enemy_dummy'));
|
||||
assert.equal(inRange(h, u).length, 0, 'nothing left in her range (enemy_far on (10,9) is out of it)');
|
||||
h.run(10);
|
||||
assert.equal(u.skill.active, true, 'the skill waits (the report: it ended by itself)');
|
||||
assert.deepEqual(ends, [], 'no skill end');
|
||||
assert.equal(u.skill.ammoLeft, full - 2, 'the bullets left are kept');
|
||||
assert.equal(locks().length, 2, 'no new lock without a target');
|
||||
assert.equal(shells().length, 0, 'no shell before the skill ends');
|
||||
assert.equal(bombs().length, 0, 'no bombardment');
|
||||
assert.equal(u.skill.sp, 0, 'no SP while the ammo skill runs');
|
||||
assert.equal(u.mem.lemLocks.length, 2, 'the locks are kept');
|
||||
assert.ok(locks().every((l) => l.hold === 1), 'fx lock `hold`: the renderer keeps these reticles up while her skill runs');
|
||||
checkInvariants(h.b);
|
||||
});
|
||||
|
||||
test('a locked walker leaves her range: the skill waits; the next enemy that comes is locked at once, and the spent bullets bomb every mark — the walker where it is now', () => {
|
||||
const { h, u, ends, locks, shells, bombs, dmg } = lemuen([{ key: 'enemy_walker', route: { motion: 'WALK', start: [9, 5], end: [9, 10], checkpoints: [] } }]);
|
||||
assert.ok(h.runUntil(() => u.skill.active, 5), 'S3 cast');
|
||||
const walker = h.enemy('enemy_walker');
|
||||
const full = u.skill.ammoMax;
|
||||
assert.ok(h.runUntil(() => inRange(h, u).length === 0, 10), 'the walker walks out of her range');
|
||||
const left = u.skill.ammoLeft;
|
||||
assert.ok(left > 0 && left < full, `locked it ${full - left} times, ${left} bullet(s) left`);
|
||||
h.run(3);
|
||||
assert.equal(u.skill.active, true, 'S3 waits with nothing in range');
|
||||
assert.equal(u.skill.ammoLeft, left, 'bullets kept');
|
||||
assert.ok(walker.alive && inRange(h, u).length === 0, 'the walker is still out of her range');
|
||||
// the next enemy in range is locked the moment it comes (her lock is ready), every 0.5 s after that
|
||||
const late = h.spawn('enemy_late', { pos: [10, 6] });
|
||||
const t0 = h.b.time;
|
||||
h.step();
|
||||
const first = locks().find((l) => l.id === late.id);
|
||||
assert.ok(first, 'the newcomer is locked on the next tick');
|
||||
assert.ok(h.runUntil(() => !u.skill.active, 10), 'the skill ends once the bullets are spent');
|
||||
assert.deepEqual(ends.map((e) => e.reason), ['ammo']);
|
||||
assert.equal(h.hooksOf('ammoUsed').filter((c) => c.unit === u).length, full, 'every bullet spent on a lock');
|
||||
assert.ok(h.b.time - t0 <= (left - 1) * u.def.skill.bb['attack@aim_interval'] + 0.1, 'the newcomer took the bullets left, 0.5 s apart');
|
||||
// the shells follow in lock order: the walker's marks first (it is followed out of her range), then the newcomer's
|
||||
const walkerLocks = full - left;
|
||||
h.run(0.3 * (full + 2));
|
||||
const s = shells();
|
||||
assert.equal(s.length, full, 'one shell per lock');
|
||||
const spread = u.def.skill.bb['attack@emit_offset'] + 0.01;
|
||||
for (let i = 0; i < walkerLocks; i++) assert.ok(s[i].x > 6.5, `shell ${i} falls on the walker out of her range (x ${s[i].x.toFixed(2)})`);
|
||||
for (let i = walkerLocks; i < full; i++) assert.ok(Math.abs(s[i].x - 6) <= spread && Math.abs(s[i].y - 10) <= spread, `shell ${i} on the newcomer`);
|
||||
assert.equal(bombs().filter((d) => d.tgt === walker.id).length, walkerLocks, 'the walker is hit by its own shells only');
|
||||
assert.equal(bombs().filter((d) => d.tgt === late.id).length, left, 'the newcomer by its own');
|
||||
assert.equal(dmg.filter((d) => d.src === u.id && !d.tags.includes('bombard') && d.t < ends[0].t).length, 0, 'no normal attack during S3');
|
||||
checkInvariants(h.b);
|
||||
});
|
||||
|
||||
test('the last enemy dies while she waits: the battle ends cleared, nothing more to bomb, no error', () => {
|
||||
const { h, u, shells } = lemuen([{ key: 'enemy_dummy', pos: [10, 5] }]);
|
||||
assert.ok(h.runUntil(() => u.skill.active && u.mem.lemLocks?.length === 2, 5), 'two locks');
|
||||
h.b.kill(h.enemy('enemy_dummy'));
|
||||
assert.ok(h.runUntil(() => h.b.finished, 5), 'the battle ends');
|
||||
assert.equal(h.result().reason, 'cleared');
|
||||
assert.equal(shells().length, 0, 'no shell after the battle');
|
||||
assert.deepEqual(h.b.contentErrors ?? [], []);
|
||||
checkInvariants(h.b);
|
||||
});
|
||||
Reference in new issue
Block a user