From 3d974ca17df3194d11eb127fd999e31f523ac175 Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Sun, 11 Oct 2026 12:34:11 -0400 Subject: [PATCH 1/6] Scope genuine prototype teaser capture --- docs/development/prototype-teaser-capture.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 docs/development/prototype-teaser-capture.md diff --git a/docs/development/prototype-teaser-capture.md b/docs/development/prototype-teaser-capture.md new file mode 100644 index 00000000..1f7da355 --- /dev/null +++ b/docs/development/prototype-teaser-capture.md @@ -0,0 +1,3 @@ +# Prototype teaser capture + +Owner-requested local Telegram teaser. Record genuine Godot workshop geometry and canonical finite-world observations through the existing machinery integration fixture. No generative imagery, game release, external uploads, runtime changes or human-playtest claim. Work in progress. From b3506211b3ea9ba1672eb144857e64cad2b48a9d Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Sun, 11 Oct 2026 12:41:27 -0400 Subject: [PATCH 2/6] Record genuine finite workshop teaser with captioned native rendering --- docs/development/prototype-teaser-capture.md | 14 +- tools/ci/capture-telegram-teaser.ps1 | 57 +++++++ tools/godot/capture_telegram_teaser.gd | 148 +++++++++++++++++++ tools/godot/capture_telegram_teaser.gd.uid | 1 + 4 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 tools/ci/capture-telegram-teaser.ps1 create mode 100644 tools/godot/capture_telegram_teaser.gd create mode 100644 tools/godot/capture_telegram_teaser.gd.uid diff --git a/docs/development/prototype-teaser-capture.md b/docs/development/prototype-teaser-capture.md index 1f7da355..0330faf1 100644 --- a/docs/development/prototype-teaser-capture.md +++ b/docs/development/prototype-teaser-capture.md @@ -1,3 +1,15 @@ # Prototype teaser capture -Owner-requested local Telegram teaser. Record genuine Godot workshop geometry and canonical finite-world observations through the existing machinery integration fixture. No generative imagery, game release, external uploads, runtime changes or human-playtest claim. Work in progress. +Owner-requested 24-second local Telegram teaser, using Godot 4.7.2 native Movie Maker and ordinary FFmpeg editing. The route extends the existing machinery integration test: finite source material is extracted, refined and assembled through real commands before the scene is retained. An additional canonical extractor batch is recorded, increasing its actual canister ice from 1 kg to 2 kg; final snapshot validation and conservation checks must pass. + +Existing ProceduralHabitatPreview, MachineryPreview and inventory meshes are rendered from current owner observations. Camera paths, capture-only lighting and captions differ from normal presentation. Debug labels, fixture IDs and the preview avatar are hidden. No canonical state grants, fabricated receipts, service listeners, external sends, new fonts/assets, generative image tools or game release are involved. Captions use the existing engine UI font. First-party geometry/capture code is MIT; existing engine font remains part of the engine runtime, not an added redistributed asset. + +Run in a clean isolated worktree after import: + +```powershell +./tools/ci/capture-telegram-teaser.ps1 -Godot 'D:/Godot_v4.7.2-stable_win64.exe/Godot_v4.7.2-stable_win64_console.exe' -OutputDirectory 'D:/GitHub/Frontier Systems/var/captures/telegram-teaser' +``` + +The wrapper owns one bounded native process, disables external telemetry, isolates the user directory and removes its temporary override. It never opens a socket or touches the regular saved world. Capture is graphical on Windows using GL compatibility. Automated fixture and camera operation are disclosed onscreen; this is not footage of human play, controller/VR acceptance or a published build. Local media can be given to the owner for the explicitly requested personal teaser; no Telegram upload is performed by this route. + +Review native keyframes before encoding. Use H.264/yuv420p MP4, 1280x720, 30 fps and faststart for mobile sharing. Keep raw provenance and output hashes alongside ignored local media; do not add current internal gallery images to the teaser or upload it to other services. diff --git a/tools/ci/capture-telegram-teaser.ps1 b/tools/ci/capture-telegram-teaser.ps1 new file mode 100644 index 00000000..902051d8 --- /dev/null +++ b/tools/ci/capture-telegram-teaser.ps1 @@ -0,0 +1,57 @@ +param( + [string]$Godot = 'godot', + [string]$OutputDirectory = '', + [ValidateRange(30, 300)][int]$TimeoutSeconds = 180 +) +# Native non-generative movie capture, no sockets, uploads or persistent game data. +$ErrorActionPreference = 'Stop' +$projectRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) +$executable = (Get-Command $Godot -CommandType Application).Source +if (-not $OutputDirectory) { $OutputDirectory = Join-Path $projectRoot 'var/captures/telegram-teaser' } +New-Item -ItemType Directory -Path $OutputDirectory -Force | Out-Null +$OutputDirectory = (Resolve-Path -LiteralPath $OutputDirectory).Path +$overridePath = Join-Path $projectRoot 'override.cfg' +if (Test-Path -LiteralPath $overridePath) { throw 'Preserve existing override.cfg; use a clean isolated worktree.' } +$profileName = 'FrontierSystems-teaser-' + [Guid]::NewGuid().ToString('N') +$process = $null +try { + [IO.File]::WriteAllText($overridePath, "[application]`nconfig/use_custom_user_dir=true`nconfig/custom_user_dir_name=`"$profileName`"`n`n[display]`nwindow/size/viewport_width=1280`nwindow/size/viewport_height=720`n") + $info = [Diagnostics.ProcessStartInfo]::new() + $info.FileName = $executable + $info.WorkingDirectory = $projectRoot + $info.UseShellExecute = $false + $info.CreateNoWindow = $true + $info.WindowStyle = [Diagnostics.ProcessWindowStyle]::Hidden + $info.RedirectStandardOutput = $true + $info.RedirectStandardError = $true + $info.Environment['FRONTIER_EVENT_URL'] = '' + $info.Environment['FRONTIER_EVENT_TOKEN'] = '' + $info.Environment['FRONTIER_TEASER_OUTPUT'] = $OutputDirectory + foreach ($argument in @('--xr-mode','off','--audio-driver','Dummy','--rendering-method','gl_compatibility','--resolution','1280x720','--path',$projectRoot,'--script','res://tools/godot/capture_telegram_teaser.gd','--fixed-fps','30','--write-movie',(Join-Path $OutputDirectory 'raw.avi'))) { + $info.ArgumentList.Add($argument) + } + $process = [Diagnostics.Process]::new() + $process.StartInfo = $info + if (-not $process.Start()) { throw 'Native movie capture did not start.' } + $stdout = $process.StandardOutput.ReadToEndAsync() + $stderr = $process.StandardError.ReadToEndAsync() + if (-not $process.WaitForExit($TimeoutSeconds * 1000)) { + $process.Kill($true) + $process.WaitForExit() + throw 'Owned movie capture exceeded its deadline.' + } + $log = $stdout.GetAwaiter().GetResult() + $stderr.GetAwaiter().GetResult() + [IO.File]::WriteAllText((Join-Path $OutputDirectory 'capture.log'), $log) + if ($process.ExitCode -ne 0 -or $log -match '(?m)SCRIPT ERROR|(?:^|\s)ERROR:') { + Write-Host $log.Substring(0, [Math]::Min(3000, $log.Length)) + throw 'Native capture failed; inspect capture.log.' + } + Write-Host "Native capture completed: $OutputDirectory" + $log | Select-String -Pattern 'Machinery integration|TEASER_' +} finally { + if ($null -ne $process) { + if (-not $process.HasExited) { $process.Kill($true) } + $process.Dispose() + } + Remove-Item -LiteralPath $overridePath -ErrorAction SilentlyContinue +} diff --git a/tools/godot/capture_telegram_teaser.gd b/tools/godot/capture_telegram_teaser.gd new file mode 100644 index 00000000..9086fdaa --- /dev/null +++ b/tools/godot/capture_telegram_teaser.gd @@ -0,0 +1,148 @@ +extends "res://tests/godot/machinery_runtime_test.gd" + +## Genuine native render of the existing finite integration world. +## Only the camera, lighting and caption layer differ from ordinary presentation. +const FPS: int = 30 +const FRAMES: int = 720 +var habitat: ProceduralHabitatPreview +var machinery: MachineryPreview +var title: Label +var detail: Label +var _output: String +var _extractor: String = "" + + +func finish() -> void: + if failures > 0 or DisplayServer.get_name() == "headless": + world.free() + quit(1) + return + _output = OS.get_environment("FRONTIER_TEASER_OUTPUT") + if _output.is_empty(): _output = ProjectSettings.globalize_path("res://var/captures/telegram-teaser") + DirAccess.make_dir_recursive_absolute(_output) + root.size = Vector2i(1280, 720) + root.content_scale_size = Vector2i(1280, 720) + root.msaa_3d = Viewport.MSAA_4X + habitat = ProceduralHabitatPreview.new() + root.add_child(habitat) + habitat.character.set_navigation_enabled(false) + habitat.character.visible = false + habitat.world_inventory.skip_machine_roots = true + habitat.world_inventory.labels_visible = false + habitat.camera.set_process(false) + habitat.camera.set_physics_process(false) + habitat.camera.fov = 49.0 + habitat.focus_production() + machinery = MachineryPreview.new() + habitat.add_child(machinery) + for child in habitat.get_children(): + if child is WorldEnvironment: + child.environment.background_color = Color("#101923") + child.environment.ambient_light_energy = 0.38 + if child is DirectionalLight3D: + child.light_energy = 0.95 + child.rotation = Vector3(-1.1, -0.5, 0) + if child is CanvasLayer: child.visible = false + var workshop: Dictionary = production.capture_snapshot().value["machinery"] + for id in workshop["mounts"]: + if workshop["parts"][id]["kind"] == "extractor": _extractor = workshop["mounts"][id] + check(not _extractor.is_empty(), "existing verified extractor found") + _make_captions() + _update_scene() + # Start recording only after native scene setup, for reproducible trimming. + print("TEASER_START_FRAME %d" % Engine.get_frames_drawn()) + for frame in range(FRAMES): + if frame == 360: + command("machine_run", {"machine_id": _extractor, "recipe": "extract.ice"}) + step(2) # Canonical 60 Hz ticks for each 30 fps recorded frame. + _update_scene() + _shot(frame) + await RenderingServer.frame_post_draw + if frame in [60, 240, 450, 660]: + check(root.get_texture().get_image().save_png(_output.path_join("frame-%03d.png" % frame)) == OK, "native keyframe") + await process_frame + check(production.capture_snapshot().value["cargo"]["resource-canister-1"]["ice"] == 2000000, "recorded extractor completed another conserved batch") + check(production.validate_snapshot_record(production.capture_snapshot().value, runner.tick_number).is_success, "recorded finite world still validates") + print("TEASER_END_FRAME %d; failures %d" % [Engine.get_frames_drawn(), failures]) + habitat.free() + world.free() + quit(1 if failures else 0) + + +func _update_scene() -> void: + var observed: Dictionary = production.observation(context).value + habitat.production.observe(observed) + habitat.production.set_labels_visible(false) + machinery.observe(observed) + habitat.world_inventory.observe(inventory.world_inventory_observation(context).value) + _quiet(habitat) + + +func _quiet(node: Node) -> void: + if node is Label3D: node.visible = false + for child in node.get_children(): _quiet(child) + + +func _make_captions() -> void: + var layer := CanvasLayer.new() + root.add_child(layer) + var header := ColorRect.new() + header.size = Vector2(1280, 82) + header.color = Color(0.035, 0.055, 0.075, 0.9) + layer.add_child(header) + var footer := ColorRect.new() + footer.position = Vector2(0, 548) + footer.size = Vector2(1280, 172) + footer.color = Color(0.035, 0.055, 0.075, 0.91) + layer.add_child(footer) + var brand := Label.new() + brand.position = Vector2(42, 26) + brand.text = "FRONTIER SYSTEMS" + brand.add_theme_font_size_override("font_size", 26) + layer.add_child(brand) + var status := Label.new() + status.position = Vector2(993, 31) + status.text = "SOURCE PROTOTYPE" + status.add_theme_font_size_override("font_size", 17) + status.modulate = Color("#a3d6d4") + layer.add_child(status) + title = Label.new() + title.position = Vector2(42, 572) + title.add_theme_font_size_override("font_size", 38) + layer.add_child(title) + detail = Label.new() + detail.position = Vector2(44, 630) + detail.add_theme_font_size_override("font_size", 22) + detail.modulate = Color("#b5c7d2") + layer.add_child(detail) + var note := Label.new() + note.position = Vector2(44, 683) + note.text = "Actual Godot scene | Automated camera and simulation commands" + note.add_theme_font_size_override("font_size", 14) + note.modulate = Color("#80949f") + layer.add_child(note) + + +func _shot(frame: int) -> void: + var segment: int = frame / 180 + var amount: float = smoothstep(0, 1, float(frame % 180) / 179.0) + var starts: Array[Vector3] = [Vector3(-9.5, 5, 4.3), Vector3(-6.5, 2.8, 1.8), Vector3(-8.6, 2.5, 0.8), Vector3(-2.5, 4, 3.1)] + var ends: Array[Vector3] = [Vector3(-6.8, 4.4, 3.8), Vector3(-4.1, 2.5, 1.8), Vector3(-7.4, 2, 0.2), Vector3(-7.5, 5.2, 3.6)] + var targets: Array[Vector3] = [Vector3(-3.8, 0.65, -1.5), Vector3(-4.3, 0.55, -2), Vector3(-6, 0.65, -2), Vector3(-4.3, 0.6, -1.6)] + var display_origin: Vector3 = ProceduralWorldInventoryPreview.DISPLAY_ORIGIN + habitat.camera.position = display_origin + starts[segment].lerp(ends[segment], amount) + habitat.camera.look_at(display_origin + targets[segment]) + match segment: + 0: + title.text = "A first look at the workshop" + detail.text = "Built through the prototype's finite material loop" + 1: + title.text = "Power runs through physical cables" + detail.text = "Modular machines, replaceable parts and portable storage" + 2: + title.text = "The extractor fills a real canister" + var ice: int = int(production.capture_snapshot().value["cargo"]["resource-canister-1"]["ice"]) + detail.text = "Ice in storage: %.2f kg" % (float(ice) / 1000000.0) + 3: + title.text = "Frontier Systems" + detail.text = "An early cooperative engineering prototype" diff --git a/tools/godot/capture_telegram_teaser.gd.uid b/tools/godot/capture_telegram_teaser.gd.uid new file mode 100644 index 00000000..e38378c3 --- /dev/null +++ b/tools/godot/capture_telegram_teaser.gd.uid @@ -0,0 +1 @@ +uid://muqcy2l6dawg From f65098e5f8f701c8650b4c985417ca4421d42a0d Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Sun, 11 Oct 2026 12:58:27 -0400 Subject: [PATCH 3/6] Show actual workshop actions in uncluttered teaser shots --- docs/development/prototype-teaser-capture.md | 4 +- tools/godot/capture_telegram_teaser.gd | 159 ++++++++++++++----- 2 files changed, 124 insertions(+), 39 deletions(-) diff --git a/docs/development/prototype-teaser-capture.md b/docs/development/prototype-teaser-capture.md index 0330faf1..3961aa23 100644 --- a/docs/development/prototype-teaser-capture.md +++ b/docs/development/prototype-teaser-capture.md @@ -1,8 +1,8 @@ # Prototype teaser capture -Owner-requested 24-second local Telegram teaser, using Godot 4.7.2 native Movie Maker and ordinary FFmpeg editing. The route extends the existing machinery integration test: finite source material is extracted, refined and assembled through real commands before the scene is retained. An additional canonical extractor batch is recorded, increasing its actual canister ice from 1 kg to 2 kg; final snapshot validation and conservation checks must pass. +Owner-requested 24-second local Telegram teaser, using Godot 4.7.2 native Movie Maker and ordinary FFmpeg editing. The route extends the existing machinery integration test: finite source material is extracted, refined and assembled through real commands before the scene is retained. The recorded commands build, dismantle and rotate a wall from one recovered plate, pack and relocate the fitted printer, replace an extractor head with a refinery head, and refine its actual 1 kg ice into water. Final snapshot validation and finite-material checks must pass. -Existing ProceduralHabitatPreview, MachineryPreview and inventory meshes are rendered from current owner observations. Camera paths, capture-only lighting and captions differ from normal presentation. Debug labels, fixture IDs and the preview avatar are hidden. No canonical state grants, fabricated receipts, service listeners, external sends, new fonts/assets, generative image tools or game release are involved. Captions use the existing engine UI font. First-party geometry/capture code is MIT; existing engine font remains part of the engine runtime, not an added redistributed asset. +Existing ProceduralHabitatPreview, MachineryPreview and inventory meshes are rendered from current owner observations. Static close camera shots, capture-only lighting and compact edge captions differ from normal presentation. The normal HUD is hidden so it cannot obscure the machinery or wall. Debug labels, fixture IDs and the preview avatar are hidden. No canonical state grants, fabricated receipts, service listeners, external sends, new fonts/assets, generative image tools or game release are involved. Captions use the existing engine UI font. First-party geometry/capture code is MIT; existing engine font remains part of the engine runtime, not an added redistributed asset. Run in a clean isolated worktree after import: diff --git a/tools/godot/capture_telegram_teaser.gd b/tools/godot/capture_telegram_teaser.gd index 9086fdaa..2f8ae5fc 100644 --- a/tools/godot/capture_telegram_teaser.gd +++ b/tools/godot/capture_telegram_teaser.gd @@ -10,13 +10,28 @@ var title: Label var detail: Label var _output: String var _extractor: String = "" +var _extractor_head: String = "" +var _refinery: String = "" +var _refinery_head: String = "" +var _printer: String = "" +var _plate: String = "" +var _wall: String = "" func finish() -> void: - if failures > 0 or DisplayServer.get_name() == "headless": + if failures > 0: world.free() quit(1) return + _prepare_actions() + if DisplayServer.get_name() == "headless": + for frame in range(FRAMES): + _action(frame) + step(2) + _verify_actions() + world.free() + quit(1 if failures else 0) + return _output = OS.get_environment("FRONTIER_TEASER_OUTPUT") if _output.is_empty(): _output = ProjectSettings.globalize_path("res://var/captures/telegram-teaser") DirAccess.make_dir_recursive_absolute(_output) @@ -43,17 +58,12 @@ func finish() -> void: child.light_energy = 0.95 child.rotation = Vector3(-1.1, -0.5, 0) if child is CanvasLayer: child.visible = false - var workshop: Dictionary = production.capture_snapshot().value["machinery"] - for id in workshop["mounts"]: - if workshop["parts"][id]["kind"] == "extractor": _extractor = workshop["mounts"][id] - check(not _extractor.is_empty(), "existing verified extractor found") _make_captions() _update_scene() # Start recording only after native scene setup, for reproducible trimming. print("TEASER_START_FRAME %d" % Engine.get_frames_drawn()) for frame in range(FRAMES): - if frame == 360: - command("machine_run", {"machine_id": _extractor, "recipe": "extract.ice"}) + _action(frame) step(2) # Canonical 60 Hz ticks for each 30 fps recorded frame. _update_scene() _shot(frame) @@ -61,14 +71,91 @@ func finish() -> void: if frame in [60, 240, 450, 660]: check(root.get_texture().get_image().save_png(_output.path_join("frame-%03d.png" % frame)) == OK, "native keyframe") await process_frame - check(production.capture_snapshot().value["cargo"]["resource-canister-1"]["ice"] == 2000000, "recorded extractor completed another conserved batch") - check(production.validate_snapshot_record(production.capture_snapshot().value, runner.tick_number).is_success, "recorded finite world still validates") + _verify_actions() print("TEASER_END_FRAME %d; failures %d" % [Engine.get_frames_drawn(), failures]) habitat.free() world.free() quit(1 if failures else 0) +func _prepare_actions() -> void: + var workshop: Dictionary = production.capture_snapshot().value["machinery"] + for id in workshop["mounts"]: + match workshop["parts"][id]["kind"]: + "extractor": + _extractor = workshop["mounts"][id] + _extractor_head = id + "refinery": + _refinery = workshop["mounts"][id] + _refinery_head = id + "printhead": _printer = workshop["mounts"][id] + check(not _extractor.is_empty() and not _printer.is_empty(), "actual modular workshop available") + # Reclaim the unused disconnected frame, retaining its working refinery head. + move([-300, 0, -1100]) + command("machine_pack", {"machine_id": _refinery}) + for id in workshop["mounts"]: + if workshop["mounts"][id] != _refinery: continue + command("machine_unfit", {"machine_id": _refinery, "item_id": id, "socket_id": ""}) + if id != _refinery_head: command("machine_recycle", {"item_id": id}) + command("machine_recycle", {"item_id": _refinery}) + move([0, 0, 0]) + command("process", {"recipe_id": "craft.plate"}) + step(120) + command("materialize_plate", {}) + var plates: Array = production.observation(context).value["components"] + check(not plates.is_empty(), "one physical plate crafted from remaining finite stock") + if not plates.is_empty(): _plate = plates.back() + move([-2000, 0, 3500]) + + +func _action(frame: int) -> void: + match frame: + 40: + command("wall_build", {"item_id": _plate, "position": [-2000, 0, 2500], "quarter_turn": 0}) + if not production.capture_snapshot().value["walls"].is_empty(): _wall = production.capture_snapshot().value["walls"].keys().back() + 120: + command("wall_remove", {"wall_id": _wall}) + var plates: Array = production.observation(context).value["components"] + if not plates.is_empty(): _plate = plates.back() + 150: + command("wall_build", {"item_id": _plate, "position": [-2000, 0, 2500], "quarter_turn": 1}) + if not production.capture_snapshot().value["walls"].is_empty(): _wall = production.capture_snapshot().value["walls"].keys().back() + 210: + move([0, 0, 3500]) + move([900, 0, 0]) + command("machine_pack", {"machine_id": _printer}) + 270: + move([2500, 0, 0]) + command("machine_deploy", {"machine_id": _printer, "position": [2500, 0, -1000], "mode": "surface"}) + 330: + command("machine_pack", {"machine_id": _printer}) + move([900, 0, 0]) + command("machine_deploy", {"machine_id": _printer, "position": [900, 0, -1000], "mode": "surface"}) + 370: + move([-6000, 0, -1000]) + command("machine_unfit", {"machine_id": _extractor, "item_id": _extractor_head, "socket_id": ""}) + 420: + fit(_extractor, _refinery_head) + 450: + command("machine_run", {"machine_id": _extractor, "recipe": "refine.ice"}) + 580: + move([-4000, 0, 3500]) + move([-2000, 0, 3500]) + command("wall_remove", {"wall_id": _wall}) + var plates: Array = production.observation(context).value["components"] + if not plates.is_empty(): _plate = plates.back() + 630: + move([-3500, 0, 3500]) + command("wall_build", {"item_id": _plate, "position": [-3500, 0, 2500], "quarter_turn": 0}) + + +func _verify_actions() -> void: + check(production.capture_snapshot().value["cargo"]["resource-canister-1"]["water"] == 1000000, "replacement refinery processes actual ice into water") + check(production.capture_snapshot().value["walls"].size() == 1, "one recovered plate rebuilds exactly one wall") + check(production.validate_snapshot_record(production.capture_snapshot().value, runner.tick_number).is_success, "recorded finite world still validates") + print("Visible workshop choreography: %d failures" % failures) + + func _update_scene() -> void: var observed: Dictionary = production.observation(context).value habitat.production.observe(observed) @@ -87,62 +174,60 @@ func _make_captions() -> void: var layer := CanvasLayer.new() root.add_child(layer) var header := ColorRect.new() - header.size = Vector2(1280, 82) + header.size = Vector2(1280, 48) header.color = Color(0.035, 0.055, 0.075, 0.9) layer.add_child(header) var footer := ColorRect.new() - footer.position = Vector2(0, 548) - footer.size = Vector2(1280, 172) + footer.position = Vector2(0, 634) + footer.size = Vector2(1280, 86) footer.color = Color(0.035, 0.055, 0.075, 0.91) layer.add_child(footer) var brand := Label.new() - brand.position = Vector2(42, 26) + brand.position = Vector2(24, 10) brand.text = "FRONTIER SYSTEMS" - brand.add_theme_font_size_override("font_size", 26) + brand.add_theme_font_size_override("font_size", 20) layer.add_child(brand) var status := Label.new() - status.position = Vector2(993, 31) + status.position = Vector2(1050, 14) status.text = "SOURCE PROTOTYPE" - status.add_theme_font_size_override("font_size", 17) + status.add_theme_font_size_override("font_size", 12) status.modulate = Color("#a3d6d4") layer.add_child(status) title = Label.new() - title.position = Vector2(42, 572) - title.add_theme_font_size_override("font_size", 38) + title.position = Vector2(24, 642) + title.add_theme_font_size_override("font_size", 25) layer.add_child(title) detail = Label.new() - detail.position = Vector2(44, 630) - detail.add_theme_font_size_override("font_size", 22) + detail.position = Vector2(24, 676) + detail.add_theme_font_size_override("font_size", 17) detail.modulate = Color("#b5c7d2") layer.add_child(detail) var note := Label.new() - note.position = Vector2(44, 683) - note.text = "Actual Godot scene | Automated camera and simulation commands" - note.add_theme_font_size_override("font_size", 14) + note.position = Vector2(845, 703) + note.text = "Godot prototype ยท Automated commands" + note.add_theme_font_size_override("font_size", 12) note.modulate = Color("#80949f") layer.add_child(note) func _shot(frame: int) -> void: var segment: int = frame / 180 - var amount: float = smoothstep(0, 1, float(frame % 180) / 179.0) - var starts: Array[Vector3] = [Vector3(-9.5, 5, 4.3), Vector3(-6.5, 2.8, 1.8), Vector3(-8.6, 2.5, 0.8), Vector3(-2.5, 4, 3.1)] - var ends: Array[Vector3] = [Vector3(-6.8, 4.4, 3.8), Vector3(-4.1, 2.5, 1.8), Vector3(-7.4, 2, 0.2), Vector3(-7.5, 5.2, 3.6)] - var targets: Array[Vector3] = [Vector3(-3.8, 0.65, -1.5), Vector3(-4.3, 0.55, -2), Vector3(-6, 0.65, -2), Vector3(-4.3, 0.6, -1.6)] + var starts: Array[Vector3] = [Vector3(-4.4, 3.2, 1), Vector3(4.2, 3, 3.5), Vector3(-8.4, 2.3, 0.7), Vector3(-5, 4, 0.3)] + var targets: Array[Vector3] = [Vector3(-2, 0.9, 2.5), Vector3(1.4, 0.7, -1), Vector3(-6, 0.8, -2), Vector3(-2.7, 0.9, 2.5)] var display_origin: Vector3 = ProceduralWorldInventoryPreview.DISPLAY_ORIGIN - habitat.camera.position = display_origin + starts[segment].lerp(ends[segment], amount) + habitat.camera.position = display_origin + starts[segment] habitat.camera.look_at(display_origin + targets[segment]) match segment: 0: - title.text = "A first look at the workshop" - detail.text = "Built through the prototype's finite material loop" + title.text = "A physical plate becomes a wall" + detail.text = "Build it, dismantle it, then turn the recovered plate" 1: - title.text = "Power runs through physical cables" - detail.text = "Modular machines, replaceable parts and portable storage" + title.text = "Pack the printer and move it" + detail.text = "The whole fitted machine keeps its components" 2: - title.text = "The extractor fills a real canister" - var ice: int = int(production.capture_snapshot().value["cargo"]["resource-canister-1"]["ice"]) - detail.text = "Ice in storage: %.2f kg" % (float(ice) / 1000000.0) + title.text = "Swap the working head" + var water: int = int(production.capture_snapshot().value["cargo"]["resource-canister-1"]["water"]) + detail.text = "Extractor to refinery. Stored water: %.2f kg" % (float(water) / 1000000.0) 3: - title.text = "Frontier Systems" - detail.text = "An early cooperative engineering prototype" + title.text = "Recover the material and build again" + detail.text = "The same plate, in a different place" From fa7e34f928d48ab488b129e07b20ffed9efd36a0 Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Sun, 11 Oct 2026 12:59:30 -0400 Subject: [PATCH 4/6] Keep printer relocation inside unobstructed camera view --- tools/godot/capture_telegram_teaser.gd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/godot/capture_telegram_teaser.gd b/tools/godot/capture_telegram_teaser.gd index 2f8ae5fc..19753565 100644 --- a/tools/godot/capture_telegram_teaser.gd +++ b/tools/godot/capture_telegram_teaser.gd @@ -212,7 +212,7 @@ func _make_captions() -> void: func _shot(frame: int) -> void: var segment: int = frame / 180 - var starts: Array[Vector3] = [Vector3(-4.4, 3.2, 1), Vector3(4.2, 3, 3.5), Vector3(-8.4, 2.3, 0.7), Vector3(-5, 4, 0.3)] + var starts: Array[Vector3] = [Vector3(-4.4, 3.2, 1), Vector3(3.8, 3.3, 1), Vector3(-8.4, 2.3, 0.7), Vector3(-5, 4, 0.3)] var targets: Array[Vector3] = [Vector3(-2, 0.9, 2.5), Vector3(1.4, 0.7, -1), Vector3(-6, 0.8, -2), Vector3(-2.7, 0.9, 2.5)] var display_origin: Vector3 = ProceduralWorldInventoryPreview.DISPLAY_ORIGIN habitat.camera.position = display_origin + starts[segment] From 539e78da3686e0b77deb7da5eb041863d5ce0a9a Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Thu, 22 Oct 2026 16:13:03 -0400 Subject: [PATCH 5/6] Center resumed teaser shots on workshop actions --- docs/development/prototype-teaser-capture.md | 6 ++++++ tools/godot/capture_telegram_teaser.gd | 15 +++++++++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/docs/development/prototype-teaser-capture.md b/docs/development/prototype-teaser-capture.md index 3961aa23..6e0ec718 100644 --- a/docs/development/prototype-teaser-capture.md +++ b/docs/development/prototype-teaser-capture.md @@ -13,3 +13,9 @@ Run in a clean isolated worktree after import: The wrapper owns one bounded native process, disables external telemetry, isolates the user directory and removes its temporary override. It never opens a socket or touches the regular saved world. Capture is graphical on Windows using GL compatibility. Automated fixture and camera operation are disclosed onscreen; this is not footage of human play, controller/VR acceptance or a published build. Local media can be given to the owner for the explicitly requested personal teaser; no Telegram upload is performed by this route. Review native keyframes before encoding. Use H.264/yuv420p MP4, 1280x720, 30 fps and faststart for mobile sharing. Keep raw provenance and output hashes alongside ignored local media; do not add current internal gallery images to the teaser or upload it to other services. + +## Resumed camera pass, 2026-10-22 + +The capture branch now includes the current integration branch's improved assets, machinery activity, walker poses and contextual UI. Four deliberate cuts separate construction, printer relocation, head replacement and material recovery. Each shot holds its subject while a small eased dolly adds depth; the last shot reframes toward the rebuilt wall before its placement command. Captions remain at the edges and the normal HUD stays hidden. This is an automated workshop demonstration, not first-person player footage. + +Acceptance requires a new native render showing each commanded object clearly before and after the change, no subject hidden by captions or geometry, smooth within-shot motion, and the existing finite-material assertions passing. Native verification and rendering are deferred while the independently owned Gaia restore holds runtime capacity. The source change alone is not visual acceptance or a finished video. diff --git a/tools/godot/capture_telegram_teaser.gd b/tools/godot/capture_telegram_teaser.gd index 19753565..6326691b 100644 --- a/tools/godot/capture_telegram_teaser.gd +++ b/tools/godot/capture_telegram_teaser.gd @@ -212,11 +212,22 @@ func _make_captions() -> void: func _shot(frame: int) -> void: var segment: int = frame / 180 + var local_frame: int = frame % 180 var starts: Array[Vector3] = [Vector3(-4.4, 3.2, 1), Vector3(3.8, 3.3, 1), Vector3(-8.4, 2.3, 0.7), Vector3(-5, 4, 0.3)] var targets: Array[Vector3] = [Vector3(-2, 0.9, 2.5), Vector3(1.4, 0.7, -1), Vector3(-6, 0.8, -2), Vector3(-2.7, 0.9, 2.5)] + # Deliberate cuts separate activities; each shot settles on its subject before + # the command fires. Ease a small dolly rather than orbiting away from it. + var travel: float = smoothstep(0.0, 179.0, float(local_frame)) + var target: Vector3 = targets[segment] + if segment == 3: + # The plate is removed at 580 and rebuilt at 630. Follow its destination + # during that empty interval, then hold on the rebuilt wall. + var relocation: float = smoothstep(580.0, 620.0, float(frame)) + target = Vector3(-2, 0.9, 2.5).lerp(Vector3(-3.5, 0.9, 2.5), relocation) var display_origin: Vector3 = ProceduralWorldInventoryPreview.DISPLAY_ORIGIN - habitat.camera.position = display_origin + starts[segment] - habitat.camera.look_at(display_origin + targets[segment]) + var camera_start: Vector3 = starts[segment] + habitat.camera.position = display_origin + camera_start.lerp(target + (camera_start - target) * 0.94, travel) + habitat.camera.look_at(display_origin + target) match segment: 0: title.text = "A physical plate becomes a wall" From 2f38cd797bfc47cd3f91b476bc101ad3a3fcd5b2 Mon Sep 17 00:00:00 2001 From: Valithor Obsidion Date: Thu, 29 Oct 2026 04:45:44 -0400 Subject: [PATCH 6/6] Verify teaser choreography against integrated dependencies --- docs/development/prototype-teaser-capture.md | 6 ++++++ tests/godot/teaser_choreography_test.gd | 4 ++++ 2 files changed, 10 insertions(+) create mode 100644 tests/godot/teaser_choreography_test.gd diff --git a/docs/development/prototype-teaser-capture.md b/docs/development/prototype-teaser-capture.md index 6e0ec718..6585f678 100644 --- a/docs/development/prototype-teaser-capture.md +++ b/docs/development/prototype-teaser-capture.md @@ -19,3 +19,9 @@ Review native keyframes before encoding. Use H.264/yuv420p MP4, 1280x720, 30 fps The capture branch now includes the current integration branch's improved assets, machinery activity, walker poses and contextual UI. Four deliberate cuts separate construction, printer relocation, head replacement and material recovery. Each shot holds its subject while a small eased dolly adds depth; the last shot reframes toward the rebuilt wall before its placement command. Captions remain at the edges and the normal HUD stays hidden. This is an automated workshop demonstration, not first-person player footage. Acceptance requires a new native render showing each commanded object clearly before and after the change, no subject hidden by captions or geometry, smooth within-shot motion, and the existing finite-material assertions passing. Native verification and rendering are deferred while the independently owned Gaia restore holds runtime capacity. The source change alone is not visual acceptance or a finished video. + +## Dependency integration, 2026-10-29 + +Integrated `dev-master` at `5b75fa462f0bab133a3f820c8d8234fdfe4d2f0d` without conflicts. The four shots and genuine command sequence are unchanged. The updated habitat preview hides its obsolete worksite wall through the existing `focus_production()` call. `teaser_choreography_test.gd` exposes the capture script's existing headless path to the standard Windows/Linux CI runner, including inherited machinery assertions and final finite-world checks. It does not render or establish camera quality. + +The retained October 22 capture belongs to `539e78da3686e0b77deb7da5eb041863d5ce0a9a`; it is not visual evidence for this integrated revision. No new local native execution or capture is authorized. Source readiness and CI review are separate from owner motion acceptance and a finished trailer. diff --git a/tests/godot/teaser_choreography_test.gd b/tests/godot/teaser_choreography_test.gd new file mode 100644 index 00000000..ff364c27 --- /dev/null +++ b/tests/godot/teaser_choreography_test.gd @@ -0,0 +1,4 @@ +extends "res://tools/godot/capture_telegram_teaser.gd" + +## Runs the genuine finite workshop choreography through the standard headless +## test runner. This verifies commands and final state, not rendered motion.