Files
TheTree_mock/scripts/ui/guest_observation_panel.gd
T

160 lines
7.8 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
class_name GuestObservationPanel
extends VBoxContainer
const NEED_NAMES := {&"hunger": "食欲", &"sleep": "睡眠欲", &"leisure": "娯楽欲", &"intimacy": "性欲", &"hygiene": "衛生", &"medical": "救護"}
const STATES := ["到着", "入館", "行先を検討", "移動中", "待機中", "利用中", "退館中", "退館", "再検討", "待機終了", "中断", "避難中", "気絶"]
const REJECTION_NAMES := {
"MISSING_INPUTS": "食材・資材不足", "FACILITY_CLOSED": "閉店・稼働停止", "UNREACHABLE": "経路なし",
"CANNOT_PAY": "利用可能な資金が不足", "ETA_AFTER_EXIT": "出発予定に間に合わない",
"WAIT_HARD_LIMIT": "待ち時間が長すぎる", "QUALITY_TOO_LOW": "希望する品質に届かない",
"RISK_REFUSED": "危険度が高い", "ILLEGALITY_REFUSED": "違法性を敬遠",
"MISSING_PERMISSION": "利用資格なし", "FORBIDDEN_GUEST_TAG": "利用対象外",
"GUEST_TYPE_NOT_ALLOWED": "利用対象外", "ADULT_NOT_VERIFIED": "年齢条件を満たさない",
"CONTENT_FILTERED": "利用設定の対象外", "INVALID_NEED_MAP": "施設設定を確認できない",
}
var guests: OptionButton
var details: Label
var decision_label: Label
var priority_label: Label
var bars: Dictionary = {}
var selected_guest_id: StringName = &""
var _runtime: Variant
var _ids: Array[StringName] = []
var _elapsed := 0.0
func configure(runtime: Variant) -> void:
_runtime = runtime
func _ready() -> void:
name = "GuestObservationPanel"
custom_minimum_size = Vector2(420, 340)
add_theme_constant_override("separation", 8)
guests = OptionButton.new()
guests.item_selected.connect(func(index: int) -> void:
selected_guest_id = StringName(guests.get_item_metadata(index))
refresh())
add_child(guests)
details = _label()
for need: StringName in GuestService.PRIMARY_NEEDS:
var row := HBoxContainer.new()
var title := Label.new()
title.text = NEED_NAMES[need]
title.custom_minimum_size.x = 85
row.add_child(title)
var bar := ProgressBar.new()
bar.max_value = 100
bar.size_flags_horizontal = Control.SIZE_EXPAND_FILL
row.add_child(bar)
bars[need] = bar
add_child(row)
priority_label = _label()
decision_label = _label()
_label().text = "欲求が高いほど、そのサービスを優先します。\n利用できる施設の中で欲求を優先し、同じ優先度なら距離・行列・料金などで比較します。\n表示は現在の欲求です。移動・利用の途中で目的地を勝手に変更しません。"
refresh()
func _label() -> Label:
var label := Label.new()
label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
add_child(label)
return label
func _process(delta: float) -> void:
if not is_visible_in_tree() or not get_window().visible:
return
_elapsed += delta
if _elapsed >= 0.5:
_elapsed = 0
refresh()
## Clear optional saved presentation while rebuilding the actual loaded roster.
## Opening or polling the visible observer retains the normal first-row choice.
func clear_selection() -> void:
selected_guest_id = &""
_elapsed = 0.0
refresh(false)
func refresh(select_first_if_empty: bool = true) -> void:
if guests == null or _runtime == null:
return
var service: Variant = _runtime.get(&"guest_service")
if service == null:
return
var next: Array[StringName] = []
for guest: GuestRuntime in service.get_guests(): next.append(guest.guest_id)
if next != _ids:
_ids = next
guests.clear()
for id: StringName in _ids:
guests.add_item(String(id))
guests.set_item_metadata(guests.item_count-1, id)
if not _ids.has(selected_guest_id) and (not selected_guest_id.is_empty() or select_first_if_empty):
selected_guest_id = _ids[0] if not _ids.is_empty() else &""
guests.disabled = _ids.is_empty()
if _ids.has(selected_guest_id):
guests.select(_ids.find(selected_guest_id))
else:
guests.select(-1)
var report: Dictionary = service.get_observation(selected_guest_id)
for need: StringName in bars:
bars[need].value = report.get(&"needs", {}).get(need, 0)
if report.is_empty():
details.text = "現在、来客はいません。" if _ids.is_empty() else "来客を選択してください。"
priority_label.text = ""
decision_label.text = ""
return
var target := String(report[&"target_facility_id"])
var facility: Variant = _runtime.facility_service.get_instance(StringName(target))
if facility != null:
var definition: Variant = _runtime.definition_registry.get_facility(facility.definition_id)
if definition != null: target = definition.display_name
if target.is_empty(): target = "未選択"
details.text = "%s · 所持金 %dG · 忍耐 %d\n目的地: %s" % [STATES[int(report[&"state"])], report[&"wallet_gold"], report[&"patience"], target]
var names: PackedStringArray = []
for need: StringName in report[&"primary_needs"]: names.append(NEED_NAMES[need])
priority_label.text = "現在最も高い欲求: " + ("なし" if names.is_empty() else "・".join(names))
decision_label.text = _decision_text(_runtime.get_guest_decision_record(selected_guest_id))
func _decision_text(record: Dictionary) -> String:
if record.is_empty():
return "直近の行先判断:記録なし(判断前、旧セーブ、または履歴の保存範囲外)"
var minute := int(record.get(&"absolute_minute", 0))
var title := "直近の行先判断 · %d日目 %02d:%02d" % [minute / 1440 + 1, (minute % 1440) / 60, minute % 60]
if StringName(record.get(&"action", &"")) == &"LEAVE":
return title + "\n条件を満たして選べる施設がなく、退館を選びました。" + _rejection_text(record)
var id := StringName(record.get(&"facility_id", &""))
var destination := String(id)
var facility: Variant = _runtime.facility_service.get_instance(id)
if facility != null:
var definition: Variant = _runtime.definition_registry.get_facility(facility.definition_id)
if definition != null: destination = definition.display_name
var needs: PackedStringArray = []
for need: StringName in record.get(&"primary_needs", []):
needs.append("%s %d" % [NEED_NAMES.get(need, String(need)), int(record.get(&"needs", {}).get(need, 0))])
return title + "\n選択:%s / 判断時の最大欲求:%s\n移動 %d分・待ち %d分・見込料金 %dG\n選択候補の欲求優先度 %d・比較評価 %d(判断時点)" % [destination, "・".join(needs) if not needs.is_empty() else "なし", int(record.get(&"travel_minutes", 0)), int(record.get(&"wait_minutes", 0)), int(record.get(&"estimated_total_gold", 0)), int(record.get(&"need_priority", 0)), int(record.get(&"utility", 0))] + _rejection_text(record)
func _rejection_text(record: Dictionary) -> String:
if not record.has(&"rejection_counts"):
return "\n候補の除外理由:この記録にはありません。"
var counts: Dictionary = record[&"rejection_counts"]
if counts.is_empty():
return "\n今回検討した候補には、利用条件による除外はありません。"
var reasons: PackedStringArray = []
var keys := counts.keys()
keys.sort()
for reason: String in keys:
reasons.append("%s %d施設" % [REJECTION_NAMES.get(reason, "条件を満たさない"), int(counts[reason])])
var text := "\n判断時の除外理由:" + "・".join(reasons)
for row: Dictionary in record.get(&"rejection_examples", []):
var id := StringName(row.get(&"facility_id", &""))
var facility: Variant = _runtime.facility_service.get_instance(id)
var label := String(id)
if facility != null:
var definition: Variant = _runtime.definition_registry.get_facility(facility.definition_id)
if definition != null: label = "%s(%s)" % [definition.display_name, id]
text += "\n・%s:%s" % [label, REJECTION_NAMES.get(String(row.get(&"reason", "")), "条件を満たさない")]
var omitted: int = int(record.get(&"rejection_count", 0)) - record.get(&"rejection_examples", []).size()
if omitted > 0: text += "\nほか%d施設(件数は上記に含みます)" % omitted
return text