Connect physical elevators and staff commutes to construction, saves, and cutaway UI

This commit is contained in:
Takeshi
2026-09-09 12:58:40 +09:00
parent 4d72de8973
commit 0f12c748e9
46 changed files with 3267 additions and 165 deletions
+102 -4
View File
@@ -49,6 +49,8 @@ var show_grid := false
var _animation_time := 0.0
var _animation_frame := -1
var _actor_tracks: Dictionary = {}
var _elevator_tracks: Dictionary = {}
var _boarded_actor_ids: Dictionary = {}
var _dragging := false
var _runtime: Variant = null
@@ -74,6 +76,8 @@ func set_runtime(value: Variant) -> void:
_disconnect_runtime()
_runtime = value
_actor_tracks.clear()
_elevator_tracks.clear()
_boarded_actor_ids.clear()
_connect_runtime()
queue_redraw()
_update_build_preview()
@@ -349,6 +353,7 @@ func _draw() -> void:
if show_grid:
_draw_congestion_overlay(bounds)
_draw_world_entity_icons()
_draw_elevators()
_draw_construction_labels()
_draw_facility_labels()
_draw_queue_badges()
@@ -611,6 +616,93 @@ func _draw_facility_labels() -> void:
draw_string(ThemeDB.fallback_font,at+Vector2(4,12),label,HORIZONTAL_ALIGNMENT_CENTER,label_width-8,font_size,Color("f0e2b9"))
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.call(&"get_report")
if not report_value is Dictionary:
return
var alive: Dictionary = {}
var p := _cell_dimensions().x/16.0
for value: Variant in report_value.get(&"shafts", []):
if not value is Dictionary:
continue
var shaft: Dictionary = value
var stops := _cells_from_serialized(shaft.get(&"stops", []))
if stops.is_empty():
continue
var shaft_id := StringName(shaft.get(&"shaft_id", &""))
alive[shaft_id] = true
var passengers: Array = shaft.get(&"boarded", [])
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)
if stops[0].z != layer_mode:
continue
var origin := get_cell_rect(stops[0])
var width := maxi(int(shaft.get(&"width", 2)), 1)
var baseline := origin.end.y-float(visual_floor-stops[0].y)*_cell_dimensions().y-3*p
var cabin := Rect2(Vector2(origin.position.x+4*p,baseline-19*p).round(),Vector2(width*_cell_dimensions().x-8*p,19*p))
if not cabin.intersects(Rect2(Vector2.ZERO,size)):
continue
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 visible_count := mini(passengers.size(), maxi(width*2-1,1))
for index: int in range(visible_count):
var actor_key := _elevator_actor_key(String(passengers[index]))
var is_staff := actor_key.begins_with("staff/")
var person: Variant = null
var people: Variant = _service(&"staff_service" if is_staff else &"guest_service")
var getter := &"get_staff" if is_staff else &"get_guest"
if people != null and people.has_method(getter):
person = people.call(getter,StringName(actor_key.get_slice("/",1)))
var carrying := is_staff and person != null and not String(person.carried_inventory_id).is_empty()
var state := int(person.state) if person != null else 0
var x := cabin.position.x+5*p+float(index)*(cabin.size.x-10*p)/maxf(float(visible_count-1),1.0)
if visible_count == 1:
x = cabin.get_center().x
var center := Vector2(x,cabin.end.y-_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 waiting := int(shaft.get(&"waiting_count",0))
if waiting > 0:
var badge := Vector2(cabin.end.x+2*p,cabin.position.y+2*p)
draw_rect(Rect2(badge,Vector2(20,12)*p),Color("76593f"))
draw_string(ThemeDB.fallback_font,badge+Vector2(2,9)*p,"待%d" % waiting,HORIZONTAL_ALIGNMENT_LEFT,-1,clampi(int(7*p),8,13),Color("f6e8bc"))
for shaft_id: Variant in _elevator_tracks.keys():
if not alive.has(shaft_id):
_elevator_tracks.erase(shaft_id)
func _elevator_actor_key(actor_id: String) -> String:
if actor_id.begins_with("guest:"):
return "guest/" + actor_id.trim_prefix("guest:")
if actor_id.begins_with("staff:"):
return "staff/" + actor_id.trim_prefix("staff:")
return actor_id
func _tracked_elevator_floor(shaft_id: StringName, current_floor: float) -> float:
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]
if not is_equal_approx(float(track[&"floor"]),current_floor):
track[&"from"] = float(track[&"floor"])
track[&"floor"] = current_floor
track[&"at"] = _animation_time
var clock := get_node_or_null("/root/GameClock")
if clock != null and bool(clock.get("paused")):
return current_floor
var t := clampf((_animation_time-float(track[&"at"]))/0.42,0.0,1.0)
return lerpf(float(track[&"from"]),current_floor,t)
func _draw_actors() -> void:
var counts: Dictionary = {}
var alive: Dictionary = {}
@@ -622,6 +714,9 @@ func _draw_actors() -> void:
continue
var key := "guest/"+String(guest.guest_id)
alive[key] = true
if _boarded_actor_ids.has(key):
_actor_tracks.erase(key)
continue
var position := _tracked_actor_position(key,guest.current_cell)
if guest.current_cell.z==layer_mode:
var receiving := not String(guest.current_service_id).is_empty()
@@ -637,6 +732,9 @@ func _draw_actors() -> void:
continue
var key := "staff/"+String(staff.staff_id)
alive[key] = true
if _boarded_actor_ids.has(key):
_actor_tracks.erase(key)
continue
var position := _tracked_actor_position(key,staff.current_cell)
if staff.current_cell.z==layer_mode:
_draw_pixel_actor(position+_actor_offset(staff.current_cell,counts),key,true,int(staff.state),false,not String(staff.carried_inventory_id).is_empty(),staff.role_id)
@@ -690,7 +788,7 @@ func _guest_activity(guest: Variant) -> StringName:
return StringName(facility.definition_id) if facility != null else &""
func _draw_pixel_actor(center: Vector2, key: String, staff: bool, state: int, receiving: bool, carrying: bool, activity: StringName = &"") -> void:
func _draw_pixel_actor(center: Vector2, key: String, staff: bool, state: int, receiving: bool, carrying: bool, activity: StringName = &"", riding := false) -> void:
var texture := visual_theme.pixel_dungeon_tiles if visual_theme != null else null
if texture==null:
return
@@ -700,7 +798,7 @@ func _draw_pixel_actor(center: Vector2, key: String, staff: bool, state: int, re
var moving: bool = track.get(&"cell",Vector3i.ZERO)!=track.get(&"from",Vector3i.ZERO) and _animation_time-float(track.get(&"at",0.0))<0.42
var clock := get_node_or_null("/root/GameClock")
var paused := clock != null and bool(clock.get("paused"))
moving = moving and not paused
moving = moving and not paused and not riding
var variant := int(absi(key.hash()))%5
var tile_id := (96 if staff else 84)+variant
var frame := int(_animation_time*9.0+variant)%4
@@ -729,7 +827,7 @@ func _draw_pixel_actor(center: Vector2, key: String, staff: bool, state: int, re
draw_rect(Rect2(at+Vector2(7,8)*p,Vector2(2,3)*p),Color("81c6ba"))
if carrying:
PixelArt.tile(self,texture,89,Rect2(at+Vector2(9,6)*p,Vector2(9,9)*p))
var working := staff and state==StaffRuntime.State.WORKING
var working := staff and state==StaffRuntime.State.WORKING and not riding
var resting := receiving and activity==&"standard_room"
if resting:
var lift := float(int(_animation_time*2)%3) if not paused else 0.0
@@ -741,7 +839,7 @@ func _draw_pixel_actor(center: Vector2, key: String, staff: bool, state: int, re
if receiving and activity in [&"cafeteria",&"tavern"]:
var lift := p if not paused and int(_animation_time*3)%2==0 else 0.0
draw_rect(Rect2(at+Vector2(11*p,8*p-lift),Vector2(3*p,2*p)),Color("e6d7b5"))
var waiting := not staff and state==GuestRuntime.State.QUEUING
var waiting := not staff and state==GuestRuntime.State.QUEUING and not riding
if (receiving and not resting) or working or waiting:
var badge := Rect2(at+Vector2(10,-4)*p,Vector2(6,5)*p)
draw_rect(badge,Color("f2e9cb"))