74 lines
2.4 KiB
GDScript
74 lines
2.4 KiB
GDScript
# Lets the game-making agent peek at the running game without disturbing it.
|
|
#
|
|
# `tools/gd peek` drops `.agent/peek_request` into the project; this autoload
|
|
# notices within a quarter second, saves a short burst of screenshots plus the
|
|
# player's state to `.agent/live/`, and finishes with a `done` marker. The kid
|
|
# keeps playing the whole time — nothing is paused or restarted.
|
|
extends Node
|
|
|
|
const SNAPS := 4
|
|
const SNAP_GAP := 0.4
|
|
|
|
var _poll := 0.0
|
|
var _busy := false
|
|
|
|
func _ready() -> void:
|
|
# Godot's default ui_accept knows Enter and Space but no gamepad button, so
|
|
# a kid on a controller could walk (ui_left/right carry D-pad and stick
|
|
# bindings) but never jump. Bind A here, where every game gets it for free.
|
|
var jump := InputEventJoypadButton.new()
|
|
jump.button_index = JOY_BUTTON_A
|
|
InputMap.action_add_event("ui_accept", jump)
|
|
|
|
func _agent_dir() -> String:
|
|
return ProjectSettings.globalize_path("res://") + ".agent"
|
|
|
|
func _process(delta: float) -> void:
|
|
_poll += delta
|
|
if _poll < 0.25 or _busy:
|
|
return
|
|
_poll = 0.0
|
|
var request := _agent_dir() + "/peek_request"
|
|
if FileAccess.file_exists(request):
|
|
DirAccess.remove_absolute(request)
|
|
_snap_burst()
|
|
|
|
func _snap_burst() -> void:
|
|
_busy = true
|
|
var live := _agent_dir() + "/live"
|
|
# Start fresh so a sheet never mixes two peeks.
|
|
if DirAccess.dir_exists_absolute(live):
|
|
for file in DirAccess.get_files_at(live):
|
|
DirAccess.remove_absolute(live + "/" + file)
|
|
else:
|
|
DirAccess.make_dir_recursive_absolute(live)
|
|
|
|
var report := ""
|
|
for index in range(SNAPS):
|
|
var image := get_viewport().get_texture().get_image()
|
|
image.save_png("%s/f%04d.png" % [live, index])
|
|
report += _state_line(index)
|
|
if index < SNAPS - 1:
|
|
await get_tree().create_timer(SNAP_GAP).timeout
|
|
|
|
var state := FileAccess.open(live + "/state.txt", FileAccess.WRITE)
|
|
if state:
|
|
state.store_string(report)
|
|
state.close()
|
|
var done := FileAccess.open(live + "/done", FileAccess.WRITE)
|
|
if done:
|
|
done.store_string("ok")
|
|
done.close()
|
|
_busy = false
|
|
|
|
func _state_line(index: int) -> String:
|
|
var scene := get_tree().current_scene
|
|
if scene == null:
|
|
return "snap %d: no scene\n" % index
|
|
var player := scene.find_child("Player", true, false)
|
|
if player == null:
|
|
return "snap %d: no node named Player\n" % index
|
|
var line := "snap %d: %s pos=%s" % [index, player.name, player.position]
|
|
if player is CharacterBody2D:
|
|
line += " vel=%s floor=%s" % [player.velocity, player.is_on_floor()]
|
|
return line + "\n"
|