Files
GameJamDungeon/src/boss/Boss.cs
2024-10-10 22:11:10 -07:00

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());
}
}
}