P23-03: 生活の観察
This commit is contained in:
+88
-2
@@ -3,6 +3,40 @@ extends RefCounted
|
||||
const Codec := preload("res://tests/fixtures/p08-02/mixed_world.gd")
|
||||
var directory: String
|
||||
var manifest: FileAccess
|
||||
var observer: MotionObserver
|
||||
|
||||
class MotionObserver extends Node:
|
||||
var view: WorldPreview
|
||||
var frames: Array = []
|
||||
var elapsed := 0.0
|
||||
var scaled_elapsed := 0.0
|
||||
func _ready() -> void:
|
||||
RenderingServer.frame_post_draw.connect(after_draw)
|
||||
func _process(delta: float) -> void:
|
||||
elapsed += delta
|
||||
var clock := get_node_or_null("/root/GameClock")
|
||||
if clock == null or not bool(clock.get("paused")):
|
||||
scaled_elapsed += delta * maxf(float(clock.get("speed")) if clock != null else 1.0, 0.0)
|
||||
func after_draw() -> void:
|
||||
if not is_instance_valid(view): return
|
||||
var report := view.get_life_observation_report()
|
||||
if report.is_empty(): return
|
||||
var sample := {}
|
||||
for key: String in ["animation_time", "paused", "layer", "zoom", "pan", "viewport", "actor_tracks", "elevator_tracks", "actor_drawings", "cabins"]:
|
||||
sample[key] = report[key]
|
||||
if not frames.is_empty() and sample == frames[-1]["display"] and not sample["paused"]: return
|
||||
# Keep only two draw frames, never whole Runtime copies per frame.
|
||||
frames.append({"frame": Engine.get_process_frames(), "delta": elapsed, "scaled_delta": scaled_elapsed, "display": sample})
|
||||
if frames.size() > 2: frames.pop_front()
|
||||
elapsed = 0.0
|
||||
scaled_elapsed = 0.0
|
||||
func _exit_tree() -> void:
|
||||
if RenderingServer.frame_post_draw.is_connected(after_draw): RenderingServer.frame_post_draw.disconnect(after_draw)
|
||||
|
||||
func watch(view: WorldPreview) -> void:
|
||||
observer = MotionObserver.new()
|
||||
observer.view = view
|
||||
view.add_child(observer)
|
||||
|
||||
func open(path: String) -> bool:
|
||||
directory = path
|
||||
@@ -33,19 +67,71 @@ func capture(label: String, runtime: Variant, view: WorldPreview) -> bool:
|
||||
var report := view.get_life_observation_report()
|
||||
var state_hash := object(state)
|
||||
var report_hash := object(report)
|
||||
if state_hash.is_empty() or report_hash.is_empty():
|
||||
var audit_hash := object(rest_audit(runtime))
|
||||
var motion_hash := object({"frames": observer.frames.duplicate(true) if is_instance_valid(observer) else []})
|
||||
var after: Dictionary = runtime.create_snapshot()
|
||||
var after_hash := object(after)
|
||||
if state_hash.is_empty() or report_hash.is_empty() or audit_hash.is_empty():
|
||||
return false
|
||||
manifest.store_line(JSON.stringify({"label": label, "pid": OS.get_process_id(),
|
||||
"ticks_usec": Time.get_ticks_usec(), "state": state_hash, "report": report_hash,
|
||||
"rest_audit": audit_hash, "motion": motion_hash, "state_after": after_hash,
|
||||
"input_method": "diagnostic", "acceptance": "pending"}))
|
||||
manifest.flush()
|
||||
return var_to_bytes(state) == var_to_bytes(runtime.create_snapshot())
|
||||
return var_to_bytes(state) == var_to_bytes(after)
|
||||
|
||||
func close() -> void:
|
||||
if manifest != null:
|
||||
manifest.close()
|
||||
manifest = null
|
||||
|
||||
# Public selection inputs; Runtime output is only a comparison operand.
|
||||
func rest_audit(runtime: Variant) -> Dictionary:
|
||||
var rows: Array = []
|
||||
for member: StaffRuntime in runtime.staff_service.get_staff_members():
|
||||
var memberships: Array = []
|
||||
for raid_id: StringName in runtime.defense_service.get_raid_ids():
|
||||
var raid: Dictionary = runtime.defense_service.get_raid(raid_id)
|
||||
if raid.get(&"combatants", {}).has(member.staff_id):
|
||||
memberships.append({"raid_id": raid_id, "state": DefenseService.RaidState.keys()[raid[&"state"]],
|
||||
"side": raid[&"combatants"][member.staff_id].get(&"side", &"")})
|
||||
var facilities: Array = []
|
||||
for room: FacilityInstance in runtime.facility_service.get_instances():
|
||||
var probes: Array = []
|
||||
var candidates: Dictionary = {}
|
||||
var occupied := room.occupied_cells.duplicate()
|
||||
if occupied.is_empty(): occupied.append(room.origin)
|
||||
for cell: Vector3i in occupied:
|
||||
for delta: Vector3i in [Vector3i.ZERO, Vector3i.LEFT, Vector3i.RIGHT]:
|
||||
var present: bool = runtime.path_service.has_cell(cell + delta)
|
||||
probes.append({"cell": cell + delta, "has_cell": present})
|
||||
if present: candidates[cell + delta] = true
|
||||
var ordered: Array = candidates.keys()
|
||||
ordered.sort_custom(func(a: Vector3i, b: Vector3i) -> bool:
|
||||
if a.z != b.z: return a.z < b.z
|
||||
if a.y != b.y: return a.y < b.y
|
||||
return a.x < b.x)
|
||||
var routes: Array = []
|
||||
for cell: Vector3i in ordered:
|
||||
var path: Array = runtime.path_service.find_path(member.current_cell, cell,
|
||||
[WorldService.LAYER_FRONT, WorldService.LAYER_BACK], [&"staff"])
|
||||
var edges: Array = []
|
||||
for index: int in range(1, path.size()):
|
||||
# Empty reports remain explicit mismatch evidence.
|
||||
edges.append(runtime.path_service.get_edge_report(path[index - 1], path[index], [&"staff"]))
|
||||
routes.append({"cell": cell, "path": path, "edges": edges,
|
||||
"calculate_path_cost": runtime.path_service.calculate_path_cost(path, [&"staff"]) if not path.is_empty() else -1})
|
||||
facilities.append({"instance_id": room.instance_id, "definition_id": room.definition_id,
|
||||
"operation_state": FacilityInstance.OperationState.keys()[room.operation_state],
|
||||
"origin": room.origin, "occupied_cells": room.occupied_cells.duplicate(),
|
||||
"probes": probes, "ordered": ordered, "routes": routes})
|
||||
rows.append({"staff_id": member.staff_id, "state": StaffRuntime.State.keys()[member.state],
|
||||
"health": member.health, "current_task_id": member.current_task_id, "current_cell": member.current_cell,
|
||||
"defense_memberships": memberships, "actor_lock": runtime.event_service.get_actor_locks().has(member.staff_id),
|
||||
"in_transit": runtime.is_staff_in_transit(member.staff_id), "facilities": facilities,
|
||||
"comparison_only": runtime.get_staff_rest_report(member.staff_id)})
|
||||
return {"staff": rows, "path_service_sha256": FileAccess.get_sha256("res://scripts/world/path_service.gd")}
|
||||
|
||||
func record(label: String, value: Dictionary) -> bool:
|
||||
if manifest == null: return false
|
||||
var digest := object(value)
|
||||
|
||||
Reference in New Issue
Block a user