extends Node ## G3 HUD captures from the real game at the running window size (1920x1080, 2560x1440, ## 1280x720, ultrawide). Every view is a real LuneGame with the real HUD; fixtures only set ## public match state before or between phases (gold, round, building HP, boss claim), exactly ## like the G1/G2 views. HUD interactions go through the HUD's own public methods when the ## build has them, so the same script photographs the G2 HUD ("vorher") and the G3 HUD. ## Run: godot --path . --resolution 1920x1080 res://tools/luneraria/g3_views.tscn -- --out=res://docs/luneraria/screenshots/g3/nachher-1920x1080 [--only=01,05] const Game = preload("res://scripts/luneraria/lune_game.gd") var out: String = "res://docs/luneraria/screenshots/g3/nachher/" var only: Array[String] = [] var shots: Array[String] = [] var game: Node3D 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 += "/" elif arg.begins_with("--only="): only.assign(arg.substr(7).split(",")) call_deferred("run") func wanted(label: String) -> bool: return only.is_empty() or only.any(func(prefix: String) -> bool: return label.begins_with(prefix)) func shot(label: String, frames: int = 12) -> void: for i: int in frames: await get_tree().process_frame # G3: wait until the portrait cache has rendered every requested model. var cache: Node = get_tree().root.get_node_or_null("LunePortraits") var guard: int = 0 while cache != null and cache.has_method("pending") and cache.pending() > 0 and guard < 600: await get_tree().process_frame guard += 1 for i: int in 3: 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("G3_VIEW ", label, " ", get_viewport().get_visible_rect().size) func wait(seconds: float) -> void: await get_tree().create_timer(seconds).timeout func fresh(seed: int = 4, slot: int = 1) -> LuneMatch: if game != null and is_instance_valid(game): game.queue_free() await get_tree().process_frame await get_tree().process_frame game = Game.new() add_child(game) game.start({"seed": seed, "human_slot": slot, "bunds": ["wurzelbund", "wurzelbund", "glutpakt", "glutpakt", "wurzelbund", "glutpakt"]}) await get_tree().process_frame return game.match_state ## Calls a HUD method if this build has it (G3); returns false otherwise (G2). func hud_call(method: String, args: Array = []) -> bool: if game.hud.has_method(method): game.hud.callv(method, args) return true return false func command(command_value: Dictionary) -> Dictionary: return game.match_state.submit(game.human_slot, command_value) ## Runs the combat clock quickly (more fixed steps per frame, rules unchanged), then holds it. func advance_combat(seconds: float, hold: bool = true) -> 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 = hold func armies(state: LuneMatch, slots: Array, round_number: int) -> void: state.round_number = round_number for slot: int in range(6): state.players[slot].draft.roster.clear() state.players[slot].draft.gold = 100000 if slots.has(slot) else 0 if slots.has(slot): LuneBot.plan(state, slot, "ausgewogen") for entry: Dictionary in state.players[slot].draft.roster: if entry.lane == LuneMatch.GROTTO: entry.lane = state.players[slot].lane var hero: Dictionary = state.players[slot].draft.hero if not hero.is_empty() and hero.lane == LuneMatch.GROTTO: hero.lane = state.players[slot].lane # The fixture gold only buys the armies; the human keeps an ordinary amount afterwards. state.players[game.human_slot].draft.gold = 185 func run() -> void: Soundscape.enabled = false await planning_views() await boss_views() await combat_views() await ancient_views() if game != null and is_instance_valid(game): game.queue_free() await get_tree().process_frame print("G3_VIEWS_DONE ", shots.size()) get_tree().quit() # ---------------------------------------------------------------- planning func planning_views() -> void: if not (wanted("01") or wanted("02") or wanted("03") or wanted("04") or wanted("05")): return var state: LuneMatch = await fresh() # A normal planning round: round 3, starter army bought, some gold left. state.round_number = 3 state.players[game.human_slot].draft.gold = 420 game._starter() await wait(.4) if wanted("01"): await shot("01-planung") # Hero hired and shown. state.players[game.human_slot].draft.gold = 900 command({"type": "hero_buy", "hero_id": "held_ringborke"}) game._rebuild_preview() if not hud_call("open_hero"): hud_call("show_page", [1]) await wait(.4) if wanted("02"): await shot("02-held-ausgewaehlt") # Item choice with one item equipped. var hero_uid: String = str(state.players[game.human_slot].draft.hero.get("uid", "")) command({"type": "item_buy", "item_id": "glutklinge", "uid": hero_uid}) hud_call("open_items") await wait(.3) if wanted("03"): await shot("03-itemauswahl") hud_call("close_modal") # A normal unit selected. var roster: Array = state.players[game.human_slot].draft.roster if not roster.is_empty(): if not hud_call("select_unit", [str(roster[0].uid)]): hud_call("show_page", [0]) await wait(.3) if wanted("04"): await shot("04-einheit-ausgewaehlt") hud_call("clear_selection") # Attack champions: one booked for the next round. command({"type": "champion_book", "champion_id": "ch_belagerung", "lane": "mid"}) if not hud_call("open_champions"): hud_call("show_page", [2]) await wait(.3) if wanted("05"): await shot("05-angriffschampion") # ---------------------------------------------------------------- boss func boss_views() -> void: if not (wanted("06") or wanted("07")): return var state: LuneMatch = await fresh() state.round_number = 10 state.boss.next_available_round = 10 game.focus_grotto(true) await wait(1.0) if wanted("06"): await shot("06-boss-verfuegbar") # Own team conquered the boss in round 11: deployment in round 12 on Mid, planning of 12. state.round_number = 12 state.boss.claim = {"team": 0, "won_round": 11, "deploy_round": 12, "chooser_slot": 1, "lane": "mid"} state.boss.next_available_round = 15 game.focus_lane("mid", true) await wait(1.0) if wanted("07"): await shot("07-boss-angekuendigt") # ---------------------------------------------------------------- combat func combat_views() -> void: if not (wanted("08") or wanted("09") or wanted("10") or wanted("13") or wanted("14")): return var state: LuneMatch = await fresh() armies(state, [0, 1, 2, 3, 4, 5], 14) state.building_hp["rot_top_outer"] = 0.0 state.building_hp["blau_bot_outer"] = 0.0 game._on_ready() await get_tree().process_frame await advance_combat(22.0) for spec: Array in [["08-kampf-top", "top"], ["09-kampf-mid", "mid"], ["10-kampf-bottom", "bot"]]: if wanted(str(spec[0]).left(2)): game.focus_lane(spec[1], true) await wait(.5) await shot(spec[0], 4) if wanted("13"): var ruin: Vector2 = state.map.building_def("blau_bot_outer").position game.camera.look_at_point(Vector3(ruin.x, 0, ruin.y), 22.0, true) await wait(.5) await shot("13-turm-zerstoert", 4) if wanted("14"): game.camera.look_at_point(Vector3(0, 0, 0), 46.0, true) await wait(.5) await shot("14-grosse-kampfansicht", 4) # ---------------------------------------------------------------- Ancients func ancient_views() -> void: if not (wanted("11") or wanted("12")): return var state: LuneMatch = await fresh() state.round_number = 21 for id: String in ["blau_mid_outer", "blau_mid_inner", "blau_base_1", "blau_base_2"]: state.building_hp[id] = 0.0 state.building_hp["blau_ancient"] = float(state.map.building_def("blau_ancient").max_hp) * .22 game.focus_base(true) await wait(1.2) if wanted("11"): await shot("11-eigener-ancient-kritisch") state = await fresh() state.round_number = 137 for id: String in ["rot_top_outer", "rot_top_inner", "rot_base_1", "rot_base_2"]: state.building_hp[id] = 0.0 state.building_hp["rot_ancient"] = float(state.map.building_def("rot_ancient").max_hp) * .18 var red: Vector2 = state.map.core_position(1) game.camera.look_at_point(Vector3(red.x, 0, red.y), 26.0, true) await wait(1.2) if wanted("12"): await shot("12-gegnerischer-ancient-kritisch")