Remove rank stamp from combat bar seats
- Drop the `rankT` text object, its draw/update logic and data spec (`rankTextPx`) so seats only show icon, veil, sweep and seconds. - Update `_ensureSkillIcons` fallback wording to reference frame/veil/sweep instead of rank stamp. - Adjust browser test: stop asserting on rank text, recompute seat clip from the seat box center, and remove the rank-pixel regression check. - Refresh screenshot fixtures (`dev/shots/*`) to match the new seat layout.
|
|
@ -25,13 +25,13 @@
|
|||
"seatOutOfRangePolicy": "ignore"
|
||||
},
|
||||
"bar": {
|
||||
"_comment": "How a SEATED skill is DRAWN on the combat bar (js/ui/CombatBar.js): the icon size, the state veils, the recharge sweep and the rank stamp. Behaviour lives in code — js/combat/CombatSystem.js → skillSeats() decides WHICH state a seat is in (ready / recharging / not affordable / inert / empty) and this block says what each one LOOKS like. Sizes: the icon is a FRACTION of the seat (the bar scales its seats down on narrow viewports, so absolute pixels would break at 1024px wide); the two text sizes are px inside a ~44px seat. Colours stay CSS strings — the bar converts them at draw time (js/skills/SkillEffects.js → hexToInt).",
|
||||
"_comment": "How a SEATED skill is DRAWN on the combat bar (js/ui/CombatBar.js): the icon size, the state veils and the recharge sweep. Behaviour lives in code — js/combat/CombatSystem.js → skillSeats() decides WHICH state a seat is in (ready / recharging / not affordable / inert / empty) and this block says what each one LOOKS like. Sizes: the icon is a FRACTION of the seat (the bar scales its seats down on narrow viewports, so absolute pixels would break at 1024px wide); the text size is px inside a ~44px seat. Colours stay CSS strings — the bar converts them at draw time (js/skills/SkillEffects.js → hexToInt).",
|
||||
"iconScale": 0.76,
|
||||
"rankTextPx": 9,
|
||||
"secondsTextPx": 13,
|
||||
"assignPulseHz": 1.6,
|
||||
"states": {
|
||||
"_comment": "Draw order: the icon, then a veil over it (an unranked skill is buried, a too-expensive one is dimmed blue), then the recharge SWEEP — a dark pie that unwinds clockwise as the skill recharges — with the SECONDS LEFT printed over it, then the rank stamp in the corner. `readyEdge` is the outline a live-and-charged seat wears; `assignEdge` outlines every seat while the tree is holding a skill up waiting to be placed.",
|
||||
"_comment": "Draw order: the icon, then a veil over it (an unranked skill is buried, a too-expensive one is dimmed blue), then the recharge SWEEP — a dark pie that unwinds clockwise as the skill recharges — with the SECONDS LEFT printed over it. `readyEdge` is the outline a live-and-charged seat wears; `assignEdge` outlines every seat while the tree is holding a skill up waiting to be placed.",
|
||||
"readyEdge": "#7ce8ff",
|
||||
"inertVeil": "#050a13",
|
||||
"inertVeilAlpha": 0.74,
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 513 KiB After Width: | Height: | Size: 512 KiB |
|
Before Width: | Height: | Size: 484 KiB After Width: | Height: | Size: 530 KiB |
|
Before Width: | Height: | Size: 565 KiB After Width: | Height: | Size: 590 KiB |
|
Before Width: | Height: | Size: 456 KiB After Width: | Height: | Size: 522 KiB |
|
|
@ -229,56 +229,48 @@
|
|||
"state": "ready",
|
||||
"icon": true,
|
||||
"frame": "0",
|
||||
"rank": "5/5",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
}
|
||||
],
|
||||
|
|
@ -290,8 +282,8 @@
|
|||
"dashing": false,
|
||||
"down": false,
|
||||
"ship": {
|
||||
"x": 3810.5218340666174,
|
||||
"y": 4088.361289263925
|
||||
"x": 5909.759469383378,
|
||||
"y": -988.8723592216014
|
||||
},
|
||||
"ledgerSeats": [
|
||||
"ramming_charge",
|
||||
|
|
@ -337,8 +329,8 @@
|
|||
"maxPoints": 5,
|
||||
"energy": 60,
|
||||
"cooldown": 6,
|
||||
"cooldownLeft": 5.722706666666667,
|
||||
"cooldownFrac": 0.046215555555555454,
|
||||
"cooldownLeft": 5.733383333333334,
|
||||
"cooldownFrac": 0.04443611111111101,
|
||||
"affordable": false,
|
||||
"ready": false,
|
||||
"inert": false
|
||||
|
|
@ -489,60 +481,52 @@
|
|||
"state": "cooldown",
|
||||
"icon": true,
|
||||
"frame": "0",
|
||||
"rank": "5/5",
|
||||
"secs": "6"
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
}
|
||||
],
|
||||
"energy": 41.66376000000001,
|
||||
"energy": 41.59970000000001,
|
||||
"maxEnergy": 100,
|
||||
"hull": 6000,
|
||||
"maxHull": 6000,
|
||||
|
|
@ -550,8 +534,8 @@
|
|||
"dashing": true,
|
||||
"down": false,
|
||||
"ship": {
|
||||
"x": 3870.629477981919,
|
||||
"y": 4033.226599552123
|
||||
"x": 5972.397742948351,
|
||||
"y": -675.1068097485484
|
||||
},
|
||||
"ledgerSeats": [
|
||||
"ramming_charge",
|
||||
|
|
@ -749,69 +733,61 @@
|
|||
"state": "noEnergy",
|
||||
"icon": true,
|
||||
"frame": "0",
|
||||
"rank": "5/5",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
}
|
||||
],
|
||||
"energy": 59.384,
|
||||
"maxEnergy": 100,
|
||||
"hull": 129,
|
||||
"hull": 101.896,
|
||||
"maxHull": 129,
|
||||
"level": 1,
|
||||
"dashing": false,
|
||||
"down": false,
|
||||
"ship": {
|
||||
"x": 3893.832344512312,
|
||||
"y": 4012.409104914982
|
||||
"x": 5967.269013350007,
|
||||
"y": -697.4064976194823
|
||||
},
|
||||
"ledgerSeats": [
|
||||
"ramming_charge",
|
||||
|
|
@ -1009,69 +985,61 @@
|
|||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": "0",
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "ready",
|
||||
"icon": true,
|
||||
"frame": "0",
|
||||
"rank": "5/5",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
}
|
||||
],
|
||||
"energy": 68.21600000000001,
|
||||
"maxEnergy": 100,
|
||||
"hull": 121,
|
||||
"hull": 71.896,
|
||||
"maxHull": 129,
|
||||
"level": 1,
|
||||
"dashing": false,
|
||||
"down": false,
|
||||
"ship": {
|
||||
"x": 3893.832344512312,
|
||||
"y": 4012.409104914982
|
||||
"x": 5967.269013350007,
|
||||
"y": -697.4064976194823
|
||||
},
|
||||
"ledgerSeats": [
|
||||
null,
|
||||
|
|
@ -1269,69 +1237,61 @@
|
|||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": "0",
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": "0",
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
}
|
||||
],
|
||||
"energy": 71.28800000000001,
|
||||
"maxEnergy": 100,
|
||||
"hull": 81,
|
||||
"hull": 61.896,
|
||||
"maxHull": 129,
|
||||
"level": 1,
|
||||
"dashing": false,
|
||||
"down": false,
|
||||
"ship": {
|
||||
"x": 3893.832344512312,
|
||||
"y": 4012.409104914982
|
||||
"x": 5967.269013350007,
|
||||
"y": -697.4064976194823
|
||||
},
|
||||
"ledgerSeats": [
|
||||
null,
|
||||
|
|
@ -1529,69 +1489,61 @@
|
|||
"state": "ready",
|
||||
"icon": true,
|
||||
"frame": "0",
|
||||
"rank": "5/5",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": "0",
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
},
|
||||
{
|
||||
"state": "empty",
|
||||
"icon": false,
|
||||
"frame": null,
|
||||
"rank": "",
|
||||
"secs": ""
|
||||
}
|
||||
],
|
||||
"energy": 73.97600000000001,
|
||||
"maxEnergy": 100,
|
||||
"hull": 41,
|
||||
"hull": 61.896,
|
||||
"maxHull": 129,
|
||||
"level": 1,
|
||||
"dashing": false,
|
||||
"down": false,
|
||||
"ship": {
|
||||
"x": 3893.832344512312,
|
||||
"y": 4012.409104914982
|
||||
"x": 5967.269013350007,
|
||||
"y": -697.4064976194823
|
||||
},
|
||||
"ledgerSeats": [
|
||||
"ramming_charge",
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.8 KiB |
|
|
@ -11,8 +11,8 @@
|
|||
* is the face and the hands):
|
||||
* · a `?skill=max&combat` run boots with the class filed, both skills 5/5 and
|
||||
* the seat Ramming Charge was GIVEN by data filled (nothing to grind);
|
||||
* · the bound seat draws its ICON off the sheet, wears its RANK stamp and reads
|
||||
* ready; an unbound seat keeps its reserved empty look;
|
||||
* · the bound seat draws its ICON off the sheet and reads ready; an unbound
|
||||
* seat keeps its reserved empty look;
|
||||
* · pressing 1 dashes: the orb drops by the skill's cost, the seat goes to its
|
||||
* recharge face with the SECONDS printed on it and the sweep unwinds toward
|
||||
* full charge (evidence in dev/shots/);
|
||||
|
|
@ -81,7 +81,6 @@ const snap = () =>
|
|||
state: x.state,
|
||||
icon: !!x.icon && x.icon.visible,
|
||||
frame: x.icon ? String(x.icon.frame?.name ?? '') : null,
|
||||
rank: x.rankT.visible ? x.rankT.text : '',
|
||||
secs: x.secs.visible ? x.secs.text : '',
|
||||
})),
|
||||
energy: g.combat.energy,
|
||||
|
|
@ -152,11 +151,12 @@ const seatClip = () =>
|
|||
const r = document.querySelector('canvas').getBoundingClientRect();
|
||||
const sx = r.width / g.scale.width;
|
||||
const sy = r.height / g.scale.height;
|
||||
const m = art.rankT.getWorldTransformMatrix();
|
||||
const cx = art.lx + bar.seatBox.size / 2;
|
||||
const cy = art.ly + bar.seatBox.size / 2;
|
||||
const S = (bar.seatBox?.size ?? 44) * 1.2;
|
||||
return {
|
||||
x: Math.max(0, Math.round(r.left + (m.tx - S / 2) * sx)),
|
||||
y: Math.max(0, Math.round(r.top + (m.ty - S / 2) * sy)),
|
||||
x: Math.max(0, Math.round(r.left + (cx - S / 2) * sx)),
|
||||
y: Math.max(0, Math.round(r.top + (cy - S / 2) * sy)),
|
||||
width: Math.max(8, Math.round(S * sx)),
|
||||
height: Math.max(8, Math.round(S * sy)),
|
||||
};
|
||||
|
|
@ -247,17 +247,14 @@ const ready = await page
|
|||
const s0 = ready.seats[0];
|
||||
check(s0.id === RAM.id && s0.rank === RAM.maxPoints, `seat 1 holds ${RAM.label} ${s0.rank}/${s0.maxPoints} — the DEFAULT binding came from data/skills.json`);
|
||||
check(ready.art[0].icon === true && ready.art[0].frame === String(RAM.frame), `the seat draws icon frame ${RAM.frame} off the sheet (got "${ready.art[0].frame}")`);
|
||||
check(ready.art[0].rank === `${RAM.maxPoints}/${RAM.maxPoints}`, `the seat wears its rank stamp "${ready.art[0].rank}"`);
|
||||
|
||||
// RENDERED PROOF: the rank stamp and the icon each contribute pixels ON TOP of
|
||||
// each other (an icon added lazily over the state layers buries them while every
|
||||
// property above still reads correct — that regression is what this catches).
|
||||
// RENDERED PROOF: the icon contributes pixels ON TOP of the state layers (an
|
||||
// icon added lazily over the state layers buries them while every property
|
||||
// above still reads correct — that regression is what this catches).
|
||||
await clearScreen();
|
||||
const seatBase = await shootSeat([]);
|
||||
const noRank = await shootSeat(['rankT']);
|
||||
const noIcon = await shootSeat(['icon']);
|
||||
check(Buffer.compare(seatBase, noRank) !== 0, 'the rank stamp actually reaches the pixels (hiding it changes the seat render, so it is not buried under the icon)');
|
||||
check(Buffer.compare(seatBase, noIcon) !== 0, 'the icon reaches the pixels too (so the two differ because of stacking, not an empty seat)');
|
||||
check(Buffer.compare(seatBase, noIcon) !== 0, 'the icon reaches the pixels too (so it is not buried under the state layers)');
|
||||
fs.writeFileSync(new URL('./shots/skills-seat.png', import.meta.url), seatBase);
|
||||
await page.screenshot({ path: `${SHOTS}skills-bar-ready.png`, timeout: 60000 });
|
||||
check(ready.art[0].state === 'ready', `a live, charged, affordable seat reads ready (got ${ready.art[0].state})`);
|
||||
|
|
|
|||
|
|
@ -8431,14 +8431,14 @@ export class GameScene extends Phaser.Scene {
|
|||
/**
|
||||
* The skill icons the bar and the console draw (loaded once, on demand, the way
|
||||
* every other UI sheet in the game is). A missing file is not an error: the seats
|
||||
* keep their frame, veil, sweep and rank stamp and simply draw no icon.
|
||||
* keep their frame, veil and sweep and simply draw no icon.
|
||||
*/
|
||||
_ensureSkillIcons() {
|
||||
if (this._skillIconsTried) return;
|
||||
const sheet = skillIconSheet();
|
||||
// The path is the data's (data/skills.json → icons.texture). No path, or a
|
||||
// sheet that never loads, is not an error — the seats keep their frame, veil,
|
||||
// sweep and rank stamp and simply draw no icon.
|
||||
// sweep and seconds and simply draw no icon.
|
||||
if (!sheet.texture) {
|
||||
this._skillIconsTried = true;
|
||||
return;
|
||||
|
|
@ -8451,7 +8451,7 @@ export class GameScene extends Phaser.Scene {
|
|||
// issued while another load runs (the deferred comms/media pass, the scene
|
||||
// restart's own queue) is dropped — so this waits for the idle beat instead of
|
||||
// losing the sheet. A handful of beats, then it gives up and the bar draws
|
||||
// seats without icons (frame, veil, sweep and rank stamp still tell the story).
|
||||
// seats without icons (frame, veil and sweep still tell the story).
|
||||
if (this.load.isLoading?.() === true) {
|
||||
this._skillIconBeats = (this._skillIconBeats ?? 0) + 1;
|
||||
if (this._skillIconBeats <= 40) this.time.delayedCall(250, () => this._ensureSkillIcons());
|
||||
|
|
|
|||
|
|
@ -71,9 +71,9 @@ function orbDotKey(scene) {
|
|||
* the run's bindings live in the skill ledger (js/skills/SkillState.js),
|
||||
* a press lands on o.onCast(index) → GameScene.castSkillSeat → the
|
||||
* fight's cast(), and the fight answers every press with a reason. A
|
||||
* seat draws its skill's icon (assets/images/skills.png), its rank
|
||||
* stamp, a recharge sweep with the seconds left, a dimmed "cannot
|
||||
* afford it" veil and an inert veil for a skill nobody has ranked;
|
||||
* seat draws its skill's icon (assets/images/skills.png), a recharge
|
||||
* sweep with the seconds left, a dimmed "cannot afford it" veil and
|
||||
* an inert veil for a skill nobody has ranked;
|
||||
* unbound seats keep their reserved look. RIGHT-pressing a seat clears
|
||||
* it (making room), and while the skill tree is holding a skill up the
|
||||
* seats breathe and a press PLACES the skill instead of casting.
|
||||
|
|
@ -274,8 +274,8 @@ export class CombatBar extends Phaser.GameObjects.Container {
|
|||
// layer only draws what each one looks like, and every colour, size and pulse
|
||||
// rate is data-owned (data/skills.json → bar). Draw order per seat: the icon,
|
||||
// a VEIL over it (an unranked skill is buried, a too-expensive one dimmed),
|
||||
// the recharge SWEEP with the SECONDS left printed over that, and the RANK
|
||||
// stamp in the corner. An empty seat draws nothing here — its reserved frame
|
||||
// the recharge SWEEP with the SECONDS left printed over that. An empty seat
|
||||
// draws nothing here — its reserved frame
|
||||
// (above) with its key number IS the empty look.
|
||||
this.seatArt = [];
|
||||
for (let i = 0; i < n; i++) {
|
||||
|
|
@ -300,26 +300,12 @@ export class CombatBar extends Phaser.GameObjects.Container {
|
|||
.setScrollFactor(0)
|
||||
.setDepth(DEPTH + 1)
|
||||
.setVisible(false);
|
||||
const rankT = scene.add
|
||||
.text(lx + S - 3, ly + S - 3, '', {
|
||||
fontFamily: fontStack('body', FONT_FALLBACK),
|
||||
fontSize: `${this._barSpec.rankTextPx}px`,
|
||||
fontStyle: 'bold',
|
||||
color: this._barSpec.states.rankColor,
|
||||
stroke: '#02070d',
|
||||
strokeThickness: 2.5,
|
||||
})
|
||||
.setOrigin(1, 1)
|
||||
.setScrollFactor(0)
|
||||
.setDepth(DEPTH + 1)
|
||||
.setVisible(false);
|
||||
this.add(veil);
|
||||
this.add(sweep);
|
||||
this.add(edge);
|
||||
this.add(secs);
|
||||
this.add(rankT);
|
||||
this.seatArt.push({
|
||||
lx, ly, veil, sweep, edge, secs, rankT,
|
||||
lx, ly, veil, sweep, edge, secs,
|
||||
state: 'empty', // what the seat is drawing (see _drawSeat)
|
||||
icon: null, // created on the first seat that carries a skill
|
||||
sig: '', // last draw signature — unchanged seats are skipped
|
||||
|
|
@ -622,9 +608,8 @@ export class CombatBar extends Phaser.GameObjects.Container {
|
|||
edge.strokeRoundedRect(...boxIn, rad);
|
||||
}
|
||||
|
||||
// 5 — the printed seconds + the rank stamp.
|
||||
// 5 — the printed seconds.
|
||||
art.secs.setVisible(cooling).setText(secs);
|
||||
art.rankT.setVisible(bound).setText(`${row.rank}/${row.maxPoints}`);
|
||||
}
|
||||
|
||||
/** The seat's icon image, made on first use (the sheet is loaded once). */
|
||||
|
|
@ -646,10 +631,10 @@ export class CombatBar extends Phaser.GameObjects.Container {
|
|||
this.add(art.icon);
|
||||
// The icon is made LAZILY (mid-frame, on the first seat that needs it), so it
|
||||
// lands on top of the seat's state layers and an opaque sheet would bury the
|
||||
// veil, the sweep, the seconds and the rank stamp. Put those back above it —
|
||||
// veil → sweep → edge → seconds → rank, the order the data describes
|
||||
// veil, the sweep and the seconds. Put those back above it —
|
||||
// veil → sweep → edge → seconds, the order the data describes
|
||||
// (data/skills.json → bar._comment). The icon never moves again afterwards.
|
||||
for (const go of [art.veil, art.sweep, art.edge, art.secs, art.rankT]) {
|
||||
for (const go of [art.veil, art.sweep, art.edge, art.secs]) {
|
||||
if (!go) continue;
|
||||
if (typeof this.bringToTop === 'function') this.bringToTop(go);
|
||||
else {
|
||||
|
|
@ -801,7 +786,7 @@ export class CombatBar extends Phaser.GameObjects.Container {
|
|||
this.onSeatUnbind = null;
|
||||
for (const art of this.seatArt ?? []) {
|
||||
art.icon = null;
|
||||
art.veil = art.sweep = art.edge = art.secs = art.rankT = null;
|
||||
art.veil = art.sweep = art.edge = art.secs = null;
|
||||
}
|
||||
this.seatArt = null;
|
||||
this.xpLabel = null;
|
||||
|
|
|
|||