using System.Collections.Generic;
using Godot;
namespace WorldSim;
///
/// A coarse spatial hash over NPC positions so proximity queries stop being
/// O(n²). Rebuilt once per tick from the live NPC list; queries return only
/// the buckets within range. At ~80 NPCs this turns every soul-pressure,
/// neighbor, and teacher scan from "look at everyone" into "look at the few
/// nearby" — the difference between a live view that stutters and one that
/// doesn't.
///
public class SpatialGrid
{
private readonly float _cell;
private readonly Dictionary<(int, int), List> _buckets = new();
public SpatialGrid(float cellSize) => _cell = cellSize;
private (int, int) Key(Vector2 p) =>
(Mathf.FloorToInt(p.X / _cell), Mathf.FloorToInt(p.Y / _cell));
public void Rebuild(IReadOnlyList npcs)
{
foreach (var list in _buckets.Values) list.Clear();
foreach (var npc in npcs)
{
var key = Key(npc.Position);
if (!_buckets.TryGetValue(key, out var list))
_buckets[key] = list = new List();
list.Add(npc);
}
}
///
/// Invoke for every NPC within of (excluding none — caller
/// filters). Scans only the buckets the radius touches.
///
public void ForEachNear(Vector2 center, float radius, System.Action action)
{
int reach = Mathf.CeilToInt(radius / _cell);
var (cx, cy) = Key(center);
for (int dx = -reach; dx <= reach; dx++)
for (int dy = -reach; dy <= reach; dy++)
{
if (_buckets.TryGetValue((cx + dx, cy + dy), out var list))
foreach (var npc in list) action(npc);
}
}
}