Save The Tree implementation and acceptance work in progress
Preserve production code, regression fixtures, audio assets and provenance, test infrastructure, and the original-completion plan and progress ledger. Final game and Windows acceptance remain pending; excluded local artifacts and native experiments are not part of this snapshot.
This commit is contained in:
@@ -37,6 +37,7 @@ var retreat_button: Button
|
||||
var confirmation: ConfirmationDialog
|
||||
|
||||
var _runtime: Variant
|
||||
var _audio_submitter := Callable()
|
||||
var _style_button: Callable
|
||||
var _report: Dictionary = {}
|
||||
var _raid_labels: Dictionary = {}
|
||||
@@ -148,16 +149,23 @@ func _build_interface() -> void:
|
||||
_style_button.call(button)
|
||||
|
||||
|
||||
func select_raid(raid_id: StringName) -> void:
|
||||
## Reset optional saved presentation without choosing a live raid implicitly.
|
||||
func clear_selection() -> void:
|
||||
_policy_drafts.clear()
|
||||
if result_label != null: result_label.text = ""
|
||||
select_raid(&"", false)
|
||||
|
||||
|
||||
func select_raid(raid_id: StringName, select_first_if_empty: bool = true) -> void:
|
||||
selected_raid_id = raid_id
|
||||
selected_staff_id = &""
|
||||
selected_objective_id = &""
|
||||
_confirmation_context.clear()
|
||||
if confirmation != null: confirmation.hide()
|
||||
refresh()
|
||||
refresh(select_first_if_empty)
|
||||
|
||||
|
||||
func refresh() -> void:
|
||||
func refresh(select_first_if_empty: bool = true) -> void:
|
||||
if not _built:
|
||||
return
|
||||
var queue: Variant = _runtime.get(&"command_queue") if _runtime != null else null
|
||||
@@ -170,11 +178,19 @@ func refresh() -> void:
|
||||
option.get_popup().hide()
|
||||
_synchronize_commands(queue)
|
||||
_report = _runtime.call(&"get_defense_management_report", selected_raid_id) if _runtime != null and _runtime.has_method(&"get_defense_management_report") else {}
|
||||
if selected_raid_id.is_empty() and not select_first_if_empty:
|
||||
# Runtime supplies its normal first-raid report. This inspector alone
|
||||
# presents no selection; it never changes the raid or queued commands.
|
||||
_report[&"selected"] = {}
|
||||
_report[&"defenders"] = []
|
||||
_report[&"assets"] = []
|
||||
_report[&"objectives"] = []
|
||||
_report[&"can_control"] = false
|
||||
_raid_labels = _build_raid_labels(_report.get(&"raids", []))
|
||||
selected_raid_id = StringName((_report.get(&"selected", {}) as Dictionary).get(&"raid_id", &""))
|
||||
selected_raid_id = _fill_option(raid_option, _report.get(&"raids", []), &"raid_id", selected_raid_id)
|
||||
selected_staff_id = _fill_option(staff_option, _report.get(&"staff", []), &"staff_id", selected_staff_id)
|
||||
selected_objective_id = _fill_option(objective_option, _report.get(&"objectives", []), &"facility_id", selected_objective_id)
|
||||
selected_raid_id = _fill_option(raid_option, _report.get(&"raids", []), &"raid_id", selected_raid_id, select_first_if_empty)
|
||||
selected_staff_id = _fill_option(staff_option, _report.get(&"staff", []), &"staff_id", selected_staff_id, select_first_if_empty)
|
||||
selected_objective_id = _fill_option(objective_option, _report.get(&"objectives", []), &"facility_id", selected_objective_id, select_first_if_empty)
|
||||
var row: Dictionary = _report.get(&"selected", {})
|
||||
var draft: Dictionary = _policy_drafts.get(selected_raid_id, row)
|
||||
var policy := StringName(draft.get(&"retreat_policy", &"HOLD"))
|
||||
@@ -182,7 +198,7 @@ func refresh() -> void:
|
||||
policy_option.select(maxi(POLICIES.find(policy), 0))
|
||||
threshold_spin.set_value_no_signal(int(draft.get(&"defender_retreat_threshold", 0)))
|
||||
_refreshing = false
|
||||
summary_label.text = "進行中・保存済みの防衛戦はありません。" if row.is_empty() else "%s · %s\n襲撃 %d / %d · 攻防 %d回\n%s" % [String(_raid_labels.get(selected_raid_id, _raid_base_label(row))), STATE_NAMES[int(row[&"state"])], int(row[&"current_wave"]), int(row[&"wave_count"]), int(row[&"current_round"]), "防衛対象が未登録です。" if not (row[&"missing_objectives"] as Array).is_empty() else "防衛対象は登録済みです。"]
|
||||
summary_label.text = ("進行中・保存済みの防衛戦はありません。" if (_report.get(&"raids", []) as Array).is_empty() else "防衛戦を選択してください。") if row.is_empty() else "%s · %s\n襲撃 %d / %d · 攻防 %d回\n%s" % [String(_raid_labels.get(selected_raid_id, _raid_base_label(row))), STATE_NAMES[int(row[&"state"])], int(row[&"current_wave"]), int(row[&"wave_count"]), int(row[&"current_round"]), "防衛対象が未登録です。" if not (row[&"missing_objectives"] as Array).is_empty() else "防衛対象は登録済みです。"]
|
||||
var names: Array[String] = []
|
||||
for defender: Dictionary in _report.get(&"defenders", []):
|
||||
var employee := _row(&"staff", &"staff_id", defender[&"actor_id"])
|
||||
@@ -282,7 +298,7 @@ func _submit(command_type: StringName, patch: Dictionary = {}) -> void:
|
||||
payload[&"raid_id"] = selected_raid_id
|
||||
var key := StringName("%s%08d" % [KEY_PREFIX, _sequence])
|
||||
_sequence += 1
|
||||
var result: Variant = _runtime.call(&"submit_command", command_type, payload, &"player", selected_raid_id, 75, -1, key)
|
||||
var result: Variant = _submit_ui_command(command_type, payload, &"player", selected_raid_id, 75, -1, key)
|
||||
if result == null or not result.has_method(&"is_success"):
|
||||
_set_result("防衛の指示に応答がありません。", false)
|
||||
return
|
||||
@@ -338,14 +354,14 @@ func _row(key: StringName, id_key: StringName, entity_id: StringName) -> Diction
|
||||
return {}
|
||||
|
||||
|
||||
func _fill_option(option: OptionButton, rows: Array, id_key: StringName, selected: StringName) -> StringName:
|
||||
func _fill_option(option: OptionButton, rows: Array, id_key: StringName, selected: StringName, select_first_if_empty: bool = true) -> StringName:
|
||||
if option.get_popup().visible: return selected
|
||||
option.clear()
|
||||
if rows.is_empty():
|
||||
option.add_item("対象なし")
|
||||
option.set_item_metadata(0, &"")
|
||||
return &""
|
||||
var index := 0
|
||||
var index := -1 if selected.is_empty() and not select_first_if_empty else 0
|
||||
for row: Dictionary in rows:
|
||||
var id := StringName(row[id_key])
|
||||
var display_name := String(_raid_labels.get(id, _raid_base_label(row))) if id_key == &"raid_id" else String(row.get(&"display_name", id))
|
||||
@@ -354,7 +370,7 @@ func _fill_option(option: OptionButton, rows: Array, id_key: StringName, selecte
|
||||
option.set_item_metadata(option.item_count - 1, id)
|
||||
if id == selected: index = option.item_count - 1
|
||||
option.select(index)
|
||||
return StringName(option.get_item_metadata(index))
|
||||
return &"" if index < 0 else StringName(option.get_item_metadata(index))
|
||||
|
||||
|
||||
## Cause labels are display-only. Never infer cause from an internal ID or
|
||||
@@ -416,3 +432,17 @@ func _button(text_value: String, node_name: String, callback: Callable) -> Butto
|
||||
button.pressed.connect(callback)
|
||||
if _style_button.is_valid(): _style_button.call(button)
|
||||
return button
|
||||
|
||||
|
||||
func configure_audio(submitter: Callable) -> void:
|
||||
_audio_submitter = submitter
|
||||
|
||||
|
||||
func _submit_ui_command(
|
||||
command_type: StringName, payload: Dictionary = {}, actor_id: StringName = &"player",
|
||||
target_id: StringName = &"", priority := 0, execute_at := -1,
|
||||
idempotency_key: StringName = &""
|
||||
) -> Variant:
|
||||
if _audio_submitter.is_valid():
|
||||
return _audio_submitter.call(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)
|
||||
|
||||
Reference in New Issue
Block a user