0.2.6-dev: a piece has no screen rect until it is first drawn — pieceScreenRect returned a just-built view's unprojected (0, 0) record, so a browser test grabbed a piece bought that frame at the canvas corner (the intermittent .fwheel__dia timeout in coop.e2e / real.e2e); the tests' piecePoint waits for a drawn piece that holds still; golden: unchanged
This commit is contained in:
1 parent
2b685011e9
commit
1df530f2b9
4 files changed
+38
-9
No files matched your search
@@ -47,6 +47,7 @@
|
||||
// a special terrain tile's own tip; the screen resolves it with gameLogic.terrainInfo)
|
||||
// tileHover { row, col, area, idx } | null (while dragging: the drop target — the tile under the pointer)
|
||||
// view.pieceScreenRect(uid) → { left, top, right, bottom, width, height, x, y } (client px: the drawn body) | null
|
||||
// (null also until the piece is first drawn)
|
||||
// Picking (user playtest #4 item 1: the ground is drawn as tiles — a press on a tile is a press on the unit standing
|
||||
// there): every "which unit is under the pointer" — prep press / click / detail / drag start / hover (pieceAt), battle
|
||||
// clicks and hover (battleUnitAt), the pen (penUnitAt) — is render/pick.js over the tile under the pointer (groundTile:
|
||||
@@ -1939,7 +1940,8 @@ export async function createFieldView(host, options = {}) {
|
||||
pieceScreenRect(uid) {
|
||||
let v = views.get('p:' + uid);
|
||||
if (!v) for (const x of views.values()) if (x.uid === uid || x.info?.uid === uid || x.id === uid) { v = x; break; }
|
||||
if (!v || v.destroyed) return null;
|
||||
// a view built this frame is placed by its first update: before that its screen record is still (0, 0)
|
||||
if (!v || v.destroyed || !v.placed) return null;
|
||||
const b = v.bounds();
|
||||
const r = canvas.getBoundingClientRect();
|
||||
const left = r.left + b.x, top = r.top + b.y;
|
||||
|
||||
@@ -632,6 +632,7 @@ export class UnitView {
|
||||
this.lastAtk = -1; this.atkInterval = defaultInterval(ctx, info);
|
||||
this.shake = 0;
|
||||
this.screen = { x: 0, y: 0, s: 1, top: 0 };
|
||||
this.placed = false; // set by the first update's projection: until then `screen` is not on screen (bounds)
|
||||
this.destroyed = false;
|
||||
this.form = typeof info.form === 'string' ? info.form : null; // the unit's model form: an enemy's mode, a 傀儡师's 替身 (setForm, FORMS)
|
||||
this._dieForm = null; // the form it died in (die): a model built while it lies down shows that form's death
|
||||
@@ -1337,6 +1338,7 @@ export class UnitView {
|
||||
const hoverTo = this.flying && (this.alive || (this.dying > DIE_FADE_TAIL && !this.down)) ? FLY_HOVER : 0;
|
||||
if (this.hover !== hoverTo) this.hover = Math.abs(hoverTo - this.hover) < 1e-3 ? hoverTo : this.hover + (hoverTo - this.hover) * Math.min(1, dt * 6);
|
||||
const p = cam.project(this.x, this.y, this.z + this.hover + this.lift, this.screen);
|
||||
this.placed = true;
|
||||
const s = p.s;
|
||||
// fades
|
||||
if (this.fadeIn < 1) this.fadeIn = Math.min(1, this.fadeIn + dt * 4);
|
||||
@@ -2143,6 +2145,7 @@ export class ItemView {
|
||||
this.alive = true; this.remove = false; this.lift = 0; this.dimmed = false;
|
||||
this.bob = Math.random() * 6;
|
||||
this.screen = { x: 0, y: 0, s: 1, top: 0 };
|
||||
this.placed = false;
|
||||
this.shadow = new P.Sprite(ctx.shadowTex || shadowTexture());
|
||||
this.shadow.anchor.set(0.5);
|
||||
ctx.layers.shadow.addChild(this.shadow);
|
||||
@@ -2169,6 +2172,7 @@ export class ItemView {
|
||||
update(dt, cam, t) {
|
||||
const lifted = this.lift > 0;
|
||||
const p = cam.project(this.x, this.y, lifted ? this.z : this.z + this.lift + 0.12 + Math.sin(t * 2 + this.bob) * 0.03, this.screen);
|
||||
this.placed = true;
|
||||
this.root.position.set(p.x, p.y);
|
||||
this.root.zIndex = unitDepthKey(cam, this.x, this.y, this.lift);
|
||||
const size = p.s * 0.62;
|
||||
@@ -2214,6 +2218,7 @@ export class DeviceView {
|
||||
this.facing = info.facing === -1 ? -1 : 1;
|
||||
this.aim = this.facing > 0 ? 0 : Math.PI; this.aimTo = this.aim; this.fire = 0;
|
||||
this.screen = { x: 0, y: 0, s: 1, top: 0 };
|
||||
this.placed = false;
|
||||
this._headTiles = this.spec.height + 0.2;
|
||||
this.box = ctx.createBox ? ctx.createBox() : null;
|
||||
this.gfx = new P.Graphics(); // fallback body (no tile field) and the turret head
|
||||
@@ -2270,6 +2275,7 @@ export class DeviceView {
|
||||
g.zIndex = zKey + 0.001;
|
||||
g.alpha = k;
|
||||
const p = cam.project(this.x, this.y, this.z + this.spec.height + 0.35, this.screen);
|
||||
this.placed = true;
|
||||
const show = this.alive && this.hp < this.maxHp - 0.5;
|
||||
this.hud.visible = show;
|
||||
if (show) {
|
||||
|
||||
+15
-4
@@ -286,12 +286,23 @@ export class Client {
|
||||
viewKind() { return this.page.evaluate(() => globalThis.__SP_VIEW__?.kind ?? null); }
|
||||
viewStats() { return this.page.evaluate(() => { try { return globalThis.__SP_VIEW__?.raw?.stats?.() ?? null; } catch { return null; } }); }
|
||||
|
||||
/** Client point to grab a prep piece (`at` = fraction of the piece rect's height, from the top). */
|
||||
piecePoint(uid, at = 0.72) {
|
||||
return this.page.evaluate((uid, at) => {
|
||||
/** Client point to grab a prep piece (`at` = fraction of the piece rect's height, from the top). Waits (up to 2.5 s)
|
||||
* until the piece is drawn and holds still: a piece bought this frame has no rect yet, and one still sliding to its
|
||||
* tile (0.22 s) would be grabbed over a neighbouring tile — picking is by the tile under the pointer. */
|
||||
async piecePoint(uid, at = 0.72) {
|
||||
const read = () => this.page.evaluate((uid, at) => {
|
||||
const r = globalThis.__SP_VIEW__?.pieceScreenRect(uid);
|
||||
return r && r.width > 0 ? { x: r.left + r.width / 2, y: r.top + r.height * at } : null;
|
||||
if (!r || !(r.width >= 4)) return null;
|
||||
const x = r.left + r.width / 2, y = r.top + r.height * at;
|
||||
return x >= 0 && y >= 0 && x <= innerWidth && y <= innerHeight ? { x, y } : null;
|
||||
}, uid, at);
|
||||
let last = null;
|
||||
for (const end = Date.now() + 2500; Date.now() < end; await sleep(80)) {
|
||||
const p = await read();
|
||||
if (p && last && Math.abs(p.x - last.x) < 1.5 && Math.abs(p.y - last.y) < 1.5) return p;
|
||||
last = p;
|
||||
}
|
||||
return last;
|
||||
}
|
||||
|
||||
/** Record the view's pieceDrop events (for diagnostics) and return + clear them. */
|
||||
|
||||
@@ -145,12 +145,22 @@ class Client {
|
||||
viewKind() { return this.page.evaluate(() => globalThis.__SP_VIEW__?.kind ?? null); }
|
||||
viewStats() { return this.page.evaluate(() => { try { return globalThis.__SP_VIEW__?.raw?.stats?.() ?? null; } catch { return null; } }); }
|
||||
|
||||
/** Client point to grab a prep piece. */
|
||||
piecePoint(uid) {
|
||||
return this.page.evaluate((uid) => {
|
||||
/** Client point to grab a prep piece. Waits (up to 2.5 s) until the piece is drawn and holds still: a piece bought
|
||||
* this frame has no rect yet, and one still sliding to its tile would be grabbed over a neighbouring tile. */
|
||||
async piecePoint(uid) {
|
||||
const read = () => this.page.evaluate((uid) => {
|
||||
const r = globalThis.__SP_VIEW__?.pieceScreenRect(uid);
|
||||
return r && r.width > 0 ? { x: r.left + r.width / 2, y: r.top + r.height * 0.72 } : null;
|
||||
if (!r || !(r.width >= 4)) return null;
|
||||
const x = r.left + r.width / 2, y = r.top + r.height * 0.72;
|
||||
return x >= 0 && y >= 0 && x <= innerWidth && y <= innerHeight ? { x, y } : null;
|
||||
}, uid);
|
||||
let last = null;
|
||||
for (const end = Date.now() + 2500; Date.now() < end; await sleep(80)) {
|
||||
const p = await read();
|
||||
if (p && last && Math.abs(p.x - last.x) < 1.5 && Math.abs(p.y - last.y) < 1.5) return p;
|
||||
last = p;
|
||||
}
|
||||
return last;
|
||||
}
|
||||
|
||||
/** Client point of a board tile centre (top face). */
|
||||
|
||||
Reference in new issue
Block a user