A raging quest
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user