P23-03: 生活の観察

This commit is contained in:
Agent Control Runner
2026-10-15 07:59:43 +09:00
parent 860136f709
commit 03100beabe
13 changed files with 1386 additions and 101 deletions
+247 -100
View File
@@ -78,6 +78,7 @@ var _queue_positions: Dictionary = {}
var _queue_hidden: Dictionary = {}
var _landing_badges: Array[Dictionary] = []
var _transport_report: Dictionary = {}
var _life_observation: Dictionary = {}
var _dragging := false
var _drag_button := MOUSE_BUTTON_NONE
@@ -511,6 +512,7 @@ func _draw() -> void:
func _draw_contents() -> void:
_life_observation = {}
if _inactive_layer_art != null:
_inactive_layer_art.visible = false
_draw_background()
@@ -521,15 +523,25 @@ func _draw_contents() -> void:
var map_rect := get_map_rect()
if map_rect.size == Vector2.ZERO:
return
_life_observation = _build_life_observation()
_room_presentations = _life_observation[&"rooms"]
_carried_patients = _life_observation[&"carried_patients"]
_treated_patients = _life_observation[&"treated_patients"]
_resting_staff = _life_observation[&"resting_staff"]
_queue_positions = _life_observation[&"queue_positions"]
_queue_hidden = _life_observation[&"queue_hidden"]
_landing_badges.assign(_life_observation[&"landing_badges"])
_transport_report = _life_observation[&"elevators"]
_boarded_actor_ids = _life_observation[&"boarded"]
_actor_tracks = _life_observation[&"actor_tracks"]
_elevator_tracks = _life_observation[&"elevator_tracks"]
_draw_world_cells(bounds, map_rect)
if show_grid:
_draw_congestion_overlay(bounds)
_prepare_room_presentations()
_prepare_queues()
_draw_world_entity_icons()
_draw_legacy_stairs()
_draw_elevators()
_draw_other_layer()
_draw_congestion_overlay(bounds)
_draw_life_badges()
_draw_construction_labels()
_draw_facility_labels()
_draw_actors()
@@ -540,6 +552,102 @@ func _draw_contents() -> void:
_draw_map_caption(map_rect)
## The report describes the last submitted draw, not an independently sampled world.
## Callers cannot mutate either the frame or its interpolation tracks.
func get_life_observation_report() -> Dictionary:
return _life_observation.duplicate(true)
## Pure with respect to Runtime and this Control: helpers mutate only fresh local
## dictionaries (including copies of visual tracks). No whole Runtime snapshot
## is copied per frame; whole typed snapshots belong to sparse diagnostics.
func _build_life_observation() -> Dictionary:
var model := _observe_rooms()
model.merge(_observe_queues())
model[&"layer"] = layer_mode
model[&"zoom"] = zoom_level
model[&"pan"] = pan_offset
model[&"viewport"] = size
model[&"animation_time"] = _animation_time
model[&"actor_tracks"] = _actor_tracks.duplicate(true)
model[&"elevator_tracks"] = _elevator_tracks.duplicate(true)
model[&"boarded"] = {}
var clock := get_node_or_null("/root/GameClock")
model[&"paused"] = clock != null and bool(clock.get("paused"))
model[&"cabins"] = _observe_elevators(model)
model[&"actor_drawings"] = _observe_actors(model)
var guests: Array[Dictionary] = []
var staff: Array[Dictionary] = []
var tasks: Array[Dictionary] = []
var guest_service: Variant = _service(&"guest_service")
if guest_service != null and guest_service.has_method(&"get_guests"):
for guest: GuestRuntime in guest_service.get_guests():
guests.append({&"id": guest.guest_id, &"state": GuestRuntime.State.keys()[guest.state],
&"cell": guest.current_cell, &"target": guest.target_facility_id,
&"service": guest.current_service_id, &"queue": guest.queue_request_id})
var staff_service: Variant = _service(&"staff_service")
if staff_service != null:
for member: StaffRuntime in staff_service.get_staff_members():
staff.append({&"id": member.staff_id, &"state": StaffRuntime.State.keys()[member.state],
&"cell": member.current_cell, &"task": member.current_task_id,
&"facility": member.assigned_facility_id, &"role": member.role_id,
&"carried_inventory_id": member.carried_inventory_id})
for task: StaffTask in staff_service.get_tasks():
tasks.append({&"id": task.task_id, &"state": StaffTask.State.keys()[task.state],
&"type": task.task_type, &"staff": task.assigned_staff_id,
&"target": task.target_entity_id, &"rescue_facility": task.rescue_facility_id})
model[&"guests"] = guests
model[&"staff"] = staff
model[&"tasks"] = tasks
var bridge: Variant = _service(&"supply_logistics_bridge")
var goods: Dictionary = bridge.get_report() if bridge != null else {&"requests": [], &"deliveries": []}
model[&"goods"] = goods
var badges: Array[Dictionary] = []
var facilities: Variant = _service(&"facility_service")
var world: Variant = _service(&"world_service")
if facilities != null and world != null:
for instance: FacilityInstance in facilities.get_instances():
if instance.origin.z != layer_mode:
continue
var cells: Array[Vector3i] = world.get_entity_cells(instance.instance_id)
if cells.is_empty():
continue
var rect := _cell_bounds_rect(cells)
var data: Dictionary = model[&"rooms"][instance.instance_id]
data[&"rect"] = rect
data[&"definition_id"] = instance.definition_id
var count := RoomPresentation.slots(rect, _cell_dimensions().x / 16.0, instance.definition_id).size()
badges.append({&"facility": instance.instance_id, &"kind": &"business",
&"at": rect.position + Vector2(2, 2),
&"text": RoomPresentation.business_label(data) + " " + RoomPresentation.label(data, count)})
var requests: Array[Dictionary] = []
for request: Dictionary in goods.get(&"requests", []):
if request[&"source_facility_id"] == instance.instance_id or request[&"destination_facility_id"] == instance.instance_id:
requests.append(request)
if not requests.is_empty():
badges.append({&"facility": instance.instance_id, &"kind": &"supply",
&"at": rect.position + Vector2(2, 16), &"requests": requests,
&"text": RoomPresentation.supply_label(requests)})
model[&"badges"] = badges
var traffic: Array[Dictionary] = []
var path: Variant = _service(&"path_service")
var bounds := _world_bounds()
if path != null and not bounds.is_empty():
for y in range(int(bounds[&"min_y"]), int(bounds[&"max_y"]) + 1):
for x in range(int(bounds[&"min_x"]), int(bounds[&"max_x"]) + 1):
var cell := Vector3i(x, y, layer_mode)
var row: Dictionary = path.get_cell_traffic(cell)
if not row.is_empty():
traffic.append({&"cell": cell, &"traffic": row, &"rect": get_cell_rect(cell).grow(-2.0)})
model[&"traffic"] = traffic
return model
func _draw_life_badges() -> void:
for badge: Dictionary in _life_observation.get(&"badges", []):
_draw_readable_badge(badge[&"at"], badge[&"text"])
func _draw_background() -> void:
var night := false
var clock := get_node_or_null("/root/GameClock")
@@ -650,40 +758,23 @@ func _draw_leaf_cluster(center: Vector2, p: float, variation: int) -> void:
draw_rect(Rect2(c+offset,Vector2(3+i%2,2)*p),Color("95ad69") if i%3==0 else Color("648957"))
func _draw_congestion_overlay(bounds: Dictionary) -> void:
var path_service: Variant = _service(&"path_service")
if path_service == null or not path_service.has_method(&"get_cell_traffic"):
return
for y in range(int(bounds[&"min_y"]), int(bounds[&"max_y"]) + 1):
for x in range(int(bounds[&"min_x"]), int(bounds[&"max_x"]) + 1):
var cell := Vector3i(x, y, layer_mode)
var traffic_value: Variant = path_service.call(&"get_cell_traffic", cell)
if not traffic_value is Dictionary or traffic_value.is_empty():
continue
var traffic: Dictionary = traffic_value
var density := int(traffic.get(&"current_density", 0))
var capacity := maxi(int(traffic.get(&"base_capacity", 1)), 1)
var blocked := bool(traffic.get(&"temporary_block", false))
if density <= 0 and not blocked:
continue
var ratio := float(density) / float(capacity)
var color := Color("55c96b")
if blocked or ratio > 1.0:
color = Color("e34332")
elif ratio >= 0.65:
color = Color("e3a62f")
var rect := get_cell_rect(cell).grow(-2.0)
var center := rect.get_center()
var route_width := clampf(rect.size.y * 0.16, 2.0, 5.0)
draw_line(
Vector2(rect.position.x + rect.size.x * 0.12, center.y),
Vector2(rect.end.x - rect.size.x * 0.12, center.y),
Color(color, 0.90),
route_width
)
if blocked:
draw_line(rect.position, rect.end, color, 2.0)
draw_line(Vector2(rect.end.x, rect.position.y), Vector2(rect.position.x, rect.end.y), color, 2.0)
func _draw_congestion_overlay(_bounds: Dictionary) -> void:
for row: Dictionary in _life_observation.get(&"traffic", []):
var rect: Rect2 = row[&"rect"]
var traffic: Dictionary = row[&"traffic"]
var density := int(traffic[&"current_density"])
var blocked := bool(traffic[&"temporary_block"])
if density <= 0 and not blocked:
continue
var ratio := float(density) / maxi(int(traffic[&"base_capacity"]), 1)
var color := Color("e34332") if blocked or ratio > 1.0 else Color("e3a62f") if ratio >= 0.65 else Color("55c96b")
var center := rect.get_center()
draw_line(Vector2(rect.position.x + rect.size.x * 0.12, center.y),
Vector2(rect.end.x - rect.size.x * 0.12, center.y),
Color(color, 0.90 if show_grid else 0.28), clampf(rect.size.y * 0.16, 2.0, 5.0))
if blocked:
draw_line(rect.position, rect.end, Color(color, 0.90 if show_grid else 0.28), 2.0)
draw_line(Vector2(rect.end.x, rect.position.y), Vector2(rect.position.x, rect.end.y), Color(color, 0.90 if show_grid else 0.28), 2.0)
## Visual-only level comes from the existing live FacilityInstance, not the
@@ -794,10 +885,6 @@ func _draw_world_entity_icons() -> void:
draw_rect(Rect2(rect.end.x-4*p,rect.position.y+2*p,2*p,2*p),_facility_color(instance_id))
if instance_id==selected_entity_id:
draw_rect(rect.grow(-p),Color("f5d582"),false,2*p)
var data: Dictionary = _room_presentations.get(instance_id, {})
if not data.is_empty() and (rect.size.x >= 80 or instance_id == selected_entity_id) and id not in [&"guest_corridor", &"service_corridor", &"guest_stairs", &"service_stairs", &"guest_elevator", &"service_elevator"]:
var status := RoomPresentation.label(data, RoomPresentation.slots(rect, p, id).size())
_draw_readable_badge(rect.position+Vector2(2, 2), status.replace("利用", "治療") if id == &"infirmary" else status.replace("利用", "休憩") if id in [&"staff_waiting_room", &"staff_dormitory"] else status)
func _draw_readable_badge(at: Vector2, text: String) -> void:
@@ -810,13 +897,21 @@ func _draw_readable_badge(at: Vector2, text: String) -> void:
func _prepare_room_presentations() -> void:
_room_presentations.clear()
_carried_patients.clear()
_treated_patients.clear()
_resting_staff.clear()
var model := _observe_rooms()
_room_presentations = model[&"rooms"]
_carried_patients = model[&"carried_patients"]
_treated_patients = model[&"treated_patients"]
_resting_staff = model[&"resting_staff"]
func _observe_rooms() -> Dictionary:
var _room_presentations: Dictionary = {}
var _carried_patients: Dictionary = {}
var _treated_patients: Dictionary = {}
var _resting_staff: Dictionary = {}
var service: Variant = _service(&"facility_service")
if service == null:
return
return {&"rooms": _room_presentations, &"carried_patients": _carried_patients, &"treated_patients": _treated_patients, &"resting_staff": _resting_staff}
for instance: Variant in service.get_instances():
if instance.origin.z == layer_mode:
_room_presentations[instance.instance_id] = RoomPresentation.describe(instance)
@@ -844,12 +939,22 @@ func _prepare_room_presentations() -> void:
else:
_carried_patients[task.target_entity_id] = task.assigned_staff_id
return {&"rooms": _room_presentations, &"carried_patients": _carried_patients, &"treated_patients": _treated_patients, &"resting_staff": _resting_staff}
func _prepare_queues() -> void:
_queue_positions.clear()
_queue_hidden.clear()
_landing_badges.clear()
_transport_report.clear()
var model := _observe_queues()
_queue_positions = model[&"queue_positions"]
_queue_hidden = model[&"queue_hidden"]
_landing_badges.assign(model[&"landing_badges"])
_transport_report = model[&"elevators"]
func _observe_queues() -> Dictionary:
var _queue_positions: Dictionary = {}
var _queue_hidden: Dictionary = {}
var _landing_badges: Array[Dictionary] = []
var _transport_report: Dictionary = {}
var groups: Dictionary = {}
var facilities: Variant = _service(&"facility_service")
var guests: Variant = _service(&"guest_service")
@@ -908,10 +1013,10 @@ func _prepare_queues() -> void:
index += 1
for hidden: int in range(index, members.size()):
_queue_hidden[members[hidden]] = true
var text := "待ち%d人" % members.size()
if index < members.size():
text += "(他%d人)" % (members.size()-index)
_landing_badges.append({&"at": get_cell_rect(cell).position + Vector2(-80 if lift_cells.has(cell) else 0, -16), &"text": text})
var text := RoomPresentation.queue_label(members.size(), members.size()-index)
_landing_badges.append({&"at": get_cell_rect(cell).position + Vector2(-80 if lift_cells.has(cell) else 0, -16), &"text": text, &"cell": cell, &"members": members.duplicate(), &"visible_count": index, &"overflow": members.size()-index})
return {&"queue_positions": _queue_positions, &"queue_hidden": _queue_hidden, &"landing_badges": _landing_badges, &"elevators": _transport_report}
func _add_queue_member(groups: Dictionary, key: String, cell: Vector3i) -> void:
@@ -1039,14 +1144,21 @@ func _draw_facility_labels() -> void:
func _draw_elevators() -> void:
_boarded_actor_ids.clear()
var transport: Variant = _service(&"elevators")
if transport == null or not transport.has_method(&"get_report"):
_elevator_tracks.clear()
return
var report_value: Variant = _transport_report
if not report_value is Dictionary:
return
for row: Dictionary in _life_observation.get(&"cabins", []):
PixelArt.elevator_cabin(self, row[&"rect"], row[&"pixel"], layer_mode == LayerMode.BACK, row[&"operational"])
for actor: Dictionary in row[&"passengers"]:
_draw_observed_actor(actor)
PixelArt.elevator_gate(self, row[&"rect"], row[&"pixel"], row[&"doors_open"])
_draw_readable_badge(row[&"badge_at"], row[&"badge"])
for badge: Dictionary in _life_observation.get(&"landing_badges", []):
_draw_readable_badge(badge[&"at"], badge[&"text"])
func _observe_elevators(observation: Dictionary) -> Array[Dictionary]:
var cabins: Array[Dictionary] = []
var _boarded_actor_ids: Dictionary = observation[&"boarded"]
var _elevator_tracks: Dictionary = observation[&"elevator_tracks"]
var report_value: Dictionary = observation[&"elevators"]
var alive: Dictionary = {}
var p := _cell_dimensions().x/16.0
for value: Variant in report_value.get(&"shafts", []):
@@ -1062,7 +1174,7 @@ func _draw_elevators() -> void:
for actor_id: Variant in passengers:
_boarded_actor_ids[_elevator_actor_key(String(actor_id))] = true
var floor_value := float(shaft.get(&"visual_floor", shaft.get(&"current_floor", stops[0].y)))
var visual_floor := _tracked_elevator_floor(shaft_id, floor_value)
var visual_floor := _tracked_elevator_floor(shaft_id, floor_value, observation)
if stops[0].z != layer_mode:
continue
var origin := get_cell_rect(stops[0])
@@ -1074,7 +1186,7 @@ func _draw_elevators() -> void:
var operational := bool(shaft.get(&"operational", true))
var phase := StringName(shaft.get(&"phase", &"IDLE"))
var doors_open := operational and phase in [&"IDLE", &"BOARDING", &"ALIGHTING"]
PixelArt.elevator_cabin(self,cabin,p,layer_mode==LayerMode.BACK,operational)
var passengers_drawn: Array[Dictionary] = []
var seat_columns := maxi(int((cabin.size.x / p - 4.0) / 8.0), 1)
var visible_count := mini(passengers.size(), seat_columns*2)
for index: int in range(visible_count):
@@ -1090,22 +1202,19 @@ func _draw_elevators() -> void:
var x := cabin.position.x + (2.0 + (index % seat_columns + 0.5)*8.0)*p
var feet := cabin.end.y - (2.0 + int(index / seat_columns)*9.0)*p
var center := Vector2(x, feet+3*p-_cell_dimensions().y*0.5)
_draw_pixel_actor(center,actor_key,is_staff,state,false,carrying,&"",true)
PixelArt.elevator_gate(self,cabin,p,doors_open)
var capacity := int(shaft.get(&"capacity", 0))
var load_text := "%d/%d人" % [passengers.size(), capacity]
if capacity > 0 and passengers.size() >= capacity:
load_text = "満員 " + load_text
if not operational:
load_text = "停止 " + load_text
_draw_readable_badge(Vector2(cabin.position.x, cabin.position.y-14), load_text)
for badge: Dictionary in _landing_badges:
_draw_readable_badge(badge[&"at"], badge[&"text"])
passengers_drawn.append(_actor_drawing(center,actor_key,is_staff,state,false,carrying,&"",true,observation))
cabins.append({&"shaft_id": shaft_id, &"rect": cabin, &"pixel": p,
&"operational": operational, &"doors_open": doors_open,
&"passengers": passengers_drawn,
&"badge_at": Vector2(cabin.position.x, cabin.position.y-14),
&"badge": RoomPresentation.elevator_label(shaft)})
for shaft_id: Variant in _elevator_tracks.keys():
if not alive.has(shaft_id):
_elevator_tracks.erase(shaft_id)
return cabins
func _elevator_actor_key(actor_id: String) -> String:
if actor_id.begins_with("guest:"):
@@ -1115,7 +1224,8 @@ func _elevator_actor_key(actor_id: String) -> String:
return actor_id
func _tracked_elevator_floor(shaft_id: StringName, current_floor: float) -> float:
func _tracked_elevator_floor(shaft_id: StringName, current_floor: float, observation: Dictionary = {}) -> float:
var _elevator_tracks: Dictionary = observation.get(&"elevator_tracks", self._elevator_tracks)
if not _elevator_tracks.has(shaft_id):
_elevator_tracks[shaft_id] = {&"floor":current_floor,&"from":current_floor,&"at":_animation_time}
var track: Dictionary = _elevator_tracks[shaft_id]
@@ -1131,6 +1241,19 @@ func _tracked_elevator_floor(shaft_id: StringName, current_floor: float) -> floa
func _draw_actors() -> void:
for row: Dictionary in _life_observation.get(&"actor_drawings", []):
_draw_observed_actor(row)
func _observe_actors(observation: Dictionary) -> Array[Dictionary]:
var rows: Array[Dictionary] = []
var _actor_tracks: Dictionary = observation[&"actor_tracks"]
var _boarded_actor_ids: Dictionary = observation[&"boarded"]
var _queue_hidden: Dictionary = observation[&"queue_hidden"]
var _queue_positions: Dictionary = observation[&"queue_positions"]
var _carried_patients: Dictionary = observation[&"carried_patients"]
var _treated_patients: Dictionary = observation[&"treated_patients"]
var _resting_staff: Dictionary = observation[&"resting_staff"]
var counts: Dictionary = {}
var alive: Dictionary = {}
var service_slots: Dictionary = {}
@@ -1144,18 +1267,18 @@ func _draw_actors() -> void:
if _boarded_actor_ids.has(key) or _queue_hidden.has(key):
_actor_tracks.erase(key)
continue
var position := _tracked_actor_position(key,guest.current_cell)
var position := _tracked_actor_position(key,guest.current_cell,observation)
if guest.current_cell.z==layer_mode:
var receiving := not String(guest.current_service_id).is_empty()
if _queue_positions.has(key):
position = _queue_positions[key]
elif receiving:
position = _service_use_position(guest,position,service_slots)
position = _service_use_position(guest,position,service_slots,observation)
if not position.is_finite():
continue
else:
position += _actor_offset(guest.current_cell,counts)
_draw_pixel_actor(position,key,false,int(guest.state),receiving,false,_guest_activity(guest))
rows.append(_actor_drawing(position,key,false,int(guest.state),receiving,false,_guest_activity(guest),false,observation))
var staff_service: Variant = _service(&"staff_service")
if staff_service != null and staff_service.has_method(&"get_staff_members"):
for staff: Variant in staff_service.call(&"get_staff_members"):
@@ -1166,27 +1289,29 @@ func _draw_actors() -> void:
if _boarded_actor_ids.has(key) or _queue_hidden.has(key):
_actor_tracks.erase(key)
continue
var position := _tracked_actor_position(key,staff.current_cell)
var position := _tracked_actor_position(key,staff.current_cell,observation)
if staff.current_cell.z==layer_mode:
if _carried_patients.has(staff.staff_id):
var carrier: Variant = staff_service.get_staff(_carried_patients[staff.staff_id])
if carrier != null:
position = _tracked_actor_position("staff/"+String(carrier.staff_id), carrier.current_cell) - Vector2(0, 12*_cell_dimensions().x/16.0)
_draw_pixel_actor(position,key,true,int(staff.state),false,false)
position = _tracked_actor_position("staff/"+String(carrier.staff_id), carrier.current_cell,observation) - Vector2(0, 12*_cell_dimensions().x/16.0)
rows.append(_actor_drawing(position,key,true,int(staff.state),false,false,&"",false,observation))
continue
if _resting_staff.has(staff.staff_id):
position = _resting_staff_position(staff.staff_id, position)
if position.is_finite(): _draw_pixel_actor(position,key,true,int(staff.state),false,false)
position = _resting_staff_position(staff.staff_id, position,observation)
if position.is_finite(): rows.append(_actor_drawing(position,key,true,int(staff.state),false,false,&"",false,observation))
continue
if _treated_patients.has(staff.staff_id):
position = _rescue_treatment_position(staff.staff_id, position)
if position.is_finite(): _draw_pixel_actor(position,key,true,int(staff.state),false,false)
position = _rescue_treatment_position(staff.staff_id, position,observation)
if position.is_finite(): rows.append(_actor_drawing(position,key,true,int(staff.state),false,false,&"",false,observation))
continue
_draw_pixel_actor(_queue_positions.get(key, _staff_work_position(staff, position+_actor_offset(staff.current_cell,counts))),key,true,int(staff.state),false,not String(staff.carried_inventory_id).is_empty(),_staff_work_animation(staff))
rows.append(_actor_drawing(_queue_positions.get(key, _staff_work_position(staff, position+_actor_offset(staff.current_cell,counts),observation)),key,true,int(staff.state),false,not String(staff.carried_inventory_id).is_empty(),_staff_work_animation(staff),false,observation))
for key: Variant in _actor_tracks.keys():
if not alive.has(key):
_actor_tracks.erase(key)
return rows
func _staff_work_animation(staff: StaffRuntime) -> StringName:
if staff.role_id != &"cleaner":
@@ -1201,7 +1326,8 @@ func _staff_work_animation(staff: StaffRuntime) -> StringName:
return &"idle"
func _staff_work_position(staff: Variant, fallback: Vector2) -> Vector2:
func _staff_work_position(staff: Variant, fallback: Vector2, observation: Dictionary = {}) -> Vector2:
var _room_presentations: Dictionary = observation.get(&"rooms", self._room_presentations)
if staff.state not in [StaffRuntime.State.WORKING, StaffRuntime.State.AVAILABLE]:
return fallback
var service: Variant = _service(&"facility_service")
@@ -1227,7 +1353,8 @@ func _staff_work_position(staff: Variant, fallback: Vector2) -> Vector2:
return Vector2(rect.position.x+(slot+0.5)*rect.size.x/columns, rect.position.y+26*p-_cell_dimensions().y*0.5)
func _tracked_actor_position(key: String, cell: Vector3i) -> Vector2:
func _tracked_actor_position(key: String, cell: Vector3i, observation: Dictionary = {}) -> Vector2:
var _actor_tracks: Dictionary = observation.get(&"actor_tracks", self._actor_tracks)
if not _actor_tracks.has(key):
_actor_tracks[key] = {&"cell":cell,&"from":cell,&"at":_animation_time,&"direction":1.0}
var track: Dictionary = _actor_tracks[key]
@@ -1246,7 +1373,8 @@ func _tracked_actor_position(key: String, cell: Vector3i) -> Vector2:
return cell_to_canvas(track[&"from"]).lerp(cell_to_canvas(cell),t)
func _service_use_position(guest: Variant, fallback: Vector2, _slots: Dictionary) -> Vector2:
func _service_use_position(guest: Variant, fallback: Vector2, _slots: Dictionary, observation: Dictionary = {}) -> Vector2:
var _room_presentations: Dictionary = observation.get(&"rooms", self._room_presentations)
# Guest paths stop at an access cell. While a service is active, project the
# occupant into that facility's use position; authoritative paths stay intact.
var id := StringName(guest.target_facility_id)
@@ -1281,6 +1409,19 @@ func _guest_activity(guest: Variant) -> StringName:
func _draw_pixel_actor(center: Vector2, key: String, staff: bool, state: int, receiving: bool, carrying: bool, activity: StringName = &"", riding := false) -> void:
_draw_observed_actor(_actor_drawing(center, key, staff, state, receiving, carrying, activity, riding))
func _draw_observed_actor(row: Dictionary) -> void:
var at: Vector2 = row[&"at"]
var p: float = row[&"scale"]
draw_rect(Rect2(at + Vector2(5, 17) * p, Vector2(8, 1) * p), Color("303c40", 0.2))
People.draw(self, at, p, row[&"mode"], row[&"frame"], row[&"variant"], row[&"staff"], row[&"flip"])
func _actor_drawing(center: Vector2, key: String, staff: bool, state: int, receiving: bool, carrying: bool, activity: StringName = &"", riding := false, observation: Dictionary = {}) -> Dictionary:
var _actor_tracks: Dictionary = observation.get(&"actor_tracks", self._actor_tracks)
var _queue_positions: Dictionary = observation.get(&"queue_positions", self._queue_positions)
var world_pixel := _cell_dimensions().x / 16.0
var scale := 0.5 if riding else 0.75
var p := maxf(floorf(world_pixel * scale), 1.0) if world_pixel >= 2.0 else world_pixel * scale
@@ -1307,8 +1448,10 @@ func _draw_pixel_actor(center: Vector2, key: String, staff: bool, state: int, re
elif _queue_positions.has(key) or (not staff and state == GuestRuntime.State.QUEUING):
mode = &"wait"
var at := (center + Vector2(-8 * p, _cell_dimensions().y * 0.5 - 3 * world_pixel - 17 * p)).round()
draw_rect(Rect2(at + Vector2(5, 17) * p, Vector2(8, 1) * p), Color("303c40", 0.2))
People.draw(self, at, p, mode, frame, variant, staff, float(track.get(&"direction", 1.0)) < 0.0)
return {&"key": key, &"center": center, &"at": at, &"scale": p, &"mode": mode,
&"frame": frame, &"variant": variant, &"staff": staff, &"state": state,
&"receiving": receiving, &"carrying": carrying, &"activity": activity,
&"riding": riding, &"flip": float(track.get(&"direction", 1.0)) < 0.0}
func _draw_excavation_jobs() -> void:
@@ -1421,14 +1564,14 @@ func _facility_color(instance_id: StringName) -> Color:
var instance: Variant = facility_service.call(&"get_instance", instance_id)
if instance == null:
return Color("6f8b67") if layer_mode == LayerMode.FRONT else Color("427582")
match int(instance.state):
0:
match int(_room_presentations.get(instance_id, {}).get(&"state", instance.state)):
FacilityInstance.State.BROKEN:
return Color("a24b45")
1, 2, 3:
FacilityInstance.State.NO_ACCESS, FacilityInstance.State.NO_POWER, FacilityInstance.State.NO_STAFF:
return Color("a77d3f")
4:
FacilityInstance.State.CLOSED:
return Color("6b6258")
5:
FacilityInstance.State.DEGRADED:
return Color("89914d")
_:
return Color("6f9a55") if layer_mode == LayerMode.FRONT else Color("67518e")
@@ -1579,7 +1722,9 @@ func _normalized_rotation(value: int) -> int:
return posmod(value, 360) / 90 * 90
func _rescue_treatment_position(patient_id: StringName, fallback: Vector2) -> Vector2:
func _rescue_treatment_position(patient_id: StringName, fallback: Vector2, observation: Dictionary = {}) -> Vector2:
var _room_presentations: Dictionary = observation.get(&"rooms", self._room_presentations)
var _treated_patients: Dictionary = observation.get(&"treated_patients", self._treated_patients)
var facility_id: StringName = _treated_patients[patient_id]
var world: Variant = _service(&"world_service")
var cells: Array[Vector3i] = world.get_entity_cells(facility_id) if world != null else []
@@ -1592,7 +1737,9 @@ func _rescue_treatment_position(patient_id: StringName, fallback: Vector2) -> Ve
return anchors[slot]+Vector2(0,-2*p-_cell_dimensions().y*0.5)
func _resting_staff_position(staff_id: StringName, fallback: Vector2) -> Vector2:
func _resting_staff_position(staff_id: StringName, fallback: Vector2, observation: Dictionary = {}) -> Vector2:
var _room_presentations: Dictionary = observation.get(&"rooms", self._room_presentations)
var _resting_staff: Dictionary = observation.get(&"resting_staff", self._resting_staff)
var facility_id: StringName = _resting_staff[staff_id]
var world: Variant = _service(&"world_service")
var cells: Array[Vector3i] = world.get_entity_cells(facility_id) if world != null else []