From 1be1379b02d592f385d5252a0decb50af8280db1 Mon Sep 17 00:00:00 2001 From: Nicholas Hydock Date: Sun, 18 Nov 2018 09:59:15 -0500 Subject: [PATCH] Add Character Growth Characters gain experience points at the end of battle, which contribute to their level. Levels determine stat growth along defined curves for each character. --- godot/assets/shaders/transition.material | Bin 303 -> 304 bytes godot/combat/CombatArena.gd | 10 ++- godot/combat/CombatArena.tscn | 49 ++++++++++++- godot/combat/Rewards.gd | 69 ++++++++++++++++++ godot/combat/battlers/Battler.gd | 12 +-- godot/combat/battlers/Battler.tscn | 2 + godot/combat/battlers/Job.gd | 4 +- godot/combat/battlers/PorcupineBattler.tscn | 15 ++-- godot/combat/battlers/jobs/DefaultJob.tres | 5 +- godot/combat/battlers/jobs/Fighter.tres | 51 +++++++++++-- godot/combat/battlers/jobs/GrowthStats.gd | 44 +++++++++++ godot/combat/battlers/jobs/Porcupine.tres | 3 +- .../battlers/jobs/PorcupineStronger.tres | 4 +- godot/combat/battlers/jobs/StartingStats.gd | 38 ++++++++-- godot/combat/battlers/stats/CharacterStats.gd | 31 +++++++- godot/combat/battlers/stats/Stats.tscn | 4 - godot/project.godot | 18 +++++ 17 files changed, 320 insertions(+), 39 deletions(-) create mode 100644 godot/combat/Rewards.gd create mode 100644 godot/combat/battlers/jobs/GrowthStats.gd diff --git a/godot/assets/shaders/transition.material b/godot/assets/shaders/transition.material index e76cb8f8232e2af2c3238eda799fb91463b3a012..f34139a9c79d4a3bf2579d17221c79166f4d4f82 100644 GIT binary patch delta 221 zcmV<303!dd0TWk<9vB}T5ETLx zjt`JTexLz)9xNI(X$+r6D{X4@GLqsic+wnuY#ZuJoA%{YE~XT*4nM#TV!AFCABEL9 zZY`V}Y*pqt_iV!$jC;{a4=O^6;r$8LcuXBrNNHYL!ueo3I9sk&?kNYCb5t&k+@}7L W-(Uxs=EDii3uY`oLi0yHk Array: + """ + Rewards the surviving party members with experience points + + @returns Array of Battlers who have leveled up + """ + var survived = [] + for member in party: + if member.stats.is_alive: + survived.append(member) + + var exp_per_survivor = experience_earned / len(survived) + var leveled_up = [] + for member in survived: + var level = member.stats.level + member.stats.experience += exp_per_survivor + if level < member.stats.level: + leveled_up.append(member) + return leveled_up + +func _on_victory(): + """ + On victory be sure to grant the battlers their earned exp + and show the interface + """ + var leveled_up = _reward_to_battlers() + $Panel.visible = true + $Panel/Label.text = "EXP Earned %d" % experience_earned + yield(get_tree().create_timer(2.0), "timeout") + for battler in leveled_up: + $Panel/Label.text = "%s Leveled Up to %d" % [battler.name, battler.stats.level + 1] + yield(get_tree().create_timer(2.0), "timeout") + for drop in drops: + $Panel/Label.text = "Found %s" % drop.name + yield(get_tree().create_timer(2.0), "timeout") + $Panel.visible = false + # TODO transition back to map + +func _on_flee(): + """ + End combat condition when the party flees + """ + experience_earned /= 2 + _reward_to_battlers() diff --git a/godot/combat/battlers/Battler.gd b/godot/combat/battlers/Battler.gd index dc4806e6..86f1cc80 100644 --- a/godot/combat/battlers/Battler.gd +++ b/godot/combat/battlers/Battler.gd @@ -5,7 +5,7 @@ class_name Battler export var TARGET_OFFSET_DISTANCE : float = 120.0 const DEFAULT_CHANCE = 0.75 -onready var stats : CharacterStats = $Job/Stats as CharacterStats +onready var stats : CharacterStats = $Job.stats onready var lifebar_anchor = $InterfaceAnchor onready var skin = $Skin onready var actions = $Actions @@ -46,7 +46,7 @@ func use_skill(target : Battler, skill : CharacterSkill) -> void: if stats.mana < skill.mana_cost: return stats.mana -= skill.mana_cost - var hit = Hit.new(stats.strength, skill.base_damage) + var hit = Hit.new(stats.strength, skill.base_damage) target.take_damage(hit) func take_damage(hit): @@ -64,9 +64,11 @@ func appear(): skin.appear() func choose_target(targets : Array): - """ This function will return a target with the following policy: - There is a chance of DEFAULT_CHANCE to target the foe with min health - else it will randomly choose an opponent""" + """ + This function will return a target with the following policy: + There is a chance of DEFAULT_CHANCE to target the foe with min health + else it will randomly choose an opponent + """ var this_chance = randi() % 100 var target_min_health = targets[randi() % len(targets)] diff --git a/godot/combat/battlers/Battler.tscn b/godot/combat/battlers/Battler.tscn index 3c3fbe3d..0e414e2f 100644 --- a/godot/combat/battlers/Battler.tscn +++ b/godot/combat/battlers/Battler.tscn @@ -57,6 +57,8 @@ party_member = false [node name="Skin" type="Position2D" parent="."] modulate = Color( 1, 1, 1, 0 ) script = ExtResource( 2 ) +TURN_START_MOVE_DISTANCE = null +TWEEN_DURATION = null [node name="Tween" type="Tween" parent="Skin"] repeat = false diff --git a/godot/combat/battlers/Job.gd b/godot/combat/battlers/Job.gd index e5c427d1..30db1a9e 100644 --- a/godot/combat/battlers/Job.gd +++ b/godot/combat/battlers/Job.gd @@ -4,7 +4,7 @@ extends Node class_name Job -onready var stats = $Stats +var stats : CharacterStats onready var skills = $Skills export var starting_stats : Resource @@ -12,7 +12,7 @@ export(Array, String) var starting_skills export(PackedScene) var character_skill_scene : PackedScene func _ready(): - stats.initialize(starting_stats) + stats = CharacterStats.new(starting_stats) if starting_skills != null and starting_skills.size() > 0: for skill in starting_skills: var new_skill = character_skill_scene.instance() diff --git a/godot/combat/battlers/PorcupineBattler.tscn b/godot/combat/battlers/PorcupineBattler.tscn index a8736359..5a66d1fe 100644 --- a/godot/combat/battlers/PorcupineBattler.tscn +++ b/godot/combat/battlers/PorcupineBattler.tscn @@ -1,18 +1,17 @@ -[gd_scene load_steps=5 format=2] +[gd_scene load_steps=4 format=2] [ext_resource path="res://combat/battlers/Battler.tscn" type="PackedScene" id=1] [ext_resource path="res://animation/PorcupineAnim.tscn" type="PackedScene" id=2] -[ext_resource path="res://combat/battlers/Job.tscn" type="PackedScene" id=3] -[ext_resource path="res://combat/battlers/jobs/Porcupine.tres" type="Resource" id=4] +[ext_resource path="res://combat/battlers/jobs/Porcupine.tres" type="Resource" id=3] -[node name="Porcupine" index="0" instance=ExtResource( 1 )] +[node name="Porcupine" instance=ExtResource( 1 )] [node name="PorcupineAnim" parent="Skin" index="0" instance=ExtResource( 2 )] -[node name="Porcupine" parent="." index="1" instance=ExtResource( 3 )] -_sections_unfolded = [ "skills", "starting_stats" ] -starting_stats = ExtResource( 4 ) +[node name="Job" parent="." index="1"] +starting_stats = ExtResource( 3 ) +starting_skills = [ ] -[node name="InterfaceAnchor" parent="." index="4"] +[node name="InterfaceAnchor" parent="." index="3"] position = Vector2( 0, 48.9708 ) diff --git a/godot/combat/battlers/jobs/DefaultJob.tres b/godot/combat/battlers/jobs/DefaultJob.tres index 89d70e28..60412935 100644 --- a/godot/combat/battlers/jobs/DefaultJob.tres +++ b/godot/combat/battlers/jobs/DefaultJob.tres @@ -7,8 +7,9 @@ script = ExtResource( 1 ) job_name = "Job" max_health = 6 -max_mana = null +max_mana = 0 strength = 2 defense = 0 -speed = null +speed = 0 +experience = 0 diff --git a/godot/combat/battlers/jobs/Fighter.tres b/godot/combat/battlers/jobs/Fighter.tres index 51617e03..009cabfb 100644 --- a/godot/combat/battlers/jobs/Fighter.tres +++ b/godot/combat/battlers/jobs/Fighter.tres @@ -1,14 +1,49 @@ -[gd_resource type="Resource" load_steps=2 format=2] +[gd_resource type="Resource" load_steps=7 format=2] -[ext_resource path="res://combat/battlers/jobs/StartingStats.gd" type="Script" id=1] +[ext_resource path="res://combat/battlers/jobs/GrowthStats.gd" type="Script" id=1] + +[sub_resource type="Curve" id=1] + +min_value = 0.99 +max_value = 210.0 +bake_resolution = 10 +_data = [ Vector2( 0, 0 ), 0.0, 210.0, 0, 1, Vector2( 1, 210 ), 210.0, 0.0, 1, 0 ] + +[sub_resource type="Curve" id=3] + +min_value = 0.0 +max_value = 999.0 +bake_resolution = 10 +_data = [ Vector2( 0, 68.7313 ), 0.0, 0.0, 0, 0, Vector2( 0.168959, 376.423 ), 1283.12, 1283.12, 0, 0, Vector2( 1, 999 ), 2386.91, 0.0, 0, 0 ] + +[sub_resource type="Curve" id=4] + +min_value = 0.0 +max_value = 80.0 +bake_resolution = 10 +_data = [ Vector2( 0, 0 ), 0.0, 0.0, 0, 0, Vector2( 0.603628, 45.632 ), 6.7181, 291.117, 0, 0, Vector2( 1, 80 ), 0.0, 0.0, 0, 0 ] + +[sub_resource type="Curve" id=5] + +min_value = 0.99 +max_value = 145.0 +bake_resolution = 10 +_data = [ Vector2( 0, 3 ), 0.0, 0.0, 0, 0, Vector2( 0.992522, 145 ), 198.8, 0.0, 0, 0 ] + +[sub_resource type="Curve" id=6] + +min_value = 0.99 +max_value = 233.0 +bake_resolution = 10 +_data = [ Vector2( 0, 8 ), 0.0, 803.058, 0, 0, Vector2( 0.251992, 114.45 ), -17.2645, 224.438, 0, 0, Vector2( 1, 233 ), 184.979, 0.0, 0, 0 ] [resource] script = ExtResource( 1 ) -job_name = "Fighter" -max_health = 15 -max_mana = 4 -strength = 4 -defense = 1 -speed = 5 +experience_curve = [ 0, 5, 15, 50, 120, 200, 325, 500, 800, 1250 ] +max_health_curve = SubResource( 3 ) +max_mana_curve = SubResource( 4 ) +strength_curve = SubResource( 6 ) +defense_curve = SubResource( 1 ) +speed_curve = SubResource( 5 ) diff --git a/godot/combat/battlers/jobs/GrowthStats.gd b/godot/combat/battlers/jobs/GrowthStats.gd new file mode 100644 index 00000000..0d5baca0 --- /dev/null +++ b/godot/combat/battlers/jobs/GrowthStats.gd @@ -0,0 +1,44 @@ +extends StartingStats + +class_name GrowthStats + +var MAX_LEVEL : int setget ,_get_max_level +var _interpolated_level : float +export var experience_curve : Array +export var max_health_curve : Curve +export var max_mana_curve : Curve +export var strength_curve : Curve +export var defense_curve : Curve +export var speed_curve : Curve + +func _get_max_level() -> int: + return len(experience_curve) + +func _get_interpolated_level() -> float: + return float(level) / float(self.MAX_LEVEL) + +func _set_experience(value : int = 0): + """ + Calculate level, which updates all stats + """ + experience = value + var l = level + while l + 1 < self.MAX_LEVEL && experience > experience_curve[l + 1]: + l += 1 + level = l + _interpolated_level = float(level) / float(self.MAX_LEVEL) + +func _get_max_health() -> int: + return int(max_health_curve.interpolate_baked(_interpolated_level)) + +func _get_max_mana() -> int: + return int(max_mana_curve.interpolate_baked(_interpolated_level)) + +func _get_strength() -> int: + return int(strength_curve.interpolate_baked(_interpolated_level)) + +func _get_defense() -> int: + return int(defense_curve.interpolate_baked(_interpolated_level)) + +func _get_speed() -> int: + return int(speed_curve.interpolate_baked(_interpolated_level)) diff --git a/godot/combat/battlers/jobs/Porcupine.tres b/godot/combat/battlers/jobs/Porcupine.tres index 0f8b49f6..c862b2c6 100644 --- a/godot/combat/battlers/jobs/Porcupine.tres +++ b/godot/combat/battlers/jobs/Porcupine.tres @@ -7,8 +7,9 @@ script = ExtResource( 1 ) job_name = "Porcupine" max_health = 6 -max_mana = null +max_mana = 0 strength = 3 defense = 0 speed = 2 +experience = 5 diff --git a/godot/combat/battlers/jobs/PorcupineStronger.tres b/godot/combat/battlers/jobs/PorcupineStronger.tres index a40e1bbb..2b51e16a 100644 --- a/godot/combat/battlers/jobs/PorcupineStronger.tres +++ b/godot/combat/battlers/jobs/PorcupineStronger.tres @@ -5,9 +5,11 @@ [resource] script = ExtResource( 1 ) -_sections_unfolded = [ "Resource", "Script", "script" ] job_name = "PorcupineStrong" max_health = 11 +max_mana = 0 strength = 4 defense = 1 +speed = 0 +experience = 8 diff --git a/godot/combat/battlers/jobs/StartingStats.gd b/godot/combat/battlers/jobs/StartingStats.gd index ed1b0c58..958c546d 100644 --- a/godot/combat/battlers/jobs/StartingStats.gd +++ b/godot/combat/battlers/jobs/StartingStats.gd @@ -4,8 +4,36 @@ class_name StartingStats export var job_name : String = "Job" -export var max_health : int -export var max_mana : int -export var strength : int -export var defense : int -export var speed : int +export var max_health : int setget ,_get_max_health +export var max_mana : int setget ,_get_max_mana +export var strength : int setget ,_get_strength +export var defense : int setget ,_get_defense +export var speed : int setget ,_get_speed +export var experience : int setget _set_experience, _get_experience +var level : int = 0 setget ,_get_level + +func _get_max_health() -> int: + return max_health + +func _get_max_mana() -> int: + return max_mana + +func _get_strength() -> int: + return strength + +func _get_defense() -> int: + return defense + +func _get_speed() -> int: + return speed + +func _get_experience() -> int: + return experience + +func _set_experience(value): + if value == null or value < 0: + value = 0 + experience = value + +func _get_level() -> int: + return level diff --git a/godot/combat/battlers/stats/CharacterStats.gd b/godot/combat/battlers/stats/CharacterStats.gd index b5eda37f..abd7798d 100644 --- a/godot/combat/battlers/stats/CharacterStats.gd +++ b/godot/combat/battlers/stats/CharacterStats.gd @@ -7,6 +7,8 @@ signal health_depleted() var modifiers = {} +var stats : StartingStats + var health : int var mana : int var max_health : int setget set_max_health @@ -14,8 +16,13 @@ var max_mana : int setget set_max_mana var strength : int var defense : int var speed : int +var is_alive : bool setget ,_is_alive +var experience : int setget set_experience +var level : int -func initialize(stats : StartingStats): +func _init(stats : StartingStats): + self.stats = stats + max_health = stats.max_health max_mana = stats.max_mana strength = stats.strength @@ -23,6 +30,25 @@ func initialize(stats : StartingStats): speed = stats.speed health = max_health mana = max_mana + experience = stats.experience + level = stats.level + +func set_experience(value): + """ + Calculate level, which updates all stats + """ + stats.experience = value + max_health = stats.max_health + max_mana = stats.max_mana + strength = stats.strength + defense = stats.defense + speed = stats.speed + + # recover hp and mana on level up + if level != stats.level: + level = stats.level + health = max_health + mana = max_mana func set_max_health(value): max_health = max(0, value) @@ -47,3 +73,6 @@ func add_modifier(id, modifier): func remove_modifier(id): modifiers.erase(id) + +func _is_alive(): + return health >= 0 diff --git a/godot/combat/battlers/stats/Stats.tscn b/godot/combat/battlers/stats/Stats.tscn index 11642b6e..1748bd75 100644 --- a/godot/combat/battlers/stats/Stats.tscn +++ b/godot/combat/battlers/stats/Stats.tscn @@ -2,10 +2,6 @@ [ext_resource path="res://combat/battlers/stats/CharacterStats.gd" type="Script" id=1] - [node name="Stats" type="Node"] script = ExtResource( 1 ) -max_health = 9 -strength = null -defense = null diff --git a/godot/project.godot b/godot/project.godot index a193f3b9..73496716 100644 --- a/godot/project.godot +++ b/godot/project.godot @@ -20,6 +20,11 @@ _global_script_classes=[ { "path": "res://animation/BattlerAnim.gd" }, { "base": "Node", +"class": "CharacterSkill", +"language": "GDScript", +"path": "res://combat/battlers/skills/CharacterSkill.gd" +}, { +"base": "Node", "class": "CharacterStats", "language": "GDScript", "path": "res://combat/battlers/stats/CharacterStats.gd" @@ -29,6 +34,11 @@ _global_script_classes=[ { "language": "GDScript", "path": "res://combat/CombatAction.gd" }, { +"base": "Resource", +"class": "GrowthStats", +"language": "GDScript", +"path": "res://combat/battlers/jobs/GrowthStats.gd" +}, { "base": "Reference", "class": "Hit", "language": "GDScript", @@ -40,6 +50,11 @@ _global_script_classes=[ { "path": "res://combat/battlers/Job.gd" }, { "base": "Resource", +"class": "Skill", +"language": "GDScript", +"path": "res://combat/battlers/skills/Skill.gd" +}, { +"base": "Resource", "class": "StartingStats", "language": "GDScript", "path": "res://combat/battlers/jobs/StartingStats.gd" @@ -52,10 +67,13 @@ _global_script_classes=[ { _global_script_class_icons={ "Battler": "", "BattlerAnim": "", +"CharacterSkill": "", "CharacterStats": "", "CombatAction": "", +"GrowthStats": "", "Hit": "", "Job": "", +"Skill": "", "StartingStats": "", "TurnQueue": "" }