174 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C#
		
	
			
		
		
	
	
			174 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C#
		
	
#define DEBUG_NPC
 | 
						|
 | 
						|
using Godot;
 | 
						|
using SupaLidlGame.Extensions;
 | 
						|
using SupaLidlGame.Items;
 | 
						|
using SupaLidlGame.Utils;
 | 
						|
using System;
 | 
						|
 | 
						|
namespace SupaLidlGame.Characters;
 | 
						|
 | 
						|
public partial class NPC : Character
 | 
						|
{
 | 
						|
    /// <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;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public float MaxWeightDistance
 | 
						|
    { 
 | 
						|
        get => _maxWeightDistance;
 | 
						|
        protected set
 | 
						|
        {
 | 
						|
            _maxWeightDistance = value;
 | 
						|
            _maxWeightDistanceSq = value * value;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public Items.Item DefaultSelectedItem { get; set; }
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public bool ShouldMoveWhenUsingItem { get; set; } = true;
 | 
						|
 | 
						|
    [Export]
 | 
						|
    public State.Thinker.ThinkerStateMachine ThinkerStateMachine { get; set; }
 | 
						|
 | 
						|
    public bool ShouldMove { get; set; } = true;
 | 
						|
 | 
						|
    public bool CanAttack { get; set; } = true;
 | 
						|
 | 
						|
    public Vector2 LastSeenPosition { get; set; }
 | 
						|
 | 
						|
    protected float[] _weights = new float[16];
 | 
						|
    protected int _bestWeightIdx;
 | 
						|
    protected double _thinkTimeElapsed = 0;
 | 
						|
    protected Vector2 _blockingDir;
 | 
						|
    protected static readonly Vector2[] _weightDirs = new Vector2[16];
 | 
						|
 | 
						|
    static NPC()
 | 
						|
    {
 | 
						|
        for (int i = 0; i < 16; i++)
 | 
						|
        {
 | 
						|
            float y = Mathf.Sin(Mathf.Pi * i * 2 / 16);
 | 
						|
            float x = Mathf.Cos(Mathf.Pi * i * 2 / 16);
 | 
						|
            _weightDirs[i] = new Vector2(x, y);
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    public override void _Ready()
 | 
						|
    {
 | 
						|
        base._Ready();
 | 
						|
        Array.Fill(_weights, 0);
 | 
						|
 | 
						|
        if (DefaultSelectedItem is not null)
 | 
						|
        {
 | 
						|
            Inventory.SelectedItem = DefaultSelectedItem;
 | 
						|
        }
 | 
						|
 | 
						|
        Inventory.UsedItem += (Items.Item item) =>
 | 
						|
        {
 | 
						|
            if (item is Items.Weapon)
 | 
						|
            {
 | 
						|
                if (AttackAnimation is not null)
 | 
						|
                {
 | 
						|
                    AttackAnimation.TryPlay("attack");
 | 
						|
                }
 | 
						|
            }
 | 
						|
        };
 | 
						|
    }
 | 
						|
 | 
						|
    /// <summary>
 | 
						|
    /// Finds the NPC's best character to target.
 | 
						|
    /// </summary>
 | 
						|
    public virtual Character FindBestTarget()
 | 
						|
    {
 | 
						|
        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 = character.Faction == Faction;
 | 
						|
                if (isFriendly || character.Health <= 0)
 | 
						|
                {
 | 
						|
                    continue;
 | 
						|
                }
 | 
						|
 | 
						|
                float score = 0;
 | 
						|
                score -= Position.DistanceTo(character.Position);
 | 
						|
 | 
						|
                if (score < bestScore)
 | 
						|
                {
 | 
						|
                    bestScore = score;
 | 
						|
                    bestChar = character;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return bestChar;
 | 
						|
    }
 | 
						|
 | 
						|
    /// <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;
 | 
						|
                score -= Position.DistanceTo(character.Position);
 | 
						|
 | 
						|
                if (score < bestScore)
 | 
						|
                {
 | 
						|
                    bestScore = score;
 | 
						|
                    bestChar = character;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return bestChar;
 | 
						|
    }
 | 
						|
 | 
						|
    public override void _Process(double delta)
 | 
						|
    {
 | 
						|
        ThinkerStateMachine.Process(delta);
 | 
						|
        base._Process(delta);
 | 
						|
    }
 | 
						|
 | 
						|
    public override void _PhysicsProcess(double delta)
 | 
						|
    {
 | 
						|
        ThinkerStateMachine.PhysicsProcess(delta);
 | 
						|
        base._PhysicsProcess(delta);
 | 
						|
    }
 | 
						|
}
 |