Init Homebase

This commit is contained in:
2026-06-02 18:28:23 -07:00
parent 2ee30c01fd
commit dd0064c377
48 changed files with 1934 additions and 12 deletions
@@ -0,0 +1,113 @@
using NUnit.Framework;
using ProjectM.Simulation;
using Unity.Mathematics;
namespace ProjectM.Tests
{
/// <summary>
/// Pure-function tests for <see cref="BaseGridMath"/> (no ECS world), mirroring PlayerSpawnRingTests.
/// Pins the locked M5 home-base grid coordinate space: planar 32x32, CellSize 1.0, corner-origin,
/// center-returning, half-open cell bounds, floor-based world-&gt;cell. M6 placement builds on this.
/// </summary>
public class BaseGridMathTests
{
const float Eps = 1e-4f;
// Locked config: center at (0,1,0); 32x32 cells of 1.0u => origin corner at (-16,1,-16).
static BaseAnchor MakeAnchor()
{
var dims = new int2(32, 32);
float cell = 1f;
var anchorPos = new float3(0f, 1f, 0f);
var gridOrigin = new float3(
anchorPos.x - dims.x * cell * 0.5f,
anchorPos.y,
anchorPos.z - dims.y * cell * 0.5f);
return new BaseAnchor { AnchorPos = anchorPos, GridOrigin = gridOrigin, CellSize = cell, GridDims = dims };
}
[Test]
public void CellToWorld_Returns_Cell_Centers()
{
var a = MakeAnchor();
AssertXz(BaseGridMath.CellToWorld(a, new int2(0, 0)), -15.5f, -15.5f);
AssertXz(BaseGridMath.CellToWorld(a, new int2(31, 31)), 15.5f, 15.5f);
AssertXz(BaseGridMath.CellToWorld(a, new int2(16, 16)), 0.5f, 0.5f);
// Centers stay on the base plane.
Assert.AreEqual(1f, BaseGridMath.CellToWorld(a, new int2(5, 9)).y, Eps);
}
[Test]
public void RoundTrip_CellToWorld_Then_WorldToCell()
{
var a = MakeAnchor();
AssertCell(BaseGridMath.WorldToCell(a, BaseGridMath.CellToWorld(a, new int2(0, 0))), 0, 0);
AssertCell(BaseGridMath.WorldToCell(a, BaseGridMath.CellToWorld(a, new int2(31, 31))), 31, 31);
AssertCell(BaseGridMath.WorldToCell(a, BaseGridMath.CellToWorld(a, new int2(16, 16))), 16, 16);
}
[Test]
public void WorldToCell_Uses_Floor_Not_Truncation()
{
var a = MakeAnchor();
// local x = worldX + 16, then floor. z = 0 -> cell.y = 16.
Assert.AreEqual(0, BaseGridMath.WorldToCell(a, new float3(-15.6f, 1f, 0f)).x);
Assert.AreEqual(-1, BaseGridMath.WorldToCell(a, new float3(-16.4f, 1f, 0f)).x);
// A point exactly on a cell's lower edge belongs to the higher cell (half-open).
Assert.AreEqual(1, BaseGridMath.WorldToCell(a, new float3(-15.0f, 1f, 0f)).x);
Assert.AreEqual(16, BaseGridMath.WorldToCell(a, new float3(-15.6f, 1f, 0f)).y);
}
[Test]
public void IsCellInPlot_Is_HalfOpen()
{
var a = MakeAnchor();
Assert.IsTrue(BaseGridMath.IsCellInPlot(a, new int2(0, 0)));
Assert.IsTrue(BaseGridMath.IsCellInPlot(a, new int2(31, 31)));
Assert.IsFalse(BaseGridMath.IsCellInPlot(a, new int2(-1, 0)));
Assert.IsFalse(BaseGridMath.IsCellInPlot(a, new int2(0, 32)));
Assert.IsFalse(BaseGridMath.IsCellInPlot(a, new int2(32, 32)));
}
[Test]
public void IsPointInPlot_Bounds()
{
var a = MakeAnchor();
Assert.IsTrue(BaseGridMath.IsPointInPlot(a, a.AnchorPos)); // center
Assert.IsTrue(BaseGridMath.IsPointInPlot(a, new float3(-16f, 1f, 0f))); // lower corner edge -> cell 0
Assert.IsTrue(BaseGridMath.IsPointInPlot(a, new float3(15.9f, 1f, 15.9f))); // cell (31,31)
Assert.IsFalse(BaseGridMath.IsPointInPlot(a, new float3(-16.1f, 1f, 0f))); // cell (-1,*) out
Assert.IsFalse(BaseGridMath.IsPointInPlot(a, new float3(16f, 1f, 0f))); // far edge -> cell 32 out
}
[Test]
public void ClampCell_Bounds_To_Plot()
{
var a = MakeAnchor();
AssertCell(BaseGridMath.ClampCell(a, new int2(-5, 99)), 0, 31);
AssertCell(BaseGridMath.ClampCell(a, new int2(10, 10)), 10, 10);
}
[Test]
public void PlotCenter_Equals_AnchorPos()
{
var a = MakeAnchor();
var c = BaseGridMath.PlotCenter(a);
Assert.AreEqual(a.AnchorPos.x, c.x, Eps);
Assert.AreEqual(a.AnchorPos.y, c.y, Eps);
Assert.AreEqual(a.AnchorPos.z, c.z, Eps);
}
static void AssertXz(float3 p, float x, float z)
{
Assert.AreEqual(x, p.x, Eps);
Assert.AreEqual(z, p.z, Eps);
}
static void AssertCell(int2 c, int x, int y)
{
Assert.AreEqual(x, c.x);
Assert.AreEqual(y, c.y);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 001a3491cf2ef57468f3e4c61e98d30c
@@ -0,0 +1,141 @@
using NUnit.Framework;
using ProjectM.Simulation;
using Unity.Entities;
namespace ProjectM.Tests
{
/// <summary>
/// Plain-Entities tests for <see cref="StorageMath"/> deposit/withdraw merge logic, using a temp World
/// and a real DynamicBuffer&lt;StorageEntry&gt; (no netcode world). Pins shared-storage semantics:
/// deposits merge by item id, withdraws clamp to available and drop empty rows.
/// </summary>
public class StorageMathTests
{
static (World world, Entity e) MakeWorld()
{
var world = new World("StorageMathTestWorld");
var e = world.EntityManager.CreateEntity(typeof(StorageEntry));
return (world, e);
}
[Test]
public void Deposit_New_Item_Appends_Row()
{
var (world, e) = MakeWorld();
try
{
var buf = world.EntityManager.GetBuffer<StorageEntry>(e);
StorageMath.Deposit(buf, 1, 5);
Assert.AreEqual(1, buf.Length);
Assert.AreEqual((ushort)1, buf[0].ItemId);
Assert.AreEqual(5, buf[0].Count);
}
finally { world.Dispose(); }
}
[Test]
public void Deposit_Same_Item_Merges()
{
var (world, e) = MakeWorld();
try
{
var buf = world.EntityManager.GetBuffer<StorageEntry>(e);
StorageMath.Deposit(buf, 1, 5);
StorageMath.Deposit(buf, 1, 3);
Assert.AreEqual(1, buf.Length);
Assert.AreEqual(8, buf[0].Count);
}
finally { world.Dispose(); }
}
[Test]
public void Deposit_Different_Items_Separate_Rows()
{
var (world, e) = MakeWorld();
try
{
var buf = world.EntityManager.GetBuffer<StorageEntry>(e);
StorageMath.Deposit(buf, 1, 5);
StorageMath.Deposit(buf, 2, 7);
Assert.AreEqual(2, buf.Length);
}
finally { world.Dispose(); }
}
[Test]
public void Withdraw_Partial_Decrements()
{
var (world, e) = MakeWorld();
try
{
var buf = world.EntityManager.GetBuffer<StorageEntry>(e);
StorageMath.Deposit(buf, 1, 5);
int taken = StorageMath.Withdraw(buf, 1, 2);
Assert.AreEqual(2, taken);
Assert.AreEqual(1, buf.Length);
Assert.AreEqual(3, buf[0].Count);
}
finally { world.Dispose(); }
}
[Test]
public void Withdraw_To_Zero_Drops_Row()
{
var (world, e) = MakeWorld();
try
{
var buf = world.EntityManager.GetBuffer<StorageEntry>(e);
StorageMath.Deposit(buf, 1, 5);
int taken = StorageMath.Withdraw(buf, 1, 5);
Assert.AreEqual(5, taken);
Assert.AreEqual(0, buf.Length);
}
finally { world.Dispose(); }
}
[Test]
public void Withdraw_More_Than_Available_Clamps()
{
var (world, e) = MakeWorld();
try
{
var buf = world.EntityManager.GetBuffer<StorageEntry>(e);
StorageMath.Deposit(buf, 1, 3);
int taken = StorageMath.Withdraw(buf, 1, 10);
Assert.AreEqual(3, taken);
Assert.AreEqual(0, buf.Length);
}
finally { world.Dispose(); }
}
[Test]
public void Withdraw_Missing_Item_Is_NoOp()
{
var (world, e) = MakeWorld();
try
{
var buf = world.EntityManager.GetBuffer<StorageEntry>(e);
StorageMath.Deposit(buf, 1, 3);
int taken = StorageMath.Withdraw(buf, 99, 1);
Assert.AreEqual(0, taken);
Assert.AreEqual(1, buf.Length);
}
finally { world.Dispose(); }
}
[Test]
public void Deposit_Ignores_NonPositive_And_ZeroItem()
{
var (world, e) = MakeWorld();
try
{
var buf = world.EntityManager.GetBuffer<StorageEntry>(e);
StorageMath.Deposit(buf, 1, 0);
StorageMath.Deposit(buf, 1, -5);
StorageMath.Deposit(buf, 0, 5);
Assert.AreEqual(0, buf.Length);
}
finally { world.Dispose(); }
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c3f5de6f166b6da45bd20e2884e2f4c0