feat: add npc decision inspector
This commit is contained in:
@@ -5,6 +5,10 @@ const DEBUG_LOGS := false
|
||||
@export var simulation_manager: Node
|
||||
|
||||
@onready var village_stats_label: Label = $VillagePanel/MarginContainer/VillageStatsLabel
|
||||
@onready var npc_inspector_label: Label = $NpcInspectorPanel/MarginContainer/NpcInspectorLabel
|
||||
|
||||
var selected_npc_index := 0
|
||||
var inspector_refresh_accumulator := 0.0
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
@@ -16,9 +20,34 @@ func _ready() -> void:
|
||||
simulation_manager.village_changed.connect(_on_village_changed)
|
||||
else:
|
||||
push_error("VillageUI: SimulationManager has no village_changed signal")
|
||||
if simulation_manager.has_signal("npc_decision_recorded"):
|
||||
simulation_manager.npc_decision_recorded.connect(_on_npc_decision_recorded)
|
||||
else:
|
||||
push_error("VillageUI: SimulationManager has no npc_decision_recorded signal")
|
||||
if simulation_manager.has_signal("state_restored"):
|
||||
simulation_manager.state_restored.connect(_on_state_restored)
|
||||
|
||||
if "village" in simulation_manager:
|
||||
_on_village_changed(simulation_manager.village)
|
||||
_refresh_npc_inspector()
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
inspector_refresh_accumulator += delta
|
||||
if inspector_refresh_accumulator < 0.25:
|
||||
return
|
||||
inspector_refresh_accumulator = 0.0
|
||||
_refresh_npc_inspector()
|
||||
|
||||
|
||||
func _unhandled_key_input(event: InputEvent) -> void:
|
||||
if not event is InputEventKey or not event.pressed or event.echo:
|
||||
return
|
||||
if event.keycode != KEY_TAB or simulation_manager.npcs.is_empty():
|
||||
return
|
||||
selected_npc_index = (selected_npc_index + 1) % simulation_manager.npcs.size()
|
||||
_refresh_npc_inspector()
|
||||
get_viewport().set_input_as_handled()
|
||||
|
||||
|
||||
func _on_village_changed(village: SimVillage) -> void:
|
||||
@@ -47,3 +76,73 @@ func _on_village_changed(village: SimVillage) -> void:
|
||||
village.knowledge_modifier
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
func _on_npc_decision_recorded(_npc: SimNPC, _decision: ActionSelectionResult) -> void:
|
||||
_refresh_npc_inspector()
|
||||
|
||||
|
||||
func _on_state_restored() -> void:
|
||||
selected_npc_index = 0
|
||||
_refresh_npc_inspector()
|
||||
|
||||
|
||||
func _refresh_npc_inspector() -> void:
|
||||
if simulation_manager == null or simulation_manager.npcs.is_empty():
|
||||
npc_inspector_label.text = "No villagers available"
|
||||
return
|
||||
selected_npc_index = clampi(selected_npc_index, 0, simulation_manager.npcs.size() - 1)
|
||||
var npc: SimNPC = simulation_manager.npcs[selected_npc_index]
|
||||
var profession_definition := SimulationDefinitions.get_profession(npc.profession)
|
||||
var profession_name := String(npc.profession)
|
||||
if profession_definition != null:
|
||||
profession_name = profession_definition.display_name
|
||||
var action_name := _get_action_name(npc.current_task)
|
||||
var destination_name := "Resolving"
|
||||
if not npc.target_id.is_empty():
|
||||
destination_name = String(npc.target_id)
|
||||
var decision: ActionSelectionResult = simulation_manager.get_latest_decision(npc.id)
|
||||
var reason_text := "Awaiting the next decision"
|
||||
var score_text := ""
|
||||
if decision != null:
|
||||
reason_text = decision.reason
|
||||
var score_keys: Array = decision.scores.keys()
|
||||
score_keys.sort()
|
||||
var score_rows: Array[String] = []
|
||||
for action_id in score_keys:
|
||||
score_rows.append(
|
||||
"%s %.2f" % [_get_action_name(StringName(action_id)), float(decision.scores[action_id])]
|
||||
)
|
||||
if not score_rows.is_empty():
|
||||
score_text = "\n\nUtility\n" + "\n".join(score_rows)
|
||||
npc_inspector_label.text = (
|
||||
"%s · %s\n"
|
||||
+ "Task: %s (%s)\n"
|
||||
+ "Destination: %s\n"
|
||||
+ "Hunger: %.0f Energy: %.0f\n"
|
||||
+ "Carrying food: %.0f\n\n"
|
||||
+ "Why\n%s%s\n\n"
|
||||
+ "[Tab] Next villager"
|
||||
) % [
|
||||
npc.npc_name,
|
||||
profession_name,
|
||||
action_name,
|
||||
npc.task_state,
|
||||
destination_name,
|
||||
npc.hunger,
|
||||
npc.energy,
|
||||
npc.get_inventory_amount(SimulationIds.RESOURCE_FOOD),
|
||||
reason_text,
|
||||
score_text
|
||||
]
|
||||
|
||||
|
||||
func _get_action_name(action_id: StringName) -> String:
|
||||
if action_id == SimulationIds.ACTION_IDLE:
|
||||
return "Idle"
|
||||
if action_id == SimulationIds.ACTION_DEAD:
|
||||
return "Dead"
|
||||
var definition := SimulationDefinitions.get_action(action_id)
|
||||
if definition != null:
|
||||
return definition.display_name
|
||||
return String(action_id).capitalize()
|
||||
|
||||
Reference in New Issue
Block a user