diff --git a/assets/images/vega/allocation.png b/assets/images/vega/allocation.png new file mode 100644 index 0000000..761bf83 Binary files /dev/null and b/assets/images/vega/allocation.png differ diff --git a/assets/images/vega/allocation.psd b/assets/images/vega/allocation.psd new file mode 100644 index 0000000..ac9d922 Binary files /dev/null and b/assets/images/vega/allocation.psd differ diff --git a/assets/music/vega/audience-ursaal.mp3 b/assets/music/vega/audience-ursaal.mp3 index 2c067b9..d640d05 100644 Binary files a/assets/music/vega/audience-ursaal.mp3 and b/assets/music/vega/audience-ursaal.mp3 differ diff --git a/data/mastervega-artwork.json b/data/mastervega-artwork.json index 875e454..ce44bca 100644 --- a/data/mastervega-artwork.json +++ b/data/mastervega-artwork.json @@ -317,6 +317,16 @@ "cols": 10, "rows": 10, "_layout": "One frame per individual tech (techs[].iconFrame), 0-90 — the 60 original techs plus two MOO1-branching passes added later, in rules order. Split out of the combined tech sheet so field icons and tech icons can be painted/sized independently. 91-99 spare." + }, + "allocation": { + "key": "vega-allocation", + "path": "assets/images/vega/allocation.png", + "kind": "icon", + "frameWidth": 48, + "frameHeight": 48, + "cols": 5, + "rows": 1, + "_layout": "One frame per allocation channel, in CHANNELS order (VegaLogic.js): 0 ships (Construction), 1 defense (Defence), 2 industry (Industry), 3 ecology (Ecology), 4 research (Research). Shown beside each slider label on the colony screen (VegaColonyView.js)." } } } diff --git a/src/games/mastervega/VegaColonyView.js b/src/games/mastervega/VegaColonyView.js index 4f3ea75..2f88a7d 100644 --- a/src/games/mastervega/VegaColonyView.js +++ b/src/games/mastervega/VegaColonyView.js @@ -42,7 +42,7 @@ import { createShipMediaPool, makeShipIcon } from './VegaShipMedia.js'; import { openShipDetail } from './VegaShipDetail.js'; import { openLeaderDetail } from './VegaLeaderDetail.js'; import { leaderOf } from './VegaLeaders.js'; -import { describeBuildingTooltip, describeLeaderTooltip } from './VegaTooltips.js'; +import { describeBuildingTooltip, describeLeaderTooltip, describeAllocationTooltip } from './VegaTooltips.js'; import { CHANNELS, colonyMaxPop, colonyProduction, colonyFactoryCap, effectiveFactories, colonyDefenseCap, colonyTrade, colonyBuildRate, setSlider, enqueue, enqueueMany, @@ -432,11 +432,18 @@ export function openColonyView(scene, rules, state, colony, art, opts = {}) { // renormalise (its `others.length === 0` early return), freezing the // allocation with no way back out. So the last one open cannot be shut. const lockable = locked || lockedCount < CHANNELS.length - 1; - const box = add(scene.add.rectangle(x + 11, y + 28, 22, 22, - locked ? 0x2a3550 : 0x16253c).setStrokeStyle(1, ACCENT, lockable ? 0.55 : 0.18)); - add(scene.add.text(x + 11, y + 27, locked ? '■' : '□', { - fontFamily: FONT, fontSize: '15px', color: locked ? '#ffd88a' : (lockable ? '#8fa8c0' : '#3c4c60'), - }).setOrigin(0.5)); + // The box itself is the lock toggle — its fill/border shows locked + // state, and the channel's icon sits inside it (in place of the old + // □/■ glyph) instead of getting its own separate column. Centred at + // y+19: the midpoint between the slider's label (top, ~y+10) and its + // track (y+28), so the icon reads as belonging to both. + const boxCx = x + 17; + const boxCy = y + 19; + const box = add(scene.add.rectangle(boxCx, boxCy, 34, 34, + locked ? 0x2a3550 : 0x16253c) + .setStrokeStyle(locked ? 3 : 2, CHANNEL_COLOUR[ch], lockable ? 0.85 : 0.18)); + add(scene.add.image(boxCx, boxCy, art.allocation, i).setDisplaySize(26, 26) + .setTint(locked ? 0xffd88a : (lockable ? 0xe8f4ff : 0x445566))); if (lockable) { box.setInteractive({ useHandCursor: true }); box.on('pointerup', () => { @@ -446,7 +453,7 @@ export function openColonyView(scene, rules, state, colony, art, opts = {}) { }); } - const s = slider(scene, x + 34, y, w - 34, + const s = slider(scene, x + 42, y, w - 42, rules.economy.channelNames[ch] ?? ch, colony.sliders[ch] ?? 0, (v) => { setSlider(rules, state, colony, ch, v); // setSlider renormalises everything else, so they all have to be @@ -463,6 +470,7 @@ export function openColonyView(scene, rules, state, colony, art, opts = {}) { onChanged?.(); }, CHANNEL_COLOUR[ch]); panelLayer.add(s.container); + tooltip.attachTo(s.zone, () => describeAllocationTooltip(rules, state, colony, ch)); sliders.push(s); y += 56; }); diff --git a/src/games/mastervega/VegaScreens.js b/src/games/mastervega/VegaScreens.js index ae5fe48..112db4f 100644 --- a/src/games/mastervega/VegaScreens.js +++ b/src/games/mastervega/VegaScreens.js @@ -175,6 +175,7 @@ export function slider(scene, x, y, w, label, value, onChange, colour = ACCENT) return { container: c, + zone, // exposed so callers can tooltip.attachTo(s.zone, ...) setValue(v) { fill.width = w * v; knob.x = w * v; diff --git a/src/games/mastervega/VegaTooltips.js b/src/games/mastervega/VegaTooltips.js index 1d2f6a7..b5f9327 100644 --- a/src/games/mastervega/VegaTooltips.js +++ b/src/games/mastervega/VegaTooltips.js @@ -4,7 +4,10 @@ // component; these functions only build the {title, lines} content it wants. import { COLORS } from '../../config.js'; -import { coloniesAt, habitableForEmpire } from './VegaLogic.js'; +import { + coloniesAt, habitableForEmpire, colonyProduction, colonyFactoryCap, + effectiveFactories, colonyDefenseCap, +} from './VegaLogic.js'; import { techCost, techCostFactor } from './VegaRules.js'; import { leaderSkillSummary } from './VegaLeaders.js'; @@ -109,6 +112,49 @@ export function describeBuildingTooltip(rules, buildingId) { }; } +// Hover tooltip for one of the colony screen's five allocation sliders +// (VegaColonyView.js). Explains what that channel spends its share of +// production on and what the MOO1-style spillover (VegaLogic.js's +// processColony) does with any of it the channel can't use. +export function describeAllocationTooltip(rules, state, colony, ch) { + const label = rules.economy.channelNames[ch] ?? ch; + const prod = colonyProduction(rules, state, colony); + const share = prod * (colony.sliders[ch] ?? 0); + const lines = [{ text: `${share.toFixed(1)} BC/turn from this channel`, color: COLORS.goldHex }]; + + switch (ch) { + case 'ships': + lines.push({ text: 'Pays for whatever is at the front of the build queue — ships and buildings, in order.' }); + lines.push({ text: 'Once the queue is empty, this overflows into Research instead of going to waste.', color: COLORS.mutedHex }); + break; + case 'defense': { + const cap = colonyDefenseCap(rules, state, colony); + lines.push({ text: `Raises planetary defences (${Math.round(colony.defenseHp)} / ${cap}), capped by your defence tech.` }); + lines.push({ text: 'Once defences are maxed, this overflows into Construction instead of going to waste.', color: COLORS.mutedHex }); + break; + } + case 'industry': { + const cap = colonyFactoryCap(rules, state, colony); + const effF = effectiveFactories(rules, state, colony); + lines.push({ text: `Builds factories (${Math.floor(effF)} / ${cap} staffed), which raise this colony's production every turn.` }); + lines.push({ text: 'Once factories are maxed, this overflows into Construction instead of going to waste.', color: COLORS.mutedHex }); + break; + } + case 'ecology': + lines.push({ text: `Cleans industrial waste (backlog ${colony.waste.toFixed(1)}), which otherwise caps this colony's population.` }); + lines.push({ text: 'Mandatory — a shortfall is drawn from the other four channels automatically. Any surplus overflows into Research.', color: COLORS.mutedHex }); + break; + case 'research': + lines.push({ text: "Funds tech research toward your empire's research queue." }); + lines.push({ text: 'Also absorbs unused overflow from Ecology and Construction, so it is rarely wasted.', color: COLORS.mutedHex }); + break; + default: + break; + } + + return { title: label, titleColor: COLORS.goldHex, lines }; +} + // Hover tooltip for a colony's posted administrator, shown beside the // building icon row. `leader` is the rules leader def (name/bio/skills). export function describeLeaderTooltip(leader) { diff --git a/src/games/mastervega/sprites.md b/src/games/mastervega/sprites.md index 8f12500..ef0b3c3 100644 --- a/src/games/mastervega/sprites.md +++ b/src/games/mastervega/sprites.md @@ -333,8 +333,53 @@ story page. ## 8. Menu icon — ✅ `assets/images/game-icons.png` frame **92** -The shared 660 × 660 sheet, 44 × 44 cells, 15 per row. Frame 92 is row 6, -column 2 → pixel origin **(88, 264)**. **Painted.** +Not part of any Vega sheet — this is the small tile icon shown for *Master of +Vega* on the main game-selection screen's `arcade-console-pc` category list, +i.e. outside the game itself, the thing you click to launch it. The sheet is +shared by **every game in the app**, one frame each, claimed via `iconFrame` +in `src/data/gamesRegistry.js` (`registerGame({ slug: 'mastervega', ..., +iconFrame: 92 })`). Editing frame 92 only touches Vega's tile; the sheet +itself is common infrastructure, not Vega's to redesign. + +660 × 660 total, 44 × 44 cells, 15 per row. Frame 92 is row 6, column 2 → +pixel origin **(88, 264)**. **Painted — nothing to do here.** + +--- + +## 9. `allocation` — ✅ `assets/images/vega/allocation.png` + +| | | +|---|---| +| Sheet size | **240 × 48** | +| Frame | **48 × 48** | +| Grid | 5 cols × 1 row = 5 frames | + +One frame per allocation channel, in `CHANNELS` order (`VegaLogic.js`) — the +same five sliders on the colony screen (`VegaColonyView.js`), each of which +also carries a hover tooltip explaining what it does +(`describeAllocationTooltip` in `VegaTooltips.js`): + +| Frame | Channel id | UI label | Icon idea | +|---|---|---|---| +| 0 | `ships` | Construction | Shipyard gantry or a hull under construction — feeds the build queue | +| 1 | `defense` | Defence | Shield or planetary turret — raises defence batteries | +| 2 | `industry` | Industry | Factory or gear — builds factories | +| 3 | `ecology` | Ecology | Leaf or recycling arrows — cleans waste | +| 4 | `research` | Research | Flask or atom — funds tech | + +Shown at 26px, inside each slider's 34px lock toggle box — replacing the old +□/■ text glyph, not sitting in a separate column. The box sits vertically +centred between the slider's label and its track, so the icon reads as +belonging to both. Tinted gold when that channel is locked, near-white when +unlockable, and dim grey on the one channel that can't be locked (the last +one open); the box's stroke is that channel's own bar colour +(`CHANNEL_COLOUR` in `VegaColonyView.js` — e.g. Construction blue, Defence +red), thicker when locked. Declared in `data/mastervega-artwork.json`'s +`sheets.allocation` (`"kind": "icon"`, same pattern as +`buildings`/`techfields`); drawn by `VegaColonyView.js`'s slider loop as +`art.allocation` frame `i` (the same index the loop already walks `CHANNELS` +with), on top of the same rectangle that already handles the lock/unlock +click. ---