Expand roadblocks with defensive bays and checkpoint equipment
This commit is contained in:
@@ -162,7 +162,7 @@ modded class ERR_GameMode
|
||||
}
|
||||
if (posts.ERR_ProbeBackgroundPending()) return;
|
||||
if (!posts.ERR_ProbeCompleteRoadblocks() || posts.ERR_ProbePostCount() <= m_ERRRoadblockInitial) { ERR_RoadblockProbeFail("physical four-guard posts, replicated props, ledger and HQ admission"); return; }
|
||||
PrintFormat("ERR SMOKE PASS: roadblocks %1 physical posts / %2 guards / %3 replicated checkpoint props with distance streaming disabled; verified model footprints and centre lane; background completed after prison unlock, no extra posts on repeat tick", posts.ERR_ProbePostCount(), posts.ERR_ProbePostCount() * 4, posts.ERR_ProbePostCount() * 10);
|
||||
PrintFormat("ERR SMOKE PASS: roadblocks %1 physical posts / %2 guards / %3 replicated checkpoint props with distance streaming disabled; verified model footprints and centre lane; background completed after prison unlock, no extra posts on repeat tick", posts.ERR_ProbePostCount(), posts.ERR_ProbePostCount() * 4, posts.ERR_ProbePostCount() * ERR_RoadblockRules.PropCount());
|
||||
ERR_StopRoadblockProbe();
|
||||
Transition(ERR_Phase.DEFEAT);
|
||||
Print("ERR SMOKE COMPLETE: roadblocks background completion on Everon; scripted local fixture");
|
||||
|
||||
@@ -4,6 +4,7 @@ class ERR_RoadblockRules
|
||||
{
|
||||
// Admission never waits longer for a high count. Remaining posts are built
|
||||
// by the same safe placement path in background, including after prison unlock.
|
||||
static int PropCount() { return 18; }
|
||||
static int SearchMilliseconds() { return 12000; }
|
||||
static int SearchAttempts() { return 300; }
|
||||
static bool Road(float width, float length, float heightDifference)
|
||||
@@ -62,6 +63,8 @@ class ERR_Roadblocks
|
||||
static const ResourceName BARRIER = "{64C9C1D1C2C1B38D}Prefabs/Props/Infrastructure/Roadblocks/RoadBlock_01.et";
|
||||
static const ResourceName OBSTACLE = "{A46D45922F8CAD6C}Prefabs/Props/Military/Fortification/CzechHedgehog_01_painted.et";
|
||||
static const ResourceName SIGN = "{C1977A326F2AA011}Prefabs/Structures/Signs/Military/SignCheckpoint_01_stop.et";
|
||||
static const ResourceName SHELTER = "{01AE5FD77A9A4C21}Prefabs/Structures/Military/Camps/TentSmallUS_01/TentSmallUS_01.et";
|
||||
static const ResourceName TABLE = "{53506DCB398C7EBE}Prefabs/Props/Furniture/Table_01.et";
|
||||
void ERR_Roadblocks(ERR_GameMode mode) { m_Mode = mode; m_Mining = new ERR_RoadblockMining(mode); }
|
||||
void MineTick(array<ref ERR_HQUnit> units, array<IEntity> attempted, float now) { if (!m_Stopped) m_Mining.Tick(units, attempted, now); }
|
||||
bool Contains(SCR_AIGroup group) { return group && m_Groups.Contains(group); }
|
||||
@@ -90,7 +93,7 @@ class ERR_Roadblocks
|
||||
if (profile && profile.IsValid()) source = profile.GetResource().ToEntitySource();
|
||||
if (!source || !source.Get("m_aUnitPrefabSlots", m_Roster) || m_Roster.IsEmpty()) return false;
|
||||
// Preload before physical creation. A failed prefab never leaves half a post.
|
||||
array<ResourceName> resources = {COVER, BARRIER, OBSTACLE, SIGN, "{EC9F1AA42D0B7863}Prefabs/ERR/PrisonPatrol.et", ERR_Resources.DefendWaypoint()};
|
||||
array<ResourceName> resources = {COVER, BARRIER, OBSTACLE, SIGN, SHELTER, TABLE, "{EC9F1AA42D0B7863}Prefabs/ERR/PrisonPatrol.et", ERR_Resources.DefendWaypoint()};
|
||||
foreach (ResourceName actor : m_Roster) resources.Insert(actor);
|
||||
foreach (ResourceName prefab : resources)
|
||||
{
|
||||
@@ -187,15 +190,23 @@ class ERR_Roadblocks
|
||||
// Footprints enclose the native model bounds, including small placement margins.
|
||||
for (int side = -1; side <= 1; side += 2)
|
||||
{
|
||||
// Two long sandbag walls shelter each pair from the road and its approach.
|
||||
AddProp(covers, COVER, side * m_Shoulder, 0, m_Right, 1.9, 0.7, 1);
|
||||
AddProp(covers, COVER, side * (m_Shoulder + 2.8), side * 2.8, m_Forward, 1.9, 0.7, 1);
|
||||
// Opposing, staggered stop lines: the inner ends leave a >=4m centre lane.
|
||||
// Two front walls, an approach wall and an outer return form
|
||||
// a sheltered bay for each pair. The rear stays open for guard access.
|
||||
AddProp(covers, COVER, side * m_Shoulder, -2, m_Right, 1.9, 0.7, 1);
|
||||
AddProp(covers, COVER, side * m_Shoulder, 2, m_Right, 1.9, 0.7, 1);
|
||||
AddProp(covers, COVER, side * (m_Shoulder + 2.8), side * 4.8, m_Forward, 1.9, 0.7, 1);
|
||||
AddProp(covers, COVER, side * (m_Shoulder + 5.6), side * 2, m_Right, 1.9, 0.7, 1);
|
||||
// Paired stop-line barriers funnel traffic into a >=4m centre passage.
|
||||
AddProp(covers, BARRIER, side * 3.3, side * 8, m_Forward, 1.2, 0.4, 1.3);
|
||||
AddProp(covers, OBSTACLE, side * (m_Shoulder + 4), side * 8, m_Forward, 1, 1, 1.6);
|
||||
AddProp(covers, BARRIER, side * 5.9, side * 8, m_Forward, 1.2, 0.4, 1.3);
|
||||
AddProp(covers, OBSTACLE, side * (m_Shoulder + 4.5), side * 8, m_Forward, 1, 1, 1.6);
|
||||
AddProp(covers, SIGN, side * 3, side * 12, m_Forward * (side * 1.0), 0.45, 0.4, 0.8);
|
||||
}
|
||||
// A small inspection/rest area behind one bay, away from the vehicle lane.
|
||||
AddProp(covers, TABLE, m_Shoulder + 3, -4.8, m_Forward, 0.9, 0.7, 1.1);
|
||||
AddProp(covers, SHELTER, m_Shoulder + 3, -8.5, m_Forward, 1.2, 2.4, 1.4);
|
||||
}
|
||||
|
||||
protected void AddProp(array<ref ERR_RoadblockProp> covers, ResourceName prefab, float across, float along, vector forward, float halfWidth, float halfDepth, float height)
|
||||
{
|
||||
vector point = ERR_Actors.Ground(m_Centre + m_Right * across + m_Forward * along);
|
||||
@@ -209,7 +220,7 @@ class ERR_Roadblocks
|
||||
// Start a transaction; physical pieces are created separately in BuildPost.
|
||||
protected bool SpawnPost(array<vector> positions, array<ref ERR_RoadblockProp> covers)
|
||||
{
|
||||
if (!PlayersClear() || m_Mode.Forces().Count() >= 250 || m_BuildGroup || positions.Count() != 4 || covers.Count() != 10) return false;
|
||||
if (!PlayersClear() || m_Mode.Forces().Count() >= 250 || m_BuildGroup || positions.Count() != 4 || covers.Count() != ERR_RoadblockRules.PropCount()) return false;
|
||||
m_BuildGroup = SCR_AIGroup.Cast(ERR_Actors.Spawn("{EC9F1AA42D0B7863}Prefabs/ERR/PrisonPatrol.et", m_Centre));
|
||||
if (!m_BuildGroup) return false;
|
||||
m_BuildGroup.SetFaction(m_Mode.EnemyFaction()); m_BuildGroup.SetSpawnImmediately(false); m_BuildGroup.SetDeleteWhenEmpty(false); m_BuildGroup.SetPermanentLOD(0);
|
||||
@@ -225,20 +236,20 @@ class ERR_Roadblocks
|
||||
m_BuildPositions.Clear(); m_BuildCovers.Clear();
|
||||
}
|
||||
// A free Game Master camera does not force-stream bare, non-editable props
|
||||
// with the nearby editable guards. Keep this bounded bank (<=300 objects) on
|
||||
// with the nearby editable guards. Keep this bounded bank (<=540 props) on
|
||||
// clients, including late joiners, rather than tying it to the player's body.
|
||||
// Do not report a completed post if any physical prop lacks a network node.
|
||||
protected bool ShareProps()
|
||||
{
|
||||
if (m_BuildCovers.Count() != 10 || m_BuildSpawned.Count() != 15) return false;
|
||||
for (int i = 4; i < 14; i++)
|
||||
if (m_BuildCovers.Count() != ERR_RoadblockRules.PropCount() || m_BuildSpawned.Count() != m_BuildPositions.Count() + m_BuildCovers.Count() + 1) return false;
|
||||
for (int i = m_BuildPositions.Count(); i < m_BuildPositions.Count() + m_BuildCovers.Count(); i++)
|
||||
{
|
||||
IEntity entity = m_BuildSpawned[i];
|
||||
RplComponent component;
|
||||
if (entity) component = RplComponent.Cast(entity.FindComponent(RplComponent));
|
||||
if (!component || component.Id() == RplId.Invalid()) return false;
|
||||
}
|
||||
for (int i = 4; i < 14; i++)
|
||||
for (int i = m_BuildPositions.Count(); i < m_BuildPositions.Count() + m_BuildCovers.Count(); i++)
|
||||
{
|
||||
RplComponent component = RplComponent.Cast(m_BuildSpawned[i].FindComponent(RplComponent));
|
||||
component.EnableStreaming(false);
|
||||
@@ -290,7 +301,7 @@ class ERR_Roadblocks
|
||||
m_BuildGroup.AddWaypoint(m_BuildHold);
|
||||
m_Mode.RegisterGroup(m_BuildGroup); m_BuildGroup.ActivateAI();
|
||||
m_Mining.Register(m_BuildGroup, m_BuildHold, m_Right);
|
||||
PrintFormat("Reintroduced [roadblocks]: %1 at %2 guards=4 props=10 networkedProps=10 propStreaming=always roadPassage=open HQ=registered", id, m_Centre);
|
||||
PrintFormat("Reintroduced [roadblocks]: %1 at %2 guards=4 props=%3 networkedProps=%3 propStreaming=always roadPassage=open HQ=registered", id, m_Centre, m_BuildCovers.Count());
|
||||
m_BuildSpawned.Clear(); m_BuildGroup = null; m_BuildHold = null;
|
||||
m_BuildPositions.Clear(); m_BuildCovers.Clear();
|
||||
m_NextSearch = System.GetTickCount() + 2000;
|
||||
|
||||
@@ -44,17 +44,20 @@ in `Escapists.json`: **0–30**, default **4**; **0 disables** them. The chat eq
|
||||
is `/esc mission roadblocks set N` (also `show` and `help`). Changes apply on the
|
||||
next round/restart, leaving current units and cover in place.
|
||||
|
||||
These are simple roadside checkpoints: two sandbag positions and four soldiers
|
||||
from the selected enemy faction. They use normal equipment rules and physical
|
||||
Each checkpoint has two defensive bays built from eight sandbag walls, four
|
||||
approach barriers, two anti-vehicle obstacles, two stop signs, a small rest tent
|
||||
and an inspection table. Four soldiers come from the selected enemy faction. They use normal equipment rules and physical
|
||||
radio reporting to VirtualHQ; HQ can send their squad to respond to contacts.
|
||||
They do not reveal player locations to HQ merely by being created. Soldiers and
|
||||
cover are owned by the round; killed guards are not replaced.
|
||||
|
||||
Placement uses the loaded terrain's road network and Soldiers navmesh before
|
||||
player admission. Posts stay at least 600 m apart, 700 m from the prison and any
|
||||
Placement uses the loaded terrain's road network and Soldiers navmesh after
|
||||
terrain/site preparation. The initial search yields after 12 seconds; unfinished
|
||||
placement continues in small background steps while players are connected,
|
||||
including while the prison is locked and after escape. Posts stay at least 600 m apart, 700 m from the prison and any
|
||||
controlled player body, 150 m from mission sites and outside settlement centres.
|
||||
Elevated bridge segments, narrow roads, steep or obstructed shoulders are rejected.
|
||||
Cover stays outside the travelled road to preserve vehicle passage. The requested
|
||||
All prop footprints preserve a straight vehicle passage at least four metres wide. The requested
|
||||
count is a maximum: a bounded search skips unsuitable posts and logs placed versus
|
||||
requested totals instead of preventing mission startup. The admin Status page
|
||||
also shows these totals. This is an interim checkpoint system, independent of the
|
||||
|
||||
@@ -121,7 +121,7 @@ Before requesting `System.MakeScreenshot("$logs:err-ordinary-prison")`, the help
|
||||
|
||||
Roadblock props remain replicated independently of player distance so an admin
|
||||
viewing the island through Game Master can see the same barriers as nearby
|
||||
players. This is limited to the ten props per post (at most 300); guard and
|
||||
vehicle streaming is unchanged. A post is counted only after all ten props have
|
||||
players. This is limited to the 18 props per post (at most 540); guard and
|
||||
vehicle streaming is unchanged. A post is counted only after all 18 props have
|
||||
valid replication nodes. Local Workbench validation does not by itself prove
|
||||
remote-client visibility.
|
||||
|
||||
@@ -109,7 +109,8 @@ s=s.replace('m_BuildGroup.', 'm_BuildGroup->').replace('m_BuildHold.', 'm_BuildH
|
||||
s=s.replace('IEntity entity', 'Node* entity').replace('RplComponent component;', 'Replica* component=nullptr;').replace('RplComponent component =', 'Replica* component =')
|
||||
s=s.replace('RplComponent.Cast', 'Replica::Cast').replace('FindComponent(RplComponent)', 'FindComponent()').replace('RplId.Invalid()', '-1')
|
||||
s=s.replace('entity.FindComponent', 'entity->FindComponent').replace('m_BuildSpawned[i].FindComponent', 'm_BuildSpawned[i]->FindComponent').replace('component.', 'component->')
|
||||
for ns in ('ERR_Actors','ERR_Resources','ERR_ForceKind'):s=s.replace(ns+'.',ns+'::')
|
||||
for ns in ('ERR_Actors','ERR_Resources','ERR_ForceKind','ERR_RoadblockRules'):s=s.replace(ns+'.',ns+'::')
|
||||
rule=body(p,'static int PropCount(')
|
||||
run(r'''
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
@@ -128,6 +129,7 @@ struct Mode{Ledger ledger;int owned=0,groups=0;Ledger& Forces(){return ledger;}i
|
||||
struct Placement{bool clear=true;bool Clear(int,void*){return clear;}};
|
||||
struct Mining{int registered=0;void Register(Node*,Node*,int){registered++;}};
|
||||
struct Prop{string Prefab="cover";int Position=5,Angles=0;};
|
||||
namespace ERR_RoadblockRules{'''+rule+r'''}
|
||||
struct Build{
|
||||
Mode m_Mode;Placement m_Placement;Mining m_Mining;Node* m_BuildGroup=nullptr;Node* m_BuildHold=nullptr;
|
||||
Array<int> m_BuildPositions,m_Positions;Array<Prop>m_BuildCovers;Array<Node*>m_BuildSpawned,m_Groups;Array<string>m_Roster={"one","two"};
|
||||
@@ -136,31 +138,31 @@ struct Build{
|
||||
void Rollback(Array<Node*>&entities,Node*group){rolledBack+=entities.Count()+(group?1:0);}
|
||||
'''+s+r'''
|
||||
};
|
||||
int main(){Array<int> positions={1,2,3,4};Array<Prop>covers(10);
|
||||
int main(){Array<int> positions={1,2,3,4};Array<Prop>covers(ERR_RoadblockRules::PropCount());const int pieces=4+covers.Count()+1;
|
||||
{Build b;int start=ERR_Actors::calls;assert(b.SpawnPost(positions,covers));assert(ERR_Actors::calls==start+1);
|
||||
for(int piece=0;piece<15;piece++){int calls=ERR_Actors::calls;b.BuildPost();assert(ERR_Actors::calls==calls+1&&b.m_Positions.Count()==0&&b.m_Mode.owned==0);}
|
||||
auto shared=b.m_BuildSpawned;for(int i=4;i<14;i++)assert(shared[i]->replica.streaming);
|
||||
b.BuildPost();for(int i=4;i<14;i++)assert(!shared[i]->replica.streaming);assert(!b.m_BuildGroup&&b.m_Positions.Count()==1&&b.m_Groups.Count()==1&&b.m_Mode.owned==16&&b.m_Mode.groups==1&&b.m_Mining.registered==1&&b.m_NextSearch==2100);
|
||||
for(int piece=0;piece<pieces;piece++){int calls=ERR_Actors::calls;b.BuildPost();assert(ERR_Actors::calls==calls+1&&b.m_Positions.Count()==0&&b.m_Mode.owned==0);}
|
||||
auto shared=b.m_BuildSpawned;for(int i=4;i<4+covers.Count();i++)assert(shared[i]->replica.streaming);
|
||||
b.BuildPost();for(int i=4;i<4+covers.Count();i++)assert(!shared[i]->replica.streaming);assert(!b.m_BuildGroup&&b.m_Positions.Count()==1&&b.m_Groups.Count()==1&&b.m_Mode.owned==pieces+1&&b.m_Mode.groups==1&&b.m_Mining.registered==1&&b.m_NextSearch==2100);
|
||||
}
|
||||
// A server-only or unregistered prop must never count as a complete roadblock.
|
||||
for(int bad=4;bad<14;bad++)for(bool missing:{false,true}){
|
||||
Build b;b.SpawnPost(positions,covers);for(int i=0;i<15;i++)b.BuildPost();
|
||||
auto pieces=b.m_BuildSpawned;pieces[bad]->missing=missing;pieces[bad]->replica.id=-1;
|
||||
b.BuildPost();assert(b.m_Positions.Count()==0&&!b.m_BuildGroup&&b.m_Mode.owned==0&&b.rolledBack==16);
|
||||
for(int i=4;i<14;i++)assert(pieces[i]->replica.streaming); // reject before changing any streaming rule
|
||||
for(int bad=4;bad<4+covers.Count();bad++)for(bool missing:{false,true}){
|
||||
Build b;b.SpawnPost(positions,covers);for(int i=0;i<pieces;i++)b.BuildPost();
|
||||
auto spawned=b.m_BuildSpawned;spawned[bad]->missing=missing;spawned[bad]->replica.id=-1;
|
||||
b.BuildPost();assert(b.m_Positions.Count()==0&&!b.m_BuildGroup&&b.m_Mode.owned==0&&b.rolledBack==pieces+1);
|
||||
for(int i=4;i<4+covers.Count();i++)assert(spawned[i]->replica.streaming); // reject before changing any streaming rule
|
||||
}
|
||||
// Player approaches at any stage: no registered post and every owned piece removed.
|
||||
for(int stage=0;stage<=15;stage++){Build b;assert(b.SpawnPost(positions,covers));for(int i=0;i<stage;i++)b.BuildPost();b.playersClear=false;b.BuildPost();assert(!b.m_BuildGroup&&b.m_Positions.Count()==0&&b.m_Mode.owned==0&&b.rolledBack==stage+1);}
|
||||
for(int stage=0;stage<=pieces;stage++){Build b;assert(b.SpawnPost(positions,covers));for(int i=0;i<stage;i++)b.BuildPost();b.playersClear=false;b.BuildPost();assert(!b.m_BuildGroup&&b.m_Positions.Count()==0&&b.m_Mode.owned==0&&b.rolledBack==stage+1);}
|
||||
// Any native entity creation failure rolls back rather than consuming a post.
|
||||
for(int stage=0;stage<16;stage++){Build b;ERR_Actors::failAt=ERR_Actors::calls+stage+1;bool begun=b.SpawnPost(positions,covers);
|
||||
for(int stage=0;stage<pieces+1;stage++){Build b;ERR_Actors::failAt=ERR_Actors::calls+stage+1;bool begun=b.SpawnPost(positions,covers);
|
||||
if(stage==0)assert(!begun);else{for(int i=0;i<stage;i++)b.BuildPost();assert(b.rolledBack==stage);}
|
||||
assert(!b.m_BuildGroup&&b.m_Positions.Count()==0&&b.m_Mode.owned==0);ERR_Actors::failAt=0;
|
||||
}
|
||||
{Build b;b.SpawnPost(positions,covers);b.m_Placement.clear=false;b.BuildPost();assert(!b.m_BuildGroup&&b.rolledBack==1);}
|
||||
{Build b;b.SpawnPost(positions,covers);for(int i=0;i<4;i++)b.BuildPost();b.coversClear=false;b.BuildPost();assert(!b.m_BuildGroup&&b.rolledBack==5);}
|
||||
{Build b;b.SpawnPost(positions,covers);for(int i=0;i<15;i++)b.BuildPost();b.m_Mode.ledger.accept=false;b.BuildPost();assert(!b.m_BuildGroup&&b.rolledBack==16&&b.m_Positions.Count()==0);}
|
||||
{Build b;b.SpawnPost(positions,covers);for(int i=0;i<pieces;i++)b.BuildPost();b.m_Mode.ledger.accept=false;b.BuildPost();assert(!b.m_BuildGroup&&b.rolledBack==pieces+1&&b.m_Positions.Count()==0);}
|
||||
{Build b;b.SpawnPost(positions,covers);b.BuildPost();b.CancelPost();assert(!b.m_BuildGroup&&b.rolledBack==2&&b.m_Mode.owned==0);}
|
||||
for(auto*n:ERR_Actors::all)delete n;
|
||||
}
|
||||
''')
|
||||
print('PASS: actual staged post creation spawns at most one entity per callback, commits only all four guards/ten props/waypoint, and rolls back player approach, obstructions, spawn/replication/ledger failure and cancellation; all ten committed props bypass distance streaming')
|
||||
print('PASS: actual staged post creation spawns at most one entity per callback, commits only all four guards/all planned props/waypoint, and rolls back player approach, obstructions, spawn/replication/ledger failure and cancellation; all committed props bypass distance streaming')
|
||||
|
||||
@@ -20,14 +20,14 @@ struct Prop{string Prefab;Vec Position,Forward,Right;double HalfWidth,HalfDepth,
|
||||
Prop(string p,Vec v,Vec f,double w,double d,double h):Prefab(p),Position(v),Forward(f),Right({f.z,0,-f.x}),HalfWidth(w),HalfDepth(d),Height(h){}
|
||||
};
|
||||
struct Checkpoint{Vec m_Centre={1000,50,2000},m_Forward,m_Right;double m_Shoulder;
|
||||
string COVER="cover",BARRIER="barrier",OBSTACLE="obstacle",SIGN="sign";
|
||||
string COVER="cover",BARRIER="barrier",OBSTACLE="obstacle",SIGN="sign",TABLE="table",SHELTER="shelter";
|
||||
'''+s+r'''
|
||||
};
|
||||
int main(){
|
||||
for(double width:{4.,6.,10.,16.})for(int degrees=0;degrees<360;degrees+=15){
|
||||
Checkpoint l;double angle=degrees*3.141592653589793/180;
|
||||
l.m_Forward={std::sin(angle),0,std::cos(angle)};l.m_Right={std::cos(angle),0,-std::sin(angle)};
|
||||
l.m_Shoulder=std::max(width*.5+1.5,4.);Array<Prop*>props;l.Layout(props);assert(props.size()==10);
|
||||
l.m_Shoulder=std::max(width*.5+1.5,4.);Array<Prop*>props;l.Layout(props);assert(props.size()==18);
|
||||
std::map<string,int>counts;std::vector<double>barriers;
|
||||
for(auto*p:props){counts[p->Prefab]++;
|
||||
Vec delta=p->Position-l.m_Centre;double across=dot(delta,l.m_Right),along=dot(delta,l.m_Forward);
|
||||
@@ -40,11 +40,11 @@ int main(){
|
||||
for(int side:{-1,1})for(int row:{-1,1}){
|
||||
Vec guard=l.m_Centre+l.m_Right*(side*(l.m_Shoulder+2.5))+l.m_Forward*(side*(row-1.5));
|
||||
Vec rel=guard-p->Position;
|
||||
assert(std::abs(dot(rel,p->Right))>p->HalfWidth+.45 || std::abs(dot(rel,p->Forward))>p->HalfDepth+.45);
|
||||
assert(std::abs(dot(rel,p->Right))>p->HalfWidth+.6 || std::abs(dot(rel,p->Forward))>p->HalfDepth+.6);
|
||||
}
|
||||
}
|
||||
assert(counts["cover"]==4&&counts["barrier"]==2&&counts["obstacle"]==2&&counts["sign"]==2);
|
||||
assert(barriers.size()==2&&std::abs(barriers[0]-barriers[1])>15.9);
|
||||
assert(counts["cover"]==8&&counts["barrier"]==4&&counts["obstacle"]==2&&counts["sign"]==2&&counts["table"]==1&&counts["shelter"]==1);
|
||||
assert(barriers.size()==4&&std::abs(barriers[0]-barriers[2])>15.9);
|
||||
// No physical prop footprint intersects another (separating axes).
|
||||
for(size_t i=0;i<props.size();i++)for(size_t j=i+1;j<props.size();j++){
|
||||
auto*a=props[i];auto*b=props[j];bool separate=false;
|
||||
|
||||
Reference in New Issue
Block a user