Tune large battle map render resolution
@@ -11,9 +11,16 @@ The first thirty campaign battles now use the same hand-painted tactical map pip
|
|||||||
- Roads, forests, rivers, camps, villages, cliffs, and forts should be recognizable by silhouette and texture, not by high saturation.
|
- Roads, forests, rivers, camps, villages, cliffs, and forts should be recognizable by silhouette and texture, not by high saturation.
|
||||||
- The grid remains subtle and embedded in the paint grade, so it helps targeting without becoming the main visual layer.
|
- The grid remains subtle and embedded in the paint grade, so it helps targeting without becoming the main visual layer.
|
||||||
|
|
||||||
|
## Resolution Policy
|
||||||
|
|
||||||
|
- Battles 1-25 keep the original 224px hand-painted tile target.
|
||||||
|
- Large maps starting at battle 26 use a 192px tile target through `LARGE_MAP_TILE_SIZE` in the generator.
|
||||||
|
- Re-evaluate the tile target before a generated map's longest edge exceeds roughly 16,384px at 192px per tile.
|
||||||
|
- Use `--tile-size` only for controlled comparisons; commit the selected production size in the map config.
|
||||||
|
|
||||||
## Generator
|
## Generator
|
||||||
|
|
||||||
`scripts/generate-handpaint-map-sample.py` reads terrain and unit positions from `src/game/data/scenario.ts`, then outputs a WebP map and documentation previews.
|
`scripts/generate-handpaint-map-sample.py` reads terrain and unit positions from `src/game/data/scenario.ts`, then outputs a WebP map and documentation previews. Map configs can set `tile_size`; otherwise the default is 224px.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@@ -49,6 +56,12 @@ python scripts/generate-handpaint-map-sample.py --map twenty-ninth --webp-qualit
|
|||||||
python scripts/generate-handpaint-map-sample.py --map thirtieth --webp-quality 90
|
python scripts/generate-handpaint-map-sample.py --map thirtieth --webp-quality 90
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Comparison example:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
python scripts/generate-handpaint-map-sample.py --map thirtieth --tile-size 192 --out tmp\thirtieth-battle-map-192-test.webp --comparison tmp\thirtieth-battle-map-192-test-before-after.png --preview tmp\thirtieth-battle-map-192-test-preview.png --webp-quality 90
|
||||||
|
```
|
||||||
|
|
||||||
## Current Assets
|
## Current Assets
|
||||||
|
|
||||||
- `src/assets/images/battle/first-battle-map.webp`
|
- `src/assets/images/battle/first-battle-map.webp`
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 1.7 MiB After Width: | Height: | Size: 1.8 MiB |
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 1.8 MiB After Width: | Height: | Size: 1.9 MiB |
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.6 MiB |
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
@@ -15,7 +15,9 @@ SCENARIO_FILE = ROOT / "src" / "game" / "data" / "scenario.ts"
|
|||||||
BATTLE_IMAGE_DIR = ROOT / "src" / "assets" / "images" / "battle"
|
BATTLE_IMAGE_DIR = ROOT / "src" / "assets" / "images" / "battle"
|
||||||
DOCS_DIR = ROOT / "docs"
|
DOCS_DIR = ROOT / "docs"
|
||||||
|
|
||||||
TILE = 224
|
DEFAULT_TILE_SIZE = 224
|
||||||
|
LARGE_MAP_TILE_SIZE = 192
|
||||||
|
TILE = DEFAULT_TILE_SIZE
|
||||||
RNG = random.Random(240704)
|
RNG = random.Random(240704)
|
||||||
|
|
||||||
MAP_CONFIGS = {
|
MAP_CONFIGS = {
|
||||||
@@ -252,6 +254,7 @@ MAP_CONFIGS = {
|
|||||||
"out": BATTLE_IMAGE_DIR / "twenty-sixth-battle-map.webp",
|
"out": BATTLE_IMAGE_DIR / "twenty-sixth-battle-map.webp",
|
||||||
"before": BATTLE_IMAGE_DIR / "twenty-sixth-battle-map.svg",
|
"before": BATTLE_IMAGE_DIR / "twenty-sixth-battle-map.svg",
|
||||||
"seed": 240954,
|
"seed": 240954,
|
||||||
|
"tile_size": LARGE_MAP_TILE_SIZE,
|
||||||
},
|
},
|
||||||
"twenty-seventh": {
|
"twenty-seventh": {
|
||||||
"slug": "twenty-seventh-battle-map",
|
"slug": "twenty-seventh-battle-map",
|
||||||
@@ -261,6 +264,7 @@ MAP_CONFIGS = {
|
|||||||
"out": BATTLE_IMAGE_DIR / "twenty-seventh-battle-map.webp",
|
"out": BATTLE_IMAGE_DIR / "twenty-seventh-battle-map.webp",
|
||||||
"before": BATTLE_IMAGE_DIR / "twenty-seventh-battle-map.svg",
|
"before": BATTLE_IMAGE_DIR / "twenty-seventh-battle-map.svg",
|
||||||
"seed": 240964,
|
"seed": 240964,
|
||||||
|
"tile_size": LARGE_MAP_TILE_SIZE,
|
||||||
},
|
},
|
||||||
"twenty-eighth": {
|
"twenty-eighth": {
|
||||||
"slug": "twenty-eighth-battle-map",
|
"slug": "twenty-eighth-battle-map",
|
||||||
@@ -270,6 +274,7 @@ MAP_CONFIGS = {
|
|||||||
"out": BATTLE_IMAGE_DIR / "twenty-eighth-battle-map.webp",
|
"out": BATTLE_IMAGE_DIR / "twenty-eighth-battle-map.webp",
|
||||||
"before": BATTLE_IMAGE_DIR / "twenty-eighth-battle-map.svg",
|
"before": BATTLE_IMAGE_DIR / "twenty-eighth-battle-map.svg",
|
||||||
"seed": 240974,
|
"seed": 240974,
|
||||||
|
"tile_size": LARGE_MAP_TILE_SIZE,
|
||||||
},
|
},
|
||||||
"twenty-ninth": {
|
"twenty-ninth": {
|
||||||
"slug": "twenty-ninth-battle-map",
|
"slug": "twenty-ninth-battle-map",
|
||||||
@@ -279,6 +284,7 @@ MAP_CONFIGS = {
|
|||||||
"out": BATTLE_IMAGE_DIR / "twenty-ninth-battle-map.webp",
|
"out": BATTLE_IMAGE_DIR / "twenty-ninth-battle-map.webp",
|
||||||
"before": BATTLE_IMAGE_DIR / "twenty-ninth-battle-map.svg",
|
"before": BATTLE_IMAGE_DIR / "twenty-ninth-battle-map.svg",
|
||||||
"seed": 240984,
|
"seed": 240984,
|
||||||
|
"tile_size": LARGE_MAP_TILE_SIZE,
|
||||||
},
|
},
|
||||||
"thirtieth": {
|
"thirtieth": {
|
||||||
"slug": "thirtieth-battle-map",
|
"slug": "thirtieth-battle-map",
|
||||||
@@ -288,6 +294,7 @@ MAP_CONFIGS = {
|
|||||||
"out": BATTLE_IMAGE_DIR / "thirtieth-battle-map.webp",
|
"out": BATTLE_IMAGE_DIR / "thirtieth-battle-map.webp",
|
||||||
"before": BATTLE_IMAGE_DIR / "thirtieth-battle-map.svg",
|
"before": BATTLE_IMAGE_DIR / "thirtieth-battle-map.svg",
|
||||||
"seed": 240994,
|
"seed": 240994,
|
||||||
|
"tile_size": LARGE_MAP_TILE_SIZE,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -846,8 +853,9 @@ def add_subtle_grid_and_grade(canvas: Image.Image, terrain: list[list[str]]) ->
|
|||||||
|
|
||||||
|
|
||||||
def build_map(config: dict[str, object]) -> Image.Image:
|
def build_map(config: dict[str, object]) -> Image.Image:
|
||||||
global RNG
|
global RNG, TILE
|
||||||
RNG = random.Random(int(config["seed"]))
|
RNG = random.Random(int(config["seed"]))
|
||||||
|
TILE = int(config.get("tile_size", DEFAULT_TILE_SIZE))
|
||||||
terrain = parse_battle_terrain(str(config["terrain_export"]))
|
terrain = parse_battle_terrain(str(config["terrain_export"]))
|
||||||
ally_positions_export = config["ally_positions_export"]
|
ally_positions_export = config["ally_positions_export"]
|
||||||
units = parse_battle_units(str(config["units_export"]), None if ally_positions_export is None else str(ally_positions_export))
|
units = parse_battle_units(str(config["units_export"]), None if ally_positions_export is None else str(ally_positions_export))
|
||||||
@@ -920,10 +928,15 @@ def main() -> None:
|
|||||||
parser.add_argument("--before", type=Path)
|
parser.add_argument("--before", type=Path)
|
||||||
parser.add_argument("--comparison", type=Path)
|
parser.add_argument("--comparison", type=Path)
|
||||||
parser.add_argument("--preview", type=Path)
|
parser.add_argument("--preview", type=Path)
|
||||||
|
parser.add_argument("--tile-size", type=int)
|
||||||
parser.add_argument("--webp-quality", type=int, default=90)
|
parser.add_argument("--webp-quality", type=int, default=90)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
config = MAP_CONFIGS[args.map]
|
config = dict(MAP_CONFIGS[args.map])
|
||||||
|
if args.tile_size is not None:
|
||||||
|
if args.tile_size < 160:
|
||||||
|
raise ValueError("--tile-size must be 160 or greater")
|
||||||
|
config["tile_size"] = args.tile_size
|
||||||
out_path = args.out or Path(config["out"])
|
out_path = args.out or Path(config["out"])
|
||||||
before_path = args.before or Path(config["before"])
|
before_path = args.before or Path(config["before"])
|
||||||
comparison_path = args.comparison or (DOCS_DIR / f"{config['slug']}-handpaint-before-after.png")
|
comparison_path = args.comparison or (DOCS_DIR / f"{config['slug']}-handpaint-before-after.png")
|
||||||
@@ -950,6 +963,7 @@ def main() -> None:
|
|||||||
print(
|
print(
|
||||||
{
|
{
|
||||||
"map": args.map,
|
"map": args.map,
|
||||||
|
"tile_size": TILE,
|
||||||
"out": str(out_path),
|
"out": str(out_path),
|
||||||
"size": after.size,
|
"size": after.size,
|
||||||
"comparison": str(comparison_path),
|
"comparison": str(comparison_path),
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 6.4 MiB After Width: | Height: | Size: 5.2 MiB |
|
Before Width: | Height: | Size: 5.4 MiB After Width: | Height: | Size: 4.5 MiB |
|
Before Width: | Height: | Size: 5.8 MiB After Width: | Height: | Size: 4.7 MiB |
|
Before Width: | Height: | Size: 4.4 MiB After Width: | Height: | Size: 3.9 MiB |
|
Before Width: | Height: | Size: 3.6 MiB After Width: | Height: | Size: 2.7 MiB |