Implemented simple quest system

This commit is contained in:
Segey Lapin
2020-04-15 12:16:14 +03:00
parent 078cc47b91
commit 32c5212209
14 changed files with 366 additions and 202 deletions

View File

@@ -1,11 +1,51 @@
extends Node
signal new_day
var raycasts_count = 100
var raycast_queue = []
var astar: AStar
func _ready():
set_save_slot(0)
enum periods {MORNING, DAY, EVENING, NIGHT}
var game_day: int = 0
var day_period: int = periods.MORNING
var game_hour: int = 0
var game_minute: int = 0
var acc_time:float = 0.0
var time_active = false
func _process(delta):
if time_active:
update_time(delta)
func update_time(delta):
acc_time += delta
if acc_time >= 1.0:
game_minute += 1
acc_time -= 1.0
if game_minute == 60:
game_minute = 0
game_hour += 1
if game_hour == 24:
game_hour = 0
game_day += 1
match(game_hour):
23,0,1,2,3:
day_period = periods.NIGHT
4,5,6,7,8,9,10,11:
day_period = periods.MORNING
12,13,14,15,16:
day_period = periods.DAY
17,18,19,20,21,22:
day_period = periods.EVENING
func start_time():
time_active = true
func stop_time():
time_active = false
func visibility_check(ch1, ch2):
var direction: Vector3 = -ch1.global_transform.basis[2].normalized()
var to_ch2: Vector3 = (ch2.global_transform.origin - ch1.global_transform.origin).normalized()
@@ -38,6 +78,10 @@ func save_characters():
spawner.set_meta("stats", stats)
func save_game():
print("save game")
save_data.game_day = game_day
save_data.game_hour = game_hour
save_data.day_period = day_period
assert(save_slot.length() > 0)
var fd = File.new()
fd.open(save_slot, File.WRITE)
@@ -51,4 +95,7 @@ func load_game():
var data = fd.get_as_text()
var parse: = JSON.parse(data)
save_data = parse.result
game_day = save_data.game_day
game_hour = save_data.game_hour
day_period = save_data.day_period
fd.close()

View File

@@ -0,0 +1,213 @@
extends Node
signal new_quest
var quests = []
func save():
var data = []
for k in quests:
data.push_back(k.save())
global.save_data.quests = data
func restore_quest(d):
var q = Quest.new(d.title, d.desc)
func restore():
for k in global.save_data.quests:
quests.push_back(restore_quest(k))
var quest_triggers = [
{
"title": "Something is not right.",
"desc": "You wake up from noise. Explore the building to find the source.",
"trigger": ["once", 0, 0, 10],
"objectives": [
["walkto_room", "Check enterance.", "Check enterance room.", "exit_room", 8.0]
]
}
]
class QuestObjective extends Reference:
var _title
var _desc
var quest
var complete = false
var active = false
func update():
pass
func activate():
active = true
func finish():
active = false
func set_quest(q):
quest = q
func _init(title, desc):
_title = title
_desc = desc
func save():
var save_data = {}
save_data.create = ["obj", _desc, _title]
save_data.active = active
save_data.complete = complete
return save_data
class Quest extends Reference:
signal quest_complete
var _title
var _description
var objectives = []
var children = []
var complete = false
var cur_objective: int = -1
func _init(title, desc):
_title = title
_description = desc
func add_objective(obj):
objectives.push_back(obj)
func remove_objective(obj):
objectives.erase(obj)
func add_child(obj):
children.push_back(obj)
func remove_child(obj: Quest):
children.erase(obj)
func save():
var save_data = {}
save_data.title = _title
save_data.desc = _description
return save_data
func update():
for k in children:
k.update()
for k in objectives:
k.update()
var old_objective:int = cur_objective
complete = true
for k in range(objectives.size()):
if !objectives[k].complete:
cur_objective = k
complete = false
break
for k in children:
if !k.complete:
complete = false
if complete:
emit_signal("quest_complete", self)
if cur_objective != old_objective:
if old_objective >= 0:
objectives[old_objective].finish()
objectives[cur_objective].activate()
func get_title():
return _title
func get_description():
return _title
class ObjectiveWalkto extends QuestObjective:
var where = Vector3()
var meta = ""
var radius = 0.0
var indicator = preload("res://ui/quest_indicator.tscn")
var ind_i: Node2D
var old_pos = Vector3()
func _init(t, d, w, r).(t, d):
meta = w
radius = r
func activate():
active = true
ind_i = indicator.instance()
var player_pos: Vector3 = global.player.global_transform.origin
var base = global.astar.get_closest_point(player_pos)
var tgt = global.astar.get_closest_point(where)
var path = global.astar.get_point_path(base, tgt)
if path.size() > 1:
ind_i.target = path[1] + Vector3(0, 1, 0) * 0.3
else:
ind_i.target = where + Vector3(0, 1, 0) * 0.3
global.get_viewport().add_child(ind_i)
print(_title, "ACTIVATED")
old_pos = player_pos
func finish():
active = false
ind_i.queue_free()
func update():
if !active:
return
var player_pos: Vector3 = global.player.global_transform.origin
if old_pos.distance_to(player_pos) > 0.5:
var base = global.astar.get_closest_point(player_pos)
var tgt = global.astar.get_closest_point(where)
var path = global.astar.get_point_path(base, tgt)
if path.size() > 1:
var ind = 0
var pt = path[ind]
while ind < path.size() - 1:
pt = path[ind]
if pt.distance_to(player_pos) > 2.0:
ind_i.target = pt + Vector3(0, 1, 0) * 0.3
break
ind += 1
else:
ind_i.target = where + Vector3(0, 1, 0) * 0.3
old_pos = player_pos
func save():
var save_data = {}
save_data.create = ["walkto_room", _title, _desc, meta, radius]
save_data.active = active
save_data.complete = complete
return save_data
func _ready():
pass
func check_trigger(t):
var t_type = t[0]
match(t_type):
"once":
var t_day = t[1]
var t_hour = t[2]
var t_minute = t[3]
if global.game_day != t_day:
return false
if global.game_hour != t_hour:
return false
if int(global.game_minute / 10) != int(t_minute / 10):
return false
return true
return false
func create_objective(v):
var o_type = v[0]
match(o_type):
"walkto_room":
var o_title = v[1]
var o_desc = v[2]
var o_meta = v[3]
var o_radius = v[4]
var obj = ObjectiveWalkto.new(o_title, o_desc, o_meta, o_radius)
return obj
return null
var up_time = 0.0
var state: int = 0
func _process(delta):
up_time += delta
if up_time > 1000.0:
up_time = 0.0
if int(up_time * 10.0) % 10 == 1:
var processed = []
while quest_triggers.size() > 0:
var k = quest_triggers.pop_front()
if check_trigger(k.trigger):
print("quest triggered: ", k.title)
var q = Quest.new(k.title, k.desc)
quests.push_back(q)
for r in k.objectives:
var obj = create_objective(r)
if obj:
q.add_objective(obj)
emit_signal("new_quest", q)
else:
processed.push_back(k)
quest_triggers = processed
for k in quests:
k.update()

View File

@@ -0,0 +1,22 @@
extends Node
signal level_up
signal new_quest
var money: int = 2000
var master_node
var current_room
var team = {}
var line = {}
var training = false
# warning-ignore:unused_class_variable
var quests : = []
# warning-ignore:unused_class_variable
var team_train_count : = 0
# warning-ignore:unused_class_variable
var arrow: Spatial
# warning-ignore:unused_class_variable
var next_scene: String
var player_visual = {
"gender": "male"
}

View File

@@ -218,6 +218,7 @@ global="*res://autoloads/global.gd"
rpg="*res://autoloads/rpg.gd"
combat="*res://autoloads/combat.gd"
phy_bones="*res://autoloads/phy_bones.gd"
quests="*res://autoloads/quests.gd"
[input]

View File

@@ -1,5 +1,11 @@
extends Spatial
var loading
func new_quest(q):
var title = q.get_title()
var desc = q.get_description()
print("title: ", title)
print("desc: ", desc)
func _ready():
loading = preload("res://ui/loading.tscn").instance()
get_tree().get_root().add_child(loading)
@@ -12,6 +18,8 @@ func _ready():
global.set_save_slot(0)
e = $dungeon.connect("prepared", self, "start_ai")
assert(e == OK)
e = quests.connect("new_quest", self, "new_quest")
assert(e == OK)
var default_meta = {
"grabbing": false,
@@ -32,6 +40,12 @@ func start_ai():
print("prepared")
$"meta-ai".start()
func save():
global.save_characters()
$dungeon.save()
quests.save()
global.save_game()
func spawn_player(spawner):
print("spawned player")
loading.queue_free()
@@ -50,6 +64,7 @@ func spawn_player(spawner):
player.set_meta("spawner", spawner)
player.add_to_group("player")
$cam_target/cam_rot/offset/Camera.current = true
global.start_time()
func spawn_npc(spawner):
var npc
var g
@@ -221,3 +236,5 @@ func _process(delta):
$cam_target.rotate_y(-angle * delta)
else:
cam_fixup_cooldown -= delta
if Input.is_action_just_pressed("save_game"):
save()

View File

@@ -23,12 +23,10 @@ var dungeon_save = {}
var spawn_save = {}
var prepared = false
func save():
global.save_characters()
for k in range(dungeon.size()):
dungeon[k].save()
dungeon_save[str(k)] = dungeon[k].save_data
global.save_data.dungeon = dungeon_save
global.save_game()
var window_rooms = []
func build_rooms():
print("build_rooms")
@@ -214,6 +212,3 @@ func _process(_delta):
state = 50
prepared = true
emit_signal("prepared")
_:
if Input.is_action_just_pressed("save_game"):
save()

View File

@@ -1,10 +1,22 @@
[gd_scene load_steps=3 format=2]
[gd_scene load_steps=5 format=2]
[ext_resource path="res://scenes/maps/stairs.escn" type="PackedScene" id=1]
[sub_resource type="BoxShape" id=1]
extents = Vector3( 1.13086, 0.0458848, 0.64188 )
[sub_resource type="Curve3D" id=2]
_data = {
"points": PoolVector3Array( 0, 0, 0, 0, 0, 0, -3.40015, 0.239252, 1.85913, 0, 0, 0, 0, 0, 0, -1.97383, 0.257656, 1.88403, 0, 0, 0, 0, 0, 0, -1.45852, 0.423293, 1.90918, 0, 0, 0, 0, 0, 0, -0.13343, 1.49073, 1.93433, 0, 0, 0, 0, 0, 0, 0.24645, 1.77444, 1.93433, 0, 0, 0, 0, 0, 0, 1.75415, 1.76855, 1.87302, 0, 0, 0, 0, 0, 0, 1.66716, 1.85563, 0.244141, 0, 0, 0, 0, 0, 0, 1.66137, 1.75269, -0.562487, 0, 0, 0, 0, 0, 0, 1.65557, 1.76514, -1.72805, 0, 0, 0, 0, 0, 0, 0.974322, 1.71509, -1.75141, 0, 0, 0, 0, 0, 0, 0.287957, 2.12817, -1.72775, 0, 0, 0, 0, 0, 0, -0.201176, 2.55396, -1.71986, 0, 0, 0, 0, 0, 0, -0.666642, 3.04525, -1.72119, 0, 0, 0, 0, 0, 0, -1.27411, 3.41605, -1.72168, 0, 0, 0, 0, 0, 0, -2.65473, 3.40816, -1.73389 ),
"tilts": PoolRealArray( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 )
}
[sub_resource type="Curve3D" id=3]
_data = {
"points": PoolVector3Array( 0, 0, 0, 0, 0, 0, -4.5026, 0.332731, -0.0107422, 0, 0, 0, 0, 0, 0, -3.99813, 0.375664, -0.0107422, 0, 0, 0, 0, 0, 0, -4.07327, 0.375664, -1.81396, 0, 0, 0, 0, 0, 0, -4.084, 0.36493, -2.81201, 0, 0, 0, 0, 0, 0, -3.81567, 0.375664, -3.27344, 0, 0, 0, 0, 0, 0, -1.64755, 0.343464, -3.28418, 0, 0, 0, 0, 0, 0, -0.520562, 0.407863, -3.32715, 0, 0, 0, 0, 0, 0, 0.101966, 0.482996, -3.68164, 0, 0, 0, 0, 0, 0, 0.917692, 0.440063, -3.30566, 0, 0, 0, 0, 0, 0, 1.86222, 0.504463, -3.27344, 0, 0, 0, 0, 0, 0, 3.33267, 0.547396, -3.30566, 0, 0, 0, 0, 0, 0, 3.7298, 0.504463, -3.31641, 0, 0, 0, 0, 0, 0, 3.62247, 0.515196, -2.55469, 0, 0, 0, 0, 0, 0, 3.65467, 0.568862, -1.3418, 0, 0, 0, 0, 0, 0, 3.93373, 0.622528, -0.665527, 0, 0, 0, 0, 0, 0, 4.245, 0.547396, -0.236328, 0, 0, 0, 0, 0, 0, 4.55626, 0.547396, -0.0429688, 0, 0, 0, 0, 0, 0, 4.406, 0.558129, 0.0751953, 0, 0, 0, 0, 0, 0, 4.0518, 0.525929, 1.0625, 0, 0, 0, 0, 0, 0, 3.8586, 0.504463, 2.66187, 0, 0, 0, 0, 0, 0, 3.5688, 0.504463, 3.14478, 0, 0, 0, 0, 0, 0, 2.17348, 0.515196, 3.06982, 0, 0, 0, 0, 0, 0, 0.552762, 0.407863, 3.00537, 0, 0, 0, 0, 0, 0, 0.316631, 0.42933, 3.55273, 0, 0, 0, 0, 0, 0, -0.252232, 0.42933, 2.94092, 0, 0, 0, 0, 0, 0, -1.29336, 0.375664, 2.9624, 0, 0, 0, 0, 0, 0, -3.28974, 0.386397, 3.01611, 0, 0, 0, 0, 0, 0, -3.9552, 0.418597, 2.95166, 0, 0, 0, 0, 0, 0, -4.16987, 0.354197, 2.52222, 0, 0, 0, 0, 0, 0, -4.3416, 0.375664, 1.42749, 0, 0, 0, 0, 0, 0, -4.2128, 0.289798, 0.493652 ),
"tilts": PoolRealArray( 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 )
}
[node name="stairs" instance=ExtResource( 1 )]
[node name="StaticBody" type="StaticBody" parent="." index="4"]
@@ -16,3 +28,18 @@ shape = SubResource( 1 )
[node name="CollisionShape2" type="CollisionShape" parent="StaticBody" index="1"]
transform = Transform( -0.768529, 0.639815, -3.25444e-07, 0.639815, 0.768529, 1.60706e-08, 2.60396e-07, -1.95874e-07, -1, -0.0615042, 2.2555, -1.95211 )
shape = SubResource( 1 )
[node name="Path" type="Path" parent="." index="5" groups=[
"path",
]]
curve = SubResource( 2 )
[node name="Path2" type="Path" parent="." index="6" groups=[
"path",
]]
curve = SubResource( 3 )
[node name="Path3" type="Path" parent="." index="7" groups=[
"path",
]]
curve = SubResource( 3 )

View File

@@ -15,6 +15,7 @@ func start():
func _ready():
astar = AStar.new()
global.astar = astar
func calc_visible_lists():
for g in get_tree().get_nodes_in_group("npc"):

View File

@@ -1,92 +0,0 @@
extends Reference
class_name Quest
signal complete
signal failed
signal started
var _objectives = []
var _children = []
var _title: String
var _description: String
var _active: bool = false
var _complete: bool = false
var _next_quest: Quest
func _init(title: String, description: String):
_title = title
_description = description
func add_child(quest: Quest):
_children.push_back(quest)
func is_complete():
return _complete
func is_active():
return _active
func update():
if !_active:
return
# var m = get_meta("quest")
# if m != null:
# for k in _objectives:
# k.set_meta("quest", m)
for k in _objectives:
k.update()
for k in _children:
k.update()
_complete = true
for k in _objectives:
if !k.is_complete():
_complete = false
break
if !_complete:
print("quest: ", _title, " objectives incomplete")
return
for k in _children:
if !k.is_complete():
_complete = false
break
if !_complete:
print("quest: ", _title, " children incomplete")
if _complete:
emit_signal("complete", self)
_active = false
quest_complete()
func quest_complete_handler(quest: Quest):
var next = quest.get_next_quest()
if next != null:
add_child(next)
next.connect("complete", self, "quest_complete_handler")
next.start()
func start():
_active = true
for k in _children:
k.connect("complete", self, "quest_complete_handler")
k.start()
emit_signal("started", self)
print("children: ", _children)
print("quest: ", _title, " started")
func get_cur_task_text():
var ret: String = "No current task"
if _active:
for p in _children:
if p.is_active():
ret = p.get_cur_task_text()
return ret
for p in _objectives:
if !p.is_complete():
return get_title() + ": " + p.get_title()
return _title
return ret
func get_title():
return _title
func get_description():
return _description
func quest_complete():
print("quest: ", _title, " complete")
func add_objective(obj: QuestObjective):
if !obj in _objectives:
_objectives.push_back(obj)
func remove_objective(obj: QuestObjective):
if obj in _objectives:
_objectives.erase(obj)
func set_next_quest(obj: Quest):
_next_quest = obj
func get_next_quest() -> Quest:
return _next_quest

View File

@@ -1,14 +0,0 @@
extends Reference
class_name QuestObjective
var _complete: bool = false
var _title: String
func _init(title: String):
_title = title
func is_complete():
return _complete
func update():
pass
func get_title():
return _title

View File

@@ -1,29 +0,0 @@
extends Quest
class_name StatsQuest
var stat_check: Dictionary
class StatsCheckObjective extends QuestObjective:
var stat_check: Dictionary
func _init(title, stats: Dictionary).(title):
stat_check = stats
func update():
_complete = true
for k in stat_check.keys():
match(k):
"player_count":
if world.team.keys().size() < stat_check[k]:
_complete = false
print("player count: ", world.team.keys().size(), " < ", stat_check[k])
"cheerleader_count":
if world.cheer_team.keys().size() < stat_check[k]:
_complete = false
"team_train_count":
if world.team_train_count < stat_check[k]:
_complete = false
_:
_complete = false
func _init(title, desc, stats: Dictionary).(title, desc):
stat_check = stats
add_objective(StatsCheckObjective.new("Comply to team stats", stat_check))

View File

@@ -1,61 +0,0 @@
extends Quest
class_name WalkQuest
# Declare member variables here. Examples:
# var a = 2
# var b = "text"
# Called when the node enters the scene tree for the first time.
var destination: Spatial
var quest_marker: Spatial
class WalkQuestObjective extends QuestObjective:
var dest: Spatial
var arrow: Spatial
var nav: Navigation
func _init(title, destination: Spatial).(title):
dest = destination
arrow = world.arrow
nav = world.arrow.get_node("/root/main/nav")
func update():
var org = world.master_node.global_transform.origin
var orgc = nav.get_closest_point(org)
var dst = dest.global_transform.origin
var dstc = nav.get_closest_point(dst)
var path = nav.get_simple_path(orgc, dstc)
var arrow_dir : = Vector3()
if path.size() > 1:
for e in path:
if (e - org).length() > 1.0:
arrow_dir = e - org
break
if arrow_dir.length() == 0 && arrow.visible:
arrow.hide()
elif arrow_dir.length() > 0:
if !arrow.visible:
arrow.show()
arrow_dir.y = 0
arrow.look_at(arrow.global_transform.origin + arrow_dir, Vector3.UP)
if org.distance_to(dest.global_transform.origin) < 2.5:
_complete = true
arrow.hide()
func _init(title, desc, dest).(title, desc):
destination = dest
add_objective(WalkQuestObjective.new("Walk to destination", dest))
func update():
.update()
func start():
.start()
quest_marker = load("res://markers/quest_marker.tscn").instance()
world.master_node.get_node("/root/main").add_child(quest_marker)
quest_marker.global_transform.origin = destination.global_transform.origin
print("destination: ", quest_marker.global_transform.origin)
if world.arrow:
world.arrow.show()
func quest_complete():
.quest_complete()
quest_marker.queue_free()
if world.arrow:
world.arrow.hide()

View File

@@ -0,0 +1,27 @@
extends Node2D
# Declare member variables here. Examples:
# var a = 2
# var b = "text"
# Called when the node enters the scene tree for the first time.
var target: = Vector3()
var rect: = Rect2()
func _ready():
var pos = get_viewport().get_camera().unproject_position(target)
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
var pos = get_viewport().get_camera().unproject_position(target)
rect = get_viewport_rect()
if rect.has_point(pos):
if !visible:
show()
else:
pos.x = clamp(pos.x, rect.position.x, rect.position.x + rect.size.x)
pos.y = clamp(pos.x, rect.position.x, rect.position.x + rect.size.x)
position = position.linear_interpolate(pos, delta)

View File

@@ -0,0 +1,10 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://ui/textures/minimapIcon_exclamationRed.png" type="Texture" id=1]
[ext_resource path="res://ui/quest_indicator.gd" type="Script" id=2]
[node name="quest_indicator" type="Node2D"]
script = ExtResource( 2 )
[node name="Sprite" type="Sprite" parent="."]
texture = ExtResource( 1 )