Expand roadblocks with defensive bays and checkpoint equipment
This commit is contained in:
@@ -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