P24-02: 操作と通知の全受入

This commit is contained in:
Agent Control Runner
2026-10-17 14:07:06 +09:00
parent 9641ed52de
commit bd3a134051
28 changed files with 23898 additions and 187 deletions
+126 -9
View File
@@ -152,6 +152,11 @@ var important_warning_cutin: OperationFeedback.ImportantWarningCutinDialog
var front_button: Button
var back_button: Button
var excavation_button: Button
var demolition_button: Button
var demolition_dialog: ConfirmationDialog
var _demolition_mode := false
var _demolition_target: StringName = &""
var _pending_demolition_commands: Dictionary = {}
var pan_button: Button
var pause_button: Button
var speed_buttons: Dictionary = {}
@@ -812,12 +817,20 @@ func _create_bottom_bar() -> Control:
palette_scroll.custom_minimum_size.y = 62
palette_scroll.visible = false
column.add_child(palette_scroll)
var palette_row := HBoxContainer.new()
palette_row.name = "BuildPaletteRow"
palette_row.add_theme_constant_override("separation", 4)
palette_scroll.add_child(palette_row)
var grid := GridContainer.new()
grid.name = "BuildPalette"
grid.columns = MVP_FACILITY_IDS.size()
grid.add_theme_constant_override("h_separation", 4)
grid.add_theme_constant_override("v_separation", 0)
palette_scroll.add_child(grid)
palette_row.add_child(grid)
demolition_button = _button("撤去", _toggle_demolition_mode, 72)
demolition_button.name = "DemolitionButton"
demolition_button.toggle_mode = true
palette_row.add_child(demolition_button)
build_status_label = Label.new()
build_status_label.name = "BuildStatusLabel"
@@ -1267,6 +1280,7 @@ func _select_build_definition(definition_id: StringName) -> void:
if definition == null:
_set_status("選択した施設定義が見つかりません。", false)
return
_set_demolition_mode(false, false)
_set_excavation_mode(false, false)
_set_pan_mode(false)
_selected_definition_id = definition_id
@@ -1293,6 +1307,9 @@ func _clear_build_selection(announce := true) -> void:
func _clear_active_tool() -> void:
if _demolition_mode:
_set_demolition_mode(false)
return
if world_preview.pan_mode:
_set_pan_mode(false)
_set_status("画面移動を解除しました。施設をクリックすると情報を表示します。", true, true)
@@ -1325,6 +1342,7 @@ func _toggle_pan_mode() -> void:
func _set_pan_mode(enabled: bool) -> void:
if enabled:
_set_demolition_mode(false, false)
_clear_build_selection(false)
_set_excavation_mode(false, false)
_set_build_palette_open(false)
@@ -1335,6 +1353,7 @@ func _set_pan_mode(enabled: bool) -> void:
func _set_excavation_mode(enabled: bool, announce := true) -> void:
if enabled:
_set_demolition_mode(false, false)
_set_pan_mode(false)
_excavation_mode = enabled
if excavation_button != null:
@@ -1380,6 +1399,9 @@ func _rotate_build_preview() -> void:
func _on_map_cell_clicked(cell: Vector3i) -> void:
if _audio_feedback != null:
_audio_feedback.request_click()
if _demolition_mode:
_preview_demolition(cell)
return
if _excavation_mode:
_start_excavation(cell)
return
@@ -2128,7 +2150,19 @@ func _preview_failure_message(report: Dictionary) -> String:
var codes_value: Variant = report.get(&"error_codes", [])
if codes_value is Array and not codes_value.is_empty():
var code := String(codes_value[0])
return "配置不可: %s" % PREVIEW_REASON_NAMES.get(code, code)
if PREVIEW_REASON_NAMES.has(code):
return "配置不可: %s" % PREVIEW_REASON_NAMES[code]
var code_reasons: Variant = report.get(&"reasons", [])
if code_reasons is Array and not code_reasons.is_empty():
var reason: Variant = code_reasons[0]
for candidate: Variant in code_reasons:
if candidate is Dictionary and String(candidate.get(&"code", "")) == code:
reason = candidate
break
if reason is Dictionary:
var message := String(reason.get(&"message", ""))
if not message.is_empty():
return "配置不可: %s" % message
var reasons_value: Variant = report.get(&"reasons", [])
if reasons_value is Array and not reasons_value.is_empty() and reasons_value[0] is Dictionary:
return "配置不可: %s" % String(reasons_value[0].get(&"message", "条件を満たしていません"))
@@ -2358,6 +2392,8 @@ func _apply_loaded_interface(result: Dictionary) -> void:
if _audio_feedback != null:
_audio_feedback.begin_loaded_epoch(_current_audio_foreground())
_pending_build_commands.clear()
_pending_demolition_commands.clear()
_set_demolition_mode(false, false)
_pending_excavation_commands.clear()
_pending_hire_commands.clear()
_pending_elevator_commands.clear()
@@ -2598,6 +2634,10 @@ func _on_snapshot_published(_snapshot: Dictionary = {}, _digest: String = "") ->
func _on_command_completed(result: Variant) -> void:
if result == null:
return
if _pending_demolition_commands.has(int(result.command_id)):
_pending_demolition_commands.erase(int(result.command_id))
_show_demolition_result(result)
return
if _pending_related_commands.has(int(result.command_id)):
_pending_related_commands.erase(int(result.command_id))
_set_status("関連施設の設定を反映しました。" if result.is_success() else result.message, result.is_success())
@@ -2781,17 +2821,17 @@ func _phase_name(phase: GameClock.Phase) -> String:
return "夜"
const RESOURCE_TITLES := {&"gold": "所持金", &"mana": "マナ", &"materials": "資材", &"faith": "信仰"}
func _resource_title(resource_name: StringName) -> String:
return String(RESOURCE_TITLES.get(resource_name, String(resource_name)))
func _on_resource_changed(resource_name: StringName, value: int) -> void:
if not resource_labels.has(resource_name):
return
var titles := {
&"gold": "所持金",
&"mana": "マナ",
&"materials": "資材",
&"faith": "信仰",
}
var label := resource_labels[resource_name] as Label
label.text = "%s %d" % [titles[resource_name], value]
label.text = "%s %d" % [_resource_title(resource_name), value]
if resource_name == &"mana":
label.mouse_filter = Control.MOUSE_FILTER_STOP
label.tooltip_text = "残量0で全館停止。接客・調理・搬入・エレベーターと新規入館が停止します。\n残量と配電が回復すると再開します。破壊・摘発による休止は解除されません。"
@@ -3114,6 +3154,83 @@ func _submit_ui_command(
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 _toggle_demolition_mode() -> void:
_set_demolition_mode(not _demolition_mode)
func _set_demolition_mode(enabled: bool, announce := true) -> void:
if enabled:
_set_pan_mode(false)
_set_excavation_mode(false, false)
_clear_build_selection(false)
_demolition_mode = enabled
_demolition_target = &""
if demolition_dialog != null:
demolition_dialog.hide()
if demolition_button != null:
demolition_button.set_pressed_no_signal(enabled)
if world_preview != null:
world_preview.set_demolition_mode(enabled)
if announce:
_set_status("撤去する施設のマスを選択してください。確認画面で取り消せます。" if enabled else "撤去モードを解除しました。", true, true)
func _preview_demolition(cell: Vector3i) -> void:
# Selection only; Runtime owns eligibility, occupancy and refunds.
_demolition_target = world_preview.facility_at_canvas(world_preview.cell_to_canvas(cell))
world_preview.selected_entity_id = _demolition_target
if demolition_dialog == null:
demolition_dialog = ConfirmationDialog.new()
demolition_dialog.name = "DemolitionDialog"
demolition_dialog.title = "施設の撤去"
demolition_dialog.ok_button_text = "撤去する"
demolition_dialog.cancel_button_text = "戻る"
demolition_dialog.confirmed.connect(_confirm_demolition)
demolition_dialog.canceled.connect(_cancel_demolition)
add_child(demolition_dialog)
_apply_button_style(demolition_dialog.get_ok_button())
_apply_button_style(demolition_dialog.get_cancel_button())
demolition_dialog.dialog_text = "座標 (%d, %d / %d)\n施設: %s\n\n撤去を指示しますか?" % [cell.x, cell.y, cell.z, String(_demolition_target)]
demolition_dialog.popup_centered(Vector2i(420, 210))
func _cancel_demolition() -> void:
_demolition_target = &""
_set_status("撤去を取り消しました。", true, true)
func _confirm_demolition() -> void:
if not _demolition_mode:
return
if _runtime == null or not _runtime.has_method(&"submit_command"):
_set_status("Simulation Runtimeへ接続できません。", false)
return
var target := _demolition_target
_demolition_target = &""
var result: CommandResult = _runtime.submit_command(&"demolish_facility", {&"instance_id": target})
# Paused play returns execution synchronously; running play returns acceptance.
if result.is_success() and _runtime.command_queue.get_processed_result(result.command_id) == null:
_pending_demolition_commands[result.command_id] = target
_set_status("撤去を受け付けました", true)
return
_show_demolition_result(result)
func _show_demolition_result(result: CommandResult) -> void:
if result.is_success():
var refund: Dictionary = result.details.get(&"refund", {})
var keys: Array = refund.keys()
keys.sort()
var amounts: PackedStringArray = []
for key: Variant in keys:
if int(refund[key]) != 0:
amounts.append("%s %d" % [_resource_title(StringName(key)), int(refund[key])])
_set_status("撤去しました(返金: %s)" % "、".join(amounts) if not amounts.is_empty() else "撤去しました(返金なし)", true)
else:
_set_status("撤去できません: " + result.message, false)
_refresh_selected_entity()
world_preview.queue_redraw()
func assign_staff_by_click(facility_id: StringName, staff_id: StringName = &"") -> void:
if staff_management_panel == null:
File diff suppressed because it is too large Load Diff