Extensive work on VFX for the guild, assets for the world, and portrait variance. Work on quests. Extra work on User Flow completion and file saving.
This commit is contained in:
@@ -1,5 +1,11 @@
|
||||
class_name Adventurer extends Node
|
||||
|
||||
class Appearance extends Resource:
|
||||
var hair_color : String
|
||||
var hair_type : int
|
||||
var skin_color : String
|
||||
var eye_color : String
|
||||
var eye_type : int
|
||||
|
||||
enum Gender{
|
||||
MASC,
|
||||
@@ -17,12 +23,11 @@ var max_energy : int = 1
|
||||
var level : int = 1
|
||||
var exp : int = 0
|
||||
var job : JobData
|
||||
var appearance : Appearance
|
||||
var stats : StatBlock
|
||||
var gold : int = 0
|
||||
var quest : Quest
|
||||
var weapon : Weapon
|
||||
var armor : Armor
|
||||
var accessory : Accessory
|
||||
var equipment : Array = [null, null, null]
|
||||
var inventory : Dictionary[Vector2, Item] = {}
|
||||
var inventory_size : Vector2i = Vector2i(3,2)
|
||||
var inventory_count : int = 0
|
||||
@@ -45,7 +50,7 @@ func generate() -> void:
|
||||
stats.CHA = randi_range(job.min_CHA, job.max_CHA)
|
||||
stats.FAI = randi_range(job.min_FAI, job.max_FAI)
|
||||
stats.LUK = randi_range(job.min_LUK, job.max_LUK)
|
||||
|
||||
generate_appearance()
|
||||
max_life = stats.STR * 10 + stats.CHA * 10
|
||||
max_energy = stats.INT * 10 + stats.FAI * 10
|
||||
life = max_life
|
||||
@@ -64,7 +69,6 @@ func gain_level() -> void:
|
||||
level += 1
|
||||
#TODO: Make stats improve based on job
|
||||
Game.notice("%s has reached level %d!" % [full_name(), level])
|
||||
#TODO: Make Sideview display level up
|
||||
changed.emit()
|
||||
levelled.emit()
|
||||
|
||||
@@ -115,6 +119,50 @@ func pickup_item(item : Item) -> void:
|
||||
inventory_count+=1
|
||||
changed.emit()
|
||||
|
||||
func equip_item(from : Vector2, slot : Item.Slots) -> void:
|
||||
if !inventory.has(from):
|
||||
printerr("Cannot equip item from %s, it is empty!" % [from])
|
||||
|
||||
var itm = inventory[from]
|
||||
if !itm.can_equip_slot(slot):
|
||||
printerr("Cannot equip item from %s to %s, it is the wrong type!" % [from, Item.slot_name(slot)])
|
||||
|
||||
if !job.can_equip(itm):
|
||||
printerr("Cannot equip item from %s to %s, %s cannot equip it!" % [from, Item.slot_name(slot), job.name])
|
||||
|
||||
if equipment[slot] != null:
|
||||
inventory[from] = equipment[slot]
|
||||
remove_stats_from_item(itm)
|
||||
else:
|
||||
inventory.erase(from)
|
||||
equipment[slot] = itm
|
||||
apply_stats_from_item(itm)
|
||||
#Apply Stats from
|
||||
|
||||
func remove_stats_from_item(itm : Item) -> void:
|
||||
stats.STR -= stats.STR
|
||||
stats.DEX-= stats.DEX
|
||||
stats.INT-= stats.INT
|
||||
stats.CHA-= stats.CHA
|
||||
stats.FAI-= stats.FAI
|
||||
stats.LUK-= stats.LUK
|
||||
stats.PATK-= stats.PATK
|
||||
stats.MATK-= stats.MATK
|
||||
stats.PDEF-= stats.PDEF
|
||||
stats.MDEF-= stats.MDEF
|
||||
|
||||
func apply_stats_from_item(itm : Item) -> void:
|
||||
stats.STR += stats.STR
|
||||
stats.DEX+= stats.DEX
|
||||
stats.INT+= stats.INT
|
||||
stats.CHA+= stats.CHA
|
||||
stats.FAI+= stats.FAI
|
||||
stats.LUK+= stats.LUK
|
||||
stats.PATK+= stats.PATK
|
||||
stats.MATK+= stats.MATK
|
||||
stats.PDEF+= stats.PDEF
|
||||
stats.MDEF+= stats.MDEF
|
||||
|
||||
func move_item(from : Vector2, to: Vector2) -> void:
|
||||
if !inventory.has(from):
|
||||
printerr("Cannot move item from %s to %s, %s is empty!" % [from, to, from])
|
||||
@@ -126,3 +174,14 @@ func move_item(from : Vector2, to: Vector2) -> void:
|
||||
inventory.erase(from)
|
||||
inventory[to] = itm
|
||||
changed.emit()
|
||||
|
||||
func generate_appearance(features=null) -> void:
|
||||
if features == null:
|
||||
appearance = Appearance.new()
|
||||
#TODO: Handle different types of hair and eyes
|
||||
appearance.hair_color = AdventurerPortrait.random_color(ColorVariant.Types.HAIR)
|
||||
#appearance.hair_type = randi_range(0,len(job.portrait.hair_types) - 1)
|
||||
appearance.eye_color = AdventurerPortrait.random_color(ColorVariant.Types.EYES)
|
||||
#appearance.eye_type = randi_range(0,len(job.portrait.eye_types) - 1)
|
||||
appearance.skin_color = AdventurerPortrait.random_color(ColorVariant.Types.SKIN)
|
||||
changed.emit()
|
||||
|
||||
@@ -1 +1,41 @@
|
||||
extends Control
|
||||
class_name AdventurerPortrait extends Control
|
||||
|
||||
|
||||
var cv_lists : Array
|
||||
|
||||
func _ready() -> void:
|
||||
cv_lists = []
|
||||
cv_lists.resize(ColorVariant.Types.size())
|
||||
for i in range(len(cv_lists)):
|
||||
cv_lists[i] = []
|
||||
for child in get_children():
|
||||
if child is ColorVariant:
|
||||
cv_lists[child.type].append(child)
|
||||
|
||||
func set_appearance(appearance) -> void:
|
||||
set_color(ColorVariant.Types.HAIR, appearance.hair_color)
|
||||
set_color(ColorVariant.Types.SKIN, appearance.skin_color)
|
||||
set_color(ColorVariant.Types.EYES, appearance.eye_color)
|
||||
|
||||
static func random_color(type : ColorVariant.Types) -> String:
|
||||
var lst
|
||||
match(type):
|
||||
ColorVariant.Types.HAIR: lst = ColorVariant.hair_colors
|
||||
ColorVariant.Types.SKIN: lst = ColorVariant.skin_colors
|
||||
ColorVariant.Types.EYES: lst = ColorVariant.eye_colors
|
||||
var max = 0
|
||||
for opt in lst.keys():
|
||||
max += lst[opt].weight
|
||||
|
||||
var pick = randi_range(1, max)
|
||||
for opt in lst.keys():
|
||||
if lst[opt].weight == 0: #Zero weighted colors are special options.
|
||||
continue
|
||||
pick -= lst[opt].weight
|
||||
if pick < 0:
|
||||
return opt
|
||||
return "ERROR"
|
||||
|
||||
func set_color(type : ColorVariant.Types, color : String) -> void:
|
||||
for cv : ColorVariant in cv_lists[type]:
|
||||
cv.set_color(color)
|
||||
|
||||
@@ -27,6 +27,11 @@ func setup(adv : Adventurer) -> void:
|
||||
for child : ItemSlot in %InventoryGrid.get_children():
|
||||
item_slots.append(child)
|
||||
child.display_item.connect(_on_display_item)
|
||||
if data.job:
|
||||
var portrait : AdventurerPortrait = data.job.portrait.instantiate()
|
||||
%PortraitFrame.add_child(portrait)
|
||||
portrait.scale = Vector2(.6,.6)
|
||||
portrait.set_appearance(data.appearance)
|
||||
#TODO: Show equipment
|
||||
update_items()
|
||||
|
||||
|
||||
@@ -1 +1,135 @@
|
||||
extends TextureRect
|
||||
class_name ColorVariant extends TextureRect
|
||||
|
||||
enum Types{
|
||||
SKIN,
|
||||
HAIR,
|
||||
EYES
|
||||
}
|
||||
static var eye_colors = {
|
||||
"blue":{
|
||||
"weight": 100,
|
||||
"color": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
"luminosity": preload("res://external/test portrait/gradients/eyes/(l)fair.tres"),
|
||||
},
|
||||
"green":{
|
||||
"weight": 100,
|
||||
"color": preload("res://external/test portrait/gradients/eyes/(c)green.tres"),
|
||||
"luminosity": preload("res://external/test portrait/gradients/eyes/(l)default.tres"),
|
||||
},
|
||||
"brown":{
|
||||
"weight": 100,
|
||||
"color": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
"luminosity": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
},
|
||||
"gold":{
|
||||
"weight": 100,
|
||||
"color": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
"luminosity": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
},
|
||||
"red":{
|
||||
"weight": 100,
|
||||
"color": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
"luminosity": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
},
|
||||
"grey":{
|
||||
"weight": 100,
|
||||
"color": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
"luminosity": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
},
|
||||
}
|
||||
|
||||
static var hair_colors = {
|
||||
"blue":{
|
||||
"weight": 100,
|
||||
"color": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
"luminosity": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
},
|
||||
"blonde":{
|
||||
"weight": 100,
|
||||
"color": preload("res://external/test portrait/gradients/hair/(c)blonde.tres"),
|
||||
"luminosity": preload("res://external/test portrait/gradients/hair/(l)fair.tres"),
|
||||
},
|
||||
"brown":{
|
||||
"weight": 100,
|
||||
"color": preload("res://external/test portrait/gradients/hair/(c)brown.tres"),
|
||||
"luminosity": preload("res://external/test portrait/gradients/hair/(l)dark.tres"),
|
||||
},
|
||||
"black":{
|
||||
"weight": 100,
|
||||
"color": preload("res://external/test portrait/gradients/hair/(c)black.tres"),
|
||||
"luminosity": preload("res://external/test portrait/gradients/hair/(l)black.tres"),
|
||||
},
|
||||
"red":{
|
||||
"weight": 100,
|
||||
"color": preload("res://external/test portrait/gradients/hair/(c)red.tres"),
|
||||
"luminosity": preload("res://external/test portrait/gradients/hair/(l)red.tres"),
|
||||
},
|
||||
"silver":{
|
||||
"weight": 100,
|
||||
"color": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
"luminosity": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
},
|
||||
}
|
||||
|
||||
static var skin_colors = {
|
||||
"pale":{
|
||||
"weight": 100,
|
||||
"color": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
"luminosity": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
},
|
||||
"medium":{
|
||||
"weight": 100,
|
||||
"color": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
"luminosity": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
},
|
||||
"olive":{
|
||||
"weight": 100,
|
||||
"color": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
"luminosity": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
},
|
||||
"brown":{
|
||||
"weight": 100,
|
||||
"color": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
"luminosity": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
},
|
||||
"dark":{
|
||||
"weight": 100,
|
||||
"color": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
"luminosity": preload("res://external/test portrait/gradients/eyes/(l)default.tres"),
|
||||
},
|
||||
"white":{
|
||||
"weight": 0,
|
||||
"color": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
"luminosity": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
},
|
||||
"red":{
|
||||
"weight": 0,
|
||||
"color": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
"luminosity": preload("res://external/test portrait/gradients/eyes/(c)blue.tres"),
|
||||
},
|
||||
}
|
||||
@export var type : Types
|
||||
|
||||
#@onready var variant_material : ShaderMaterial = material
|
||||
|
||||
func set_color(color : String) -> void:
|
||||
var col_gradients
|
||||
match(type):
|
||||
Types.SKIN:
|
||||
if !skin_colors.has(color):
|
||||
printerr("Tried to set a color '%s' that isn't part of the skin color list!" % color)
|
||||
return
|
||||
col_gradients = skin_colors[color]
|
||||
Types.HAIR:
|
||||
if !hair_colors.has(color):
|
||||
printerr("Tried to set a color '%s' that isn't part of the hair color list!" % color)
|
||||
return
|
||||
col_gradients = hair_colors[color]
|
||||
Types.EYES:
|
||||
if !eye_colors.has(color):
|
||||
printerr("Tried to set a color '%s' that isn't part of the eye color list!" % color)
|
||||
return
|
||||
col_gradients = eye_colors[color]
|
||||
var mat = material
|
||||
mat.set_shader_parameter("color_gradient",col_gradients.color)
|
||||
mat.set_shader_parameter("luminosity_gradient",col_gradients.luminosity)
|
||||
|
||||
@@ -72,3 +72,54 @@ func notice(msg : String, time : float = 1) -> void:
|
||||
|
||||
func calculate_kill_exp(killer : QuestSprite, killed : QuestSprite) -> int:
|
||||
return clamp(1, (killed.level - killer.level) * 5, 100)
|
||||
|
||||
func test_save() -> void:
|
||||
var image : Image = get_viewport().get_texture().get_image()
|
||||
var save_dict = {
|
||||
"savetime": Time.get_datetime_string_from_system(),
|
||||
"screenshot": image.save_png_to_buffer().hex_encode()
|
||||
}
|
||||
#Save the guild data
|
||||
save_dict["guildname"] = Guild.name
|
||||
save_dict["guildlevel"] = Guild.level
|
||||
#Save the player data
|
||||
save_dict["playername"] = player.data.full_name()
|
||||
save_dict["playerlevel"] = player.data.level
|
||||
#Save the employee data
|
||||
#Save the adventurer data
|
||||
#Save the quest data
|
||||
#Save the quest progress
|
||||
var save_file = FileAccess.open("user://savefile.save", FileAccess.WRITE)
|
||||
save_file.store_line(JSON.stringify(save_dict))
|
||||
|
||||
func get_savefile_data(filename : String) -> Dictionary:
|
||||
var load_file = FileAccess.open("user://" + filename, FileAccess.READ)
|
||||
var json = JSON.new()
|
||||
var json_string = load_file.get_line()
|
||||
var parse_result = json.parse(json_string)
|
||||
if not parse_result == OK:
|
||||
printerr("JSON Parse Error: ", json.get_error_message(), " in ", json_string, " at line ", json.get_error_line())
|
||||
return {}
|
||||
var image = Image.new()
|
||||
var ss : String = json.data.screenshot
|
||||
#print(ss.data)
|
||||
image.load_png_from_buffer(ss.hex_decode())
|
||||
var data_dict = {
|
||||
"playername": json.data.playername,
|
||||
"playerlevel": json.data.playerlevel,
|
||||
"guildname": json.data.guildname,
|
||||
"guildlevel": json.data.guildlevel,
|
||||
"savetime": json.data.savetime,
|
||||
"screenshot": image
|
||||
}
|
||||
return data_dict
|
||||
|
||||
func test_load(filename : String) -> void:
|
||||
var load_file = FileAccess.open("user://" + filename, FileAccess.READ)
|
||||
var json = JSON.new()
|
||||
var json_string = load_file.get_line()
|
||||
var parse_result = json.parse_string(json_string)
|
||||
if not parse_result == OK:
|
||||
printerr("JSON Parse Error: ", json.get_error_message(), " in ", json_string, " at line ", json.get_error_line())
|
||||
return
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ var quest : Quest
|
||||
func generate_quest() -> void:
|
||||
quest = Quest.new()
|
||||
quest.name = "A Test Quest"
|
||||
quest.location = "Nestor Woods"
|
||||
quest.location = Quest.Locations.NESTORS_WOODS
|
||||
quest.difficulty = 1
|
||||
quest.length = 60
|
||||
quest.rewards = {"exp":100,"gold":1}
|
||||
@@ -16,7 +16,7 @@ func update_quest_window() -> void:
|
||||
if quest:
|
||||
%NameField.text = quest.name
|
||||
%DifficultyField.text = quest.difficulty_name()
|
||||
%LocationField.text = quest.location
|
||||
%LocationField.text = quest.location_name()
|
||||
#for reward in quest.rewards.:
|
||||
|
||||
func reset() -> void:
|
||||
|
||||
@@ -21,6 +21,19 @@ func setup(itm : Item) -> void:
|
||||
item_grade.text = item.grade
|
||||
primary_stat.text = item.primary_stat()
|
||||
#TODO: Implement item secondary stats meaningfully
|
||||
var ss_string = ""
|
||||
var ss_count = 0
|
||||
var pp_list : Array = item.stats.get_property_list()
|
||||
print(item.stats)
|
||||
print(item.stats.INT)
|
||||
for stat in pp_list.slice(9, len(pp_list)-1):
|
||||
var val = item.stats.get(stat.name)
|
||||
if val:
|
||||
ss_string += stat.name + " " + ("+" if val > 0 else "")
|
||||
ss_string += str(val) + ("\n" if ss_count % 2 else " ")
|
||||
ss_count += 1
|
||||
if ss_count > 0:
|
||||
secondary_stats.text = ss_string
|
||||
brief.text = item.brief
|
||||
|
||||
func _on_drag_region_gui_input(event: InputEvent) -> void:
|
||||
|
||||
19
scripts/loader_screen.gd
Normal file
19
scripts/loader_screen.gd
Normal file
@@ -0,0 +1,19 @@
|
||||
extends Control
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
var data = Game.get_savefile_data("savefile.save")
|
||||
%GuildName.text = data.guildname
|
||||
%GuildLevel.text = "Lv" + str(int(data.guildlevel))
|
||||
%PlayerName.text = data.playername
|
||||
%PlayerLevel.text = "Lv" + str(int(data.playerlevel))
|
||||
%SaveTime.text = data.savetime
|
||||
%Screenshot.texture = ImageTexture.create_from_image(data.screenshot)
|
||||
|
||||
|
||||
func _on_continue_button_pressed() -> void:
|
||||
Game.test_load("savefile.save")
|
||||
|
||||
|
||||
func _on_cancel_button_pressed() -> void:
|
||||
get_tree().change_scene_to_file("res://scenes/start_menu.tscn")
|
||||
1
scripts/loader_screen.gd.uid
Normal file
1
scripts/loader_screen.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dntthy6adxa44
|
||||
@@ -14,6 +14,8 @@ func _ready() -> void:
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
time_changed.emit(timer.time_left)
|
||||
if Input.is_action_just_pressed("save"):
|
||||
Game.test_save()
|
||||
|
||||
func add_quest_progress_bar(quest : Quest) -> void:
|
||||
var qpb : QuestProgressBar = quest_progress_bar_template.instantiate()
|
||||
|
||||
258
scripts/quest.gd
258
scripts/quest.gd
@@ -1,258 +0,0 @@
|
||||
class_name Quest extends Object
|
||||
|
||||
|
||||
|
||||
enum Status{
|
||||
OPEN,
|
||||
TAKEN,
|
||||
IN_PROGRESS,
|
||||
COMPLETED,
|
||||
FAILED,
|
||||
CLOSED
|
||||
}
|
||||
|
||||
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 type : Type = Type.WAIT
|
||||
var enemies : Array[String] = []
|
||||
var time : float = 1
|
||||
var time_elapsed
|
||||
signal completed()
|
||||
signal failed()
|
||||
|
||||
var participants : Array = []
|
||||
var turn_queue : Array = []
|
||||
var busy_list : Array = []
|
||||
var combat_state
|
||||
var dex_speed : int
|
||||
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)
|
||||
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 / 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 / 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 = 10
|
||||
|
||||
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:
|
||||
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()
|
||||
execute_attack(combatant, target)
|
||||
|
||||
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:
|
||||
completed.emit()
|
||||
Type.WAIT:
|
||||
time_elapsed += delta
|
||||
if time_elapsed >= time:
|
||||
completed.emit()
|
||||
|
||||
var name : String = "A Basic Quest"
|
||||
var desc : String = "The default quest, with no special anything."
|
||||
var difficulty : int = 1
|
||||
var location : String
|
||||
var steps : int = 1
|
||||
var rewards : Dictionary
|
||||
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)
|
||||
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 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"
|
||||
@@ -1 +0,0 @@
|
||||
uid://bowt76gfx40pv
|
||||
@@ -16,7 +16,7 @@ func setup(qst : Quest) -> void:
|
||||
func update() -> void:
|
||||
nameLabel.text = quest.name
|
||||
difficultyLabel.text = quest.difficulty_name()
|
||||
locationLabel.text = quest.location
|
||||
locationLabel.text = quest.location_name()
|
||||
#TODO: Show the current status of the quest
|
||||
|
||||
func close() -> void:
|
||||
|
||||
12
scripts/quest_notice_banner.gd
Normal file
12
scripts/quest_notice_banner.gd
Normal file
@@ -0,0 +1,12 @@
|
||||
extends Sprite2D
|
||||
|
||||
func _ready():
|
||||
var move_tween = create_tween()
|
||||
move_tween.tween_property(self, "position:y", -40, 1.25)
|
||||
var fade_tween = create_tween()
|
||||
modulate.a = 0
|
||||
fade_tween.tween_property(self, "modulate:a", 1, .15)
|
||||
fade_tween.tween_interval(0.85)
|
||||
fade_tween.tween_property(self, "modulate:a", 0, .25)
|
||||
fade_tween.tween_callback(queue_free)
|
||||
|
||||
1
scripts/quest_notice_banner.gd.uid
Normal file
1
scripts/quest_notice_banner.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cv7wuquahc53c
|
||||
@@ -1,7 +1,8 @@
|
||||
class_name QuestorSprite extends QuestSprite
|
||||
|
||||
|
||||
@onready var levelup_banner_template = preload("res://templates/levelup_banner.tscn")
|
||||
var data : Adventurer = null
|
||||
var banner_lag : float
|
||||
|
||||
func _ready() -> void:
|
||||
if data:
|
||||
@@ -14,6 +15,10 @@ func _ready() -> void:
|
||||
stats = StatBlock.copy(data.stats)
|
||||
gold = data.gold
|
||||
|
||||
func _process(delta) -> void:
|
||||
if banner_lag > 0:
|
||||
banner_lag -= delta
|
||||
|
||||
func set_animation(anim_name : String) -> void:
|
||||
anim_player.play(anim_name)
|
||||
|
||||
@@ -28,6 +33,23 @@ func setup(adv : Adventurer) -> void:
|
||||
stats = StatBlock.copy(data.stats)
|
||||
gold = data.gold
|
||||
adv.quest_sprite = self
|
||||
data.levelled.connect(_on_level_up)
|
||||
|
||||
func show_levelup_banner(lagged : bool = false) -> void:
|
||||
if !lagged and banner_lag > 0:
|
||||
var tween = create_tween()
|
||||
tween.tween_interval(banner_lag)
|
||||
tween.tween_callback(show_levelup_banner.bind(true))
|
||||
banner_lag += .75
|
||||
return
|
||||
var banner = levelup_banner_template.instantiate()
|
||||
banner.position = Vector2.ZERO
|
||||
$BannerOffset.add_child(banner)
|
||||
banner_lag = .75
|
||||
|
||||
|
||||
func check_levelup() -> void:
|
||||
data.gain_exp(exp)
|
||||
|
||||
func _on_level_up() -> void:
|
||||
show_levelup_banner()
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
extends Control
|
||||
|
||||
|
||||
func _on_button_pressed() -> void:
|
||||
func _ready() -> void:
|
||||
%ContinueButton.disabled = !FileAccess.file_exists("user://savefile.save")
|
||||
|
||||
func _on_start_button_pressed() -> void:
|
||||
get_tree().change_scene_to_file("res://scenes/active_scene.tscn")
|
||||
|
||||
|
||||
func _on_continue_button_pressed() -> void:
|
||||
get_tree().change_scene_to_file("res://scenes/loader_screen.tscn")
|
||||
|
||||
58
scripts/void_ripple.gd
Normal file
58
scripts/void_ripple.gd
Normal file
@@ -0,0 +1,58 @@
|
||||
@tool
|
||||
extends Node2D
|
||||
|
||||
@onready var center : Node2D = $Center
|
||||
@onready var outline : Line2D = $Line2D
|
||||
@export var cycle_max : float = 30.0
|
||||
@export var ripple_num : int = 3
|
||||
@export var speed : float = 200
|
||||
@export var ripple_width : float = 3
|
||||
@export var gradient : Gradient
|
||||
var l_norms : Array[Vector2] = []
|
||||
func _ready():
|
||||
recalc_lnorms()
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
queue_redraw()
|
||||
print("test")
|
||||
|
||||
func recalc_lnorms():
|
||||
var polypoints : PackedVector2Array
|
||||
polypoints = outline.points.slice(0, -1)
|
||||
|
||||
var left_neighbor : Vector2
|
||||
var right_neighbor : Vector2
|
||||
#print("Recalc set")
|
||||
for i in range(len(outline.points)):
|
||||
var p = outline.points[i]
|
||||
if i == 0:
|
||||
left_neighbor = outline.points[-2]
|
||||
else:
|
||||
left_neighbor = outline.points[i-1]
|
||||
|
||||
if i == len(outline.points) - 1:
|
||||
right_neighbor = outline.points[1]
|
||||
else:
|
||||
right_neighbor = outline.points[i+1]
|
||||
var shift : Vector2 = ((left_neighbor - p).normalized() + (right_neighbor - p).normalized()).normalized()
|
||||
if Geometry2D.is_point_in_polygon(p + shift, polypoints):
|
||||
#print("Offset moves the point into the polygon, flip!")
|
||||
shift *= -1
|
||||
l_norms.append(shift)
|
||||
|
||||
|
||||
func _draw() -> void:
|
||||
var ripple_line : PackedVector2Array
|
||||
ripple_line.resize(len(outline.points))
|
||||
|
||||
var min : float = -cycle_max / ripple_num
|
||||
var max : float = cycle_max + min
|
||||
var cycle = wrapf(Time.get_ticks_msec()*speed, min, max)
|
||||
for j in range(ripple_num):
|
||||
var color = gradient.sample((cycle / cycle_max))
|
||||
for i in len(ripple_line):
|
||||
ripple_line[i] = outline.points[i] + l_norms[i] * cycle
|
||||
#print(str(outline.points[i]) + " becomes " + str(ripple_line[i]) + " because of offset " + str(l_norms[i]))
|
||||
draw_polyline(ripple_line, Color(color, .8 * (cycle_max - cycle) / cycle_max), ripple_width * (cycle_max - cycle) / cycle_max, true)
|
||||
cycle = wrapf(cycle + cycle_max / ripple_num, min, max)
|
||||
pass
|
||||
1
scripts/void_ripple.gd.uid
Normal file
1
scripts/void_ripple.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://2g4ja3a7o2l
|
||||
Reference in New Issue
Block a user