343 lines
8.5 KiB
GDScript
343 lines
8.5 KiB
GDScript
class_name Quest extends Resource
|
|
|
|
#The list of available quests
|
|
static var list : Array[Quest]
|
|
|
|
|
|
enum Status{
|
|
OPEN,
|
|
TAKEN,
|
|
IN_PROGRESS,
|
|
COMPLETED,
|
|
FAILED,
|
|
CLOSED
|
|
}
|
|
|
|
enum Locations{
|
|
NESTORS_WOODS
|
|
}
|
|
|
|
class Event:
|
|
var enemy_types: Dictionary[String, PackedScene] = {
|
|
"goo": preload("res://templates/enemies/goo.tscn")
|
|
}
|
|
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_dict() -> 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_types[enemy_name].instantiate())
|
|
quest.questview.set_questor_animation("idle")
|
|
for enemy in enemy_list:
|
|
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)
|
|
|
|
var c_order : Array = []
|
|
var dex_speed = 0
|
|
for p in participants:
|
|
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 = 5
|
|
|
|
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:
|
|
pass
|
|
|
|
func process(delta : float) -> void:
|
|
#TODO: Make quest combat work
|
|
match(type):
|
|
Type.COMBAT:
|
|
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:
|
|
time_elapsed += delta
|
|
if time_elapsed >= time:
|
|
complete = true
|
|
completed.emit()
|
|
Type.WAIT:
|
|
time_elapsed += delta
|
|
if time_elapsed >= time:
|
|
complete = true
|
|
completed.emit()
|
|
|
|
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:
|
|
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
|
|
|
|
func location_name() -> String:
|
|
match(location):
|
|
Locations.NESTORS_WOODS: return "Nestor's Woods"
|
|
return "ERROR"
|
|
|
|
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:
|
|
return null
|
|
|
|
func save_dict() -> Dictionary:
|
|
var d : Dictionary = {}
|
|
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_dict())
|
|
d.events = lst
|
|
return d
|