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:
Takeshi
2026-10-11 15:58:00 +09:00
parent 39f6068461
commit b84bade045
235 changed files with 495831 additions and 312 deletions
+27 -4
View File
@@ -86,6 +86,7 @@ var selected_kitchen_id: StringName = &""
var selected_tab: StringName = &"purchase"
var _runtime: Variant
var _audio_submitter := Callable()
var _style_button: Callable
var _report: Dictionary = {}
var _quote: Dictionary = {}
@@ -282,7 +283,11 @@ func _refresh_purchase() -> void:
if catalog.is_empty() or warehouse.is_empty():
purchase_quote_label.text = "受入倉庫と品目を選んでください。"
return
purchase_quote_label.text = "単価 %dG / %s\n合計 %dG · 購入後 %dG" % [catalog.get(&"unit_price", 0), catalog.get(&"unit_name", "個"), _quote.get(&"total_cost", 0), _quote.get(&"cash_after", _report.get(&"cash", 0))]
if _quote.has(&"purchase_quote"):
var frozen: Dictionary = _quote[&"purchase_quote"]
purchase_quote_label.text = "基準単価 %dG / %s\n基準合計 %dG · ミミの仕入れ20%%割引\n合計 %dG · 購入後 %dG" % [frozen[&"base_unit_price"], catalog.get(&"unit_name", "個"), frozen[&"base_total"], frozen[&"effective_total"], _quote.get(&"cash_after", _report.get(&"cash", 0))]
else:
purchase_quote_label.text = "単価 %dG / %s\n合計 %dG · 購入後 %dG" % [catalog.get(&"unit_price", 0), catalog.get(&"unit_name", "個"), _quote.get(&"total_cost", 0), _quote.get(&"cash_after", _report.get(&"cash", 0))]
if not bool(_quote.get(&"ok", false)):
purchase_quote_label.text += "\n" + _reason_text(_quote.get(&"reason", &"UNAVAILABLE"))
@@ -323,7 +328,11 @@ func _refresh_auto_purchase(warehouse: Dictionary) -> void:
if int(row.get(&"deficit", 0)) > 0 and not quote.is_empty():
auto_purchase_status.text += "\n不足 %d · 今の購入見積 %dG" % [row[&"deficit"], quote.get(&"total_cost", 0)]
if row.get(&"last_result", &"") == &"PURCHASED":
auto_purchase_status.text += "\n前回入庫 %d個 · 代金 %dG(単価 %dG)" % [row[&"last_quantity"], row[&"last_total_cost"], row[&"last_unit_price"]]
if row.has(&"last_quote"):
var frozen: Dictionary = row[&"last_quote"]
auto_purchase_status.text += "\n前回入庫 %d個 · 基準合計 %dG · ミミの仕入れ20%%割引 · 代金 %dG" % [row[&"last_quantity"], frozen[&"base_total"], frozen[&"effective_total"]]
else:
auto_purchase_status.text += "\n前回入庫 %d個 · 代金 %dG(単価 %dG)" % [row[&"last_quantity"], row[&"last_total_cost"], row[&"last_unit_price"]]
if _auto_purchase_drafts.has(_auto_purchase_key()):
auto_purchase_status.text += "\n未保存の変更です。設定を保存すると反映します。"
auto_purchase_status.text += "\n在庫が減ると目標まで不足分を購入します。料金は手動購入と同じです。"
@@ -430,7 +439,7 @@ func _submit(type: StringName, payload: Dictionary) -> void:
var context := {&"type": type, &"payload": payload.duplicate(true)}
var key := StringName("%s%08d" % [KEY_PREFIX, _sequence])
_sequence += 1
var result: Variant = _runtime.call(&"submit_command", type, payload, &"player", StringName(payload.get(&"facility_id", payload.get(&"warehouse_id", &""))), 75, -1, key)
var result: Variant = _submit_ui_command(type, payload, &"player", StringName(payload.get(&"facility_id", payload.get(&"warehouse_id", &""))), 75, -1, key)
if result == null or not result.has_method(&"is_success"):
_set_result("操作に応答がありません。", false)
return
@@ -643,9 +652,9 @@ func _button(value: String, node_name: String, callback: Callable) -> Button:
button.text = value
button.custom_minimum_size.y = 30
button.add_theme_font_size_override("font_size", 13)
button.pressed.connect(callback)
if _style_button.is_valid():
_style_button.call(button)
button.pressed.connect(callback)
return button
@@ -670,3 +679,17 @@ func _spin(node_name: String, minimum: int, value: int) -> SpinBox:
spin.size_flags_horizontal = Control.SIZE_EXPAND_FILL
spin.custom_minimum_size.y = 30
return spin
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)