Capture current mission, AI, map and administration development

Include the current addon, terrain integrations, tools, regression tests and recorded development evidence.
This commit is contained in:
2026-10-13 22:52:53 +02:00
parent a951a96edc
commit 6a667d8deb
8104 changed files with 2124945 additions and 2061 deletions
@@ -0,0 +1,89 @@
// Conservative native volume check; no scenery removal or alternate placement.
class ERR_ConvoyPlacement
{
protected bool m_Obstructed;
protected int m_Visited;
protected string m_LastReason;
protected float m_LastLowest;
protected float m_LastHighest;
protected IEntity m_FirstObstruction;
protected float m_LastTrace = -1;
float LastTraceFraction() { return m_LastTrace; }
string Diagnostic()
{
string prefab = "none";
vector position;
if (m_FirstObstruction)
{
prefab = "no prefab";
if (m_FirstObstruction.GetPrefabData()) prefab = m_FirstObstruction.GetPrefabData().GetPrefabName();
position = m_FirstObstruction.GetOrigin();
}
return string.Format("%1; terrainY=%2..%3 span=%4; visited=%5; first=%6 at %7; trace=%8",
m_LastReason, m_LastLowest, m_LastHighest, m_LastHighest - m_LastLowest, m_Visited, prefab, position, m_LastTrace);
}
protected bool Occupancy(IEntity entity)
{
m_Visited++;
if (m_Visited > 128) { m_Obstructed = true; m_LastReason = "native query visit cap"; }
else if (entity && entity.GetVObject())
{
m_Obstructed = true; m_LastReason = "native VObject volume overlap"; m_FirstObstruction = entity;
}
return !m_Obstructed;
}
static bool Terrain(float origin, float lowest, float highest)
{
return ERR_ForceRules.Finite(origin) && ERR_ForceRules.Finite(lowest) && ERR_ForceRules.Finite(highest)
&& lowest > 0.3 && highest >= lowest && highest - lowest <= 0.75 && origin >= lowest - 0.3 && origin <= highest + 0.3;
}
bool Clear(vector position, float heading)
{
m_LastReason = "invalid origin or heading"; m_LastLowest = 1e20; m_LastHighest = -1e20;
m_FirstObstruction = null; m_Visited = 0; m_LastTrace = -1;
if (!ERR_ForceRules.FiniteVector(position) || !ERR_ForceRules.Finite(heading)) return false;
float radians = heading * Math.DEG2RAD;
vector forward = Vector(Math.Sin(radians), 0, Math.Cos(radians));
vector right = Vector(forward[2], 0, -forward[0]);
float lowest = 1e20, highest = -1e20;
vector low = position, high = position;
for (int x = -1; x <= 1; x++)
{
for (int z = -1; z <= 1; z++)
{
vector sample = ERR_Actors.Ground(position + right * (x * 2.0) + forward * (z * 4.5));
if (!ERR_ForceRules.FiniteVector(sample)) { m_LastReason = "nonfinite terrain sample"; return false; }
if (ChimeraWorldUtils.TryGetWaterSurfaceSimple(GetGame().GetWorld(), sample))
{ m_LastReason = "water at footprint sample"; return false; }
lowest = Math.Min(lowest, sample[1]); highest = Math.Max(highest, sample[1]);
low[0] = Math.Min(low[0], sample[0]); low[2] = Math.Min(low[2], sample[2]);
high[0] = Math.Max(high[0], sample[0]); high[2] = Math.Max(high[2], sample[2]);
}
}
m_LastLowest = lowest; m_LastHighest = highest;
if (!Terrain(position[1], lowest, highest))
{ m_LastReason = "terrain origin/height/span check"; return false; }
low[1] = lowest + 0.05; high[1] = highest + 3.5;
m_Obstructed = false; m_Visited = 0;
GetGame().GetWorld().QueryEntitiesByAABB(low, high, Occupancy);
// Entity bounding boxes can include distant branches or overhead wires.
// Their overlap is diagnostic only; the filled native volume decides collision.
vector center = (low + high) * 0.5;
TraceBox volume = new TraceBox();
volume.Start = center; volume.End = center + "0 0.01 0";
volume.Mins = low - center; volume.Maxs = high - center;
// Nine terrain/water samples already qualify the supporting ground below us.
volume.Flags = TraceFlags.ENTS | TraceFlags.OCEAN;
m_LastTrace = GetGame().GetWorld().TraceMove(volume, null);
m_Obstructed = !ERR_ForceRules.Finite(m_LastTrace) || m_LastTrace < 1;
m_FirstObstruction = null;
if (m_Obstructed)
{
m_FirstObstruction = volume.TraceEnt;
m_LastReason = "native filled volume collision";
if (!ERR_ForceRules.Finite(m_LastTrace)) m_LastReason = "invalid native trace fraction";
}
else m_LastReason = "clear";
return !m_Obstructed;
}
}
@@ -0,0 +1,60 @@
"""Execute actual footprint sampling and native query callback policy with API doubles."""
import re
from policy_harness import ROOT,body,run
p=ROOT/'addons/core/Scripts/Game/ERR/10_AI/ERR_ConvoyPlacement.c'
code='\n'.join(body(p,s) for s in ('protected bool Occupancy(', 'static bool Terrain(', 'bool Clear('))
code=code.replace('null','nullptr').replace('protected ','').replace('IEntity entity','IEntity* entity').replace('entity.GetVObject()', 'entity->GetVObject()')
code=code.replace('TraceBox volume', 'TraceBox* volume').replace('volume.', 'volume->').replace('\"0 0.01 0\"', 'Vector(0,.01,0)')
for cls in ('Math','ERR_ForceRules','ERR_Actors','ChimeraWorldUtils','TraceFlags'):code=code.replace(cls+'.',cls+'::')
code=code.replace('GetGame().GetWorld().','GetGame()->GetWorld()->').replace('GetGame().GetWorld()', 'GetGame()->GetWorld()')
code=code.replace('low, high, Occupancy','low, high, [this](IEntity* entity){return Occupancy(entity);}')
run(r'''
#include <cassert>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
struct vector{float x=0,y=0,z=0;float& operator[](int i){return i==0?x:i==1?y:z;}vector operator+(vector b)const{return{x+b.x,y+b.y,z+b.z};}vector operator-(vector b)const{return{x-b.x,y-b.y,z-b.z};}vector operator*(float n)const{return{x*n,y*n,z*n};}};
vector Vector(float x,float y,float z){return{x,y,z};}
namespace Math{float DEG2RAD=3.141592653589793/180;float Sin(float n){return std::sin(n);}float Cos(float n){return std::cos(n);}float Min(float a,float b){return std::min(a,b);}float Max(float a,float b){return std::max(a,b);}}
namespace ERR_ForceRules{bool Finite(float n){return std::isfinite(n);}bool FiniteVector(vector v){return Finite(v.x)&&Finite(v.y)&&Finite(v.z);}}
struct IEntity{bool visible=false;bool GetVObject(){return visible;}};
struct TraceBox {vector Start,End,Mins,Maxs;int Flags=0;IEntity* TraceEnt=nullptr;};
namespace TraceFlags{constexpr int ENTS=1,OCEAN=2,WORLD=4;}
struct World{int traces=0;float fraction=1;IEntity* hit=nullptr;TraceBox last;float TraceMove(TraceBox* volume,void*){traces++;last=*volume;volume->TraceEnt=hit;return fraction;}int queries=0,visits=0;vector low,high;std::vector<IEntity*> entities;template<class C>void QueryEntitiesByAABB(vector a,vector b,C callback){queries++;low=a;high=b;for(auto e:entities){visits++;if(!callback(e))break;}}};
struct Game{World world;World* GetWorld(){return &world;}}game;Game* GetGame(){return &game;}
namespace ERR_Actors{int grounds=0;float elevation=10,slope=0;bool invalid=false;vector Ground(vector v){grounds++;v.y=invalid?NAN:elevation+slope*v.x;return v;}}
namespace ChimeraWorldUtils{int waters=0;bool water=false;bool TryGetWaterSurfaceSimple(World*,vector){waters++;return water;}}
struct Placement{float m_LastTrace=-1;std::string m_LastReason;float m_LastLowest=0,m_LastHighest=0;IEntity* m_FirstObstruction=nullptr;bool m_Obstructed=false;int m_Visited=0;'''+code+r'''};
void reset(){game.world=World();ERR_Actors::grounds=0;ERR_Actors::elevation=10;ERR_Actors::slope=0;ERR_Actors::invalid=false;ChimeraWorldUtils::waters=0;ChimeraWorldUtils::water=false;}
bool near(float a,float b){return std::abs(a-b)<.001;}
int main(){
Placement p;reset();assert(p.Clear({0,10,0},0));assert(ERR_Actors::grounds==9&&ChimeraWorldUtils::waters==9&&game.world.queries==1);
assert(near(game.world.low.x,-2)&&near(game.world.high.x,2)&&near(game.world.low.z,-4.5)&&near(game.world.high.z,4.5));assert(near(game.world.low.y,10.05)&&near(game.world.high.y,13.5));
assert(game.world.traces==1&&game.world.last.Flags==(TraceFlags::ENTS|TraceFlags::OCEAN));
assert(near(game.world.last.Start.y+game.world.last.Mins.y,10.05)&&near(game.world.last.Start.y+game.world.last.Maxs.y,13.5));
assert(near(game.world.last.Mins.x,-2)&&near(game.world.last.Maxs.x,2)&&near(game.world.last.Mins.z,-4.5)&&near(game.world.last.Maxs.z,4.5));
assert(game.world.last.End.y>game.world.last.Start.y&&near(game.world.last.End.y-game.world.last.Start.y,.01));
reset();assert(p.Clear({0,10,0},90));assert(near(game.world.low.x,-4.5)&&near(game.world.high.x,4.5)&&near(game.world.low.z,-2)&&near(game.world.high.z,2));
reset();assert(!p.Clear({NAN,10,0},0)&&game.world.queries==0&&ERR_Actors::grounds==0);assert(!p.Clear({0,10,0},NAN));
reset();ERR_Actors::slope=.2;assert(!p.Clear({0,10,0},0)&&game.world.queries==0); // 0.8m cross-footprint rise.
reset();ERR_Actors::elevation=.29;assert(!p.Clear({0,.29,0},0)&&game.world.queries==0);
reset();assert(!p.Clear({0,10.31,0},0)&&game.world.queries==0);
reset();ChimeraWorldUtils::water=true;assert(!p.Clear({0,10,0},0)&&game.world.queries==0&&ERR_Actors::grounds==1);
reset();ERR_Actors::invalid=true;assert(!p.Clear({0,10,0},0)&&game.world.queries==0);
IEntity abstract,obstacle;obstacle.visible=true;
reset();game.world.entities={&abstract,&obstacle,&abstract};assert(p.Clear({0,10,0},0)&&game.world.visits==2&&!p.m_Obstructed&&!p.m_FirstObstruction&&p.m_LastReason=="clear");
// Broad tree/cable bounds overlap, but their actual low geometry does not: accept.
assert(game.world.traces==1&&p.m_LastTrace==1);
// Actual geometry refuses even when the broad diagnostic callback found only abstract entities.
reset();game.world.entities={&abstract};game.world.fraction=.4;game.world.hit=&obstacle;
assert(!p.Clear({0,10,0},0)&&p.m_Obstructed&&p.m_FirstObstruction==&obstacle&&p.m_LastReason=="native filled volume collision");
for(float fraction:{0.f,.999f,NAN,INFINITY}){reset();game.world.fraction=fraction;assert(!p.Clear({0,10,0},0));}
// New query resets previous obstruction. Abstract logic entities do not block.
reset();game.world.entities={nullptr,&abstract};assert(p.Clear({0,10,0},0)&&game.world.visits==2&&!p.m_Obstructed&&!p.m_FirstObstruction&&p.m_LastReason=="clear");
reset();game.world.entities.assign(128,&abstract);assert(p.Clear({0,10,0},0)&&game.world.visits==128);
reset();game.world.entities.assign(130,&abstract);assert(p.Clear({0,10,0},0)&&game.world.visits==129&&game.world.traces==1&&p.m_LastReason=="clear");
assert(Placement::Terrain(10,9.7,10.4));assert(!Placement::Terrain(10,9.7,10.451));assert(!Placement::Terrain(10,10.1,10));assert(!Placement::Terrain(10,NAN,10));
}
''')
print('PASS: actual filled rotated-footprint TraceBox and nine terrain/water samples; broad tree/cable bounds alone accepted, actual collision/nonfinite trace refused, nonzero sweep and ENTS/OCEAN flags, bounded diagnostic query and reset')