Files
pomchronicles/data/quest.gd

371 lines
9.2 KiB
GDScript

class_name Quest extends Resource
#The list of available quests
static var list : Array[Quest]
static var last_id : int = 1
enum Status{
OPEN,
TAKEN,
IN_PROGRESS,
COMPLETED,
FAILED,
CLOSED
}
enum Locations{
VOID,
NESTORS_WOODS
}
class Event:
enum Type{
WAIT,
COMBAT,
CHOICE
}
enum CombatState{
FIGHTING,
VICTORY,
DEFEAT
}
var hidden : bool = false
var type : Type = Type.WAIT
var enemies : Array[String] = []
var progress_point : float = 0
var time : float = 1
var time_elapsed
var complete : bool = false
signal completed()
signal failed()
var participants : Array = []
var turn_queue : Array = []
var busy_list : Array = []
var combat_state
var dex_speed : int
func setup() -> void:
pass
func save() -> Dictionary:
var d : Dictionary = {}
d.hidden = hidden
d.type = type
d.enemies = enemies
d.progress_point = progress_point
d.time = time
d.time_elapsed = time_elapsed
d.complete = complete
return d
func start(quest : Quest) -> void:
match(type):
Type.WAIT:
return
Type.COMBAT:
combat_state = CombatState.FIGHTING
var enemy_list = []
for enemy_name in enemies:
enemy_list.append(Enemy.list[enemy_name].instantiate())
quest.questview.set_questor_animation("idle")
for enemy in enemy_list:
enemy.flip_h()
quest.questview.pause_setting()
quest.questview.place_enemy(enemy, true)
quest.questview.set_enemy_animation(enemy, "idle")
start_combat([quest.questor.quest_sprite], enemy_list)
func start_combat(adventurers : Array, enemies : Array) -> void:
participants = []
participants.append_array(adventurers)
participants.append_array(enemies)
time = 1.25
time_elapsed = 0
var c_order : Array = []
var dex_speed = 0
for p in participants:
p.show_lifebar(true)
c_order.append([p, p.stats.DEX])
if p.stats.DEX > dex_speed:
dex_speed = p.stats.DEX
c_order.sort_custom(func(a,b): return a[1] > b[1])
var delay = 5
var last_time = 0
for c in c_order:
c[0].busy.connect(_on_busy.bind(c[0]))
c[0].action_complete.connect(_on_combat_action_complete.bind(c[0]))
c[0].died.connect(_on_death.bind(c[0]))
var time = delay * dex_speed
if c[1] != 0:
time = delay * dex_speed / c[1]
turn_queue.append({"combatant":c[0], "time": time - last_time})
last_time = time
func execute_attack(combatant : QuestSprite, target : QuestSprite) -> void:
var tween = combatant.create_tween()
tween.tween_interval(.25)
tween.tween_callback(combatant.attack.bind(target))
func add_to_turn_queue(combatant) -> void:
#Calculate time
var time = dex_speed / max(1, combatant.stats.DEX)
#Walk through list to find insertion point
var idx = -1
for i in range(len(turn_queue)):
if turn_queue[i].time > time:
idx = i
break
else:
time -= turn_queue[i].time
var entry = {"combatant":combatant, "time":time}
if idx == -1:
turn_queue.append(entry)
else:
turn_queue[idx].time -= time
turn_queue.insert(idx,entry)
func _on_busy(combatant : QuestSprite) -> void:
busy_list.append(combatant)
func _on_death(killer : QuestSprite, combatant : QuestSprite) -> void:
busy_list.erase(combatant)
remove_from_queue(combatant)
participants.erase(combatant)
if killer != combatant:
var xp = Game.calculate_kill_exp(killer, combatant)
print("%s has earned %d exp" % [killer.name, xp])
killer.exp += xp
var enemy_list : Array = get_enemy_list(killer)
if len(enemy_list) == 0:
if killer is QuestorSprite:
victory()
else:
defeat()
func victory():
print("Questor won!")
combat_state = CombatState.VICTORY
for p : QuestorSprite in participants:
p.check_levelup()
#TODO: Notify player if level up occurs
time = 3
time_elapsed = 0
func defeat():
print("Questor lost!")
combat_state = CombatState.DEFEAT
failed.emit()
func remove_from_queue(combatant : QuestSprite) -> void:
var idx = -1
for i in range(len(turn_queue)):
if turn_queue[i].combatant == combatant:
idx = i
break
if idx != -1:
turn_queue.remove_at(idx)
else:
printerr("Tried to remove someone not in the turn queue")
func _on_combat_action_complete(requeue : bool, combatant : QuestSprite) -> void:
if combatant.position != combatant.reset_position:
combatant.position_reset()
combatant.arrived.connect(_on_combat_action_complete.bind(requeue, combatant), CONNECT_ONE_SHOT)
return
busy_list.erase(combatant)
if requeue:
add_to_turn_queue(combatant)
func execute_action(combatant) -> void:
busy_list = [combatant]
#TODO: Come up with other options than just swinging at each other
var enemies : Array = get_enemy_list(combatant)
var target = enemies.pick_random()
combatant.approach(target, combatant.melee_range)
combatant.arrived.connect(execute_attack.bind(combatant, target), CONNECT_ONE_SHOT)
func get_enemy_list(combatant) -> Array:
var lst = []
for p in participants:
if p != combatant:
lst.append(p)
return lst
func resolve_combat() -> void:
for p in participants:
p.show_lifebar(false)
pass
func process(delta : float) -> void:
#TODO: Make quest combat work
match(type):
Type.COMBAT:
if time != 0:
time_elapsed += delta
if time_elapsed >= time:
time = 0
time_elapsed = 0
else:
return
match(combat_state):
CombatState.FIGHTING:
if len(busy_list) < 1:
if len(turn_queue) > 0:
turn_queue[0].time -= delta
if turn_queue[0].time <= 0:
var c = turn_queue.pop_front()
print("%s taking a turn!" % [c.combatant.name])
if len(turn_queue) > 0:
turn_queue[0].time += c.time
execute_action(c.combatant)
else:
resolve_combat()
CombatState.VICTORY:
resolve_combat()
complete = true
completed.emit()
Type.WAIT:
time_elapsed += delta
if time_elapsed >= time:
complete = true
completed.emit()
var id : int
var base_name : String = ""
var name : String = "A Basic Quest"
var desc : String = "The default quest, with no special anything."
var difficulty : int = 1
var location : Locations
var steps : int = 1
var rewards : Dictionary
var guild_rewards : Dictionary
var covenant_cost : int = 1
var length : float = 10
var events : Array[Event] = []
var progress : float = 0
var current_step : int = 0
var taken : bool = false
var status : Status = Status.OPEN
var questview : QuestView = null
var questor : Adventurer = null
signal status_changed(status : Status)
func _init() -> void:
last_id += 1
id = last_id
pass
func initiate(member : Adventurer) -> void:
questor = member
status = Status.TAKEN
status_changed.emit(Status.TAKEN)
func fail() -> void:
status = Status.FAILED
status_changed.emit(Status.FAILED)
func complete() -> void:
status = Status.COMPLETED
status_changed.emit(Status.COMPLETED)
questview.show_quest_complete()
for reward in rewards.keys():
if reward == "gold":
questor.gain_gold(rewards[reward])
elif reward == "exp":
questor.gain_exp(rewards[reward])
#TODO: Implement other reward types
#elif rewards[reward] is Item:
# questor.gain_item()
#else it's a guild item they'll bring back for us
Game.notice("%s completed the quest '%s'!" % [questor.full_name(), name])
func num_events() -> int:
return len(events)
#TODO: Put in quest requirements
func is_eligible(member : Adventurer) -> bool:
return !taken
func is_taken() -> bool:
return status == Status.TAKEN
static func location_name(location : Locations) -> String:
match(location):
Locations.VOID: return "The Endless Void"
Locations.NESTORS_WOODS: return "Nestor's Woods"
return "ERROR"
func get_location_name() -> String:
return Quest.location_name(location)
func difficulty_name() -> String:
match(difficulty):
0: return "None"
1: return "Trivial"
2: return "Moderate"
3: return "Severe"
4: return "Extreme"
5: return "Legendary"
_: return "Unknown"
static func load_quest_list() -> void:
var path = ProjectSettings.get_setting_with_override("data/quests/directory")
var dir = DirAccess.open(path)
var quest : Quest
if dir:
dir.list_dir_begin()
var filename = dir.get_next()
while filename != "":
if not dir.current_is_dir() and filename.get_extension() == "gd":
var file = load(path + "/" + filename).new()
if file is Quest:
list.append(file)
filename = dir.get_next()
dir.list_dir_end()
static func generate(parameters : Dictionary) -> Quest:
var candidates : Array[Quest] = []
var l = list
for q in list:
if parameters.location != -1 and q.location != parameters.location:
continue
if q.difficulty < parameters.min_difficulty or q.difficulty > parameters.max_difficulty:
continue
candidates.append(q)
var choice : Quest = candidates.pick_random()
return choice.duplicate()
func save() -> Dictionary:
var d : Dictionary = {}
d.id = id
d.name = name
d.base_name = base_name
d.desc = desc
d.difficulty = difficulty
d.location = location
d.steps = steps
#TODO: Convert these!
#d.rewards = rewards
#d.guild_rewards = guild_rewards
d.covenant_cost = covenant_cost
d.length = length
d.progress = progress
d.current_step = current_step
d.taken = taken
d.status = status
var lst : Array = []
for evt in events:
lst.append(evt.save())
d.events = lst
return d