cirnogodot/Scripts/Actors/Boss.cs

210 lines
6 KiB
C#
Raw Normal View History

2025-02-12 18:16:16 +01:00
using System.Threading.Tasks;
2025-03-15 11:44:30 +01:00
using Cirno.Scripts.AttackPatterns;
2025-02-12 18:16:16 +01:00
using Cirno.Scripts.Resources;
2025-03-04 14:49:43 +01:00
using Cirno.Scripts.Resources.ScriptableBullets;
2025-02-12 18:16:16 +01:00
using Cirno.Scripts.UI;
2025-02-05 19:41:49 +01:00
using Godot;
using Godot.Collections;
namespace Cirno.Scripts.Actors;
2025-03-15 11:44:30 +01:00
public partial class Boss : Enemy, IActivable, IScriptHost
2025-02-05 19:41:49 +01:00
{
2025-02-13 11:15:06 +01:00
[Export] public string BossName { get; private set; }
2025-03-04 14:49:43 +01:00
//[Export] private Array<BossPhase> Phases;
2025-03-24 10:26:13 +01:00
[Export] public BossScript BossScript { get; set; }
2025-03-04 15:00:39 +01:00
//[Export] private PackedScene BossHudPrefab;
2025-03-22 17:47:24 +01:00
[Export] public Vector2 BossPhaseAnimationStartingPosition = new(180, 10);
2025-02-14 13:27:30 +01:00
2025-02-05 19:41:49 +01:00
private int currentPhaseIndex = 0;
2025-02-06 11:36:21 +01:00
private bool _started = false;
2025-02-14 09:33:10 +01:00
private bool _waiting = false;
2025-03-24 10:26:13 +01:00
2025-02-05 19:41:49 +01:00
private Vector2 _homePosition;
public Vector2 HomePosition => _homePosition;
2025-05-16 17:38:02 +02:00
public CharacterBody2D Body => this;
public Node2D ParentObject => this;
2025-03-04 14:49:43 +01:00
private BossPhase CurrentPhase => BossScript.Phases[currentPhaseIndex];
2025-02-12 18:16:16 +01:00
2025-02-13 11:59:51 +01:00
private Marker2D _cameraMarker;
2025-02-14 09:33:10 +01:00
2025-02-13 11:59:51 +01:00
[Export]
public Vector2 CameraOffset = Vector2.Zero;
2025-02-12 18:16:16 +01:00
private TextureRect _animationTextureRect;
2025-03-04 15:00:39 +01:00
//[Export] private Texture2D _bossPortraitTexture;
2025-02-13 11:15:06 +01:00
private BossHud _bossHud;
2025-02-05 19:41:49 +01:00
2025-03-06 22:09:11 +01:00
[Signal] public delegate void ActorSpriteChangeEventHandler(Vector2 direction);
2025-02-05 19:41:49 +01:00
public override void _Ready()
{
base._Ready();
2025-03-04 14:49:43 +01:00
if (BossScript is null)
{
GD.PrintErr($"No boss script defined in {this.Name}");
return;
}
2025-02-05 19:41:49 +01:00
_homePosition = this.GlobalPosition;
2025-03-24 16:56:35 +01:00
CallDeferred(MethodName.InitDeferred);
}
private void InitDeferred()
{
2025-02-13 11:59:51 +01:00
_cameraMarker = new Marker2D();
2025-03-24 10:26:13 +01:00
GameManager.Instance.CallDeferred("add_child", _cameraMarker);
2025-02-13 11:59:51 +01:00
_cameraMarker.GlobalPosition = _homePosition + CameraOffset;
2025-02-12 18:16:16 +01:00
2025-03-04 15:00:39 +01:00
if (BossScript.HudPrefab is not null)
2025-02-12 18:16:16 +01:00
{
2025-03-04 15:00:39 +01:00
_bossHud = BossScript.HudPrefab.Instantiate<BossHud>();
2025-03-24 10:26:13 +01:00
GameManager.Instance.CallDeferred("add_child", _bossHud);
2025-02-13 11:59:51 +01:00
2025-02-25 14:06:41 +01:00
_bossHud.Name = $"{BossName}_BossHud";
2025-02-13 11:59:51 +01:00
_bossHud.Visible = false;
2025-02-13 11:15:06 +01:00
_bossHud.BossName = BossName;
_bossHud.BossMaxHealth = this.Health;
_bossHud.BossHealth = this._currentHealth;
_bossHud.SpellCardName = CurrentPhase.PhaseName;
// TODO: Do some translation for health values to match the thresholds
this.HealthChanged += (float newValue) => {_bossHud.BossHealth = newValue;};
2025-03-04 15:00:39 +01:00
if (BossScript.PortraitTexture is not null)
2025-02-13 11:15:06 +01:00
{
// var canvas = new CanvasLayer();
// canvas.Name = "BossPhaseAnimationCanvas";
2025-02-12 18:16:16 +01:00
2025-02-13 11:15:06 +01:00
// _gameManager.CallDeferred("add_child", canvas);
2025-02-12 18:16:16 +01:00
2025-02-13 11:15:06 +01:00
_animationTextureRect = new TextureRect();
2025-03-04 15:00:39 +01:00
_animationTextureRect.Texture = BossScript.PortraitTexture;
2025-02-12 18:16:16 +01:00
2025-02-13 11:15:06 +01:00
_bossHud.CallDeferred("add_child", _animationTextureRect);
2025-02-12 18:16:16 +01:00
2025-02-14 13:27:30 +01:00
_animationTextureRect.Position = BossPhaseAnimationStartingPosition;
2025-02-12 18:16:16 +01:00
2025-02-13 11:15:06 +01:00
_animationTextureRect.Visible = false;
}
2025-02-12 18:16:16 +01:00
}
2025-02-05 19:41:49 +01:00
}
2025-03-24 16:56:35 +01:00
2025-02-05 19:41:49 +01:00
public override void _Process(double delta)
{
base._Process(delta);
2025-02-06 11:36:21 +01:00
if (!_started) return;
2025-02-14 09:33:10 +01:00
if (_waiting) return;
2025-02-05 19:41:49 +01:00
CurrentPhase.UpdatePhase(delta);
2025-03-04 14:49:43 +01:00
if (_currentHealth <= CurrentPhase.Threshold && currentPhaseIndex + 1 < BossScript.Phases.Count)
2025-02-05 19:41:49 +01:00
{
currentPhaseIndex++;
2025-02-13 11:15:06 +01:00
_bossHud.SpellCardName = CurrentPhase.PhaseName;
2025-02-05 19:41:49 +01:00
StartPhase(CurrentPhase);
}
}
2025-02-14 13:27:30 +01:00
2025-03-22 17:47:24 +01:00
protected override void ActivateDefeatScript()
{
GameManager.Instance.ClearBullets();
base.ActivateDefeatScript();
}
2025-02-14 13:27:30 +01:00
protected override void Explode()
{
if (_bossHud is not null)
{
_bossHud.QueueFree();
}
2025-03-24 10:26:13 +01:00
GameManager.Instance.CameraTargetPlayer();
2025-02-14 13:27:30 +01:00
base.Explode();
}
2025-02-05 19:41:49 +01:00
private void StartPhase(BossPhase phase)
{
2025-03-22 17:47:24 +01:00
GameManager.Instance.ClearBullets();
2025-02-12 18:16:16 +01:00
if (phase.PlayAnimation)
{
2025-02-14 09:33:10 +01:00
_waiting = true;
_invulnerable = true;
2025-02-12 18:16:16 +01:00
_ = Switchphase(phase);
}
else
{
phase.Start(this);
}
2025-02-05 19:41:49 +01:00
}
public void TakeDamage(int amount)
{
2025-02-06 11:36:21 +01:00
if (!_started) return;
2025-02-05 19:41:49 +01:00
_currentHealth -= amount;
}
2025-02-06 11:36:21 +01:00
2025-03-09 21:58:25 +01:00
public bool Activate(ActivationType activationType = ActivationType.Toggle)
2025-02-06 11:36:21 +01:00
{
_started = true;
2025-02-13 11:59:51 +01:00
if (_bossHud is not null)
{
_bossHud.Visible = true;
}
2025-05-09 10:00:33 +02:00
//GameManager.Instance.CameraTargetObject(_cameraMarker);
2025-02-06 11:36:21 +01:00
StartPhase(CurrentPhase);
2025-03-09 21:58:25 +01:00
return true;
2025-02-06 11:36:21 +01:00
}
2025-06-18 11:33:27 +02:00
public void Toggle()
{
this.Activate();
}
2025-02-12 18:16:16 +01:00
private async Task Switchphase(BossPhase phase)
{
await PlayAnimation();
2025-02-14 09:33:10 +01:00
_waiting = false;
_invulnerable = false;
2025-02-12 18:16:16 +01:00
phase.Start(this);
}
private async Task PlayAnimation()
{
_animationTextureRect.Modulate = new Color(_animationTextureRect.Modulate.R, _animationTextureRect.Modulate.G, _animationTextureRect.Modulate.B, 0f);
2025-02-14 13:27:30 +01:00
_animationTextureRect.Position = BossPhaseAnimationStartingPosition;
2025-02-12 18:16:16 +01:00
_animationTextureRect.Visible = true;
var tween = GetTree().CreateTween();
tween.SetEase(Tween.EaseType.InOut);
tween.SetTrans(Tween.TransitionType.Linear);
tween.TweenProperty(_animationTextureRect, "modulate:a", 1f, 0.2f);
tween.TweenProperty(_animationTextureRect, "global_position", _animationTextureRect.GlobalPosition + new Vector2(-64f, 20f), 1.5f);
tween.TweenProperty(_animationTextureRect, "modulate:a", 0f, 0.2f);
//await Task.Delay(800);
// Wait for the tween to finish
await ToSignal(tween, "finished");
_animationTextureRect.Visible = false;
}
2025-03-06 22:09:11 +01:00
public void ChangeSpriteDirection(Vector2 direction)
{
EmitSignal(SignalName.ActorSpriteChange, direction);
}
2025-02-05 19:41:49 +01:00
}