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;
|
|
|
|
|
|
[Export] public BossScript BossScript { get; private 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-02-06 11:36:21 +01:00
|
|
|
|
|
2025-02-05 19:41:49 +01:00
|
|
|
|
private GameManager _gameManager;
|
2025-02-12 18:16:16 +01:00
|
|
|
|
public GameManager GameManager => _gameManager;
|
2025-02-05 19:41:49 +01:00
|
|
|
|
private Vector2 _homePosition;
|
|
|
|
|
|
public Vector2 HomePosition => _homePosition;
|
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-07 09:36:45 +01:00
|
|
|
|
_gameManager = this.GetGameManager();
|
2025-02-05 19:41:49 +01:00
|
|
|
|
|
|
|
|
|
|
_homePosition = this.GlobalPosition;
|
2025-02-13 11:59:51 +01:00
|
|
|
|
|
|
|
|
|
|
_cameraMarker = new Marker2D();
|
|
|
|
|
|
_gameManager.CallDeferred("add_child", _cameraMarker);
|
|
|
|
|
|
_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-02-13 11:15:06 +01:00
|
|
|
|
_gameManager.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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_gameManager.CameraTargetPlayer();
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
_gameManager.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-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
|
|
|
|
}
|