summaryrefslogtreecommitdiff
path: root/OnekoLocal.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OnekoLocal.cs')
-rw-r--r--OnekoLocal.cs118
1 files changed, 98 insertions, 20 deletions
diff --git a/OnekoLocal.cs b/OnekoLocal.cs
index 249c5d1..d8c40f3 100644
--- a/OnekoLocal.cs
+++ b/OnekoLocal.cs
@@ -11,10 +11,8 @@ class OnekoLocal : Oneko
static int Id => OnekoOnline.Client?.Id ?? -1;
- OnekoAIState? CurrentAIState;
-
- Vector2 TargetPosition = Vector2.Zero;
- float InactivityTimer = 0f;
+ OnekoAIState CurrentAIState;
+ readonly OnekoAIState[] AIStates;
OnekoAnimation Animation;
float updateTimer = 0f;
@@ -29,6 +27,9 @@ class OnekoLocal : Oneko
Client.UserConnected += OnekoNet.SpawnNetNeko;
Name = OnekoOnline.Config.GetValue("NekoName", "Oneko");
+
+ AIStates = [new BoredState(this), new ChaseMouseState(this), new ItchyState(this)];
+ CurrentAIState = AIStates[0];
}
public override void Update(float delta)
@@ -42,23 +43,14 @@ class OnekoLocal : Oneko
public void OnekoUpdate(float delta)
{
- InactivityTimer += delta;
-
- Mouse? NearestMouse = Mouse.AllMice.Where(m => m.Visible).MinBy(m => Vector2.Distance(m.Position, Position));
- if (NearestMouse != null) TargetPosition = NearestMouse.Position;
- else TargetPosition = new Vector2(Math.Clamp((Id+1)*40, 20, 300), 240/2);
-
- Vector2 TargetDirection = (TargetPosition-Position).LimitLength(10f);
- if (TargetDirection.Length() > 1) {
- Animation = GetDirectionalRun(TargetDirection);
- InactivityTimer = 0f;
+ //AI STUFF
+ OnekoAIState HighestPriority = AIStates.MaxBy(state => state.GetPriority())!;
+ if (HighestPriority != CurrentAIState) {
+ CurrentAIState.ExitState();
+ CurrentAIState = HighestPriority;
+ CurrentAIState.StartState();
}
- else Animation = Idle;
- Position += TargetDirection;
-
- if (InactivityTimer > 3f) Animation = ScratchSelf;
- if (InactivityTimer > 5f) Animation = Yawn;
- if (InactivityTimer > 6f) Animation = Sleep;
+ CurrentAIState.Update(delta);
FrameCounter = (FrameCounter+1)%(Animation.AnimSpeed+1);
Frame = (int)MathF.Round(FrameCounter/(float)Animation.AnimSpeed);
@@ -74,12 +66,98 @@ class OnekoLocal : Oneko
}
}
+ void MoveTowardsPosition(Vector2 TargetPosition)
+ {
+ Vector2 TargetDirection = (TargetPosition-Position).LimitLength(10f);
+ if (TargetDirection.Length() > 1) {
+ Animation = GetDirectionalRun(TargetDirection);
+ }
+
+ Position += TargetDirection;
+ }
+
protected abstract class OnekoAIState(OnekoLocal neko)
{
protected OnekoLocal Neko = neko;
public abstract int GetPriority();
public abstract void Update(float delta);
+ public virtual void StartState() {}
+ public virtual void ExitState() {}
protected bool IsCurrentState => Neko.CurrentAIState == this;
}
+
+ class BoredState(OnekoLocal neko) : OnekoAIState(neko)
+ {
+ float InactivityTimer = 0f;
+
+ public override int GetPriority() => 0;
+
+ public override void Update(float delta)
+ {
+ InactivityTimer += delta;
+
+ Neko.Animation = Idle;
+ if (InactivityTimer > 3f) Neko.Animation = ScratchSelf;
+ if (InactivityTimer > 5f) Neko.Animation = Yawn;
+ if (InactivityTimer > 6f) Neko.Animation = Sleep;
+ }
+
+ public override void ExitState() => InactivityTimer = 0f;
+ }
+
+ class ChaseMouseState : OnekoAIState
+ {
+ Mouse? MouseToChase;
+ float AlertTimer = 0f;
+
+ public ChaseMouseState(OnekoLocal neko) : base(neko)
+ {
+ Mouse.Clicked += mouse => {
+ if (Random.Shared.NextSingle() > 0.4f) MouseToChase = mouse;
+ };
+ }
+
+ public override int GetPriority()
+ {
+ MouseToChase ??= Mouse.AllMice.Where(m => m.Visible).MinBy(m => Vector2.Distance(m.Position, Neko.Position));
+ if (MouseToChase != null && MouseToChase.Visible && Vector2.Distance(MouseToChase.Position, Neko.Position) > 15) return 100;
+ return -1;
+ }
+
+ public override void Update(float delta)
+ {
+ if (AlertTimer < 1f) {
+ AlertTimer += delta;
+ Neko.Animation = Alert;
+ return;
+ }
+
+ if (MouseToChase is null || !MouseToChase.Visible) return;
+ Neko.MoveTowardsPosition(MouseToChase.Position);
+ }
+
+ public override void ExitState() {
+ MouseToChase = null;
+ AlertTimer = 0f;
+ }
+ }
+
+ class ItchyState(OnekoLocal neko) : OnekoAIState(neko)
+ {
+ float ItchTimer = -1f;
+
+ public override int GetPriority()
+ {
+ if (ItchTimer > 0f || Random.Shared.NextSingle() > 0.997f) return 1000;
+ else return -1;
+ }
+
+ public override void StartState() => ItchTimer = 0.6f + Random.Shared.NextSingle()*3f;
+
+ public override void Update(float delta) {
+ ItchTimer -= delta;
+ Neko.Animation = ScratchSelf;
+ }
+ }
} \ No newline at end of file