P24-02: 操作と通知の全受入
This commit is contained in:
@@ -22,6 +22,8 @@ const SupplyManagementPanelScript := preload("res://scripts/ui/supply_management
|
||||
const BusinessManagementPanelScript := preload("res://scripts/ui/business_management_panel.gd")
|
||||
const DefenseManagementPanelScript := preload("res://scripts/ui/defense_management_panel.gd")
|
||||
const WorldPreviewScript := preload("res://scenes/main/world_preview.gd")
|
||||
const OperationsCameraControlsScript := preload("res://scripts/ui/operations_camera_controls.gd")
|
||||
const OperationFeedbackScript := preload("res://scripts/ui/operation_feedback.gd")
|
||||
const LayerSkin := preload("res://scripts/ui/layer_interface_skin.gd")
|
||||
const DefaultVisualTheme := preload("res://assets/themes/japanese_fantasy/visual-theme-v1.tres")
|
||||
var _last_presented_close_minute := 0
|
||||
@@ -145,6 +147,8 @@ var alert_icon_node: TextureRect
|
||||
var resource_labels: Dictionary = {}
|
||||
var resource_icon_nodes: Dictionary = {}
|
||||
var world_preview: WorldPreview
|
||||
var operations_camera_controls: OperationsCameraControls
|
||||
var important_warning_cutin: OperationFeedback.ImportantWarningCutinDialog
|
||||
var front_button: Button
|
||||
var back_button: Button
|
||||
var excavation_button: Button
|
||||
@@ -206,6 +210,7 @@ var _pending_excavation_commands: Dictionary = {}
|
||||
var _pending_hire_commands: Dictionary = {}
|
||||
var _staff_candidate_panel_open := false
|
||||
var _operations_start_report: Dictionary = {}
|
||||
var _subscribed_event_stream: Variant = null
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
@@ -244,6 +249,7 @@ func _ready() -> void:
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
_unbind_domain_event_stream()
|
||||
var tree := get_tree()
|
||||
if tree != null:
|
||||
if tree.root.close_requested.is_connected(_on_window_close_requested):
|
||||
@@ -402,6 +408,18 @@ func _build_interface() -> void:
|
||||
management_panel.offset_bottom = -8
|
||||
management_panel.visible = false
|
||||
|
||||
operations_camera_controls = OperationsCameraControlsScript.new()
|
||||
operations_camera_controls.name = "OperationsCameraControls"
|
||||
operations_camera_controls.configure(world_preview, self)
|
||||
world_preview.add_child(operations_camera_controls)
|
||||
operations_camera_controls.set_anchors_and_offsets_preset(Control.PRESET_BOTTOM_RIGHT)
|
||||
operations_camera_controls.offset_right = -12
|
||||
operations_camera_controls.offset_bottom = -12
|
||||
|
||||
important_warning_cutin = OperationFeedbackScript.ImportantWarningCutinDialog.new()
|
||||
important_warning_cutin.visibility_changed.connect(_update_modal_input_guard)
|
||||
add_child(important_warning_cutin)
|
||||
|
||||
layout.add_child(_create_bottom_bar())
|
||||
|
||||
|
||||
@@ -441,6 +459,7 @@ func _create_top_bar() -> Control:
|
||||
_set_status(message,success)
|
||||
_refresh_dashboard()
|
||||
)
|
||||
rank_dialog.visibility_changed.connect(_update_modal_input_guard)
|
||||
add_child(rank_dialog)
|
||||
rank_button.pressed.connect(rank_dialog.show_rank)
|
||||
|
||||
@@ -756,6 +775,7 @@ func _create_bottom_bar() -> Control:
|
||||
row.add_child(management_toggle_button)
|
||||
event_log_dialog = EventLogDialogScript.new()
|
||||
event_log_dialog.configure(_runtime)
|
||||
event_log_dialog.visibility_changed.connect(_update_modal_input_guard)
|
||||
add_child(event_log_dialog)
|
||||
var event_log_button := _button("記録", event_log_dialog.show_events, 62)
|
||||
event_log_button.name = "EventLogButton"
|
||||
@@ -947,6 +967,9 @@ func _connect_signals() -> void:
|
||||
world_preview.staff_assignment_dropped.connect(func(data: Dictionary, facility_id: StringName) -> void:
|
||||
staff_management_panel.assign_from_drop(data, facility_id)
|
||||
)
|
||||
world_preview.staff_assignment_clicked.connect(func(staff_id: StringName, facility_id: StringName) -> void:
|
||||
assign_staff_by_click(facility_id, staff_id)
|
||||
)
|
||||
world_preview.legacy_stair_selected.connect(_on_legacy_stair_selected)
|
||||
world_preview.legacy_stair_selected.connect(_on_audio_world_selection)
|
||||
world_preview.construction_selected.connect(_on_construction_selected)
|
||||
@@ -965,6 +988,7 @@ func _connect_signals() -> void:
|
||||
_runtime.connect(&"save_operation_completed", Callable(self, "_on_save_operation_completed"))
|
||||
if _runtime.has_signal(&"command_completed"):
|
||||
_runtime.connect(&"command_completed", Callable(self, "_on_command_completed"))
|
||||
_bind_domain_event_stream()
|
||||
|
||||
|
||||
func _populate_build_palette() -> void:
|
||||
@@ -2276,6 +2300,7 @@ func _ensure_manual_save_slots() -> Variant:
|
||||
manual_save_slots_dialog = ManualSaveSlotsDialogScript.new()
|
||||
manual_save_slots_dialog.connect(&"new_slot_requested", _save_new_manual_slot)
|
||||
manual_save_slots_dialog.connect(&"load_slot_requested", _load_selected_manual_slot)
|
||||
manual_save_slots_dialog.visibility_changed.connect(_update_modal_input_guard)
|
||||
add_child(manual_save_slots_dialog)
|
||||
return manual_save_slots_dialog
|
||||
|
||||
@@ -2352,6 +2377,7 @@ func _apply_loaded_interface(result: Dictionary) -> void:
|
||||
_restore_product_interface_selection(result.get(&"manifest", {}))
|
||||
_refresh_dashboard()
|
||||
_update_pause_and_speed()
|
||||
_bind_domain_event_stream()
|
||||
|
||||
|
||||
func _valid_interface_selection_version(value: Variant) -> bool:
|
||||
@@ -2937,6 +2963,7 @@ func _ensure_guest_observer() -> GuestObservationPanel:
|
||||
content.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
guest_observer.add_child(scroll)
|
||||
scroll.add_child(content)
|
||||
guest_observer.visibility_changed.connect(_update_modal_input_guard)
|
||||
add_child(guest_observer)
|
||||
return guest_observer.find_child("GuestObservationPanel", true, false) as GuestObservationPanel
|
||||
|
||||
@@ -2964,6 +2991,7 @@ func _preview_dimension_migration() -> void:
|
||||
dimension_migration_dialog.cancel_button_text="戻る"
|
||||
dimension_migration_dialog.confirmed.connect(_confirm_dimension_migration)
|
||||
dimension_migration_dialog.canceled.connect(func(): _dimension_migration_digest="")
|
||||
dimension_migration_dialog.visibility_changed.connect(_update_modal_input_guard)
|
||||
add_child(dimension_migration_dialog)
|
||||
var lines: PackedStringArray=[]
|
||||
for change: Dictionary in report[&"changes"]:
|
||||
@@ -3083,3 +3111,90 @@ func _submit_ui_command(
|
||||
if _audio_feedback != null:
|
||||
return _audio_feedback.submit_command(command_type, payload, actor_id, target_id, priority, execute_at, idempotency_key)
|
||||
return _runtime.call(&"submit_command", command_type, payload, actor_id, target_id, priority, execute_at, idempotency_key)
|
||||
|
||||
|
||||
func assign_staff_by_click(facility_id: StringName, staff_id: StringName = &"") -> void:
|
||||
if staff_management_panel == null:
|
||||
return
|
||||
var target_staff := staff_id
|
||||
if target_staff.is_empty():
|
||||
target_staff = StringName(staff_management_panel.get("selected_staff_id"))
|
||||
if target_staff.is_empty():
|
||||
_set_status("配属する従業員を一覧から選択してください。", false)
|
||||
return
|
||||
var queue_id: int = int(staff_management_panel.get("_queue_instance_id"))
|
||||
var template_val: Variant = staff_management_panel.get("_draft_template_id")
|
||||
var drop_data := {
|
||||
&"kind": &"staff_assignment",
|
||||
&"staff_id": target_staff,
|
||||
&"template_id": template_val if template_val != null else &"DAY",
|
||||
&"queue_instance_id": queue_id,
|
||||
}
|
||||
staff_management_panel.assign_from_drop(drop_data, facility_id)
|
||||
|
||||
|
||||
func _update_modal_input_guard() -> void:
|
||||
if world_preview == null:
|
||||
return
|
||||
var any_visible := false
|
||||
for dialog: Variant in [manual_save_slots_dialog, event_log_dialog, rank_dialog, dimension_migration_dialog, guest_observer, defense_manager, important_warning_cutin]:
|
||||
if dialog != null and is_instance_valid(dialog) and dialog is Window and bool(dialog.visible):
|
||||
any_visible = true
|
||||
break
|
||||
world_preview.modal_input_blocked = any_visible
|
||||
|
||||
|
||||
func _ensure_important_warning_cutin() -> OperationFeedback.ImportantWarningCutinDialog:
|
||||
if important_warning_cutin == null:
|
||||
important_warning_cutin = OperationFeedbackScript.ImportantWarningCutinDialog.new()
|
||||
important_warning_cutin.visibility_changed.connect(_update_modal_input_guard)
|
||||
add_child(important_warning_cutin)
|
||||
return important_warning_cutin
|
||||
|
||||
|
||||
func show_warning_cutin(kind: StringName, heading: String, message: String) -> void:
|
||||
_ensure_important_warning_cutin().show_cutin(kind, heading, message)
|
||||
|
||||
|
||||
func _bind_domain_event_stream() -> void:
|
||||
_unbind_domain_event_stream()
|
||||
if _runtime == null or not is_instance_valid(_runtime):
|
||||
return
|
||||
var stream: Variant = _runtime.get(&"domain_events") if _runtime is Object and "domain_events" in _runtime else null
|
||||
if stream != null and is_instance_valid(stream) and stream.has_method(&"subscribe"):
|
||||
stream.subscribe(DomainEventStream.WILDCARD_EVENT_TYPE, &"main_scene", Callable(self, "_on_domain_event_published"))
|
||||
_subscribed_event_stream = stream
|
||||
|
||||
|
||||
func _unbind_domain_event_stream() -> void:
|
||||
if _subscribed_event_stream != null and is_instance_valid(_subscribed_event_stream):
|
||||
if _subscribed_event_stream.has_method(&"unsubscribe"):
|
||||
_subscribed_event_stream.unsubscribe(DomainEventStream.WILDCARD_EVENT_TYPE, &"main_scene")
|
||||
_subscribed_event_stream = null
|
||||
|
||||
|
||||
func _on_domain_event_published(event: Variant) -> void:
|
||||
var event_type: StringName = &""
|
||||
var payload: Dictionary = {}
|
||||
if event is Dictionary:
|
||||
event_type = StringName(event.get(&"event_type", event.get("event_type", &"")))
|
||||
var raw_payload: Variant = event.get(&"payload", event.get("payload", {}))
|
||||
if raw_payload is Dictionary:
|
||||
payload = raw_payload
|
||||
elif event is Object:
|
||||
if "event_type" in event:
|
||||
event_type = StringName(event.get(&"event_type"))
|
||||
if "payload" in event:
|
||||
var raw_payload: Variant = event.get(&"payload")
|
||||
if raw_payload is Dictionary:
|
||||
payload = raw_payload
|
||||
match event_type:
|
||||
&"DEFENSE_RAID_CREATED":
|
||||
show_warning_cutin(OperationFeedbackScript.CUTIN_DEFENSE, "防衛戦開始演出", "敵の襲撃が発生しました!防衛部隊を配置してください。")
|
||||
&"RELATIONSHIP_SHIELD_CHANGED":
|
||||
# Existing love-shield vanishing transition
|
||||
var old_val: int = int(payload.get(&"old_value", payload.get("old_value", -1)))
|
||||
var new_val: int = int(payload.get(&"new_value", payload.get("new_value", -1)))
|
||||
if old_val > 0 and new_val == 0:
|
||||
show_warning_cutin(OperationFeedbackScript.CUTIN_INVASION, "重要警告:愛の盾消失", "愛の盾が消失しました。人物関係を確認してください。")
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ extends Control
|
||||
|
||||
signal facility_selected(instance_id: StringName)
|
||||
signal staff_assignment_dropped(data: Dictionary, facility_id: StringName)
|
||||
signal staff_assignment_clicked(staff_id: StringName, facility_id: StringName)
|
||||
signal construction_selected(job_id: StringName)
|
||||
signal legacy_stair_selected(stair_id: StringName)
|
||||
signal cell_clicked(cell: Vector3i)
|
||||
@@ -15,6 +16,7 @@ enum LayerMode {
|
||||
BACK,
|
||||
}
|
||||
|
||||
const OperationFeedbackScript := preload("res://scripts/ui/operation_feedback.gd")
|
||||
const RoomPresentation := preload("res://scripts/ui/room_presentation.gd")
|
||||
const People := preload("res://scripts/ui/pixel_people.gd")
|
||||
const PixelArt := preload("res://scripts/ui/pixel_tower_art.gd")
|
||||
@@ -57,6 +59,11 @@ var zoom_level := 1.0
|
||||
var pan_offset := Vector2.ZERO
|
||||
var show_grid := false
|
||||
var pan_mode := false
|
||||
var modal_input_blocked := false:
|
||||
set(value):
|
||||
modal_input_blocked = value
|
||||
if modal_input_blocked:
|
||||
_end_pan_drag()
|
||||
var _drawing := false
|
||||
var _draw_cache: Dictionary = {}
|
||||
|
||||
@@ -420,6 +427,9 @@ func facility_at_canvas(canvas_position: Vector2) -> StringName:
|
||||
|
||||
|
||||
func _gui_input(event: InputEvent) -> void:
|
||||
if modal_input_blocked:
|
||||
accept_event()
|
||||
return
|
||||
if event is InputEventMouseButton:
|
||||
if not event.pressed and event.button_index == _drag_button:
|
||||
_end_pan_drag()
|
||||
@@ -1145,6 +1155,10 @@ func _draw_actors() -> void:
|
||||
else:
|
||||
position += _actor_offset(guest.current_cell,counts)
|
||||
_draw_pixel_actor(position,key,false,int(guest.state),receiving,false,_guest_activity(guest))
|
||||
var rel_guest: Variant = _guest_relationship(guest.guest_id)
|
||||
var guest_bubble := OperationFeedbackScript.resolve_guest_bubble(guest, rel_guest)
|
||||
if guest_bubble != OperationFeedbackScript.BUBBLE_NONE:
|
||||
_draw_unit_bubble(position, guest_bubble)
|
||||
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"):
|
||||
@@ -1171,7 +1185,12 @@ func _draw_actors() -> void:
|
||||
position = _rescue_treatment_position(staff.staff_id, position)
|
||||
if position.is_finite(): _draw_pixel_actor(position,key,true,int(staff.state),false,false)
|
||||
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))
|
||||
var staff_draw_pos: Vector2 = _queue_positions.get(key, _staff_work_position(staff, position+_actor_offset(staff.current_cell,counts)))
|
||||
_draw_pixel_actor(staff_draw_pos,key,true,int(staff.state),false,not String(staff.carried_inventory_id).is_empty(),_staff_work_animation(staff))
|
||||
var rel_staff: Variant = _staff_relationship(staff.staff_id)
|
||||
var staff_bubble := OperationFeedbackScript.resolve_staff_bubble(staff, rel_staff)
|
||||
if staff_bubble != OperationFeedbackScript.BUBBLE_NONE:
|
||||
_draw_unit_bubble(staff_draw_pos, staff_bubble)
|
||||
for key: Variant in _actor_tracks.keys():
|
||||
if not alive.has(key):
|
||||
_actor_tracks.erase(key)
|
||||
@@ -1300,6 +1319,54 @@ func _draw_pixel_actor(center: Vector2, key: String, staff: bool, state: int, re
|
||||
People.draw(self, at, p, mode, frame, variant, staff, float(track.get(&"direction", 1.0)) < 0.0)
|
||||
|
||||
|
||||
func _draw_unit_bubble(actor_center: Vector2, bubble_type: StringName) -> void:
|
||||
var world_pixel := _cell_dimensions().x / 16.0
|
||||
var p := maxf(floorf(world_pixel * 0.75), 1.0) if world_pixel >= 2.0 else world_pixel * 0.75
|
||||
var bubble_pos := actor_center + Vector2(-9 * p, _cell_dimensions().y * 0.5 - 3 * world_pixel - 32 * p)
|
||||
var bubble_size := Vector2(18 * p, 13 * p)
|
||||
var rect := Rect2(bubble_pos.round(), bubble_size.round())
|
||||
draw_rect(rect, Color("ffffff", 0.94))
|
||||
draw_rect(rect, Color("303c40", 0.85), false, 1.0)
|
||||
var tri_p1 := Vector2(rect.get_center().x - 2.5 * p, rect.end.y)
|
||||
var tri_p2 := Vector2(rect.get_center().x + 2.5 * p, rect.end.y)
|
||||
var tri_p3 := Vector2(rect.get_center().x, rect.end.y + 3.5 * p)
|
||||
draw_colored_polygon(PackedVector2Array([tri_p1, tri_p2, tri_p3]), Color("ffffff", 0.94))
|
||||
draw_line(tri_p1, tri_p3, Color("303c40", 0.85), 1.0)
|
||||
draw_line(tri_p2, tri_p3, Color("303c40", 0.85), 1.0)
|
||||
|
||||
var font := ThemeDB.fallback_font
|
||||
var font_size := clampi(int(9 * p), 9, 14)
|
||||
match bubble_type:
|
||||
OperationFeedbackScript.BUBBLE_ZZZ:
|
||||
draw_string(font, rect.position + Vector2(2 * p, rect.size.y - 3 * p), "Zzz", HORIZONTAL_ALIGNMENT_CENTER, rect.size.x - 4 * p, font_size, Color("2d68c4"))
|
||||
OperationFeedbackScript.BUBBLE_ANGER:
|
||||
draw_string(font, rect.position + Vector2(2 * p, rect.size.y - 3 * p), "💢", HORIZONTAL_ALIGNMENT_CENTER, rect.size.x - 4 * p, font_size, Color("d63031"))
|
||||
OperationFeedbackScript.BUBBLE_HEART:
|
||||
draw_string(font, rect.position + Vector2(2 * p, rect.size.y - 3 * p), "♥", HORIZONTAL_ALIGNMENT_CENTER, rect.size.x - 4 * p, font_size, Color("e84393"))
|
||||
|
||||
|
||||
func _staff_relationship(staff_id: StringName) -> Variant:
|
||||
var service: Variant = _service(&"relationship_service")
|
||||
if service == null or not service.has_method(&"get_relationships"):
|
||||
return null
|
||||
for rel_val: Variant in service.call(&"get_relationships"):
|
||||
if rel_val is RelationshipState:
|
||||
if rel_val.actor_a_id == staff_id or rel_val.actor_b_id == staff_id:
|
||||
return rel_val
|
||||
return null
|
||||
|
||||
|
||||
func _guest_relationship(guest_id: StringName) -> Variant:
|
||||
var service: Variant = _service(&"relationship_service")
|
||||
if service == null or not service.has_method(&"get_relationships"):
|
||||
return null
|
||||
for rel_val: Variant in service.call(&"get_relationships"):
|
||||
if rel_val is RelationshipState:
|
||||
if rel_val.actor_a_id == guest_id or rel_val.actor_b_id == guest_id:
|
||||
return rel_val
|
||||
return null
|
||||
|
||||
|
||||
func _draw_excavation_jobs() -> void:
|
||||
var excavation: Variant = _service(&"excavation_service")
|
||||
if excavation == null or not excavation.has_method(&"get_jobs"):
|
||||
@@ -1594,6 +1661,8 @@ func _resting_staff_position(staff_id: StringName, fallback: Vector2) -> Vector2
|
||||
|
||||
|
||||
func _can_drop_data(_position: Vector2, data: Variant) -> bool:
|
||||
if modal_input_blocked:
|
||||
return false
|
||||
return data is Dictionary and data.get(&"kind", &"") == &"staff_assignment" and data.get(&"staff_id", &"") is StringName
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user