2023-07-31 01:12:47 -07:00
|
|
|
#define DEBUG_NPC
|
2023-07-21 02:54:13 -07:00
|
|
|
|
2022-11-10 20:29:53 -08:00
|
|
|
using Godot;
|
2022-11-27 19:37:16 -08:00
|
|
|
using SupaLidlGame.Extensions;
|
2022-11-19 21:21:12 -08:00
|
|
|
using SupaLidlGame.Items;
|
2024-06-04 22:12:01 -07:00
|
|
|
using SupaLidlGame.Utils;
|
2022-11-10 20:29:53 -08:00
|
|
|
using System;
|
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
namespace SupaLidlGame.Characters;
|
|
|
|
|
|
|
|
public partial class NPC : Character
|
2022-11-10 20:29:53 -08:00
|
|
|
{
|
2023-06-03 18:21:46 -07:00
|
|
|
/// <summary>
|
|
|
|
/// Time in seconds it takes for the NPC to think FeelsDankCube
|
|
|
|
/// </summary>
|
|
|
|
public const float ThinkTime = 0.125f;
|
|
|
|
|
|
|
|
public float[] Weights => _weights;
|
|
|
|
|
|
|
|
protected float _preferredWeightDistance = 64.0f;
|
|
|
|
protected float _maxWeightDistance = 8.0f;
|
|
|
|
protected float _preferredWeightDistanceSq = 4096.0f;
|
|
|
|
protected float _maxWeightDistanceSq = 64.0f;
|
|
|
|
|
|
|
|
[Export]
|
|
|
|
public float PreferredWeightDistance
|
|
|
|
{
|
|
|
|
get => _preferredWeightDistance;
|
|
|
|
protected set
|
|
|
|
{
|
|
|
|
_preferredWeightDistance = value;
|
|
|
|
_preferredWeightDistanceSq = value * value;
|
2023-04-01 16:25:00 -07:00
|
|
|
}
|
2023-06-03 18:21:46 -07:00
|
|
|
}
|
2023-04-01 16:25:00 -07:00
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
[Export]
|
|
|
|
public float MaxWeightDistance
|
|
|
|
{
|
|
|
|
get => _maxWeightDistance;
|
|
|
|
protected set
|
|
|
|
{
|
|
|
|
_maxWeightDistance = value;
|
|
|
|
_maxWeightDistanceSq = value * value;
|
2023-04-01 16:25:00 -07:00
|
|
|
}
|
2023-06-03 18:21:46 -07:00
|
|
|
}
|
2022-11-27 19:37:16 -08:00
|
|
|
|
2023-07-31 01:12:47 -07:00
|
|
|
[Export]
|
|
|
|
public Items.Item DefaultSelectedItem { get; set; }
|
|
|
|
|
2023-08-05 23:50:08 -07:00
|
|
|
[Export]
|
|
|
|
public bool ShouldMoveWhenUsingItem { get; set; } = true;
|
|
|
|
|
|
|
|
[Export]
|
|
|
|
public State.Thinker.ThinkerStateMachine ThinkerStateMachine { get; set; }
|
|
|
|
|
2023-07-21 02:54:13 -07:00
|
|
|
public bool ShouldMove { get; set; } = true;
|
|
|
|
|
2023-07-23 11:05:01 -07:00
|
|
|
public bool CanAttack { get; set; } = true;
|
|
|
|
|
2023-08-05 23:50:08 -07:00
|
|
|
public Vector2 LastSeenPosition { get; set; }
|
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
protected float[] _weights = new float[16];
|
|
|
|
protected int _bestWeightIdx;
|
|
|
|
protected double _thinkTimeElapsed = 0;
|
|
|
|
protected Vector2 _blockingDir;
|
|
|
|
protected static readonly Vector2[] _weightDirs = new Vector2[16];
|
2022-11-10 20:29:53 -08:00
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
static NPC()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 16; i++)
|
2022-11-10 20:29:53 -08:00
|
|
|
{
|
2023-06-03 18:21:46 -07:00
|
|
|
float y = Mathf.Sin(Mathf.Pi * i * 2 / 16);
|
|
|
|
float x = Mathf.Cos(Mathf.Pi * i * 2 / 16);
|
|
|
|
_weightDirs[i] = new Vector2(x, y);
|
2022-11-10 20:29:53 -08:00
|
|
|
}
|
2023-06-03 18:21:46 -07:00
|
|
|
}
|
2022-11-10 20:29:53 -08:00
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
public override void _Ready()
|
|
|
|
{
|
|
|
|
base._Ready();
|
|
|
|
Array.Fill(_weights, 0);
|
2023-07-31 01:12:47 -07:00
|
|
|
|
|
|
|
if (DefaultSelectedItem is not null)
|
|
|
|
{
|
|
|
|
Inventory.SelectedItem = DefaultSelectedItem;
|
|
|
|
}
|
2023-08-05 23:50:08 -07:00
|
|
|
|
|
|
|
Inventory.UsedItem += (Items.Item item) =>
|
|
|
|
{
|
|
|
|
if (item is Items.Weapon)
|
|
|
|
{
|
|
|
|
if (AttackAnimation is not null)
|
|
|
|
{
|
2023-08-14 14:07:57 -07:00
|
|
|
AttackAnimation.TryPlay("attack");
|
2023-08-05 23:50:08 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2023-06-03 18:21:46 -07:00
|
|
|
}
|
2022-11-27 19:37:16 -08:00
|
|
|
|
2023-08-31 19:03:16 -07:00
|
|
|
/// <summary>
|
|
|
|
/// Finds the NPC's best character to target.
|
|
|
|
/// </summary>
|
2023-07-31 01:12:47 -07:00
|
|
|
public virtual Character FindBestTarget()
|
2023-06-03 18:21:46 -07:00
|
|
|
{
|
2023-08-05 23:50:08 -07:00
|
|
|
float bestScore = float.MaxValue;
|
2023-06-03 18:21:46 -07:00
|
|
|
Character bestChar = null;
|
2024-06-06 07:59:29 -07:00
|
|
|
|
|
|
|
foreach (var character in World.Instance.CurrentMap.GetCharacters())
|
2022-11-10 20:29:53 -08:00
|
|
|
{
|
2024-06-06 07:59:29 -07:00
|
|
|
bool isFriendly = ((IFaction)character).AlignsWith(this);
|
|
|
|
if (isFriendly || !character.IsAlive)
|
2022-11-10 20:29:53 -08:00
|
|
|
{
|
2024-06-06 07:59:29 -07:00
|
|
|
continue;
|
|
|
|
}
|
2023-06-13 02:55:30 -07:00
|
|
|
|
2024-06-06 07:59:29 -07:00
|
|
|
float score = 0;
|
|
|
|
score += GlobalPosition.DistanceSquaredTo(character.GlobalPosition);
|
2023-08-05 23:50:08 -07:00
|
|
|
|
2024-06-06 07:59:29 -07:00
|
|
|
if (score < bestScore)
|
|
|
|
{
|
|
|
|
bestScore = score;
|
|
|
|
bestChar = character;
|
2022-11-10 20:29:53 -08:00
|
|
|
}
|
|
|
|
}
|
2024-06-06 07:59:29 -07:00
|
|
|
|
2023-06-03 18:21:46 -07:00
|
|
|
return bestChar;
|
|
|
|
}
|
2022-11-10 20:29:53 -08:00
|
|
|
|
2024-06-04 22:12:01 -07:00
|
|
|
/// <summary>
|
|
|
|
/// Finds the best character whose faction aligns with this character's.
|
|
|
|
/// </summary>
|
|
|
|
public virtual Character FindBestNeutral()
|
|
|
|
{
|
|
|
|
float bestScore = float.MaxValue;
|
|
|
|
Character bestChar = null;
|
|
|
|
// NOTE: this relies on all Characters being under the Entities node
|
|
|
|
foreach (Node node in GetParent().GetChildren())
|
|
|
|
{
|
|
|
|
if (node is Character character)
|
|
|
|
{
|
|
|
|
bool isFriendly = ((IFaction)this).AlignsWith(character);
|
|
|
|
if (isFriendly || character.Health <= 0)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
float score = 0;
|
2024-06-24 16:07:15 -07:00
|
|
|
score -= Position.DistanceTo(character.Position);
|
2024-06-04 22:12:01 -07:00
|
|
|
|
|
|
|
if (score < bestScore)
|
|
|
|
{
|
|
|
|
bestScore = score;
|
|
|
|
bestChar = character;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return bestChar;
|
|
|
|
}
|
|
|
|
|
2023-08-05 23:50:08 -07:00
|
|
|
public override void _Process(double delta)
|
|
|
|
{
|
|
|
|
ThinkerStateMachine.Process(delta);
|
|
|
|
base._Process(delta);
|
|
|
|
}
|
|
|
|
|
|
|
|
public override void _PhysicsProcess(double delta)
|
|
|
|
{
|
|
|
|
ThinkerStateMachine.PhysicsProcess(delta);
|
|
|
|
base._PhysicsProcess(delta);
|
|
|
|
}
|
2022-11-10 20:29:53 -08:00
|
|
|
}
|