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.
449 lines
21 KiB
GDScript
449 lines
21 KiB
GDScript
class_name DefenseManagementPanel
|
||
extends VBoxContainer
|
||
|
||
const LayerSkin := preload("res://scripts/ui/layer_interface_skin.gd")
|
||
|
||
## Player controls for an incident that already exists. Threat creation,
|
||
## combat balancing, skill authoring and automatic combat belong to Runtime.
|
||
signal status_requested(message: String, success: bool)
|
||
|
||
const KEY_PREFIX := "ui.defense."
|
||
const COMMANDS := [&"register_defense_combatant", &"register_defense_asset", &"start_defense_raid", &"set_defense_retreat_policy", &"begin_next_defense_wave", &"retreat_defenders"]
|
||
const STATE_NAMES := ["準備中", "戦闘中", "次の襲撃待ち", "撃退", "突破された", "撤退", "準備終了"]
|
||
const SOURCE_NAMES := {"THIEF": "盗賊の襲撃", "BANKRUPTCY_RIOT": "客の暴動", "BANK_DELINQUENCY": "銀行による取り立て", "AUTHORITY_ESCALATION": "監査による摘発"}
|
||
const POLICIES := [&"HOLD", &"MANUAL", &"THRESHOLD", &"PROTECT_AND_RETREAT"]
|
||
const POLICY_LABELS := ["持ち場を守る", "自分で撤退を指示", "体力が減ったら撤退", "体力・施設の損傷で撤退"]
|
||
|
||
var selected_raid_id: StringName = &""
|
||
var selected_staff_id: StringName = &""
|
||
var selected_objective_id: StringName = &""
|
||
var raid_option: OptionButton
|
||
var staff_option: OptionButton
|
||
var objective_option: OptionButton
|
||
var policy_option: OptionButton
|
||
var threshold_spin: SpinBox
|
||
var summary_label: Label
|
||
var defenders_label: Label
|
||
var assets_label: Label
|
||
var staff_label: Label
|
||
var objective_label: Label
|
||
var result_label: Label
|
||
var register_staff_button: Button
|
||
var register_objective_button: Button
|
||
var start_button: Button
|
||
var policy_button: Button
|
||
var next_wave_button: Button
|
||
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 = {}
|
||
var _pending: Dictionary = {}
|
||
var _queue_instance_id := 0
|
||
var _sequence := 1
|
||
var _built := false
|
||
var _confirmation_context: Dictionary = {}
|
||
var _policy_drafts: Dictionary = {}
|
||
var _refreshing := false
|
||
|
||
|
||
func configure(runtime: Variant, style_button := Callable()) -> void:
|
||
_runtime = runtime
|
||
_style_button = style_button
|
||
|
||
|
||
func _ready() -> void:
|
||
name = "DefenseManagementPanel"
|
||
size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
add_theme_constant_override("separation", 6)
|
||
_build_interface()
|
||
_built = true
|
||
if _runtime != null:
|
||
if _runtime.has_signal(&"state_updated"):
|
||
_runtime.connect(&"state_updated", _on_snapshot_published)
|
||
elif _runtime.has_signal(&"snapshot_published"):
|
||
_runtime.connect(&"snapshot_published", _on_snapshot_published)
|
||
if _runtime.has_signal(&"command_completed"):
|
||
_runtime.connect(&"command_completed", _on_command_completed)
|
||
refresh()
|
||
|
||
|
||
func _build_interface() -> void:
|
||
raid_option = _option("DefenseRaidOption")
|
||
raid_option.fit_to_longest_item = false
|
||
raid_option.clip_text = true
|
||
raid_option.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||
raid_option.item_selected.connect(func(index: int) -> void: select_raid(StringName(raid_option.get_item_metadata(index))))
|
||
add_child(raid_option)
|
||
summary_label = _label("", "DefenseSummary")
|
||
add_child(summary_label)
|
||
defenders_label = _label("", "DefenseParticipants")
|
||
add_child(defenders_label)
|
||
assets_label = _label("", "DefenseAssets")
|
||
add_child(assets_label)
|
||
add_child(HSeparator.new())
|
||
add_child(_label("参加する従業員"))
|
||
staff_option = _option("DefenseStaffOption")
|
||
staff_option.item_selected.connect(func(index: int) -> void:
|
||
selected_staff_id = StringName(staff_option.get_item_metadata(index))
|
||
_refresh_controls())
|
||
add_child(staff_option)
|
||
staff_label = _label("", "DefenseStaffStatus")
|
||
add_child(staff_label)
|
||
register_staff_button = _button("防衛に参加", "DefenseRegisterStaff", _register_staff)
|
||
add_child(register_staff_button)
|
||
add_child(_label("防衛する施設"))
|
||
objective_option = _option("DefenseObjectiveOption")
|
||
objective_option.item_selected.connect(func(index: int) -> void:
|
||
selected_objective_id = StringName(objective_option.get_item_metadata(index))
|
||
_refresh_controls())
|
||
add_child(objective_option)
|
||
objective_label = _label("", "DefenseObjectiveStatus")
|
||
add_child(objective_label)
|
||
register_objective_button = _button("防衛対象に登録", "DefenseRegisterObjective", _register_objective)
|
||
add_child(register_objective_button)
|
||
add_child(HSeparator.new())
|
||
add_child(_label("撤退の方針"))
|
||
policy_option = _option("DefenseRetreatPolicy")
|
||
for i: int in POLICIES.size():
|
||
policy_option.add_item(POLICY_LABELS[i])
|
||
policy_option.set_item_metadata(i, POLICIES[i])
|
||
policy_option.item_selected.connect(func(_index: int) -> void: _store_policy_draft())
|
||
add_child(policy_option)
|
||
var threshold_row := HBoxContainer.new()
|
||
threshold_row.add_child(_label("撤退する残量(%)"))
|
||
threshold_spin = SpinBox.new()
|
||
threshold_spin.name = "DefenseRetreatThreshold"
|
||
threshold_spin.min_value = 0
|
||
threshold_spin.max_value = 100
|
||
threshold_spin.step = 1
|
||
threshold_spin.value_changed.connect(func(_value: float) -> void: _store_policy_draft())
|
||
threshold_row.add_child(threshold_spin)
|
||
add_child(threshold_row)
|
||
policy_button = _button("撤退方針を適用", "DefenseApplyPolicy", _apply_policy)
|
||
add_child(policy_button)
|
||
var actions := HBoxContainer.new()
|
||
start_button = _button("防衛を開始", "DefenseStart", func() -> void: _submit(&"start_defense_raid"))
|
||
next_wave_button = _button("次の襲撃に対応", "DefenseNextWave", func() -> void: _submit(&"begin_next_defense_wave"))
|
||
retreat_button = _button("撤退する", "DefenseRetreat", _request_retreat_confirmation)
|
||
for button: Button in [start_button, next_wave_button, retreat_button]:
|
||
button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
actions.add_child(button)
|
||
add_child(actions)
|
||
result_label = _label("", "DefenseResult")
|
||
add_child(result_label)
|
||
confirmation = ConfirmationDialog.new()
|
||
confirmation.name = "DefenseConfirmation"
|
||
confirmation.ok_button_text = "実行する"
|
||
confirmation.cancel_button_text = "戻る"
|
||
confirmation.confirmed.connect(_confirm_operation)
|
||
confirmation.canceled.connect(func() -> void: _confirmation_context.clear())
|
||
add_child(confirmation)
|
||
confirmation.get_label().add_theme_color_override("font_color", Color("55371e"))
|
||
LayerSkin.register_text(confirmation.get_label(), LayerSkin.active_layer(self))
|
||
if _style_button.is_valid():
|
||
for button: Button in [confirmation.get_ok_button(), confirmation.get_cancel_button()]:
|
||
_style_button.call(button)
|
||
|
||
|
||
## 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(select_first_if_empty)
|
||
|
||
|
||
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
|
||
if queue != null and queue.get_instance_id() != _queue_instance_id:
|
||
_queue_instance_id = queue.get_instance_id()
|
||
_policy_drafts.clear()
|
||
_confirmation_context.clear()
|
||
confirmation.hide()
|
||
for option: OptionButton in [raid_option, staff_option, objective_option, policy_option]:
|
||
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, 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"))
|
||
_refreshing = true
|
||
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 (_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"])
|
||
names.append("%s 体力%d/%d・戦闘%d" % [employee.get(&"display_name", defender[&"actor_id"]), defender[&"health"], defender[&"max_health"], defender[&"combat"]])
|
||
defenders_label.text = "参加者:" + ("、".join(names) if not names.is_empty() else "未登録")
|
||
names.clear()
|
||
for asset: Dictionary in _report.get(&"assets", []):
|
||
var facility := _row(&"objectives", &"facility_id", asset[&"asset_id"])
|
||
names.append("%s 耐久%d/%d" % [facility.get(&"display_name", asset[&"asset_id"]), asset[&"integrity"], asset[&"max_integrity"]])
|
||
assets_label.text = "対象:" + ("、".join(names) if not names.is_empty() else "未登録")
|
||
_refresh_controls()
|
||
|
||
|
||
func _refresh_controls() -> void:
|
||
var row: Dictionary = _report.get(&"selected", {})
|
||
var state := int(row.get(&"state", -1))
|
||
var pending := _has_pending(selected_raid_id)
|
||
var blocked := row.is_empty() or pending or not bool(_report.get(&"can_control", false))
|
||
var staff := _row(&"staff", &"staff_id", selected_staff_id)
|
||
var objective := _row(&"objectives", &"facility_id", selected_objective_id)
|
||
staff_label.text = "従業員がいません。" if staff.is_empty() else "体力%d/100・戦闘%d\n%s" % [staff[&"health"], staff[&"combat"], "参加できます。参加中は配属を変更できません。" if staff[&"can_register"] else staff[&"reason"]]
|
||
objective_label.text = "防衛対象がありません。" if objective.is_empty() else "耐久%d/10000\n%s" % [objective[&"durability"], "登録できます。" if objective[&"can_register"] else objective[&"reason"]]
|
||
register_staff_button.disabled = blocked or staff.is_empty() or not bool(staff.get(&"can_register", false))
|
||
register_objective_button.disabled = blocked or objective.is_empty() or not bool(objective.get(&"can_register", false))
|
||
start_button.visible = state == DefenseService.RaidState.PREPARING
|
||
start_button.disabled = blocked or not (row.get(&"missing_objectives", []) as Array).is_empty()
|
||
next_wave_button.visible = state == DefenseService.RaidState.BETWEEN_WAVES
|
||
next_wave_button.disabled = blocked
|
||
retreat_button.visible = state in [DefenseService.RaidState.ACTIVE, DefenseService.RaidState.BETWEEN_WAVES]
|
||
retreat_button.disabled = blocked
|
||
var terminal := state in DefenseService.TERMINAL_STATES or state < 0
|
||
policy_option.disabled = blocked or terminal
|
||
var chosen_policy := StringName(policy_option.get_item_metadata(policy_option.selected))
|
||
threshold_spin.editable = not blocked and not terminal and chosen_policy in [&"THRESHOLD", &"PROTECT_AND_RETREAT"]
|
||
policy_button.disabled = blocked or terminal
|
||
staff_option.disabled = blocked or state != DefenseService.RaidState.PREPARING or staff.is_empty()
|
||
objective_option.disabled = blocked or state != DefenseService.RaidState.PREPARING or objective.is_empty()
|
||
if pending: result_label.text = "指示を受理しました。次の時間更新で反映します。"
|
||
|
||
|
||
func _register_staff() -> void:
|
||
var staff := _row(&"staff", &"staff_id", selected_staff_id)
|
||
if staff.is_empty() or not bool(staff.get(&"can_register", false)): return
|
||
_submit(&"register_defense_combatant", {&"actor_id": selected_staff_id, &"side": &"DEFENDER", &"combat": int(staff[&"combat"])})
|
||
|
||
|
||
func _register_objective() -> void:
|
||
var facility := _row(&"objectives", &"facility_id", selected_objective_id)
|
||
if facility.is_empty() or not bool(facility.get(&"can_register", false)): return
|
||
# Plain objectives have no defense-device bonus. Omit numeric overrides and
|
||
# use the existing Runtime command defaults and current live durability.
|
||
_submit(&"register_defense_asset", {&"asset_id": selected_objective_id, &"kind": &"OBJECTIVE"})
|
||
|
||
|
||
func _apply_policy() -> void:
|
||
_submit(&"set_defense_retreat_policy", {&"policy": policy_option.get_item_metadata(policy_option.selected), &"threshold": int(threshold_spin.value)})
|
||
|
||
|
||
func _store_policy_draft() -> void:
|
||
if _refreshing or selected_raid_id.is_empty() or _has_pending(selected_raid_id): return
|
||
_policy_drafts[selected_raid_id] = {&"retreat_policy": policy_option.get_item_metadata(policy_option.selected), &"defender_retreat_threshold": int(threshold_spin.value)}
|
||
_refresh_controls()
|
||
|
||
|
||
func _request_retreat_confirmation() -> void:
|
||
refresh()
|
||
var row: Dictionary = _report.get(&"selected", {})
|
||
if _has_pending(selected_raid_id) or selected_raid_id.is_empty() or not bool(_report.get(&"can_control", false)) or int(row.get(&"state", -1)) not in [DefenseService.RaidState.ACTIVE, DefenseService.RaidState.BETWEEN_WAVES]: return
|
||
_confirmation_context = {&"raid_id": selected_raid_id, &"queue_instance_id": _queue_instance_id}
|
||
confirmation.title = "撤退の確認"
|
||
confirmation.dialog_text = "撤退すると防衛戦は終了します。参加者の状態と施設の損傷が反映されます。"
|
||
confirmation.popup_centered(Vector2i(430, 170))
|
||
|
||
|
||
func _confirm_operation() -> void:
|
||
var context := _confirmation_context.duplicate()
|
||
_confirmation_context.clear()
|
||
var queue: Variant = _runtime.get(&"command_queue") if _runtime != null else null
|
||
if queue == null or queue.get_instance_id() != int(context.get(&"queue_instance_id", 0)) or context.get(&"raid_id", &"") != selected_raid_id:
|
||
_set_result("対象が変わりました。防衛戦を選び直してください。", false)
|
||
refresh()
|
||
return
|
||
refresh()
|
||
if context.get(&"raid_id", &"") != selected_raid_id:
|
||
_set_result("対象が変わりました。防衛戦を選び直してください。", false)
|
||
return
|
||
var row: Dictionary = _report.get(&"selected", {})
|
||
if _has_pending(selected_raid_id) or not bool(_report.get(&"can_control", false)) or int(row.get(&"state", -1)) not in [DefenseService.RaidState.ACTIVE, DefenseService.RaidState.BETWEEN_WAVES]:
|
||
_set_result("現在の防衛戦では撤退を指示できません。", false)
|
||
return
|
||
_submit(&"retreat_defenders")
|
||
|
||
|
||
func _submit(command_type: StringName, patch: Dictionary = {}) -> void:
|
||
if selected_raid_id.is_empty() or _has_pending(selected_raid_id) or command_type not in COMMANDS: return
|
||
var payload := patch.duplicate(true)
|
||
payload[&"raid_id"] = selected_raid_id
|
||
var key := StringName("%s%08d" % [KEY_PREFIX, _sequence])
|
||
_sequence += 1
|
||
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
|
||
var queue: Variant = _runtime.get(&"command_queue")
|
||
var queued := false
|
||
for command: Dictionary in queue.create_snapshot().get("pending", []):
|
||
queued = queued or int(command.get("command_id", 0)) == int(result.command_id)
|
||
if result.is_success() and queued:
|
||
_pending[int(result.command_id)] = payload
|
||
else:
|
||
_show_result(result, payload)
|
||
refresh()
|
||
|
||
|
||
func _show_result(result: Variant, payload: Dictionary) -> void:
|
||
if result.is_success() and payload.has(&"policy"):
|
||
_policy_drafts.erase(StringName(payload.get(&"raid_id", &"")))
|
||
_set_result("防衛の指示を反映しました。" if result.is_success() else "指示を反映できませんでした。体調・業務中の状態・対象施設を確認してください。", result.is_success())
|
||
|
||
|
||
func _synchronize_commands(queue: Variant) -> void:
|
||
_pending.clear()
|
||
for metadata: Dictionary in queue.get_accepted_command_metadata():
|
||
var key := String(metadata.get("idempotency_key", ""))
|
||
if key.begins_with(KEY_PREFIX) and key.trim_prefix(KEY_PREFIX).is_valid_int():
|
||
_sequence = maxi(_sequence, int(key.trim_prefix(KEY_PREFIX)) + 1)
|
||
for command: Dictionary in queue.create_snapshot().get("pending", []):
|
||
if StringName(command.get("command_type", &"")) in COMMANDS:
|
||
_pending[int(command.get("command_id", 0))] = command.get("payload", {}).duplicate(true)
|
||
|
||
|
||
func _on_command_completed(result: Variant) -> void:
|
||
if not _pending.has(int(result.command_id)): return
|
||
var payload: Dictionary = _pending[int(result.command_id)]
|
||
_pending.erase(int(result.command_id))
|
||
_show_result(result, payload)
|
||
refresh()
|
||
|
||
|
||
func _on_snapshot_published(_snapshot: Dictionary = {}, _digest: String = "") -> void:
|
||
if is_visible_in_tree(): refresh()
|
||
|
||
|
||
func _has_pending(raid_id: StringName) -> bool:
|
||
for payload: Dictionary in _pending.values():
|
||
if payload.get(&"raid_id", &"") == raid_id: return true
|
||
return false
|
||
|
||
|
||
func _row(key: StringName, id_key: StringName, entity_id: StringName) -> Dictionary:
|
||
for row: Dictionary in _report.get(key, []):
|
||
if row.get(id_key, &"") == entity_id: return row
|
||
return {}
|
||
|
||
|
||
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 := -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))
|
||
option.add_item(display_name)
|
||
if id_key == &"raid_id": option.set_item_tooltip(option.item_count - 1, display_name)
|
||
option.set_item_metadata(option.item_count - 1, id)
|
||
if id == selected: index = option.item_count - 1
|
||
option.select(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
|
||
## source_id, and never use the label as an option's metadata or command target.
|
||
func _build_raid_labels(rows: Array) -> Dictionary:
|
||
var totals: Dictionary = {}
|
||
for row: Dictionary in rows:
|
||
var base := _raid_base_label(row)
|
||
totals[base] = int(totals.get(base, 0)) + 1
|
||
var seen: Dictionary = {}
|
||
var labels: Dictionary = {}
|
||
for row: Dictionary in rows:
|
||
var base := _raid_base_label(row)
|
||
seen[base] = int(seen.get(base, 0)) + 1
|
||
labels[StringName(row[&"raid_id"])] = base + ("(%d)" % int(seen[base]) if int(totals[base]) > 1 else "")
|
||
return labels
|
||
|
||
|
||
func _raid_base_label(row: Dictionary) -> String:
|
||
var cause := String(SOURCE_NAMES.get(String(row.get(&"source_kind", "")), "防衛戦"))
|
||
var raw_minute: Variant = row.get(&"created_at", null)
|
||
if typeof(raw_minute) != TYPE_INT or int(raw_minute) < 0:
|
||
return cause + " · 時刻不明"
|
||
var minute := int(raw_minute)
|
||
return "%s · %d日目 %02d:%02d" % [cause, minute / 1440 + 1, minute % 1440 / 60, minute % 60]
|
||
|
||
|
||
func _set_result(message: String, success: bool) -> void:
|
||
result_label.text = message
|
||
LayerSkin.register_status_text(result_label, success, LayerSkin.active_layer(self))
|
||
status_requested.emit(message, success)
|
||
|
||
|
||
func _label(text_value: String, node_name := "") -> Label:
|
||
var label := Label.new()
|
||
if not node_name.is_empty(): label.name = node_name
|
||
label.text = text_value
|
||
label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||
label.add_theme_font_size_override("font_size", 13)
|
||
label.add_theme_color_override("font_color", Color("55371e"))
|
||
LayerSkin.register_text(label, LayerSkin.active_layer(self))
|
||
return label
|
||
|
||
|
||
func _option(node_name: String) -> OptionButton:
|
||
var option := OptionButton.new()
|
||
option.name = node_name
|
||
option.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
if _style_button.is_valid(): _style_button.call(option)
|
||
return option
|
||
|
||
|
||
func _button(text_value: String, node_name: String, callback: Callable) -> Button:
|
||
var button := Button.new()
|
||
button.name = node_name
|
||
button.text = text_value
|
||
button.custom_minimum_size.y = 30
|
||
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)
|