diff --git a/docs/battle-map-terrain-atlas-pipeline.md b/docs/battle-map-terrain-atlas-pipeline.md index edd36bc..20e3555 100644 --- a/docs/battle-map-terrain-atlas-pipeline.md +++ b/docs/battle-map-terrain-atlas-pipeline.md @@ -4,6 +4,10 @@ All sixty-six campaign battles now use the same hand-painted tactical map pipeline. The renderer still reads the original scenario tile coordinates and battle logic; only the map texture asset changes from flat/vector map art to low-contrast raster terrain. +## V2 Pilot + +The first battle now uses `src/assets/images/battle/first-battle-map-v2.webp` as the approved V2 vertical slice. It is an original 96 px/tile repaint with reduced runtime tint and grid weight. The procedural generator still targets the retired `first-battle-map.webp` filename for controlled V1 comparisons; do not promote that output over the V2 asset without a new runtime visual review. + ## Art Direction - Units must read before the terrain at 100% desktop browser scale. diff --git a/src/assets/images/battle/first-battle-map.webp b/docs/first-battle-map-v1-source.webp similarity index 100% rename from src/assets/images/battle/first-battle-map.webp rename to docs/first-battle-map-v1-source.webp diff --git a/docs/first-battle-map-v2-before-after.png b/docs/first-battle-map-v2-before-after.png new file mode 100644 index 0000000..d733807 Binary files /dev/null and b/docs/first-battle-map-v2-before-after.png differ diff --git a/docs/first-battle-map-v2-deploy-1920.png b/docs/first-battle-map-v2-deploy-1920.png new file mode 100644 index 0000000..866e1ba Binary files /dev/null and b/docs/first-battle-map-v2-deploy-1920.png differ diff --git a/docs/first-battle-map-v2-move-1920.png b/docs/first-battle-map-v2-move-1920.png new file mode 100644 index 0000000..43e464b Binary files /dev/null and b/docs/first-battle-map-v2-move-1920.png differ diff --git a/docs/visual-asset-first-map-repaint-v2-report.md b/docs/visual-asset-first-map-repaint-v2-report.md new file mode 100644 index 0000000..5069783 --- /dev/null +++ b/docs/visual-asset-first-map-repaint-v2-report.md @@ -0,0 +1,45 @@ +# First Battle Map Repaint V2 + +Date: 2026-07-15 + +## Target + +- Battle: `first-battle-zhuo-commandery` +- Runtime key: `battle-map-first` +- Production asset: `src/assets/images/battle/first-battle-map-v2.webp` +- Retired source reference: `docs/first-battle-map-v1-source.webp` +- Desktop output density: 96 px per tactical tile (`1920x1728` for the `20x18` map) + +## Art pass + +- Repainted the procedural terrain as an original late-Han rural battlefield. +- Preserved the top-down map framing, major cliff routes, two water bodies, forest distribution, watch posts, camps, and open deployment lanes. +- Replaced circular tree stamps, flat water marks, and uniform ground fills with layered woodland, reeds, stones, grasses, worn soil, and eroded cliff faces. +- Kept the palette muted so unit sprites and tactical feedback remain the primary visual focus. +- Added no text, logo, copyrighted source image, or franchise-identifying mark. + +## Runtime composition + +- Reduced the passive full-map dark wash from `0.16` to `0.07` alpha. +- Reduced passive terrain tint to `36%` of each terrain rule's former alpha, capped at `0.12`. +- Reduced passive grid strokes while retaining a stronger boundary for impassable terrain. +- Movement, targeting, objective, and command feedback remain separate tactical layers. + +## Generation prompt + +The production image was created with the built-in image generation workflow using the previous first-battle map as the strict topology reference and `docs/battle-map-art-direction-reference.png` as a mood and material-density reference. The prompt required an original hand-painted late-Han rural battlefield, exact orthographic top-down framing, preserved passability cues, organic terrain transitions, varied foliage and water detail, tactical readability, and explicitly prohibited UI, characters, text, logos, watermarks, repeated stamps, hard tile seams, and copyrighted asset replication. + +## Evidence + +- Before/after contact sheet: `docs/first-battle-map-v2-before-after.png` +- 1920x1080 deployment capture: `docs/first-battle-map-v2-deploy-1920.png` +- 1920x1080 movement-range capture: `docs/first-battle-map-v2-move-1920.png` + +## Validation + +- `pnpm run verify:battle-map-assets`: passed. +- `pnpm run verify:static-data`: passed. +- `pnpm run build`: passed. +- Local 1920x1080 deployment and active-battle QA: passed. +- Movement-range overlay remained distinct over the reduced passive grid. +- Browser warning/error logs during the runtime pass: none. diff --git a/docs/visual-asset-quality-audit.md b/docs/visual-asset-quality-audit.md index eea98fe..4714d6e 100644 --- a/docs/visual-asset-quality-audit.md +++ b/docs/visual-asset-quality-audit.md @@ -7,7 +7,7 @@ Generated by `scripts/audit-visual-asset-quality.mjs`. - Target desktop viewport: 1920x1080 - Battle map assets: 66 - Flagged battle map review rows: 43 -- Flagged battle map rows with repaint reports: 3 +- Flagged battle map rows with repaint reports: 4 - Battle map source pixel density range: 64.0-224.0 px/tile - Story source backgrounds: 136 - Story page background references: 466 @@ -82,7 +82,7 @@ Generated by `scripts/audit-visual-asset-quality.mjs`. | `fifty-seventh-battle-jieting-crisis` | `battle-map-fifty-seventh` | 114x96 | 14592x12288 | 128.0 | 16.89 | reduced tile pixel density | | `fifty-sixth-battle-tianshui-advance` | `battle-map-fifty-sixth` | 112x94 | 14336x12032 | 128.0 | 15.45 | reduced tile pixel density | | `fifty-third-battle-meng-huo-sixth-capture` | `battle-map-fifty-third` | 106x88 | 14416x11968 | 136.0 | 19.00 | reduced tile pixel density | -| `first-battle-zhuo-commandery` | `battle-map-first` | 20x18 | 4480x4032 | 224.0 | 0.50 | very low WebP byte density | +| `first-battle-zhuo-commandery` | `battle-map-first` | 20x18 | 1920x1728 | 96.0 | 1.02 | low tile pixel density
repaint report exists | | `fortieth-battle-han-river-flood` | `battle-map-fortieth` | 80x62 | 15360x11904 | 192.0 | 11.76 | none | | `forty-eighth-battle-meng-huo-main-force` | `battle-map-forty-eighth` | 96x78 | 15360x12480 | 160.0 | 16.44 | none | | `forty-fifth-battle-yiling-vanguard` | `battle-map-forty-fifth` | 90x72 | 14400x11520 | 160.0 | 13.98 | none | diff --git a/src/assets/images/battle/first-battle-map-v2.webp b/src/assets/images/battle/first-battle-map-v2.webp new file mode 100644 index 0000000..cd629b9 Binary files /dev/null and b/src/assets/images/battle/first-battle-map-v2.webp differ diff --git a/src/game/data/battleMapAssets.ts b/src/game/data/battleMapAssets.ts index 4d8c0f0..380692b 100644 --- a/src/game/data/battleMapAssets.ts +++ b/src/game/data/battleMapAssets.ts @@ -20,7 +20,7 @@ import fiftySeventhBattleMapUrl from '../../assets/images/battle/fifty-seventh-b import fiftySixthBattleMapUrl from '../../assets/images/battle/fifty-sixth-battle-map.webp'; import fifthBattleMapUrl from '../../assets/images/battle/fifth-battle-map.webp'; import fifteenthBattleMapUrl from '../../assets/images/battle/fifteenth-battle-map.webp'; -import firstBattleMapUrl from '../../assets/images/battle/first-battle-map.webp'; +import firstBattleMapUrl from '../../assets/images/battle/first-battle-map-v2.webp'; import fortiethBattleMapUrl from '../../assets/images/battle/fortieth-battle-map.webp'; import fortyEighthBattleMapUrl from '../../assets/images/battle/forty-eighth-battle-map.webp'; import fortyFifthBattleMapUrl from '../../assets/images/battle/forty-fifth-battle-map.webp'; diff --git a/src/game/scenes/BattleScene.ts b/src/game/scenes/BattleScene.ts index 08bec09..bc4faf5 100644 --- a/src/game/scenes/BattleScene.ts +++ b/src/game/scenes/BattleScene.ts @@ -4726,7 +4726,7 @@ export class BattleScene extends Phaser.Scene { background.setMask(mapMask); this.mapBackground = background; - this.add.rectangle(layout.mapX, layout.mapY, layout.mapWidth, layout.mapHeight, 0x06090b, 0.16).setOrigin(0).setDepth(1); + this.add.rectangle(layout.mapX, layout.mapY, layout.mapWidth, layout.mapHeight, 0x06090b, 0.07).setOrigin(0).setDepth(1); this.add .rectangle(layout.mapX, layout.mapY, layout.mapWidth, layout.mapHeight) .setOrigin(0) @@ -4737,9 +4737,11 @@ export class BattleScene extends Phaser.Scene { battleMap.terrain.forEach((row, y) => { row.forEach((terrain, x) => { const terrainRule = getTerrainRule(terrain); - const tile = this.add.rectangle(0, 0, layout.tileSize, layout.tileSize, terrainRule.color, terrainRule.alpha); + const idleTintAlpha = Math.min(0.12, terrainRule.alpha * 0.36); + const idleStrokeAlpha = terrainRule.passable === false ? 0.46 : 0.22; + const tile = this.add.rectangle(0, 0, layout.tileSize, layout.tileSize, terrainRule.color, idleTintAlpha); tile.setOrigin(0); - tile.setStrokeStyle(1, terrainRule.passable === false ? 0x111111 : 0x0a1014, terrainRule.passable === false ? 0.82 : 0.5); + tile.setStrokeStyle(1, terrainRule.passable === false ? 0x111111 : 0x0a1014, idleStrokeAlpha); tile.setDepth(2); tile.setMask(mapMask); this.terrainTileViews.push({ x, y, tile });