125 lines
3.5 KiB
C#
125 lines
3.5 KiB
C#
using Chickensoft.AutoInject;
|
|
using Chickensoft.Collections;
|
|
using Chickensoft.GodotNodeInterfaces;
|
|
using Chickensoft.Introspection;
|
|
using Godot;
|
|
|
|
namespace GameJamDungeon
|
|
{
|
|
public interface IBoss : ICharacterBody3D
|
|
{
|
|
public AnimationTree AnimationTree { get; }
|
|
|
|
public AnimationPlayer HitAnimation { get; }
|
|
|
|
public Timer AttackTimer { get; }
|
|
|
|
public void Activate();
|
|
|
|
public AutoProp<double> CurrentHP { get; }
|
|
}
|
|
|
|
[Meta(typeof(IAutoNode))]
|
|
public partial class Boss : CharacterBody3D, IBoss, IProvide<IBossLogic>
|
|
{
|
|
public override void _Notification(int what) => this.Notify(what);
|
|
|
|
public IBossLogic BossLogic { get; set; } = default!;
|
|
|
|
[Export] public EnemyStatResource BossResource { get; set; } = default!;
|
|
|
|
IBossLogic IProvide<IBossLogic>.Value() => BossLogic;
|
|
|
|
public BossLogic.IBinding BossBinding { get; set; } = default!;
|
|
|
|
[Dependency] public IGameRepo GameRepo => this.DependOn<IGameRepo>();
|
|
|
|
[Node] public AnimationTree AnimationTree { get; set; } = default!;
|
|
|
|
[Node] public Timer AttackTimer { get; set; } = default!;
|
|
|
|
[Node] public AnimationPlayer HitAnimation { get; set; } = default!;
|
|
|
|
[Node] public Area3D Hitbox { get; set; } = default!;
|
|
|
|
public AutoProp<double> CurrentHP { get; set; }
|
|
|
|
public void Setup()
|
|
{
|
|
BossLogic = new BossLogic();
|
|
BossLogic.Set(this as IBoss);
|
|
BossLogic.Set(GameRepo);
|
|
|
|
SetPhysicsProcess(false);
|
|
Hide();
|
|
}
|
|
|
|
public void OnResolved()
|
|
{
|
|
BossBinding = BossLogic.Bind();
|
|
BossBinding
|
|
.Handle((in BossLogic.Output.Defeated output) =>
|
|
{
|
|
HitAnimation.Play("Defeated");
|
|
});
|
|
this.Provide();
|
|
BossLogic.Start();
|
|
CurrentHP = new AutoProp<double>(BossResource.MaximumHP);
|
|
CurrentHP.Sync += OnHPChanged;
|
|
AttackTimer.Timeout += AttackTimer_Timeout;
|
|
Hitbox.AreaEntered += Hitbox_AreaEntered;
|
|
HitAnimation.AnimationFinished += HitAnimation_AnimationFinished;
|
|
}
|
|
|
|
private void HitAnimation_AnimationFinished(StringName animName)
|
|
{
|
|
if (animName == "Defeated")
|
|
QueueFree();
|
|
}
|
|
|
|
private void Hitbox_AreaEntered(Area3D area)
|
|
{
|
|
if (area is IHitbox)
|
|
{
|
|
var isCriticalHit = false;
|
|
var rng = new RandomNumberGenerator();
|
|
rng.Randomize();
|
|
var roll = rng.Randf();
|
|
if (roll <= GameRepo.PlayerData.Inventory.EquippedWeapon.Value.WeaponStats.Luck)
|
|
isCriticalHit = true;
|
|
var damage = DamageCalculator.CalculateWeaponAttackDamage(GameRepo.PlayerData.CurrentAttack.Value + GameRepo.PlayerData.BonusAttack, BossResource, GameRepo.PlayerData.Inventory.EquippedWeapon.Value.WeaponStats, isCriticalHit);
|
|
GD.Print($"Enemy Hit for {damage} damage.");
|
|
BossLogic.Input(new BossLogic.Input.HitByPlayer(damage));
|
|
}
|
|
}
|
|
|
|
public void Activate()
|
|
{
|
|
BossLogic.Input(new BossLogic.Input.Activate());
|
|
}
|
|
|
|
private void AttackTimer_Timeout()
|
|
{
|
|
var random = new RandomNumberGenerator();
|
|
random.Randomize();
|
|
var selection = random.RandWeighted([0.2f, 0.8f]);
|
|
if (selection == 0)
|
|
BossLogic.Input(new BossLogic.Input.SecondaryAttack());
|
|
else
|
|
BossLogic.Input(new BossLogic.Input.PrimaryAttack());
|
|
}
|
|
|
|
public void OnPhysicsProcess(double delta)
|
|
{
|
|
BossLogic.Input(new BossLogic.Input.PhysicsTick(delta));
|
|
MoveAndSlide();
|
|
}
|
|
|
|
private void OnHPChanged(double newHP)
|
|
{
|
|
if (newHP <= 0)
|
|
BossLogic.Input(new BossLogic.Input.BossDefeated());
|
|
}
|
|
}
|
|
}
|