Add hit animations and death for bosses
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.Collections;
|
||||
using Chickensoft.GodotNodeInterfaces;
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
@@ -12,6 +13,8 @@ namespace GameJamDungeon
|
||||
public Timer AttackTimer { get; }
|
||||
|
||||
public void Activate();
|
||||
|
||||
public AutoProp<double> CurrentHP { get; }
|
||||
}
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
@@ -21,6 +24,8 @@ namespace GameJamDungeon
|
||||
|
||||
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!;
|
||||
@@ -31,6 +36,12 @@ namespace GameJamDungeon
|
||||
|
||||
[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();
|
||||
@@ -44,10 +55,38 @@ namespace GameJamDungeon
|
||||
public void OnResolved()
|
||||
{
|
||||
BossBinding = BossLogic.Bind();
|
||||
BossBinding
|
||||
.Handle((in BossLogic.Output.HitByPlayer output) =>
|
||||
{
|
||||
|
||||
})
|
||||
.Handle((in BossLogic.Output.Defeated output) =>
|
||||
{
|
||||
QueueFree();
|
||||
});
|
||||
this.Provide();
|
||||
BossLogic.Start();
|
||||
CurrentHP = new AutoProp<double>(BossResource.MaximumHP);
|
||||
CurrentHP.Sync += OnHPChanged;
|
||||
AttackTimer.Timeout += AttackTimer_Timeout;
|
||||
Hitbox.AreaEntered += Hitbox_AreaEntered;
|
||||
}
|
||||
|
||||
private void Hitbox_AreaEntered(Area3D area)
|
||||
{
|
||||
if (area is IHitbox)
|
||||
{
|
||||
HitAnimation.Play("Hit");
|
||||
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()
|
||||
@@ -71,5 +110,11 @@ namespace GameJamDungeon
|
||||
BossLogic.Input(new BossLogic.Input.PhysicsTick(delta));
|
||||
MoveAndSlide();
|
||||
}
|
||||
|
||||
private void OnHPChanged(double newHP)
|
||||
{
|
||||
if (newHP <= 0)
|
||||
BossLogic.Input(new BossLogic.Input.BossDefeated());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
public readonly record struct PrimaryAttack();
|
||||
|
||||
public readonly record struct SecondaryAttack();
|
||||
|
||||
public readonly record struct HitByPlayer(double Damage);
|
||||
|
||||
public readonly record struct BossDefeated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
{
|
||||
public static class Output
|
||||
{
|
||||
public readonly record struct HitByPlayer(double CurrentHP);
|
||||
|
||||
public readonly record struct Defeated();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
@startuml BossLogic
|
||||
state "BossLogic State" as GameJamDungeon_BossLogic_State {
|
||||
state "Alive" as GameJamDungeon_BossLogic_State_Alive {
|
||||
state "Idle" as GameJamDungeon_BossLogic_State_Idle
|
||||
}
|
||||
state "ApproachPlayer" as GameJamDungeon_BossLogic_State_ApproachPlayer
|
||||
state "Defeated" as GameJamDungeon_BossLogic_State_Defeated
|
||||
state "EngagePlayer" as GameJamDungeon_BossLogic_State_EngagePlayer
|
||||
state "Idle" as GameJamDungeon_BossLogic_State_Idle
|
||||
}
|
||||
|
||||
GameJamDungeon_BossLogic_State_Alive --> GameJamDungeon_BossLogic_State_Alive : HitByPlayer
|
||||
GameJamDungeon_BossLogic_State_ApproachPlayer --> GameJamDungeon_BossLogic_State_ApproachPlayer : PhysicsTick
|
||||
GameJamDungeon_BossLogic_State_ApproachPlayer --> GameJamDungeon_BossLogic_State_EngagePlayer : PhysicsTick
|
||||
GameJamDungeon_BossLogic_State_EngagePlayer --> GameJamDungeon_BossLogic_State_ApproachPlayer : PhysicsTick
|
||||
@@ -12,5 +16,9 @@ GameJamDungeon_BossLogic_State_EngagePlayer --> GameJamDungeon_BossLogic_State_E
|
||||
GameJamDungeon_BossLogic_State_EngagePlayer --> GameJamDungeon_BossLogic_State_EngagePlayer : PrimaryAttack
|
||||
GameJamDungeon_BossLogic_State_EngagePlayer --> GameJamDungeon_BossLogic_State_EngagePlayer : SecondaryAttack
|
||||
GameJamDungeon_BossLogic_State_Idle --> GameJamDungeon_BossLogic_State_ApproachPlayer : Activate
|
||||
|
||||
GameJamDungeon_BossLogic_State : On() → HitByPlayer
|
||||
GameJamDungeon_BossLogic_State_Alive : OnHitByPlayer → HitByPlayer
|
||||
|
||||
[*] --> GameJamDungeon_BossLogic_State_Idle
|
||||
@enduml
|
||||
31
src/boss/state/states/BossLogic.State.Alive.cs
Normal file
31
src/boss/state/states/BossLogic.State.Alive.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class BossLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta, Id("boss_logic_state_alive")]
|
||||
public abstract partial record Alive : State, IGet<Input.HitByPlayer>, IGet<Input.BossDefeated>
|
||||
{
|
||||
}
|
||||
|
||||
public Transition On(in Input.HitByPlayer input)
|
||||
{
|
||||
var enemy = Get<IBoss>();
|
||||
enemy.CurrentHP.OnNext(enemy.CurrentHP.Value - input.Damage);
|
||||
GD.Print("Current HP: " + enemy.CurrentHP.Value);
|
||||
Output(new Output.HitByPlayer());
|
||||
return ToSelf();
|
||||
}
|
||||
|
||||
public Transition On(in Input.BossDefeated input)
|
||||
{
|
||||
Output(new Output.Defeated());
|
||||
return To<Defeated>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ namespace GameJamDungeon
|
||||
public partial record State
|
||||
{
|
||||
[Meta, Id("boss_logic_state_approach_player")]
|
||||
public partial record ApproachPlayer : State, IGet<Input.PhysicsTick>
|
||||
public partial record ApproachPlayer : Alive, IGet<Input.PhysicsTick>
|
||||
{
|
||||
public Transition On(in Input.PhysicsTick input)
|
||||
{
|
||||
|
||||
15
src/boss/state/states/BossLogic.State.Defeated.cs
Normal file
15
src/boss/state/states/BossLogic.State.Defeated.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Chickensoft.Introspection;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class BossLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta, Id("boss_logic_state_defeated")]
|
||||
public partial record Defeated : State
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ namespace GameJamDungeon
|
||||
public partial record State
|
||||
{
|
||||
[Meta, Id("boss_logic_state_engage_player")]
|
||||
public partial record EngagePlayer : State, IGet<Input.PhysicsTick>, IGet<Input.PrimaryAttack>, IGet<Input.SecondaryAttack>
|
||||
public partial record EngagePlayer : Alive, IGet<Input.PhysicsTick>, IGet<Input.PrimaryAttack>, IGet<Input.SecondaryAttack>
|
||||
{
|
||||
public EngagePlayer()
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Chickensoft.Introspection;
|
||||
using static GameJamDungeon.BossLogic.Input;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
@@ -8,7 +7,7 @@ namespace GameJamDungeon
|
||||
public partial record State
|
||||
{
|
||||
[Meta, Id("boss_logic_state_idle")]
|
||||
public partial record Idle : State, IGet<Activate>
|
||||
public partial record Idle : Alive, IGet<Input.Activate>
|
||||
{
|
||||
public Idle()
|
||||
{
|
||||
@@ -21,7 +20,7 @@ namespace GameJamDungeon
|
||||
}
|
||||
);
|
||||
}
|
||||
public Transition On(in Activate input)
|
||||
public Transition On(in Input.Activate input)
|
||||
{
|
||||
return To<ApproachPlayer>();
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ void fragment() {
|
||||
[resource]
|
||||
render_priority = 0
|
||||
shader = SubResource("Shader_veoq4")
|
||||
shader_parameter/albedo = Color(0.784314, 0, 0, 1)
|
||||
shader_parameter/albedo = Color(0.784314, 0, 0, 0.00784314)
|
||||
shader_parameter/point_size = 1.0
|
||||
shader_parameter/roughness = 1.0
|
||||
shader_parameter/metallic_texture_channel = null
|
||||
|
||||
Reference in New Issue
Block a user