feat(render): 干员入场、换位及回合开始时播放入场动画与落地音效
This commit is contained in:
1 parent
3eced7bdba
commit
c44a484e11
5 files changed
+235
-6
No files matched your search
+30
-2
@@ -252,6 +252,7 @@ export async function createFieldView(host, options = {}) {
|
||||
// or 机变 grant at ROUND_START, a SETTLE merge) is still recognised by the next setPrep (QA 6b)
|
||||
let promoBase = [];
|
||||
const promotions = []; // the last merges cued by setPrep (fx.promote): { uid, id, area, row, col, idx, copies } — dev / tests
|
||||
const deployTimers = new Set();
|
||||
let battleMeta = null;
|
||||
let sceneHold = null; // release function of the Spine cache hold of battle mode (holdScene)
|
||||
const infos = new Map(); // battle unit id → UnitInfo
|
||||
@@ -290,6 +291,7 @@ export async function createFieldView(host, options = {}) {
|
||||
const heightAt = (r, c) => (tiles ? tiles.heightAt(r, c) : 0);
|
||||
const ctx = {
|
||||
P, layers, assets, settings, fx: null, shadowTex: shadowTexture(),
|
||||
audio: opts.audio || options.audio || null,
|
||||
cam: () => cam, heightAt,
|
||||
animRate: () => (mode === 'battle' ? interp.rate : 1),
|
||||
timeScale: () => (mode === 'battle' ? interp.rate : 1),
|
||||
@@ -742,12 +744,12 @@ export async function createFieldView(host, options = {}) {
|
||||
views.set(key, v);
|
||||
if (promoFrom.has(e.uid)) {
|
||||
// a merge's elite: on the tile of the deployed copy it replaced, or on its bench slot
|
||||
if (e.area === 'board') v.onDeploy?.();
|
||||
if (e.area === 'board') v.onDeploy?.(); else v.onDeploy?.();
|
||||
fx.promote(v, promoFrom.get(e.uid).filter((f) => Math.abs(f.x - w.x) + Math.abs(f.y - w.y) > 1e-3));
|
||||
promotions.push({ uid: e.uid, id: e.piece.id, area: e.area, row: e.row ?? null, col: e.col ?? null, idx: e.idx ?? null, copies: promoFrom.get(e.uid).length });
|
||||
if (promotions.length > 20) promotions.shift();
|
||||
} else if (e.area === 'board' && prevBoard.size && !prevBoard.has(e.uid)) { v.onDeploy?.(); fx.deploy(v); }
|
||||
else if (before.length && (e.area === 'hand' || e.area === 'temp') && !before.some((g) => g.uid === e.uid)) fx.deploy(v);
|
||||
else if (before.length && (e.area === 'hand' || e.area === 'temp') && !before.some((g) => g.uid === e.uid)) { v.onDeploy?.(); fx.deploy(v); }
|
||||
} else {
|
||||
const prevHome = v._home;
|
||||
const moved = !prevHome || prevHome.x !== w.x || prevHome.y !== w.y || prevHome.z !== w.z;
|
||||
@@ -760,6 +762,7 @@ export async function createFieldView(host, options = {}) {
|
||||
if (moved || Math.abs(v.x - w.x) + Math.abs(v.y - w.y) > 1e-3) v._tween = { fx: v.x, fy: v.y, fz: v.z, tx: w.x, ty: w.y, tz: w.z, t: 0 };
|
||||
v.lift = 0;
|
||||
if (e.area === 'board' && !prevBoard.has(e.uid) && v.onDeploy) { v.onDeploy(); fx.deploy(v); }
|
||||
else if (moved && v.onDeploy) { v.onDeploy(); }
|
||||
}
|
||||
}
|
||||
v._home = w;
|
||||
@@ -1780,6 +1783,29 @@ export async function createFieldView(host, options = {}) {
|
||||
v.setDir(prepXf.dirToDisp(dir.toUpperCase()));
|
||||
return true;
|
||||
},
|
||||
deployPiece(uid, force = false) {
|
||||
if (destroyed || !Number.isInteger(uid)) return false;
|
||||
const v = views.get('p:' + uid);
|
||||
if (v && !v.destroyed && typeof v.onDeploy === 'function') {
|
||||
v.onDeploy(force);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
deploySequence(uids, delayMs = 50) {
|
||||
if (destroyed || !Array.isArray(uids)) return;
|
||||
uids.forEach((uid, i) => {
|
||||
const tid = setTimeout(() => {
|
||||
deployTimers.delete(tid);
|
||||
if (destroyed) return;
|
||||
const v = views.get('p:' + uid);
|
||||
if (v && !v.destroyed && typeof v.onDeploy === 'function') {
|
||||
v.onDeploy(true);
|
||||
}
|
||||
}, i * delayMs);
|
||||
deployTimers.add(tid);
|
||||
});
|
||||
},
|
||||
setSettings(s) {
|
||||
if (!s || typeof s !== 'object') return;
|
||||
const q = settings.quality;
|
||||
@@ -1818,6 +1844,8 @@ export async function createFieldView(host, options = {}) {
|
||||
clearTimeout(recover.timer);
|
||||
drag.reset();
|
||||
for (const k of [...views.keys()]) dropView(k);
|
||||
for (const tid of deployTimers) clearTimeout(tid);
|
||||
deployTimers.clear();
|
||||
clearPen();
|
||||
holdScene(false); // no scene any more: the quiet budget may free the skeletons (lobby / room / result)
|
||||
try { fx.destroy(); } catch { /* ignore */ }
|
||||
|
||||
@@ -458,6 +458,7 @@ export class UnitView {
|
||||
this.spineReady = false;
|
||||
this._modelDirty = false; // died / stood up since the last frame: update() checks Front ⇄ Back (_syncModel)
|
||||
this._spineBusy = false; // a Spine load of this view is in flight
|
||||
this._deployPending = false; // deploy() was requested before the Spine model finished loading
|
||||
this._spineTries = 0; // failed loads since the last model (SPINE_RETRY_MS)
|
||||
this._retryAt = 0; // when the next retry is due (ms, performance clock; 0 = none)
|
||||
this._spineHidden = false; // the load in flight began while the tab was hidden (SPINE_STUCK_MS)
|
||||
@@ -599,7 +600,14 @@ export class UnitView {
|
||||
} else {
|
||||
if (this.flags & UF.SKILL) this.actor.setSkill(true);
|
||||
this.actor.setBase(this._baseFromAnim());
|
||||
if (deployed != null) { this.actor.deploy(); if (deployed > 0) this.actor.update(deployed); }
|
||||
if (deployed != null) {
|
||||
this.actor.deploy();
|
||||
if (deployed > 0) this.actor.update(deployed);
|
||||
} else if (this._deployPending) {
|
||||
this.actor.deploy();
|
||||
this._playDeploySound();
|
||||
this._deployPending = false;
|
||||
}
|
||||
}
|
||||
}, () => {
|
||||
if (req === this._spineReq) { this._spineBusy = false; this._stuckAt = 0; }
|
||||
@@ -879,10 +887,30 @@ export class UnitView {
|
||||
if (this.actor) this.actor.setSkillIndex(index);
|
||||
}
|
||||
|
||||
onDeploy() {
|
||||
onDeploy(force = false) {
|
||||
this.fadeIn = 0;
|
||||
if (!this.alive) this.revive();
|
||||
if (this.actor) this.actor.deploy();
|
||||
if (this.actor) {
|
||||
const elapsed = this.actor.deployElapsed();
|
||||
if (force || elapsed == null || elapsed > 0.3) {
|
||||
this.actor.deploy();
|
||||
this._playDeploySound();
|
||||
}
|
||||
this._deployPending = false;
|
||||
} else {
|
||||
this._deployPending = true;
|
||||
}
|
||||
}
|
||||
|
||||
_playDeploySound() {
|
||||
if (this.isEnemy) return;
|
||||
const a = this.ctx?.audio;
|
||||
if (!a) return;
|
||||
const def = this.info?.spine || this.info?.defId;
|
||||
const played = def && typeof a.unit === 'function' ? a.unit(def, 'born', this.id) : false;
|
||||
if (!played && typeof a.battle === 'function') {
|
||||
a.battle(this.isToken ? 'tokenDeploy' : 'deploy', { unitKey: `deploy:${this.id}`, volume: 0.7 });
|
||||
}
|
||||
}
|
||||
|
||||
onStatus(key, on) {
|
||||
|
||||
Reference in new issue
Block a user