Boss logic update
This commit is contained in:
74
src/boss/Boss.cs
Normal file
74
src/boss/Boss.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.GodotNodeInterfaces;
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public interface IBoss : ICharacterBody3D
|
||||
{
|
||||
public AnimationTree AnimationTree { get; }
|
||||
|
||||
public Timer AttackTimer { get; }
|
||||
|
||||
public void Activate();
|
||||
}
|
||||
|
||||
[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!;
|
||||
|
||||
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!;
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
BossLogic = new BossLogic();
|
||||
BossLogic.Set(this as IBoss);
|
||||
BossLogic.Set(GameRepo);
|
||||
|
||||
SetPhysicsProcess(false);
|
||||
Hide();
|
||||
}
|
||||
|
||||
public void OnResolved()
|
||||
{
|
||||
BossBinding = BossLogic.Bind();
|
||||
|
||||
this.Provide();
|
||||
BossLogic.Start();
|
||||
AttackTimer.Timeout += AttackTimer_Timeout;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/boss/state/BossLogic.Input.cs
Normal file
16
src/boss/state/BossLogic.Input.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class BossLogic
|
||||
{
|
||||
public static class Input
|
||||
{
|
||||
public readonly record struct Activate();
|
||||
|
||||
public readonly record struct PhysicsTick(double Delta);
|
||||
|
||||
public readonly record struct PrimaryAttack();
|
||||
|
||||
public readonly record struct SecondaryAttack();
|
||||
}
|
||||
}
|
||||
}
|
||||
10
src/boss/state/BossLogic.Output.cs
Normal file
10
src/boss/state/BossLogic.Output.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class BossLogic
|
||||
{
|
||||
public static class Output
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/boss/state/BossLogic.State.cs
Normal file
16
src/boss/state/BossLogic.State.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class BossLogic
|
||||
{
|
||||
[Meta]
|
||||
public abstract partial record State : StateLogic<State>
|
||||
{
|
||||
protected State()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/boss/state/BossLogic.cs
Normal file
14
src/boss/state/BossLogic.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public interface IBossLogic : ILogicBlock<BossLogic.State>;
|
||||
|
||||
[Meta, Id("boss_logic")]
|
||||
[LogicBlock(typeof(State), Diagram = true)]
|
||||
public partial class BossLogic : LogicBlock<BossLogic.State>, IBossLogic
|
||||
{
|
||||
public override Transition GetInitialState() => To<State.Idle>();
|
||||
}
|
||||
}
|
||||
16
src/boss/state/BossLogic.g.puml
Normal file
16
src/boss/state/BossLogic.g.puml
Normal file
@@ -0,0 +1,16 @@
|
||||
@startuml BossLogic
|
||||
state "BossLogic State" as GameJamDungeon_BossLogic_State {
|
||||
state "ApproachPlayer" as GameJamDungeon_BossLogic_State_ApproachPlayer
|
||||
state "EngagePlayer" as GameJamDungeon_BossLogic_State_EngagePlayer
|
||||
state "Idle" as GameJamDungeon_BossLogic_State_Idle
|
||||
}
|
||||
|
||||
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
|
||||
GameJamDungeon_BossLogic_State_EngagePlayer --> GameJamDungeon_BossLogic_State_EngagePlayer : PhysicsTick
|
||||
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_Idle
|
||||
@enduml
|
||||
35
src/boss/state/states/BossLogic.State.ApproachPlayer.cs
Normal file
35
src/boss/state/states/BossLogic.State.ApproachPlayer.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class BossLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta, Id("boss_logic_state_approach_player")]
|
||||
public partial record ApproachPlayer : State, IGet<Input.PhysicsTick>
|
||||
{
|
||||
public Transition On(in Input.PhysicsTick input)
|
||||
{
|
||||
var gameRepo = Get<IGameRepo>();
|
||||
var boss = Get<IBoss>();
|
||||
var delta = (float)input.Delta;
|
||||
|
||||
var playerPosition = new Vector3(gameRepo.PlayerGlobalPosition.Value.X, boss.GlobalPosition.Y, gameRepo.PlayerGlobalPosition.Value.Z);
|
||||
|
||||
if (boss.GlobalPosition.DistanceTo(gameRepo.PlayerGlobalPosition.Value) <= 5.0f)
|
||||
return To<EngagePlayer>();
|
||||
|
||||
var moveToward = boss.GlobalPosition.MoveToward(playerPosition, (float)delta * 3f);
|
||||
boss.GlobalPosition = moveToward;
|
||||
|
||||
var targetDirection = boss.GlobalPosition - gameRepo.PlayerGlobalPosition.Value;
|
||||
boss.GlobalRotation = new Vector3(boss.GlobalRotation.X, Mathf.LerpAngle(boss.GlobalRotation.Y, Mathf.Atan2(-targetDirection.X, -targetDirection.Z), delta * 3f), boss.GlobalRotation.Z);
|
||||
boss.AnimationTree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>().Travel("WALK");
|
||||
return ToSelf();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
48
src/boss/state/states/BossLogic.State.EngagePlayer.cs
Normal file
48
src/boss/state/states/BossLogic.State.EngagePlayer.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class BossLogic
|
||||
{
|
||||
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 EngagePlayer()
|
||||
{
|
||||
OnAttach(() => Get<IBoss>().AnimationTree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>().Travel("IDLE"));
|
||||
}
|
||||
|
||||
public Transition On(in Input.PhysicsTick input)
|
||||
{
|
||||
var gameRepo = Get<IGameRepo>();
|
||||
var boss = Get<IBoss>();
|
||||
var delta = (float)input.Delta;
|
||||
var playerPosition = new Vector3(gameRepo.PlayerGlobalPosition.Value.X, boss.GlobalPosition.Y, gameRepo.PlayerGlobalPosition.Value.Z);
|
||||
var targetDirection = boss.GlobalPosition - gameRepo.PlayerGlobalPosition.Value;
|
||||
boss.GlobalRotation = new Vector3(boss.GlobalRotation.X, Mathf.LerpAngle(boss.GlobalRotation.Y, Mathf.Atan2(-targetDirection.X, -targetDirection.Z), delta * 3f), boss.GlobalRotation.Z);
|
||||
if (boss.GlobalPosition.DistanceTo(gameRepo.PlayerGlobalPosition.Value) > 5.0f)
|
||||
return To<ApproachPlayer>();
|
||||
|
||||
return ToSelf();
|
||||
}
|
||||
|
||||
public Transition On(in Input.PrimaryAttack input)
|
||||
{
|
||||
var boss = Get<IBoss>();
|
||||
boss.AnimationTree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>().Travel("SPEAR");
|
||||
return ToSelf();
|
||||
}
|
||||
|
||||
public Transition On(in Input.SecondaryAttack input)
|
||||
{
|
||||
var boss = Get<IBoss>();
|
||||
boss.AnimationTree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>().Travel("SHIELD BASH");
|
||||
return ToSelf();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/boss/state/states/BossLogic.State.Idle.cs
Normal file
31
src/boss/state/states/BossLogic.State.Idle.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Chickensoft.Introspection;
|
||||
using static GameJamDungeon.BossLogic.Input;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class BossLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta, Id("boss_logic_state_idle")]
|
||||
public partial record Idle : State, IGet<Activate>
|
||||
{
|
||||
public Idle()
|
||||
{
|
||||
OnDetach(() =>
|
||||
{
|
||||
var boss = Get<IBoss>();
|
||||
boss.SetPhysicsProcess(true);
|
||||
boss.Show();
|
||||
boss.AttackTimer.Start();
|
||||
}
|
||||
);
|
||||
}
|
||||
public Transition On(in Activate input)
|
||||
{
|
||||
return To<ApproachPlayer>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user