P23-03: 生活の観察

This commit is contained in:
Agent Control Runner
2026-10-16 17:15:06 +09:00
parent 93248f4d79
commit 26f60095bf
7 changed files with 422 additions and 53 deletions
+75 -2
View File
@@ -1561,6 +1561,50 @@ func _life_track_region(key: String, cell: Vector3i, model: Dictionary) -> Dicti
return {&"bounds": rect, &"transit": transit}
## Keep non-passengers on a real corridor landing when a cabin crosses their pose.
func _life_landing_region(region: Dictionary, cell: Vector3i, model: Dictionary) -> Dictionary:
var blocked := false
for cabin: Dictionary in model[&"cabins"]:
if (region[&"bounds"] as Rect2).intersects(cabin[&"rect"]):
blocked = true
break
if not blocked:
return region
var world: Variant = _service(&"world_service")
var facilities: Variant = _service(&"facility_service")
var footprint: Array = model[&"shaft_footprint"]
var origins: Array = footprint.filter(func(c: Vector3i) -> bool: return c.y == cell.y and c.z == cell.z)
origins.sort_custom(func(a: Vector3i, b: Vector3i) -> bool: return absi(a.x-cell.x) < absi(b.x-cell.x) or (absi(a.x-cell.x) == absi(b.x-cell.x) and a.x < b.x))
var lane: Array[Vector3i] = []
for origin: Vector3i in origins:
for direction: int in [-1, 1]:
for distance: int in range(1, 9):
var target := origin + Vector3i(direction*distance, 0, 0)
if world == null or footprint.has(target) or not world.is_walkable(target):
break
var facility: Variant = facilities.get_instance(world.get_occupant(target)) if facilities != null else null
if facility != null and facility.definition_id not in [&"guest_corridor", &"service_corridor"]:
break
if not lane.has(target):
lane.append(target)
lane.sort_custom(func(a: Vector3i, b: Vector3i) -> bool: return absi(a.x-cell.x) < absi(b.x-cell.x) or (absi(a.x-cell.x) == absi(b.x-cell.x) and a.x < b.x))
if lane.is_empty():
var anchor := get_cell_rect(cell).position+Vector2(-80, -16)
var nearest := INF
for badge: Dictionary in model[&"landing_badges"]:
var landing: Vector3i = badge[&"cell"]
if landing.y == cell.y and landing.z == cell.z and absf(landing.x-cell.x) < nearest:
nearest = absf(landing.x-cell.x)
anchor = badge[&"at"]
return {&"bounds": region[&"bounds"], &"transit": false, &"no_lane": true, &"badge_at": anchor}
var selected: Vector3i = lane[0]
for target: Vector3i in lane:
if not model[&"queue_positions"].values().has(cell_to_canvas(target)):
selected = target
break
return {&"bounds": get_cell_rect(selected), &"transit": false, &"center": cell_to_canvas(selected)}
func _life_actor_region(row: Dictionary, person: Dictionary, model: Dictionary) -> Dictionary:
var key: String = row[&"key"]
var cell: Vector3i = person[&"cell"]
@@ -1580,10 +1624,13 @@ func _life_actor_region(row: Dictionary, person: Dictionary, model: Dictionary)
if model[&"carried_patients"].has(id):
for carrier: Dictionary in model[&"staff"]:
if carrier[&"id"] == model[&"carried_patients"][id]:
result = _life_track_region("staff/"+String(carrier[&"id"]), carrier[&"cell"], model)
result = _life_actor_region({&"key": "staff/"+String(carrier[&"id"]), &"staff": true}, carrier, model)
var rect: Rect2 = result[&"bounds"]
rect = rect.grow(p)
rect.position.y -= 12*p
result[&"bounds"] = rect
if result.has(&"center"):
result[&"center"] -= Vector2(0, 12*p)
elif model[&"resting_staff"].has(id):
facility_id = model[&"resting_staff"][id]
elif model[&"treated_patients"].has(id):
@@ -1600,6 +1647,8 @@ func _life_actor_region(row: Dictionary, person: Dictionary, model: Dictionary)
var cells: Array[Vector3i] = world.get_entity_cells(facility_id)
if not cells.is_empty():
result = {&"bounds": _cell_bounds_rect(cells), &"transit": false}
if not model[&"queue_positions"].has(key) and not result.has(&"center") and not result.get(&"no_lane", false):
result = _life_landing_region(result, cell, model)
result[&"bounds"] = (result[&"bounds"] as Rect2).grow(-p)
return result
@@ -1680,6 +1729,7 @@ func _fit_life_actors(model: Dictionary) -> void:
for person: Dictionary in model[&"staff"]:
people["staff/"+String(person[&"id"])] = person
var members: Array = []
var stranded: Array = []
var all_rows: Array = model[&"actor_drawings"].duplicate()
var passenger_bounds: Dictionary = {}
for cabin: Dictionary in model[&"cabins"]:
@@ -1690,12 +1740,35 @@ func _fit_life_actors(model: Dictionary) -> void:
var person: Dictionary = people[row[&"key"]]
row[&"cell"] = person[&"cell"]
var region := _life_actor_region(row, person, model) if not row[&"riding"] else {&"bounds": passenger_bounds[row[&"key"]], &"transit": false}
if region.has(&"center"):
var jitter := Vector2.ZERO
if not model[&"carried_patients"].has(person[&"id"]):
var track: Dictionary = model[&"actor_tracks"][row[&"key"]]
var t := 1.0 if model[&"paused"] else clampf((_animation_time-float(track[&"at"]))/0.42, 0, 1)
jitter = row[&"center"]-cell_to_canvas(track[&"from"]).lerp(cell_to_canvas(person[&"cell"]), t)
row[&"center"] = region[&"center"]+jitter
row.merge(_life_scaled(row, row[&"scale"]), true)
region[&"row"] = row
if not region[&"transit"]:
if region.get(&"no_lane", false):
stranded.append(region)
elif not region[&"transit"]:
members.append(region)
var groups := _life_fitted_groups(members)
model[&"actor_aggregations"] = []
var hidden: Dictionary = {}
for member: Dictionary in stranded:
var row: Dictionary = member[&"row"]
hidden[row[&"key"]] = true
var aggregate: Dictionary = {}
for existing: Dictionary in model[&"actor_aggregations"]:
if existing[&"at"] == member[&"badge_at"]:
aggregate = existing
break
if aggregate.is_empty():
aggregate = {&"members": [], &"drawn_keys": [], &"at": member[&"badge_at"], &"text": ""}
model[&"actor_aggregations"].append(aggregate)
aggregate[&"members"].append({&"key": row[&"key"], &"id": people[row[&"key"]][&"id"], &"state": row[&"state"], &"cell": row[&"cell"], &"mode": row[&"mode"]})
aggregate[&"text"] = "+%d" % aggregate[&"members"].size()
for group: Array in groups:
group.sort_custom(func(a: Dictionary, b: Dictionary) -> bool: return a[&"row"][&"center"].x < b[&"row"][&"center"].x or (a[&"row"][&"center"].x == b[&"row"][&"center"].x and a[&"row"][&"key"] < b[&"row"][&"key"]))
var base := 1.0