feedback1 WD: form changes survive the client's catch-up frames, hidden tab and pre-entry buffer
Every setForm fx now carries 'form' (shared/protocol.js fxForm). The runner keeps these fx in catch-up frames (keepsState), holds the state-bearing events of the battle on screen while the tab is hidden and delivers them with the first frame back (re-entering from the field meta past HELD_MAX); screens/game.js buffers them before a field is entered. A husk beaten while its stand-up clip plays dies on the husk's clip. 幽灵 form cited from PRTS; 特战术师 flyer question recorded.
This commit is contained in:
1 parent
e1ce0316dd
commit
2591b8638e
11 files changed
+382
-47
No files matched your search
+16
-12
@@ -21,8 +21,10 @@
|
||||
// operators on the field under a redeploy ring and `elem` draws the
|
||||
// element gauges (user playtest #4 items 8 / 9, render/units.js); a
|
||||
// 'die' with reason FORCED_EXIT (an operator entering 联防 knocked out,
|
||||
// user playtest #5 item 2) goes straight to the held pose, no burst
|
||||
// view.setLocalFeed({ on, speed }) frames come from the local sim every frame (client-side combat):
|
||||
// user playtest #5 item 2) goes straight to the held pose, no burst; an
|
||||
// fx with a `form` (shared/protocol.js fxForm) switches the enemy's
|
||||
// model to that clip set (render/units.js FORMS)
|
||||
// view.setLocalFeed({ on, speed }) frames come from the local sim every frame (client-side combat):
|
||||
// ~2-frame buffer at the battle's game speed
|
||||
// view.highlightTiles(tiles, style) [[r,c]] | [{row,col}]; style 'legal'|'illegal'|'range'|'rangeStand'|
|
||||
// 'hover'|'target'|{color,fill,line,group}; highlightTiles(null) clears all
|
||||
@@ -89,6 +91,7 @@
|
||||
// `data` is the client data store (public/js/data.js: lookup(file, id)) or plain { chess, tokens, items, enemies } maps.
|
||||
|
||||
import { GEO, ANIM, UF } from '../../../shared/constants.js';
|
||||
import { fxForm } from '../../../shared/protocol.js';
|
||||
import { Camera, presetCamera, lerpCamera, easeInOutCubic, pickTile, normRect } from './projection.js';
|
||||
import { SnapshotBuffer, frameTime } from './interp.js';
|
||||
import { TileField } from './tiles.js';
|
||||
@@ -1430,24 +1433,25 @@ export async function createFieldView(host, options = {}) {
|
||||
break;
|
||||
}
|
||||
case 'status': { const v = views.get(e[1]); if (v) v.onStatus?.(e[2], !!e[3]); break; }
|
||||
case 'fx':
|
||||
case 'fx': {
|
||||
if (e[4] && typeof e[4] === 'object' && e[4].consumed && e[4].id != null) {
|
||||
consumedIds.add(e[4].id);
|
||||
if (consumedIds.size > 200) consumedIds.delete(consumedIds.values().next().value);
|
||||
}
|
||||
// an enemy's mode change (掠海漂移体 → 爬行模式, user playtest #5 item 1; 转译基底's forms, a 逐火 ember and its
|
||||
// revival, the leaders' 重生 — user report after 0.1.0): a 'phase' kind, or the `form` any other fx carries,
|
||||
// switches the view's clip set (UnitView.setForm, a kind without a clip set of that skeleton changes nothing);
|
||||
// the info keeps it for a view built later
|
||||
const ex4 = e[4] && typeof e[4] === 'object' && e[4].id != null ? e[4] : null;
|
||||
const form = !ex4 ? undefined : e[1] === 'phase' ? ex4.kind : 'form' in ex4 ? ex4.form : undefined;
|
||||
// an enemy's mode change — the `form` of a sim setForm fx (shared/protocol.js fxForm: 掠海漂移体 → 爬行模式, user
|
||||
// playtest #5 item 1; 转译基底's forms, a 逐火 ember and its revival, the leaders' 重生, 守墓石像 — user report after
|
||||
// 0.1.0) — switches the view's clip set (UnitView.setForm; a kind without a clip set of that skeleton changes
|
||||
// nothing); the info keeps it for a view built later. The client runner never drops these fx (catch-up frames,
|
||||
// hidden-tab backlog), nor does the game screen's pre-entry buffer.
|
||||
const form = fxForm(e);
|
||||
if (form !== undefined) {
|
||||
const inf = infos.get(ex4.id);
|
||||
if (inf && (typeof form !== 'string' || FORMS[inf.spine || inf.defId]?.[form])) inf.form = typeof form === 'string' ? form : null;
|
||||
views.get(ex4.id)?.setForm?.(form, ex4);
|
||||
const inf = infos.get(e[4].id);
|
||||
if (inf && (form === null || FORMS[inf.spine || inf.defId]?.[form])) inf.form = form;
|
||||
views.get(e[4].id)?.setForm?.(form, e[4]);
|
||||
}
|
||||
fx.simFx(e[1], Number(e[2]), Number(e[3]), e[4]);
|
||||
break;
|
||||
}
|
||||
case 'layer': {
|
||||
const bondId = e[2], n = Number(e[3]) || 0;
|
||||
if (!(n > 0) || typeof bondId !== 'string') break;
|
||||
|
||||
@@ -109,7 +109,8 @@ export const EL_BAR = Object.freeze({ icon: 0.15, min: 8, max: 15, enemy: 0.8, g
|
||||
* - 转译基底·α (user report after 0.1.0, #5): its 2 s change clip A_Die_B / _C / _D, then 寻仇者 B_*, 幽灵 C_* (no attack
|
||||
* clip: it never attacks) or 特战术师 D_* — its manifest roles are the original form's A_Idle / A_Move;
|
||||
* - 深池逐火战士 / 精锐战士 / 护卫 (#8): knocked out → 'Die' (the 1 s 重生), the 余烬 on Idle_2 / Move_2 and its death on
|
||||
* Die_2; standing up again → 'Revive', then the warrior's manifest clips;
|
||||
* Die_2 (also while 'Revive' plays: the ember can still be beaten); standing up again → 'Revive', then the warrior's
|
||||
* manifest clips;
|
||||
* - 假想敌:再生: knocked out → A_Die, the 傀儡 on B_Idle / B_Move / B_Die; back → B_Revive, then the A_* manifest clips;
|
||||
* (both: the stand-up clip (`end`) is timed from the 'ember' fx's `dur` to end as the husk stands up — the 'revive'
|
||||
* fx then only lands in the manifest clips);
|
||||
@@ -393,11 +394,11 @@ export class UnitView {
|
||||
}
|
||||
|
||||
/**
|
||||
* The unit changed mode (sim fx 'phase' { id, kind } or another fx's `form`; `fx` = that fx's extra): the mode's clip
|
||||
* The unit changed mode (the `form` of a sim fx — shared/protocol.js fxForm; `fx` = that fx's extra): the mode's clip
|
||||
* set (FORMS) after its change clip, and its closing clip (`end`, landing in the `next` form's clips) timed to end
|
||||
* `fx.dur` game s later; null goes back
|
||||
* to the manifest clips; a kind this skeleton has no clip set for (an arts barrier, a broken charge …) changes
|
||||
* nothing. Kept for a model built later.
|
||||
* `fx.dur` game s later — the unit is still in this mode while it plays (an ember can be beaten in its last second),
|
||||
* so this mode's death clip stays until the next mode's fx; null goes back to the manifest clips; a kind this skeleton
|
||||
* has no clip set for (an arts barrier, a broken charge …) changes nothing. Kept for a model built later.
|
||||
*/
|
||||
setForm(kind, fx = null) {
|
||||
const k = typeof kind === 'string' ? kind : null;
|
||||
@@ -409,7 +410,8 @@ export class UnitView {
|
||||
const f = this._formSpec();
|
||||
if (!this.actor) return;
|
||||
const dur = fx && Number(fx.dur);
|
||||
const next = f && f.next ? FORMS[this.info.spine || this.info.defId]?.[f.next]?.roles || null : null;
|
||||
let next = f && f.next ? FORMS[this.info.spine || this.info.defId]?.[f.next]?.roles || null : null;
|
||||
if (next && typeof f.roles.die === 'string') next = { ...next, die: f.roles.die };
|
||||
if (f) this.actor.setForm(f.roles, f.change || null, f.end && dur > 0 ? { clip: f.end, in: dur, roles: next } : null);
|
||||
else if (had) this.actor.setForm(null);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user