Visual: sprinkle grass+flower decoration overlay (ElvGames bundle)
First visual pass: replaces nothing, adds a decoration TileMapLayer above Terrain that paints ~8% of cells with random overlay sprites pulled from FG_Grasslands_Spring.png. Mix of 3 grass-sprout variants, 3 white-with- colored-center flowers, and 2 scattered-grass-dot patches. ~540 cells populated on the 80x80 map; deterministic seed so layout is stable. The bundle's design assumes flat-color terrain + overlay sprites for visual richness — so this is the cheapest possible win that uses the art we own. Terrain base, walls, trees, pawns, UI all unchanged. Implementation lives in world.gd as _build_decoration_tileset() + _paint_decorations(); the Decoration TileMapLayer is added to world.tscn at z_index=0 (siblings render in tree order so it draws between Terrain and Floor). Tileset is built at runtime pointing at res://art/tiles/FG_Grasslands_Spring.png, mirroring the existing _build_placeholder_tileset pattern. Verified MCP runtime: world feels like a meadow now, no perf hit. Headless boot logs '[world] decoration: painted 541 overlay cells'. License: ElvGames Humble bundle — commercial use OK with credit (see docs/art.md). Credit string compilation is still an open audit item. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f435c3c467
commit
314c7a70b4
4 changed files with 111 additions and 0 deletions
|
|
@ -85,6 +85,7 @@ const SEASON_TINTS: Dictionary = {
|
|||
|
||||
@onready var dark_overlay: CanvasModulate = $DarkOverlay
|
||||
@onready var terrain_layer: TileMapLayer = $Terrain
|
||||
@onready var decoration_layer: TileMapLayer = $Decoration
|
||||
@onready var floor_layer: TileMapLayer = $Floor
|
||||
@onready var wall_layer: TileMapLayer = $Wall
|
||||
@onready var designation_layer: TileMapLayer = $Designation
|
||||
|
|
@ -112,6 +113,11 @@ func _ready() -> void:
|
|||
for layer in [terrain_layer, floor_layer, wall_layer, designation_layer, roof_layer, fog_layer]:
|
||||
layer.tile_set = tileset
|
||||
_paint_terrain()
|
||||
# Decoration overlay — sparse grass tufts + flowers from the ElvGames bundle.
|
||||
# Lives on its own TileMapLayer above Terrain so a future season swap is one
|
||||
# tileset reassignment.
|
||||
decoration_layer.tile_set = _build_decoration_tileset()
|
||||
_paint_decorations()
|
||||
# Phase 5: removed _paint_sample_walls() — the old 8×8 stone ring rendered
|
||||
# only on the (now-hidden) Wall TileMap layer. With the rendering pivot
|
||||
# to entity-based walls, that ring was invisible to the player but still
|
||||
|
|
@ -285,6 +291,68 @@ func _paint_terrain() -> void:
|
|||
terrain_layer.set_cell(Vector2i(x, y), PLACEHOLDER_SOURCE_ID, TILE_GRASS)
|
||||
|
||||
|
||||
# ── decoration overlay (ElvGames Grasslands sprinkle) ────────────────────────
|
||||
|
||||
## Source id for the decoration tileset (separate atlas from PLACEHOLDER_SOURCE_ID).
|
||||
const DECORATION_SOURCE_ID: int = 10
|
||||
|
||||
## Atlas-tile coords inside FG_Grasslands_Spring.png for overlay variants.
|
||||
## Each is a 16×16 sprite on transparent background, picked by visual eyeball
|
||||
## (see /tmp/deco_review.png in the 2026-05-12 visual-pass session). Mix of
|
||||
## small-grass sprouts, flowers, and scattered-tuft sprites — all sit cleanly
|
||||
## on top of the base grass tile without occluding pawns.
|
||||
const DECORATION_TILES: Array[Vector2i] = [
|
||||
Vector2i(12, 0), # grass sprout A
|
||||
Vector2i(13, 0), # grass sprout B
|
||||
Vector2i(14, 0), # scattered grass dots — small
|
||||
Vector2i(16, 0), # white flower / red center
|
||||
Vector2i(17, 0), # white flower / yellow center
|
||||
Vector2i(18, 0), # white flower / purple center
|
||||
Vector2i(16, 2), # scattered grass dots — medium
|
||||
Vector2i(17, 2), # scattered grass dots — large
|
||||
]
|
||||
|
||||
## How often a tile gets a decoration. 0.08 = ~8% density — visually rich but
|
||||
## not so dense that pawns/items get hidden under it. Tunable; tested at
|
||||
## ~500 decorations on the 80×80 map.
|
||||
const DECORATION_DENSITY: float = 0.08
|
||||
|
||||
|
||||
func _build_decoration_tileset() -> TileSet:
|
||||
var ts := TileSet.new()
|
||||
ts.tile_size = Vector2i(TILE_SIZE_PX, TILE_SIZE_PX)
|
||||
|
||||
var tex: Texture2D = load("res://art/tiles/FG_Grasslands_Spring.png")
|
||||
if tex == null:
|
||||
Audit.log("world", "decoration: FG_Grasslands_Spring.png not loadable; skipping overlay")
|
||||
return ts
|
||||
|
||||
var src := TileSetAtlasSource.new()
|
||||
src.texture = tex
|
||||
src.texture_region_size = Vector2i(TILE_SIZE_PX, TILE_SIZE_PX)
|
||||
for coord in DECORATION_TILES:
|
||||
src.create_tile(coord)
|
||||
ts.add_source(src, DECORATION_SOURCE_ID)
|
||||
return ts
|
||||
|
||||
|
||||
func _paint_decorations() -> void:
|
||||
# Deterministic-ish seed so the layout is stable across runs (cosmetic only;
|
||||
# not save-round-tripped). Mix world size into seed for "feels random but
|
||||
# reproducible." Use a fresh RNG so we don't perturb the global state.
|
||||
var rng := RandomNumberGenerator.new()
|
||||
rng.seed = MAP_SIZE_TILES.x * 31 + MAP_SIZE_TILES.y * 17
|
||||
var placed: int = 0
|
||||
for x in MAP_SIZE_TILES.x:
|
||||
for y in MAP_SIZE_TILES.y:
|
||||
if rng.randf() >= DECORATION_DENSITY:
|
||||
continue
|
||||
var coord: Vector2i = DECORATION_TILES[rng.randi() % DECORATION_TILES.size()]
|
||||
decoration_layer.set_cell(Vector2i(x, y), DECORATION_SOURCE_ID, coord)
|
||||
placed += 1
|
||||
Audit.log("world", "decoration: painted %d overlay cells" % placed)
|
||||
|
||||
|
||||
func _paint_sample_walls() -> void:
|
||||
var origin := Vector2i(36, 36)
|
||||
var size: int = 8
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue