extends SceneTree const SANDBOX_STAGE: PackedScene = preload("res://scenes/stage/sandbox_stage.tscn") const STAGE_SELECTION := preload("res://scripts/stage/stage_selection.gd") const STAGE_DIALOG_STYLER := preload("res://scripts/shared/stage_ui/stage_dialog_styler.gd") const CAPTURE_DIR := "res://tests/phase20b_captures/pickup_action_marker" var _stage: SandboxStage var _failures: int = 0 func _initialize() -> void: call_deferred("_run") func _run() -> void: root.size = Vector2i(1280, 720) root.gui_embed_subwindows = true DirAccess.make_dir_recursive_absolute(ProjectSettings.globalize_path(CAPTURE_DIR)) _stage = SANDBOX_STAGE.instantiate() as SandboxStage root.add_child(_stage) await _wait_frames(10) var recovery: ConfirmationDialog = _stage.find_child("AutosaveRecoveryDialog", true, false) as ConfirmationDialog if recovery != null: STAGE_DIALOG_STYLER.hide(recovery) _stage.call("_on_grid_toggled", false) var spawner: StageSpawner = _stage.get("_spawner") as StageSpawner var world: Node2D = _stage.get_node("World") as Node2D var actor: StickmanRig = spawner.spawn_stickman_asset("res://content/stickmen/break.stk", Vector2(-160.0, -190.0)) var prop: PropBlock = spawner.spawn_prop_asset("crate", Vector2(150.0, -10.0)) _check(actor != null and prop != null, "production Stage fixture creates a character and pickup target") if actor == null or prop == null: await _finish() return _stage.call("_finalize_placed_node", actor) _stage.call("_finalize_placed_node", prop) var identity: StageObjectIdentity = _stage.get("_object_identity") as StageObjectIdentity var rename_result: Dictionary = identity.rename(prop, "Brown Suitcase", world) _check(bool(rename_result.get("ok", false)), "pickup target receives its friendly stage name") var pickup_action: Dictionary = { "action_id": "pickup-marker-step-one", "type": "pick_up_prop", "target_prop_id": prop.stage_object_id, "hand": "auto", } _check(actor.set_action_plan([{"actions": [pickup_action]}]), "Step 1 receives a real Pick Up Prop action") _stage.call("set_mode", SandboxStage.StageMode.DIRECT) var selection: StageSelection = _stage.get("_selection") as StageSelection selection.select_only(actor) _stage.call("_refresh_character_settings_card") await _wait_frames(8) var visuals: StageDirectorVisuals = _stage.get("_director_visuals") as StageDirectorVisuals var projection: Dictionary = {} for entry: Dictionary in visuals._plan_projection(actor): if String(entry.get("action_id", "")) == String(pickup_action["action_id"]): projection = entry break _check(String(projection.get("kind", "")) == "other" and String((projection.get("action", {}) as Dictionary).get("type", "")) == "pick_up_prop", "pickup action reaches the production Stage overlay projection") var canvas_transform: Transform2D = _stage.get_viewport().get_canvas_transform() var prop_world_bounds: Rect2 = STAGE_SELECTION.get_world_aabb(prop) var prop_screen_bounds: Rect2 = visuals._world_rect_to_screen_rect(prop_world_bounds, canvas_transform) var badge_size: Vector2 = visuals._text_action_badge_screen_size("Pick up", visuals._zoom()) var viewport_size: Vector2 = root.size var safe_rect: Rect2 = Rect2(Vector2(8.0, 72.0), Vector2(viewport_size.x - 16.0, viewport_size.y - 112.0)) var actor_name_rect: Rect2 = visuals._actor_name_badge_screen_rect(actor, visuals._zoom()) var badge_rect: Rect2 = visuals._pickup_badge_screen_rect(prop_screen_bounds, badge_size, safe_rect, actor_name_rect) _check(safe_rect.encloses(badge_rect), "Pick up pill stays inside the visible Stage safe area") _check(not badge_rect.intersects(prop_screen_bounds), "Pick up pill sits outside the target prop bounds") if _can_capture_image(): await RenderingServer.frame_post_draw var image: Image = root.get_texture().get_image() var path: String = CAPTURE_DIR.path_join("pickup_step_one_pill_over_prop.png") var save_error: Error = image.save_png(ProjectSettings.globalize_path(path)) if image != null and not image.is_empty() else ERR_CANT_CREATE _check(save_error == OK, "focused Godot run saves the rendered pickup action marker PNG") print("PICKUP_ACTION_MARKER_CAPTURE path=%s size=%s" % [path, str(image.get_size()) if image != null else "missing"]) print("PHASE20B_PICKUP_ACTION_MARKER_RESULT failures=%d" % _failures) await _finish() quit(1 if _failures > 0 else 0) func _wait_frames(count: int) -> void: for _index: int in count: await process_frame func _can_capture_image() -> bool: var display_name: String = DisplayServer.get_name().to_lower() return not OS.has_feature("headless") and display_name != "headless" and display_name != "dummy" func _check(condition: bool, message: String) -> void: if condition: print("PASS: %s" % message) else: _failures += 1 push_error("FAIL: " + message) func _finish() -> void: if _stage != null and is_instance_valid(_stage): _stage.queue_free() await process_frame