SupaLidlGame/Characters/NPC.cs

195 lines
6.1 KiB
C#
Raw Normal View History

2022-11-10 20:29:53 -08:00
using Godot;
using System;
namespace SupaLidlGame.Characters
{
public partial class NPC : Character
{
2022-11-12 16:45:04 -08:00
/// <summary>
/// Time in seconds it takes for the NPC to think FeelsDankCube
/// </summary>
2022-11-13 15:42:04 -08:00
public const float ThinkTime = 0.25f;
2022-11-10 20:29:53 -08:00
public float[] Weights => _weights;
2022-11-13 19:52:09 -08:00
protected float[] _weights = new float[16];
protected Vector2[] _weightDirs = new Vector2[16];
protected int _bestWeightIdx;
2022-11-10 20:29:53 -08:00
protected double _thinkTimeElapsed = 0;
public override void _Ready()
{
base._Ready();
Array.Fill(_weights, 0);
2022-11-12 16:45:04 -08:00
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);
}
2022-11-10 20:29:53 -08:00
}
2022-11-13 15:42:04 -08:00
/*
2022-11-10 20:29:53 -08:00
public override void _Process(double delta)
{
if ((_thinkTimeElapsed += delta) > ThinkTime)
{
_thinkTimeElapsed = 0;
Think();
}
2022-11-12 16:45:04 -08:00
Direction = _weightDirs[_bestWeightIdx];
2022-11-10 20:29:53 -08:00
//Direction = (Target.GlobalPosition - GlobalPosition).Normalized();
base._Process(delta);
}
2022-11-13 15:42:04 -08:00
*/
2022-11-10 20:29:53 -08:00
public override void _Draw()
{
2022-11-12 16:45:04 -08:00
for (int i = 0; i < 16; i++)
2022-11-10 20:29:53 -08:00
{
2022-11-12 16:45:04 -08:00
Vector2 vec = _weightDirs[i] * _weights[i] * 128;
Color c = Colors.Green;
if (_bestWeightIdx == i)
{
c = Colors.Blue;
}
else if (_weights[i] < 0)
{
c = Colors.Red;
vec = -vec;
}
DrawLine(Vector2.Zero, vec, c);
2022-11-10 20:29:53 -08:00
}
base._Draw();
}
2022-11-12 16:45:04 -08:00
protected virtual Character FindBestTarget()
2022-11-10 20:29:53 -08:00
{
float bestDist = float.MaxValue;
Character bestChar = null;
foreach (Node node in GetParent().GetChildren())
{
2022-11-12 16:45:04 -08:00
if (node != this && node is Player character)
2022-11-10 20:29:53 -08:00
{
float dist = Position.DistanceTo(character.Position);
if (dist < bestDist)
{
bestDist = dist;
bestChar = character;
}
}
}
return bestChar;
}
2022-11-13 15:42:04 -08:00
public void ThinkProcess(double delta)
{
if ((_thinkTimeElapsed += delta) > ThinkTime)
{
_thinkTimeElapsed = 0;
Think();
}
Direction = _weightDirs[_bestWeightIdx];
}
2022-11-10 20:29:53 -08:00
private void Think()
{
2022-11-12 16:45:04 -08:00
Vector2 pos = FindBestTarget().GlobalPosition;
2022-11-13 15:42:04 -08:00
Vector2 dir = Target = GlobalPosition.DirectionTo(pos);
2022-11-12 16:45:04 -08:00
float dist = GlobalPosition.DistanceSquaredTo(pos);
2022-11-10 20:29:53 -08:00
2022-11-12 16:45:04 -08:00
for (int i = 0; i < 16; i++)
2022-11-10 20:29:53 -08:00
{
2022-11-12 16:45:04 -08:00
float directDot = _weightDirs[i].Dot(dir);
directDot = (directDot + 1) / 2;
// this dot product resembles values of sine rather than cosine
// use it to weigh direction horizontally
Vector2 rotatedDir = new Vector2(-dir.y, dir.x);
float horizDot = Math.Abs(_weightDirs[i].Dot(rotatedDir));
2022-11-13 15:42:04 -08:00
// this is a smaller weight so they are more likely to pick the
// direction they are currently heading when choosing between two
// horizontal weights
2022-11-12 16:45:04 -08:00
float currDirDot = (_weightDirs[i].Dot(Direction) + 1) / 16;
// square so lower values are even lower
horizDot = Mathf.Pow((horizDot + 1) / 2, 2) + currDirDot;
// "When will I use math in the real world" Clueful
if (dist > 1024)
{
_weights[i] = directDot;
}
else if (dist > 64)
2022-11-10 20:29:53 -08:00
{
2022-11-12 16:45:04 -08:00
float directDotWeighting = (dist - 64) / 960;
float horizDotWeighting = 1 - directDotWeighting;
_weights[i] = (directDot * directDotWeighting) +
(horizDot * horizDotWeighting);
2022-11-10 20:29:53 -08:00
}
else
{
2022-11-12 16:45:04 -08:00
// shorter than 64
_weights[i] = horizDot;
2022-11-10 20:29:53 -08:00
}
2022-11-12 16:45:04 -08:00
// now we shall subtract weights whose rays collide
// with something
2022-11-10 20:29:53 -08:00
{
var spaceState = GetWorld2d().DirectSpaceState;
2022-11-12 16:45:04 -08:00
var exclude = new Godot.Collections.Array<RID>();
exclude.Add(this.GetRid());
var rayParams = new PhysicsRayQueryParameters2D
{
Exclude = exclude,
CollideWithBodies = true,
From = GlobalPosition,
To = GlobalPosition + (_weightDirs[i] * 16)
};
var result = spaceState.IntersectRay(rayParams);
// if our ray cast hits something
2022-11-10 20:29:53 -08:00
if (result.Count > 0)
{
2022-11-12 16:45:04 -08:00
// then we subtract the dot product of other directions
for (int j = 0; j < 16; j++)
{
if (i == j)
{
_weights[i] = 0;
}
else
{
float dot = _weightDirs[i].Dot(_weightDirs[j]);
2022-11-13 15:42:04 -08:00
_weights[j] -= (dot + 1) / 2;
2022-11-12 16:45:04 -08:00
}
}
2022-11-10 20:29:53 -08:00
}
}
2022-11-12 16:45:04 -08:00
}
float bestWeight = 0;
2022-11-10 20:29:53 -08:00
2022-11-12 16:45:04 -08:00
for (int i = 0; i < 16; i++)
{
2022-11-10 20:29:53 -08:00
if (_weights[i] > bestWeight)
{
bestWeight = _weights[i];
2022-11-12 16:45:04 -08:00
_bestWeightIdx = i;
2022-11-10 20:29:53 -08:00
}
}
2022-11-12 16:45:04 -08:00
2022-11-10 20:29:53 -08:00
QueueRedraw();
}
}
}