saumwacht-godot/tools/luneraria/g_views.gd
Martin Stickelbroeck 8b17563814 G1: Drei-Lane-Layout mit Höhenfeld, Grotte, Aufmarschbereichen und MOBA-Kamera
Top windet sich über das Hochland, Mid quert die Kluft über die Mittelplattform,
Bottom führt durch das Tiefland über die Deltabrücke; Top und Bottom sind exakt
gleich lang, alles bleibt spiegelsymmetrisch. LuneTerrain liefert ein exakt
gespiegeltes Höhenfeld, auf dem Figuren stehen. Bossgrotte als Terrasse auf der
Achse zwischen Top und Mid, Aufmarschbereiche je Lane, perspektivische Kamera
mit Sprungtasten (F2–F4, B, G, H, O, R, Q/E). Bestandsassets wiederverwendet,
neue Formen als Blockout für G2/G4.

Geprüft: verify_g1 443/443, Regeln 91/91, M2 73/73, verify_m1 38/38,
verify_m2 14/14, Netz 43/43, Loopback 12/12, Balance 40 Partien (Seitentausch
20/20), Legacy 209/463. Vorher/Nachher-Aufnahmen und Messungen in G1-KARTE.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 00:09:48 +02:00

136 lines
5.3 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

extends Node
## Comparison captures for the graphics phases G1–G6, always from the real game camera
## and the real HUD at the running window size (use 1920x1080).
## Run: godot --path . --resolution 1920x1080 res://tools/luneraria/g_views.tscn -- --out=res://docs/luneraria/screenshots/g1/nachher
## Views: 01 map, 02–04 lanes in combat, 05/06 bases, 07 grotto, 08 many units, 09 HUD,
## 10 hero selected, 11 close zoom, 12 camera in motion. Match and fixture are fixed
## (seed 4, Wurzelbund on Mid) so every phase is photographed the same way. Fixture gold
## only exists in this capture run; the rules are the ones of the running build.
const Game = preload("res://scripts/luneraria/lune_game.gd")
var out: String = "res://docs/luneraria/screenshots/g1/nachher/"
var main: Node
var shots: Array[String] = []
func _ready() -> void:
for arg: String in OS.get_cmdline_user_args():
if arg.begins_with("--out="):
out = arg.substr(6)
if not out.ends_with("/"):
out += "/"
call_deferred("run")
func shot(label: String, frames: int = 14) -> void:
for i: int in frames:
await get_tree().process_frame
await RenderingServer.frame_post_draw
DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path(out))
get_viewport().get_texture().get_image().save_png(out + label + ".png")
shots.append(label)
print("G_VIEW ", label)
## Camera helpers that only use public map data, so they frame old and new layouts alike.
func grotto_point(game: Node3D) -> Vector2:
var map: LuneMap = game.match_state.map
return map.grotto_center() if map.has_method("grotto_center") else LuneMap.GROTTO
func look(game: Node3D, at: Vector2, zoom: float) -> void:
game.camera.look_at_point(Vector3(at.x, 0, at.y), zoom, true)
## Runs the combat clock quickly (more fixed steps per frame, rules unchanged) and holds it.
func advance_combat(game: Node3D, seconds: float) -> void:
var state: LuneMatch = game.match_state
game.paused = false
while state.phase == LuneMatch.Phase.COMBAT and state.combat.elapsed < seconds:
for i: int in range(20):
if state.phase != LuneMatch.Phase.COMBAT or state.combat.elapsed >= seconds:
break
state.step_combat()
await get_tree().process_frame
game.paused = true
## Documentation only (not a game view): orthographic top-down plan without the HUD.
func plan_view(game: Node3D) -> void:
var plan := Camera3D.new()
plan.projection = Camera3D.PROJECTION_ORTHOGONAL
plan.size = 86.0
plan.far = 400.0
game.add_child(plan)
plan.position = Vector3(0, 150, 0)
plan.rotation = Vector3(-PI * .5, 0, 0)
plan.current = true
game.hud.visible = false
await shot("00-kartenplan-draufsicht", 10)
game.hud.visible = true
game.camera.current = true
plan.queue_free()
func run() -> void:
Soundscape.enabled = false
main = load("res://scenes/luneraria.tscn").instantiate()
add_child(main)
var menu: MainMenu = main.menu
while not (menu.stage.is_ready("glutpakt") and menu.stage.is_ready("wurzelbund")):
await get_tree().process_frame
menu.bund = "wurzelbund"
menu.lane = "mid"
main.start_match({"seed": 4})
await get_tree().create_timer(.8).timeout
var game: Node3D = main.game
var state: LuneMatch = game.match_state
var slot: int = game.human_slot
# Fixture: enough gold for a hero with one item next to the starting army.
state.players[slot].draft.gold = 900
game._on_command({"type": "hero_buy", "hero_id": "held_ringborke"})
game._on_command({"type": "item_buy", "item_id": "basaltschild"})
game.hud._starter.pressed.emit()
game.hud.show_page(0)
game.focus_lane("mid", true)
await shot("09-hud-planung", 24)
game.hud.show_page(1)
await shot("10-held-ausgewaehlt")
game.hud.show_page(0)
game.camera.overview(true)
await shot("01-karte", 20)
await plan_view(game)
look(game, state.map.core_position(0), 26.0)
await shot("05-eigene-basis")
look(game, state.map.core_position(1), 26.0)
await shot("06-gegnerbasis")
look(game, grotto_point(game), 22.0)
await shot("07-bossgrotte")
game._on_ready()
await get_tree().process_frame
await advance_combat(game, 16.0)
var names := {"top": "02-top-lane", "mid": "03-mid-lane", "bot": "04-bottom-lane"}
for lane: String in LuneMap.LANES:
game.focus_lane(lane, true)
await shot(names[lane])
# Camera in motion: a real, non-instant pan from Mid towards the own base.
game.focus_lane("mid", true)
await get_tree().process_frame
var base: Vector2 = state.map.core_position(0)
game.camera.look_at_point(Vector3(base.x, 0, base.y), 30.0, false)
await shot("12-kamera-bewegt", 5)
# Many units: all six armies at the supply cap in a round-13 wave (capture fixture).
main.queue_free()
await get_tree().process_frame
var crowd: Node3D = Game.new()
add_child(crowd)
crowd.start({"seed": 5, "human_slot": 1})
var crowd_state: LuneMatch = crowd.match_state
crowd_state.round_number = 13
for other: int in range(6):
crowd_state.players[other].draft.gold = 100000
crowd_state.players[other].draft.roster.clear()
LuneBot.plan(crowd_state, other, "ausgewogen")
crowd._on_ready()
await get_tree().process_frame
await advance_combat(crowd, 13.0)
crowd.focus_lane("mid", true)
await shot("08-kampf-viele-einheiten", 20)
var fight: Vector2 = crowd_state.map.lane_point(0, "mid", crowd_state.map.lanes["mid"].length * .5)
look(crowd, fight, 13.0)
await shot("11-zoom-nah")
print("G_VIEWS_DONE ", shots.size())
get_tree().quit()