class_name Combatant extends Node2D signal healthChanged(health: float) signal stunned @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 # 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: 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 && !casting: timing(delta) 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: casting = true match data.difficulty: Data.Difficulty.EASY: spellIndex = randi_range(0, spellbook.spells.size() - 1) Data.Difficulty.NORMAL: spellIndex = randi_range(0, spellbook.spells.size() - 1) Data.Difficulty.HARD: spellIndex = randi_range(0, spellbook.spells.size() - 1) Data.Difficulty.GAMER: spellIndex = randi_range(0, spellbook.spells.size() - 1) spell = spellbook.spells[spellIndex] anim = data.animations[spell.animation].instantiate() anim.setProgress(0, spell.castCombo.size()) anim.inverted = true anim.spell = spell anim.connect("animationFinished", finalizeSpell) get_node("/root").add_child(anim) attemptCast() func attemptCast(): #if (!casting): #_finishedCasting() #return match data.difficulty: Data.Difficulty.EASY: await get_tree().create_timer(randf_range(0.75, 1.75)).timeout if (spell == null): return if randi_range(0, 99) < 90: 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 _finishedCasting() return else: attemptCast() else: alterHealth(-spell.backfireStrength, true) failCast() return Data.Difficulty.NORMAL: await get_tree().create_timer(randf_range(0.6, 1.2)).timeout if (spell == null): return if randi_range(0, 99) < 95: 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 _finishedCasting() return else: attemptCast() else: alterHealth(-spell.backfireStrength, true) failCast() Data.Difficulty.HARD: await get_tree().create_timer(randf_range(0.3, 0.75)).timeout if (spell == null): return if randi_range(0, 99) < 98: 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 _finishedCasting() return else: attemptCast() else: alterHealth(-spell.backfireStrength, true) failCast() Data.Difficulty.GAMER: await get_tree().create_timer(.2).timeout if (spell == null): return 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 data.player.alterHealth(-spell.damage, false) _finishedCasting() return else: attemptCast() func alterHealth(change: float, stun: bool) -> void: health += change if stun: casting = false renderer.play("hit") if player: stunned.emit() else: failCast() 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: renderer.play("idle") func finalizeSpell(finish: Spell) -> void: if (player): data.opponent.alterHealth(-finish.damage, finish.stunning) else: data.player.alterHealth(-finish.damage, finish.stunning)