159 lines
4.8 KiB
GDScript
159 lines
4.8 KiB
GDScript
extends Node
|
|
|
|
signal quest_registered(quest_id: StringName)
|
|
signal quest_started(quest_id: StringName)
|
|
signal quest_step_advanced(quest_id: StringName, step_index: int, step_text: String)
|
|
signal quest_completed(quest_id: StringName)
|
|
signal quest_state_changed
|
|
|
|
var _quests: Dictionary = {}
|
|
var _active_quest_id: StringName = &""
|
|
var _active_step_index: int = -1
|
|
var _completed_quests: Dictionary = {}
|
|
|
|
|
|
func register_quest(definition: Dictionary) -> bool:
|
|
var raw_id := String(definition.get("id", "")).strip_edges()
|
|
if raw_id.is_empty():
|
|
push_warning("Quest definition is missing an id.")
|
|
return false
|
|
var quest_id: StringName = StringName(raw_id)
|
|
if _quests.has(quest_id):
|
|
return true
|
|
|
|
var raw_steps: Variant = definition.get("steps", [])
|
|
if typeof(raw_steps) != TYPE_ARRAY:
|
|
push_warning("Quest '%s' has invalid steps." % raw_id)
|
|
return false
|
|
var normalized_steps: Array[Dictionary] = []
|
|
var idx := 0
|
|
for raw_step in raw_steps:
|
|
if typeof(raw_step) != TYPE_DICTIONARY:
|
|
continue
|
|
var step_dict: Dictionary = raw_step
|
|
var step := step_dict.duplicate(true)
|
|
step["id"] = String(step.get("id", "step_%d" % idx)).strip_edges()
|
|
step["text"] = String(step.get("text", "")).strip_edges()
|
|
step["complete_event"] = StringName(String(step.get("complete_event", "")).strip_edges())
|
|
normalized_steps.append(step)
|
|
idx += 1
|
|
|
|
if normalized_steps.is_empty():
|
|
push_warning("Quest '%s' has no usable steps." % raw_id)
|
|
return false
|
|
|
|
var quest := definition.duplicate(true)
|
|
quest["id"] = raw_id
|
|
quest["title"] = String(definition.get("title", raw_id))
|
|
quest["description"] = String(definition.get("description", ""))
|
|
quest["steps"] = normalized_steps
|
|
_quests[quest_id] = quest
|
|
quest_registered.emit(quest_id)
|
|
quest_state_changed.emit()
|
|
return true
|
|
|
|
|
|
func has_quest(quest_id: String) -> bool:
|
|
return _quests.has(StringName(quest_id))
|
|
|
|
|
|
func start_quest(quest_id: String) -> bool:
|
|
var id := StringName(quest_id)
|
|
if not _quests.has(id):
|
|
push_warning("Cannot start missing quest '%s'." % quest_id)
|
|
return false
|
|
_active_quest_id = id
|
|
_active_step_index = 0
|
|
quest_started.emit(_active_quest_id)
|
|
var step := _get_active_step()
|
|
if not step.is_empty():
|
|
quest_step_advanced.emit(_active_quest_id, _active_step_index, String(step.get("text", "")))
|
|
quest_state_changed.emit()
|
|
return true
|
|
|
|
|
|
func emit_event(event_name: StringName, payload: Dictionary = {}) -> void:
|
|
if event_name == StringName():
|
|
return
|
|
if _active_quest_id == StringName():
|
|
return
|
|
if is_quest_completed(String(_active_quest_id)):
|
|
return
|
|
|
|
var active_step := _get_active_step()
|
|
if active_step.is_empty():
|
|
return
|
|
var expected: StringName = active_step.get("complete_event", StringName())
|
|
if expected != event_name:
|
|
return
|
|
_advance_active_step(payload)
|
|
|
|
|
|
func is_quest_completed(quest_id: String) -> bool:
|
|
return bool(_completed_quests.get(StringName(quest_id), false))
|
|
|
|
|
|
func is_active_quest(quest_id: String) -> bool:
|
|
return _active_quest_id == StringName(quest_id)
|
|
|
|
|
|
func get_active_quest_id() -> StringName:
|
|
return _active_quest_id
|
|
|
|
|
|
func get_active_quest_state() -> Dictionary:
|
|
if _active_quest_id == StringName() or not _quests.has(_active_quest_id):
|
|
return {
|
|
"active": false,
|
|
}
|
|
|
|
var quest: Dictionary = _quests[_active_quest_id]
|
|
var steps: Array = quest.get("steps", [])
|
|
var completed: bool = is_quest_completed(String(_active_quest_id))
|
|
var step_text := ""
|
|
var step_id := ""
|
|
if not completed and _active_step_index >= 0 and _active_step_index < steps.size():
|
|
var step: Dictionary = steps[_active_step_index]
|
|
step_text = String(step.get("text", ""))
|
|
step_id = String(step.get("id", ""))
|
|
|
|
return {
|
|
"active": true,
|
|
"quest_id": String(_active_quest_id),
|
|
"title": String(quest.get("title", "")),
|
|
"description": String(quest.get("description", "")),
|
|
"current_step_index": _active_step_index,
|
|
"total_steps": steps.size(),
|
|
"current_step_id": step_id,
|
|
"current_step_text": step_text,
|
|
"completed": completed,
|
|
}
|
|
|
|
|
|
func _get_active_step() -> Dictionary:
|
|
if _active_quest_id == StringName():
|
|
return {}
|
|
if not _quests.has(_active_quest_id):
|
|
return {}
|
|
var quest: Dictionary = _quests[_active_quest_id]
|
|
var steps: Array = quest.get("steps", [])
|
|
if _active_step_index < 0 or _active_step_index >= steps.size():
|
|
return {}
|
|
return steps[_active_step_index]
|
|
|
|
|
|
func _advance_active_step(_payload: Dictionary) -> void:
|
|
if _active_quest_id == StringName():
|
|
return
|
|
var quest: Dictionary = _quests.get(_active_quest_id, {})
|
|
var steps: Array = quest.get("steps", [])
|
|
_active_step_index += 1
|
|
if _active_step_index >= steps.size():
|
|
_completed_quests[_active_quest_id] = true
|
|
quest_completed.emit(_active_quest_id)
|
|
quest_state_changed.emit()
|
|
return
|
|
var step: Dictionary = steps[_active_step_index]
|
|
quest_step_advanced.emit(_active_quest_id, _active_step_index, String(step.get("text", "")))
|
|
quest_state_changed.emit()
|