merge: integrate remote player quest slices
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
class_name QuestJournalHud
|
||||
extends Control
|
||||
|
||||
const ENTER_OFFSET := 8.0
|
||||
const THANKS_SECONDS := 3.4
|
||||
|
||||
@export var simulation_manager: Node
|
||||
|
||||
@onready var accent: ColorRect = $Accent
|
||||
@onready var copy: VBoxContainer = $Copy
|
||||
@onready var standing_label: Label = $Copy/Standing
|
||||
@onready var quest_label: Label = $Copy/Quest
|
||||
@onready var quest_kicker: Label = $Copy/QuestKicker
|
||||
@onready var thanks_label: Label = $Copy/Thanks
|
||||
|
||||
var resting_position: Vector2
|
||||
var feedback_version := 0
|
||||
var feedback_tween: Tween
|
||||
var journal_tween: Tween
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
resting_position = position
|
||||
visible = false
|
||||
modulate.a = 0.0
|
||||
if simulation_manager == null:
|
||||
push_error("QuestJournalHud: simulation_manager missing")
|
||||
return
|
||||
if simulation_manager.has_signal("player_quest_opened"):
|
||||
simulation_manager.player_quest_opened.connect(_on_quest_opened)
|
||||
if simulation_manager.has_signal("player_quest_completed"):
|
||||
simulation_manager.player_quest_completed.connect(_on_quest_completed)
|
||||
if simulation_manager.has_signal("player_quest_expired"):
|
||||
simulation_manager.player_quest_expired.connect(_on_quest_expired)
|
||||
if simulation_manager.has_signal("player_standing_changed"):
|
||||
simulation_manager.player_standing_changed.connect(_on_standing_changed)
|
||||
if simulation_manager.has_signal("player_tier_advanced"):
|
||||
simulation_manager.player_tier_advanced.connect(_on_tier_advanced)
|
||||
if simulation_manager.has_signal("opportunity_resolved"):
|
||||
simulation_manager.opportunity_resolved.connect(_on_opportunity_resolved)
|
||||
if simulation_manager.has_signal("state_restored"):
|
||||
simulation_manager.state_restored.connect(_on_state_restored)
|
||||
_refresh()
|
||||
|
||||
|
||||
func _on_quest_opened(_quest: PlayerQuestRecord) -> void:
|
||||
_refresh()
|
||||
|
||||
|
||||
func _on_quest_expired(_quest: PlayerQuestRecord) -> void:
|
||||
_refresh()
|
||||
|
||||
|
||||
func _on_standing_changed(_standing: PlayerStandingRecord) -> void:
|
||||
_refresh()
|
||||
|
||||
|
||||
func _on_tier_advanced(_tier: int, tier_name: String) -> void:
|
||||
_refresh()
|
||||
_show_thanks("Standing grows", "The village now calls you a %s." % tier_name)
|
||||
|
||||
|
||||
func _on_opportunity_resolved(
|
||||
_opportunity: OpportunityStateRecord, _cause_event: EconomicEventRecord
|
||||
) -> void:
|
||||
_refresh()
|
||||
|
||||
|
||||
func _on_state_restored() -> void:
|
||||
_refresh()
|
||||
|
||||
|
||||
func _refresh() -> void:
|
||||
if simulation_manager == null:
|
||||
return
|
||||
var standing: PlayerStandingRecord = simulation_manager.get_player_standing()
|
||||
if standing == null:
|
||||
return
|
||||
var standing_text := (
|
||||
"Standing · %s · %.0f/100" % [standing.get_tier_name(), standing.get_standing()]
|
||||
)
|
||||
standing_label.text = standing_text
|
||||
var quest: PlayerQuestRecord = simulation_manager.get_active_player_quest()
|
||||
if quest == null:
|
||||
quest_kicker.text = ""
|
||||
quest_label.text = ""
|
||||
elif quest.has_animal_target():
|
||||
var requester_name := _get_npc_name(quest.get_requester_npc_id())
|
||||
var animal_name := _get_animal_name(quest.get_animal_id())
|
||||
quest_kicker.text = "ACTIVE QUEST"
|
||||
quest_label.text = (
|
||||
"%s · bring %.0f %s to %s"
|
||||
% [
|
||||
requester_name,
|
||||
quest.get_target_amount(),
|
||||
String(quest.get_resource_id()).replace("_", " "),
|
||||
animal_name,
|
||||
]
|
||||
)
|
||||
else:
|
||||
var requester_name := _get_npc_name(quest.get_requester_npc_id())
|
||||
quest_kicker.text = "ACTIVE QUEST"
|
||||
quest_label.text = (
|
||||
"%s · bring %.0f %s to the %s"
|
||||
% [
|
||||
requester_name,
|
||||
quest.get_target_amount(),
|
||||
String(quest.get_resource_id()).replace("_", " "),
|
||||
String(quest.get_target_id()).replace("_", " "),
|
||||
]
|
||||
)
|
||||
var has_content := not quest_kicker.text.is_empty() or not standing_text.is_empty()
|
||||
if not has_content:
|
||||
return
|
||||
if not visible:
|
||||
visible = true
|
||||
modulate.a = 0.0
|
||||
position = resting_position + Vector2(0.0, ENTER_OFFSET)
|
||||
if journal_tween != null and journal_tween.is_valid():
|
||||
journal_tween.kill()
|
||||
journal_tween = create_tween().set_parallel(true)
|
||||
journal_tween.set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
|
||||
journal_tween.tween_property(self, "position", resting_position, 0.3)
|
||||
journal_tween.tween_property(self, "modulate:a", 1.0, 0.22)
|
||||
|
||||
|
||||
func _on_quest_completed(quest: PlayerQuestRecord) -> void:
|
||||
_refresh()
|
||||
var requester_name := _get_npc_name(quest.get_requester_npc_id())
|
||||
_show_thanks(
|
||||
"Gratitude",
|
||||
(
|
||||
"%s thanks you for your help. +%.0f Standing"
|
||||
% [requester_name, quest.get_standing_reward()]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
func _show_thanks(heading: String, message: String) -> void:
|
||||
feedback_version += 1
|
||||
var version := feedback_version
|
||||
if feedback_tween != null and feedback_tween.is_valid():
|
||||
feedback_tween.kill()
|
||||
thanks_label.text = "%s\n%s" % [heading, message]
|
||||
thanks_label.visible = true
|
||||
thanks_label.modulate.a = 0.0
|
||||
feedback_tween = create_tween().set_parallel(true)
|
||||
feedback_tween.set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
|
||||
feedback_tween.tween_property(thanks_label, "modulate:a", 1.0, 0.3)
|
||||
feedback_tween.chain().tween_interval(THANKS_SECONDS)
|
||||
feedback_tween.chain().tween_property(thanks_label, "modulate:a", 0.0, 0.5)
|
||||
feedback_tween.chain().tween_callback(
|
||||
func() -> void:
|
||||
if version == feedback_version:
|
||||
thanks_label.visible = false
|
||||
)
|
||||
|
||||
|
||||
func _get_npc_name(npc_id: int) -> String:
|
||||
for npc in simulation_manager.npcs:
|
||||
if npc.id == npc_id:
|
||||
return npc.npc_name
|
||||
return "Someone"
|
||||
|
||||
|
||||
func _get_animal_name(animal_id: StringName) -> String:
|
||||
if (
|
||||
simulation_manager.has_method("get_animal_display_name")
|
||||
and simulation_manager.get_animal_display_name(animal_id) != ""
|
||||
):
|
||||
return simulation_manager.get_animal_display_name(animal_id)
|
||||
return String(animal_id).replace("_", " ").capitalize()
|
||||
@@ -0,0 +1 @@
|
||||
uid://bj0u6l60v2o5f
|
||||
+35
-1
@@ -87,6 +87,8 @@ func _on_village_changed(village: SimVillage) -> void:
|
||||
wood_rate = " (−%.0f/d)" % rates["wood_per_day"]
|
||||
var opportunity_text := _build_active_opportunity_display()
|
||||
var season_text := "Cold" if simulation_manager.is_cold_season() else "Warm"
|
||||
var standing_text := _build_standing_display()
|
||||
var player_needs_text := _build_player_needs_display()
|
||||
village_stats_label.text = (
|
||||
"""
|
||||
Village [%s]
|
||||
@@ -101,7 +103,7 @@ func _on_village_changed(village: SimVillage) -> void:
|
||||
Food Mod: %.2f
|
||||
Safety Mod: %.2f
|
||||
Knowledge Mod: %.2f
|
||||
%s%s
|
||||
%s%s%s%s
|
||||
"""
|
||||
% [
|
||||
speed_text,
|
||||
@@ -116,12 +118,44 @@ func _on_village_changed(village: SimVillage) -> void:
|
||||
village.food_modifier,
|
||||
village.safety_modifier,
|
||||
village.knowledge_modifier,
|
||||
player_needs_text,
|
||||
standing_text,
|
||||
opportunity_text,
|
||||
event_text,
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
func _build_player_needs_display() -> String:
|
||||
if not simulation_manager.has_method("get_player_state"):
|
||||
return ""
|
||||
var state: PlayerStateRecord = simulation_manager.get_player_state()
|
||||
if state == null:
|
||||
return ""
|
||||
var status := "dead" if state.is_dead() else ("starving" if state.is_starving() else "ok")
|
||||
return (
|
||||
"\n\nYou\nHunger: %.0f Energy: %.0f (%s)"
|
||||
% [state.get_hunger(), state.get_energy(), status]
|
||||
)
|
||||
|
||||
|
||||
func _build_standing_display() -> String:
|
||||
if not simulation_manager.has_method("get_player_standing"):
|
||||
return ""
|
||||
var standing: PlayerStandingRecord = simulation_manager.get_player_standing()
|
||||
if standing == null:
|
||||
return ""
|
||||
return (
|
||||
"\n\nStanding\n◆ %s · %.0f/100 · %d need%s met"
|
||||
% [
|
||||
standing.get_tier_name(),
|
||||
standing.get_standing(),
|
||||
standing.get_resolved_needs(),
|
||||
"" if standing.get_resolved_needs() == 1 else "s",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
func _on_npc_decision_recorded(_npc: SimNPC, _decision: ActionSelectionResult) -> void:
|
||||
_refresh_npc_inspector()
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@ func _present(context: VillagerInspectionResult, animate: bool) -> void:
|
||||
else:
|
||||
need_label.visible = false
|
||||
reason_label.text = "Why · %s" % context.decision_reason
|
||||
_present_opportunity_note(context.opportunity_note)
|
||||
visible = true
|
||||
accent.pivot_offset = accent.size * 0.5
|
||||
if not animate:
|
||||
@@ -95,6 +96,20 @@ func _present(context: VillagerInspectionResult, animate: bool) -> void:
|
||||
motion_tween.tween_property(accent, "scale:y", 1.0, 0.26)
|
||||
|
||||
|
||||
func _present_opportunity_note(note: VillagerOpportunityNote) -> void:
|
||||
if note == null:
|
||||
return
|
||||
if not note.has_need:
|
||||
return
|
||||
need_label.visible = true
|
||||
var existing_text := need_label.text.strip_edges()
|
||||
var note_text := note.help_text.strip_edges()
|
||||
if existing_text.is_empty():
|
||||
need_label.text = "Need · %s" % note.need_text
|
||||
if not note_text.is_empty() and note_text not in need_label.text:
|
||||
need_label.text += "\n" + note_text
|
||||
|
||||
|
||||
func _hide_note() -> void:
|
||||
if not visible or is_hiding:
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user