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") spellbook.initCooldowns() healthbar.maxHealth = maxHealth if !player: 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) spell = spellbook.spells[spellIndex] anim = data.animations[spell.animation].instantiate() anim.setProgress(0, spell.castCombo.size()) anim.inverted = true get_node("/root").add_child(anim) attemptCast() Data.Difficulty.NORMAL: 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 get_node("/root").add_child(anim) attemptCast() Data.Difficulty.HARD: 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 get_node("/root").add_child(anim) attemptCast() 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 get_node("/root").add_child(anim) attemptCast() func attemptCast(): match data.difficulty: Data.Difficulty.EASY: await get_tree().create_timer(randf_range(0.5, 1.5)).timeout if randi_range(0, 99) < 90: castProgress += 1 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() else: attemptCast() else: alterHealth(-spell.backfireStrength, true) casting = false anim.castFailed() castProgress = 0 spellbook.cooldowns[spellIndex] = spell.cooldown * (float(castProgress) / float(spell.castCombo.size())) _finishedCasting() Data.Difficulty.NORMAL: await get_tree().create_timer(randf_range(0.4, 1)).timeout if randi_range(0, 99) < 95: castProgress += 1 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() else: attemptCast() else: alterHealth(-spell.backfireStrength, true) casting = false anim.castFailed() castProgress = 0 spellbook.cooldowns[spellIndex] = spell.cooldown * (float(castProgress) / float(spell.castCombo.size())) _finishedCasting() Data.Difficulty.HARD: await get_tree().create_timer(randf_range(0.2, 0.6)).timeout if randi_range(0, 99) < 98: castProgress += 1 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() else: attemptCast() else: alterHealth(-spell.backfireStrength, true) casting = false anim.castFailed() castProgress = 0 spellbook.cooldowns[spellIndex] = spell.cooldown * (float(castProgress) / float(spell.castCombo.size())) _finishedCasting() Data.Difficulty.GAMER: await get_tree().create_timer(.1).timeout castProgress += 1 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() else: attemptCast() func alterHealth(change: float, stun: bool) -> void: health += change if stun: casting = false renderer.play("hit") if player: stunned.emit() healthChanged.emit(health) func animationFinished() -> void: renderer.play("idle")