Playtest #4 fixes: tile picking, knocked-down state, element gauges, draft timer, live stats, shop items, drones, 赫默, 炎佑, UI flash

- #1 picking by the tile under the pointer (prep, battle, pen, equipment drops); the dragged model stays
  under the pointer (mouse and touch); pixel probe removed; phone ghost-click on the underframe fixed
- #2 two-tap confirm on 机变 cards (like the shop)
- #3 a 同盟 room with one human is untimed like 独立模拟 (no countdown outside battles)
- #4 strategy draft: one synced countdown, 30 s per human turn, timeout takes the highlighted band
- #5 special 维式重锤 (4) and 突变细胞 no longer sold; item card shows where they come from
- #6 operator skill-mode sounds no longer play on normal attacks (纯烬艾雅法拉 and 55 others)
- #7 detail card shows live battle stats and the prep-time battle-start stats (g.unitStats)
- #8 official element gauges on units; burst cooldown (阻回) per PRTS
- #9 knocked-down operators stay on their tile with a redeploy countdown ring
- #10 drone behaviour per PRTS (暴鸰, 先兆者 shells, 黑云, 萨科塔之眼)
- #11 赫默's drone only from her skill; placeable summons limited to talent tokens
- #12 炎佑 stays where it is with no enemies; 祛恶之焰 as a continuous channel
- #13 the occasional giant bond icon (loading texture scaled ×46) fixed
- docs (DESIGN §18), recordings, data and tests updated (2467 unit tests, all browser suites green)
This commit is contained in:
sganggs committed 2026-09-30 05:35:45 +08:00
1 parent 0aba602ecf
commit d4f868cd19
107 files changed
+4510 -1653

No files matched your search

+5
View File
@@ -37,6 +37,11 @@ input, textarea, select { -webkit-user-select: text; user-select: text; }
/* the selected unit's underframe (ui/underframe.js) is placed at the tile's CLIENT px (view.tileScreen) inside the HUD
layer: step back over the HUD's safe-area offset so it stays on its unit on a notched phone */
.gm__hud > .uframe { margin-left: calc(-1 * var(--sa-l)); margin-top: calc(-1 * var(--sa-t)); }
/* the shop bar keeps its place at the bottom edge (it may reach into the home-indicator strip): raised by the bottom
inset it covered ~3/4 of the bench pads 2–9 on a notched phone (844×390 with the iPhone insets: 23 % of those tiles
left free, 71 % now — as without insets), and a bench piece is pressed / dropped on by its tile (user playtest #4
item 1). Its right edge stays inside the inset (the notch / rounded corner). */
.gm__hud > .shopbar { bottom: calc(.2rem - var(--sa-b)); }
.gm > .spov { left: calc(2.8rem + var(--sa-l)); right: var(--sa-r); }
/* every other screen is laid out inside the safe area (the app backdrop fills the notch strip) */
.screen:not(.gm) { top: var(--sa-t); right: var(--sa-r); bottom: var(--sa-b); left: var(--sa-l); }
+30
View File
@@ -111,6 +111,19 @@
.dstat__row { display: flex; align-items: baseline; min-width: 0; white-space: nowrap; }
.dstat__v { font-size: .17rem; line-height: 1.15; color: #fff; }
.dstat small { font-size: .11rem; color: var(--text-dim); margin-left: .04rem; }
/* live stats (user playtest #4 item 7): buffed values green, debuffed red (the official card), the difference beside
them; a 实时 (battle) / 开战时 (prep preview) tag over the grid's right edge */
.dstats.is-live, .dstats-wrap.is-live { margin-top: .14rem; }
.dstats.is-live, .dstats-wrap.is-live .dstats { position: relative; }
.dstat.is-up .dstat__v { color: #6dfa9c; text-shadow: 0 0 .08rem rgba(80, 240, 140, .35); }
.dstat.is-up small { color: #56e08a; }
.dstat.is-down .dstat__v { color: var(--red-premium); text-shadow: 0 0 .08rem rgba(255, 84, 84, .3); }
.dstat.is-down small { color: #ff8a80; }
.dstats__tag {
position: absolute; right: -1px; bottom: 100%; padding: .01rem .08rem; font-size: .11rem; font-weight: 700; letter-spacing: .1em;
color: var(--text-on-mint); background: var(--mint-500); pointer-events: auto; cursor: help;
}
.dstats__tag.is-battle { background: var(--orange); color: #1d0c00; }
.drange { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .06rem; padding: .06rem .1rem; background: #0f1311; border: 1px solid var(--line); min-width: 1.1rem; }
.rgrid { --rg: .14rem; display: grid; gap: 2px; }
.rgrid i { width: var(--rg); height: var(--rg); border: 1px solid rgba(255, 255, 255, .06); }
@@ -370,6 +383,23 @@
font-size: .14rem; font-weight: 700; letter-spacing: .2em; color: var(--mint-400); background: rgba(8, 11, 10, .85);
}
.spcard__busy::after { content: ''; position: absolute; left: 0; bottom: 0; width: 40%; height: 2px; background: var(--mint-400); animation: btn-busy 1.1s var(--ease-in-out) infinite; }
/* two-tap pick (user playtest #4 item 2 — the shop's confirm language, game-shop.css .scard.is-armed): the selected card
lifts with a gold frame and a 确认选择 · 再次点击 strip; the others dim; the header shows 确认选择 */
.spcard.is-armed, .spcard.is-armed:hover {
z-index: 2; transform: translateY(-.1rem) scale(1.03); border-color: var(--gold);
background: linear-gradient(180deg, rgba(255, 198, 0, .16), rgba(255, 198, 0, .04));
box-shadow: 0 0 0 2px var(--gold), 0 .14rem .32rem rgba(0, 0, 0, .6), 0 0 .3rem rgba(255, 198, 0, .35);
}
.spov.has-armed .spcard.is-pickable:not(.is-armed) { filter: brightness(.7); }
.spov.has-armed .spcard.is-pickable:not(.is-armed):hover { filter: none; }
.spcard__confirm {
position: absolute; left: 0; right: 0; bottom: 0; z-index: 3; display: flex; flex-direction: column; align-items: center;
padding: .05rem 0 .05rem; background: linear-gradient(0deg, var(--gold), #e0a800); color: #1d1400; pointer-events: none;
animation: confirm-in var(--t-fast) var(--ease-out);
}
.spcard__confirm b { font-size: .18rem; font-weight: 900; letter-spacing: .22em; padding-left: .22em; }
.spcard__confirm small { font-size: .11rem; letter-spacing: .1em; opacity: .75; }
.spov__confirm { animation: pulse-gold 1.2s var(--ease-in-out) infinite; }
/* Grid image boxes: definite 100% tracks so `height: 100%` images resolve against the box (an auto track would
let non-square art overflow its frame). */
+3 -4
View File
@@ -316,10 +316,9 @@
font-size: .8em; font-weight: 700; color: var(--text-on-mint); background: var(--mint-500); border-radius: 1em;
}
/* Untimed phases (user playtest #11: the official 独立模拟 shows no countdown outside combat — the server sends
deadline 0 for them, Match.soloUntimed): ui/components.js Countdown renders its "无倒计时" placeholder ("--" digits +
COUNTDOWN label), which reads as a timer — it is not shown at all. (Owner of components.js: returning null there
for a missing deadline makes this rule a no-op.) */
/* Untimed phases (user playtest #11 / #4 item 3: the official 独立模拟 shows no countdown outside combat — the server
sends deadline 0 for them, Match.soloUntimed): ui/components.js Countdown now renders nothing without a deadline;
this rule stays as a safety net for the old "无倒计时" placeholder ("--" digits + COUNTDOWN label). */
.countdown[aria-label="无倒计时"] { display: none; }
/* the app screens behind the overlay do not need to animate */