
- Added more spells - Added plans for more spells - Added support for healing spells - Worked on getting enemy spellbook randomization implemented - Added some enemy animations - Tweaked healthbar size so that red doesn't poke over the top - Cleaned up some unused scripts
160 lines
4.6 KiB
GDScript
160 lines
4.6 KiB
GDScript
class_name Combatant extends Node2D
|
|
|
|
signal healthChanged(health: float)
|
|
signal stunned
|
|
signal died(victory: bool)
|
|
|
|
@onready var renderer: AnimatedSprite2D = $AnimatedSprite2D
|
|
@onready var healthbar: HealthBar = $HealthBar
|
|
@onready var data: Data = get_node("/root/Root/Data")
|
|
|
|
@export var spellbook: Spellbook
|
|
@export var maxHealth: float = 10
|
|
@export var health: float = maxHealth
|
|
@export var player: bool = false
|
|
|
|
var spell: Spell
|
|
var spellIndex: int
|
|
var anim: AnimationBase
|
|
var casting: bool = false
|
|
var castCooldown: float = 0
|
|
var castProgress: int = 0
|
|
var defending: Array[Spell]
|
|
var availableSpells: Array[int]
|
|
|
|
# Called when the node enters the scene tree for the first time.
|
|
func _ready():
|
|
renderer.animation_finished.connect(animationFinished)
|
|
renderer.play("idle")
|
|
healthbar.maxHealth = maxHealth
|
|
if !player:
|
|
data.getSpellbook()
|
|
spellbook.initCooldowns()
|
|
for spel: Spell in spellbook.spells:
|
|
if (spel == null): continue
|
|
if !data.animations.has(spel.animation):
|
|
data.animations[spel.animation] = load(spel.animation)
|
|
data.opponent = self
|
|
renderer.flip_h = true
|
|
healthbar.position.x *= -1
|
|
_finishedCasting()
|
|
else:
|
|
data.player = self
|
|
|
|
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
|
func _process(delta):
|
|
if !player && data.playing:
|
|
if (!casting): timing(delta)
|
|
for i in range(spellbook.cooldowns.size()):
|
|
spellbook.cooldowns[i] -= delta
|
|
if spellbook.cooldowns[i] < 0: spellbook.cooldowns[i] = 0
|
|
|
|
func _finishedCasting() -> void:
|
|
casting = false
|
|
match data.difficulty:
|
|
Data.Difficulty.EASY: castCooldown = randf_range(2.7, 3.3)
|
|
Data.Difficulty.NORMAL: castCooldown = randf_range(0.8, 1.3)
|
|
Data.Difficulty.HARD: castCooldown = randf_range(0.3, 0.7)
|
|
Data.Difficulty.GAMER: castCooldown = 0
|
|
|
|
func timing(delta) -> void:
|
|
castCooldown -= delta
|
|
if castCooldown <= 0:
|
|
cast()
|
|
|
|
func cast() -> void:
|
|
var spellOptions: Array[Spell] = spellbook.spells.filter(func(spel): return spellbook.cooldowns[spellbook.spells.find(spel)] == 0)
|
|
if (spellOptions.size() == 0): return
|
|
spell = spellOptions[randi_range(0, spellOptions.size() - 1)]
|
|
spellIndex = spellbook.spells.find(spell)
|
|
anim = data.animations[spell.animation].instantiate()
|
|
anim.setProgress(0, spell.castCombo.size())
|
|
anim.inverted = true
|
|
casting = true
|
|
anim.spell = spell
|
|
anim.connect("animationFinished", finalizeSpell)
|
|
get_node("/root").add_child(anim)
|
|
attemptCast()
|
|
|
|
func attemptCast():
|
|
match data.difficulty:
|
|
Data.Difficulty.EASY:
|
|
await get_tree().create_timer(randf_range(0.75, 1.75)).timeout
|
|
if (spell == null): return
|
|
aiCast(90)
|
|
Data.Difficulty.NORMAL:
|
|
await get_tree().create_timer(randf_range(0.6, 1.2)).timeout
|
|
if (spell == null): return
|
|
aiCast(95)
|
|
Data.Difficulty.HARD:
|
|
await get_tree().create_timer(randf_range(0.3, 0.75)).timeout
|
|
if (spell == null): return
|
|
aiCast(98)
|
|
Data.Difficulty.GAMER:
|
|
await get_tree().create_timer(.2).timeout
|
|
if (spell == null): return
|
|
aiCast(100)
|
|
|
|
func aiCast(chance: int):
|
|
if randi_range(0, 99) < chance:
|
|
castProgress += 1
|
|
if (is_instance_valid(anim)): anim.setProgress(castProgress)
|
|
|
|
if (castProgress == spell.castCombo.size()):
|
|
casting = false
|
|
castProgress = 0
|
|
renderer.play("attack1")
|
|
spellbook.cooldowns[spellIndex] = spell.cooldown
|
|
defending.append(spell)
|
|
_finishedCasting()
|
|
return
|
|
else:
|
|
attemptCast()
|
|
else:
|
|
alterHealth(-spell.backfireStrength, true, spell.element)
|
|
failCast()
|
|
|
|
func alterHealth(change: float, stun: bool, element: Data.Element) -> void:
|
|
if (!data.playing): return
|
|
for spel in defending:
|
|
change *= spel.blockStrength[element]
|
|
if (change == 0): return
|
|
health += change
|
|
if stun:
|
|
casting = false
|
|
renderer.play("hit")
|
|
if player:
|
|
stunned.emit()
|
|
else:
|
|
failCast()
|
|
if (health <= 0):
|
|
failCast()
|
|
health = 0
|
|
renderer.play("death")
|
|
died.emit(!player)
|
|
if (health > maxHealth):
|
|
health = maxHealth
|
|
|
|
healthChanged.emit(health)
|
|
|
|
func failCast() -> void:
|
|
casting = false
|
|
if (is_instance_valid(anim)): anim.castFailed()
|
|
castProgress = 0
|
|
if (spell != null): spellbook.cooldowns[spellIndex] = spell.cooldown * (float(castProgress) / float(spell.castCombo.size()))
|
|
spell = null
|
|
_finishedCasting()
|
|
return
|
|
|
|
func animationFinished() -> void:
|
|
if (health > 0): renderer.play("idle")
|
|
|
|
func finalizeSpell(finish: Spell) -> void:
|
|
defending.erase(finish)
|
|
if (player):
|
|
if (finish.damage > 0): data.opponent.alterHealth(-finish.damage, finish.stunning, finish.element)
|
|
else: data.player.alterHealth(-finish.damage, finish.stunning, finish.element)
|
|
else:
|
|
if (finish.damage > 0): data.player.alterHealth(-finish.damage, finish.stunning, finish.element)
|
|
else: data.opponent.alterHealth(-finish.damage, finish.stunning, finish.element)
|