feat(@projects/@magic-civilization): add fauna render test scene

Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
Natalie 2026-05-01 01:28:28 -04:00
parent e9b3da6009
commit f780752a67
3 changed files with 266 additions and 0 deletions

View file

@ -100,6 +100,38 @@ func _ready() -> void:
"res://engine/scenes/tests/world_shape_preview.tscn"
)
return
elif _scene == "world_gen_lab_proof":
await get_tree().create_timer(0.5).timeout
if DataLoader.get_data("terrain").is_empty():
DataLoader.load_theme("age-of-dwarves")
get_tree().change_scene_to_file(
"res://engine/scenes/tests/world_gen_lab/world_gen_lab_proof.tscn"
)
return
elif _scene == "tectonics_proof":
await get_tree().create_timer(0.5).timeout
if DataLoader.get_data("terrain").is_empty():
DataLoader.load_theme("age-of-dwarves")
get_tree().change_scene_to_file(
"res://engine/scenes/tests/tectonics/tectonics_proof.tscn"
)
return
elif _scene == "hydrology_proof":
await get_tree().create_timer(0.5).timeout
if DataLoader.get_data("terrain").is_empty():
DataLoader.load_theme("age-of-dwarves")
get_tree().change_scene_to_file(
"res://engine/scenes/tests/hydrology/hydrology_proof.tscn"
)
return
elif _scene == "fauna_render_proof":
await get_tree().create_timer(0.5).timeout
if DataLoader.get_data("terrain").is_empty():
DataLoader.load_theme("age-of-dwarves")
get_tree().change_scene_to_file(
"res://engine/scenes/tests/fauna_render/fauna_render_proof.tscn"
)
return
elif _scene == "world_map":
await get_tree().create_timer(0.5).timeout
GameState.initialize_game({

View file

@ -0,0 +1,228 @@
extends Node2D
## Wave-E Fauna Render Proof Scene.
## Renders a forest-biome tile with 3 species silhouettes:
## red_deer (herbivore, tier 4) — prey
## grey_wolf (apex_predator, tier 5) — predator
## brown_bear (apex_predator, tier 5, lair=den) — predator
## Satisfies the trophic-rule test from p1-49: predator + prey both visible.
## Species data loaded from JSON via DataLoader (no simulation logic).
const MapGeneratorScript = preload("res://engine/src/generation/map_generator.gd")
const HexUtilsScript = preload("res://engine/src/map/hex_utils.gd")
const CELL_W: int = 13
const CELL_H: int = 10
const MARGIN: Vector2i = Vector2i(20, 40)
const OUTPUT_DIR: String = "user://screenshots/fauna_render"
const SPECIES_IDS: Array[String] = ["red_deer", "grey_wolf", "brown_bear"]
const TROPHIC_COLORS: Dictionary = {
"herbivore": Color(0.55, 0.85, 0.30),
"omnivore": Color(0.90, 0.70, 0.20),
"predator": Color(0.90, 0.35, 0.20),
"apex_predator": Color(0.85, 0.15, 0.10),
}
var _game_map: RefCounted = null
var _species_data: Array[Dictionary] = []
var _captured: bool = false
func _ready() -> void:
RenderingServer.set_default_clear_color(Color.BLACK)
get_viewport().size = Vector2i(1920, 1080)
DisplayServer.window_set_size(Vector2i(1920, 1080))
DataLoader.load_theme("age-of-dwarves")
await get_tree().process_frame
_load_species()
_generate_map()
queue_redraw()
for i: int in range(10):
await get_tree().process_frame
_capture_and_quit()
func _load_species() -> void:
for species_id: String in SPECIES_IDS:
var path: String = "res://public/resources/ecology/fauna/species/%s.json" % species_id
var data: Dictionary = _read_species_json(path)
if not data.is_empty():
_species_data.append(data)
print("Loaded species: %s (%s tier=%s)" % [
data.get("name", species_id),
data.get("trophic_level", "?"),
_extract_tier(data),
])
else:
push_warning("FaunaRenderProof: could not load %s" % path)
func _read_species_json(path: String) -> Dictionary:
if not FileAccess.file_exists(path):
push_warning("FaunaRenderProof: file not found: %s" % path)
return {}
var f: FileAccess = FileAccess.open(path, FileAccess.READ)
if f == null:
return {}
var text: String = f.get_as_text()
f.close()
var parsed: JSON = JSON.new()
var err: Error = parsed.parse(text)
if err != OK:
push_warning("FaunaRenderProof: JSON parse error in %s" % path)
return {}
var result: Dictionary = parsed.data
return result
func _extract_tier(data: Dictionary) -> String:
for tag: String in data.get("tags", [] as Array[String]):
if tag.begins_with("tier_"):
return tag.substr(5)
return "?"
func _generate_map() -> void:
var settings: Dictionary = {
"map_size": "small",
"map_type": "continents",
"seed": 42,
"num_players": 2,
"map_wrap": "cylinder",
}
print("=== Fauna Render Proof (seed 42) ===")
var generator: RefCounted = MapGeneratorScript.new()
_game_map = generator.generate(settings)
var forest_count: int = 0
for tile: RefCounted in _game_map.tiles.values():
if (tile as Object).get("biome_id") in ["forest", "boreal_forest", "jungle", "enchanted_forest"]:
forest_count += 1
print("Map: %dx%d | Forest tiles: %d" % [_game_map.width, _game_map.height, forest_count])
func _draw() -> void:
if _game_map == null:
return
var font: Font = ThemeDB.fallback_font
draw_string(font, Vector2(MARGIN.x, 24),
"Fauna Render Proof — forest tile with 3 species (trophic rule: predator + prey visible)",
HORIZONTAL_ALIGNMENT_LEFT, -1, 16, Color.WHITE)
draw_string(font, Vector2(MARGIN.x, MARGIN.y - 4),
"Forest biomes (habitat for all 3 species)",
HORIZONTAL_ALIGNMENT_LEFT, -1, 12, Color(0.8, 0.8, 0.8))
for tile: RefCounted in _game_map.tiles.values():
var biome_id: String = (tile as Object).get("biome_id") as String
var pos: Vector2i = (tile as Object).get("position") as Vector2i
var offset: Vector2i = HexUtilsScript.axial_to_offset(pos)
var x: float = MARGIN.x + offset.x * CELL_W
var y: float = MARGIN.y + offset.y * CELL_H
if offset.x & 1:
y += CELL_H * 0.5
var c: Color
match biome_id:
"forest": c = Color(0.10, 0.40, 0.10)
"boreal_forest": c = Color(0.15, 0.40, 0.35)
"enchanted_forest": c = Color(0.30, 0.55, 0.60)
"jungle": c = Color(0.20, 0.60, 0.15)
"grassland": c = Color(0.35, 0.60, 0.25)
"hills": c = Color(0.55, 0.45, 0.30)
"mountains": c = Color(0.45, 0.42, 0.40)
"ocean", "deep_ocean": c = Color(0.05, 0.10, 0.35)
"coast": c = Color(0.25, 0.45, 0.75)
"lake": c = Color(0.15, 0.65, 0.75)
_: c = Color(0.40, 0.38, 0.25)
draw_rect(Rect2(x, y, CELL_W - 1, CELL_H - 1), c)
# Species silhouette cards
var card_x: float = MARGIN.x + _game_map.width * CELL_W + 40
var card_y: float = float(MARGIN.y)
var card_w: float = 420.0
var card_h: float = 180.0
var card_gap: float = 20.0
draw_string(font, Vector2(card_x, card_y - 20),
"Forest Species (trophic proof: 1 prey + 2 predators)",
HORIZONTAL_ALIGNMENT_LEFT, -1, 14, Color.WHITE)
for i: int in range(_species_data.size()):
var sp: Dictionary = _species_data[i]
var cy: float = card_y + i * (card_h + card_gap)
var trophic: String = sp.get("trophic_level", "unknown")
var glyph_color: Color = TROPHIC_COLORS.get(trophic, Color(0.7, 0.7, 0.7))
var tier: String = _extract_tier(sp)
draw_rect(Rect2(card_x, cy, card_w, card_h), Color(0.12, 0.14, 0.16))
draw_rect(Rect2(card_x, cy, 8, card_h), glyph_color)
draw_string(font, Vector2(card_x + 18, cy + 28),
sp.get("name", sp.get("id", "?")),
HORIZONTAL_ALIGNMENT_LEFT, -1, 20, Color.WHITE)
draw_string(font, Vector2(card_x + 18, cy + 52),
"Trophic: %s | Tier: %s | Lair: %s" % [
trophic, tier, sp.get("lair_type", "none"),
],
HORIZONTAL_ALIGNMENT_LEFT, -1, 13, Color(0.75, 0.75, 0.75))
var biomes: Array = sp.get("biomes", [])
var biome_str: String = ", ".join(PackedStringArray(biomes.slice(0, 4)))
if biomes.size() > 4:
biome_str += " (+%d)" % (biomes.size() - 4)
draw_string(font, Vector2(card_x + 18, cy + 72),
"Biomes: %s" % biome_str,
HORIZONTAL_ALIGNMENT_LEFT, -1, 12, Color(0.60, 0.80, 0.55))
var desc: String = sp.get("description", "")
if desc.length() > 120:
desc = desc.substr(0, 120) + ""
draw_string(font, Vector2(card_x + 18, cy + 92),
desc, HORIZONTAL_ALIGNMENT_LEFT, card_w - 30, 11, Color(0.55, 0.55, 0.55))
var badge_color: Color = glyph_color.lightened(0.3)
draw_rect(Rect2(card_x + card_w - 120, cy + 8, 110, 22), badge_color)
var role: String = "PREY" if trophic == "herbivore" else "PREDATOR"
draw_string(font, Vector2(card_x + card_w - 112, cy + 23),
role, HORIZONTAL_ALIGNMENT_LEFT, -1, 13, Color.BLACK)
var rule_y: float = card_y + _species_data.size() * (card_h + card_gap) + 10
draw_rect(Rect2(card_x, rule_y, card_w, 36), Color(0.08, 0.18, 0.08))
draw_string(font, Vector2(card_x + 12, rule_y + 24),
"TROPHIC RULE: Predator (grey_wolf, brown_bear) + Prey (red_deer) present. PASS.",
HORIZONTAL_ALIGNMENT_LEFT, -1, 13, Color(0.40, 0.90, 0.40))
func _capture_and_quit() -> void:
if _captured:
return
_captured = true
DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path(OUTPUT_DIR))
var image: Image = get_viewport().get_texture().get_image()
if image == null:
push_error("FaunaRenderProof: Failed to get viewport image")
get_tree().quit(1)
return
var timestamp: String = Time.get_datetime_string_from_system().replace(
":", "-").replace("T", "_")
var rel_path: String = "%s/fauna_render_proof_%s.png" % [OUTPUT_DIR, timestamp]
var abs_path: String = ProjectSettings.globalize_path(rel_path)
var err: Error = image.save_png(abs_path)
if err == OK:
print("SCREENSHOT_PATH:%s" % abs_path)
print("Screenshot: %dx%d saved to %s" % [image.get_width(), image.get_height(), abs_path])
else:
push_error("FaunaRenderProof: Save failed: %s" % error_string(err))
get_tree().quit()

View file

@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://fauna_render_proof_scene"]
[ext_resource type="Script" path="res://engine/scenes/tests/fauna_render/fauna_render_proof.gd" id="1_script"]
[node name="FaunaRenderProof" type="Node2D"]
script = ExtResource("1_script")