Files
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
Complete disaster-recovery snapshot: engine/game source, game data assets,
VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive.
Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false,
no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
2026-06-24 21:28:16 -05:00

799 lines
17 KiB
C++

#include "MW4Headers.hpp"
#include "adept\application.hpp"
#include "rail_move.hpp"
//#include "quadedge.h"
#include <MW4\MWMission.hpp>
#include <Adept\Map.hpp>
#include <Adept\CollisionGrid.hpp>
#include <ElementRenderer\StateChange.hpp>
#include <adept\collisionvolume.hpp>
#include "path.hpp"
#include "bridge.hpp"
#pragma warning (push)
#include <vector>
#include <map>
#pragma warning (pop)
using namespace MW4AI;
using namespace MechWarrior4;
using namespace ElementRenderer;
void CRailLink::DestroyBridge (void)
{
m_UsageFlags[0] = JUMPFLAG;
m_UsageFlags[1] = JUMPFLAG;
}
bool CRailGraph::LinkWeight (CRailLink *link)
{
Stuff::Scalar dist;
float weight[NUM_MOVE_TYPES][2];
int k;
CRailNode *src,*dest;
src = link->Location (0);
dest = link->Location (1);
dist = src->LinkLength (link);
if (dist<0)
{
dist = dist * -1.0f;
weight[0][0] = -1.0f; // turn off jumping for these types of nodes
weight[0][1] = -1.0f;
for (k=1;k<NUM_MOVE_TYPES;k++)
{
weight[k][0] = dist;
weight[k][1] = dist;
}
}
else if (dist > GRIDMOVE_FAIL_THRESHOLD)
{
DeleteLink (link->ID ());
return false;
}
else
{
for (k=0;k<NUM_MOVE_TYPES;k++)
{
weight[k][0] = m_WorkGrid->CalcWeight (src->SubNode (link).location,dest->SubNode (link).location,k);
if (weight[k][0] != -1.0f)
{
weight[k][0] += src->Radius ();
weight[k][0] += dest->Radius ();
}
weight[k][1] = m_WorkGrid->CalcWeight (dest->SubNode (link).location,src->SubNode (link).location,k);
if (weight[k][1] != -1.0f)
{
weight[k][1] += src->Radius ();
weight[k][1] += dest->Radius ();
}
}
}
for (k=0;k<NUM_MOVE_TYPES;k++)
if ((weight[k][0] != -1.0f) || (weight[k][1] != -1.0f))
break;
if (k == NUM_MOVE_TYPES) // path is impassable for all move types
{
DeleteLink (link->ID ());
return false;
}
for (k=0;k<NUM_MOVE_TYPES;k++)
{
link->Weight (weight[k][0],k,link->Location (0),true);
link->Weight (weight[k][1],k,link->Location (1),true);
}
return true;
}
void CRailGraph::CalcLinks (int *start,int numnodes,int min,int max,int range,WorkCallBack callback)
{
int i,j,k,size,totallinks;
Stuff::Point3D loc;
float curpercent,dpercent;
CreateGrid (callback,80.0f);
Check_Pointer (m_WorkGrid);
CalcEditorDistances ();
UpdateNodes (callback,80);
CreateRectData (callback);
my_heap<NodeDist,true,raildistcmp> distheap;
totallinks = 0;
int count=0;
size = numnodes;
curpercent = 90.0f;
dpercent = 10.0f / size;
totallinks += m_Links.size ();
for (i=0;i<(size-1);i++)
{
curpercent += dpercent;
if (callback)
callback (curpercent);
if (start[i] == -1)
continue;
if (!m_Nodes[start[i]])
continue;
distheap.clear ();
for (j=i+1;j<size;j++)
{
if (start[j] == -1)
continue;
if (m_Nodes[start[j]])
distheap.Insert (NodeDist (m_Nodes[start[i]],m_Nodes[start[j]]));
}
count = m_Nodes[start[i]]->NumLinks ();
while ((count < max) && (totallinks <MAXLINKS))
{
if (!distheap.size ())
break;
NodeDist tempdist = distheap.ExtractMin ();
float dist;
Verify (tempdist.src == m_Nodes[start[i]]);
dist = tempdist.src->EditorDistance (tempdist.dest);
if (dist > range)
{
distheap.Insert (tempdist);
break;
}
float weight[NUM_MOVE_TYPES][2];
for (k=0;k<NUM_MOVE_TYPES;k++)
{
weight[k][0] = 0.0f;
weight[k][1] = 0.0f;
}
int linkid = AddLink (tempdist.src,tempdist.dest,weight);
CRailLink *link;
link = Link (linkid);
LinkWeight (link);
count++;
totallinks++;
}
count = m_Nodes[start[i]]->NumLinks ();
while ((count < min) && (totallinks <MAXLINKS))
{
if (!distheap.size ())
break;
NodeDist tempdist = distheap.ExtractMin ();
Verify (tempdist.src == m_Nodes[start[i]]);
float weight[NUM_MOVE_TYPES][2];
for (k=0;k<NUM_MOVE_TYPES;k++)
{
weight[k][0] = 0.0f;
weight[k][1] = 0.0f;
}
int linkid = AddLink (tempdist.src,tempdist.dest,weight);
CRailLink *link;
link = Link (linkid);
LinkWeight (link);
count++;
totallinks++;
}
}
size = m_BlockedLinks.size ();
int numlink;
numlink = m_Links.size ();
for (i=0;i<size;i++)
{
if (m_BlockedLinks[i] != Stuff::Point3D (-1,-1,-1))
{
for (j=0;j<numlink;j++)
{
if (!m_Links[j])
continue;
if ((m_Links[j]->Location(0)->ID () == m_BlockedLinks[i].x) && (m_Links[j]->Location(1)->ID () == m_BlockedLinks[i].y))
{
DeleteLink (j);
continue;
}
if ((m_Links[j]->Location(0)->ID () == m_BlockedLinks[i].y) && (m_Links[j]->Location(1)->ID () == m_BlockedLinks[i].x))
{
DeleteLink (j);
continue;
}
}
}
}
// AddBridgeLinks ();
AnalyzePaths ();
KillGrid ();
}
void CRailGraph::AddBridgeLinks (void)
{
int i,j,k,max;
Verify (m_WorkGrid);
max = m_Nodes.size ();
for (i=0;i<max-1;i++)
{
if (!m_Nodes[i])
continue;
for (j=i+1;j<max;j++)
{
if (!m_Nodes[j])
continue;
/*
int numlinks;
numlinks = m_Nodes[j]->NumLinks ();
for (k=0;k<numlinks;k++)
{
CRailLink *link;
link = m_Nodes[j]->Link (k);
if (link)
if (link->OtherLocation (m_Nodes[j]) == m_Nodes[i])
continue;
}
*/
if (!m_Nodes[i]->LinkTo (m_Nodes[j]))
{
float weight[NUM_MOVE_TYPES][2];
for (k=0;k<NUM_MOVE_TYPES;k++)
{
weight[k][0] = 0.0f;
weight[k][1] = 0.0f;
}
CRailLink *link;
int linkid = AddLink (i,j,weight);
link = Link (linkid);
int bridgeid;
bridgeid = m_WorkGrid->CalcBridgeLink (m_Nodes[i]->SubNode (link).location,m_Nodes[j]->SubNode (link).location);
if (bridgeid == -1)
{
DeleteLink (linkid);
continue;
}
if (!TestBridge (bridgeid,i,j)) // test for the only two nodes that can go over bridge
{
DeleteLink (linkid);
continue;
}
Scalar dist;
dist = m_Nodes[i]->LinkLength (link);
Verify (dist > 0); // should not be any overlap in the nodes because a bridge is a building
for (k=0;k<NUM_MOVE_TYPES;k++)
{
weight[k][0] = dist;
weight[k][0] += m_Nodes[i]->Radius ();
weight[k][0] += m_Nodes[j]->Radius ();
weight[k][1] = weight[k][0];
}
for (k=0;k<NUM_MOVE_TYPES;k++)
{
link->Weight (weight[k][0],k,link->Location (0),true);
link->Weight (weight[k][1],k,link->Location (1),true);
}
link->Bridge (bridgeid);
}
}
}
}
bool CRailGraph::TestBridge (int id,int node1,int node2)
{
Point3D bridgeloc;
Point3D node1loc,node2loc;
int max,i;
Point3D destloc2,center,loc,extra;
Scalar rad,slope,w,t1,t2,a,b,c;
Scalar intercept,inner,root,x1,x2;
node1loc = m_Nodes[node1]->Location ();
node2loc = m_Nodes[node2]->Location ();
Entity* ent = NameTable::GetInstance()->FindData(id);
Verify (ent);
bridgeloc = (Point3D) ent->GetLocalToWorld ();
max = m_Nodes.size ();
for (i=0;i<max;i++)
{
if (i == node1)
continue;
if (i == node2)
continue;
if (!m_Nodes[i])
continue;
destloc2 = bridgeloc;
loc = m_Nodes[node1]->Location ();
loc.y = 0;
center = m_Nodes[i]->Location ();
center.y = 0;
destloc2.y = 0;
rad = m_Nodes[i]->RadiusSquared();
if (destloc2.x == loc.x)
slope = 100000.0f;
else
slope = (destloc2.z-loc.z)/(destloc2.x-loc.x);
intercept = destloc2.z-(slope*destloc2.x);
w = intercept-center.z;
a = (slope*slope)+1.0f;
b = (2.0f*slope*w)-(2.0f*center.x);
c = (w*w)-(rad)+(center.x*center.x);
inner = (b*b) - (4.0f*a*c);
if (inner < 0)
{
// ok because they never cross, should never happen
}
else
{
root = (float) sqrt (inner);
x1 = (-b+root)/(2*a);
x2 = (-b-root)/(2*a);
if (destloc2.x == loc.x)
{
t1 = (x1-loc.x)/(0.00001f);
t2 = (x2-loc.x)/(0.00001f);
}
else
{
t1 = (x1-loc.x)/(destloc2.x-loc.x);
t2 = (x2-loc.x)/(destloc2.x-loc.x);
}
if ((t1>=0) && (t1<=1.0))
{
// intersect
return false;
}
else if ((t2>=0) && (t2<=1.0))
{
// intersect
return false;
}
else
{
// don't intersect on line segment
}
}
loc = m_Nodes[node2]->Location ();
destloc2 = bridgeloc;
center = m_Nodes[i]->Location ();
center.y = 0;
loc.y = destloc2.y = 0;
rad = m_Nodes[i]->RadiusSquared();
if (destloc2.x == loc.x)
slope = 100000.0f;
else
slope = (destloc2.z-loc.z)/(destloc2.x-loc.x);
intercept = destloc2.z-(slope*destloc2.x);
w = intercept-center.z;
a = (slope*slope)+1.0f;
b = (2.0f*slope*w)-(2.0f*center.x);
c = (w*w)-(rad)+(center.x*center.x);
inner = (b*b) - (4.0f*a*c);
if (inner < 0)
{
// ok because they never cross, should never happen
}
else
{
root = (float) sqrt (inner);
x1 = (-b+root)/(2*a);
x2 = (-b-root)/(2*a);
if (destloc2.x == loc.x)
{
t1 = (x1-loc.x)/(0.00001f);
t2 = (x2-loc.x)/(0.00001f);
}
else
{
t1 = (x1-loc.x)/(destloc2.x-loc.x);
t2 = (x2-loc.x)/(destloc2.x-loc.x);
}
if ((t1>=0) && (t1<=1.0))
{
// intersect
return false;
}
else if ((t2>=0) && (t2<=1.0))
{
// intersect
return false;
}
else
{
// don't intersect on line segment
}
}
}
return true;
}
int CRailWorkGrid::CalcBridgeLink (Point3D src, Point3D dest)
{
Stuff::Point3D extra;
Stuff::Scalar sx,sy,dx,dy;
int ix,iy,ex,ey;
int toret;
Stuff::Scalar curweight;
Stuff::Scalar lastheight,curheight,dh;
int count;
dest.x -= MinX;
dest.z -= MinZ;
src.x -= MinX;
src.z -= MinZ;
extra.Subtract (dest,src);
toret = -1;
dest.x /= 10.0f;
dest.z /= 10.0f;
src.x /= 10.0f;
src.z /= 10.0f;
dx = dest.x - src.x;
dy = dest.z - src.z;
sx = dx < 0 ? dx*-1.0f : dx;
sy = dy < 0 ? dy*-1.0f : dy;
if (sx > sy)
{
dy = dy / sx;
count = (int) (dx<0.0 ? dx*-1.0f : dx);
dx = dx<0 ? -1.0f : 1.0f;
}
else
{
dx = dx / sy;
count = (int) (dy<0.0 ? dy*-1.0f : dy);
dy = dy<0 ? -1.0f : 1.0f;
}
sx = src.x;
sy = src.z;
ex = (int) dest.x;
ey = (int) dest.z;
ix = (int) sx;
iy = (int) sy;
lastheight = m_Grid[ix][iy].height;
curweight = 0;
while (count)
{
count--;
sx += dx;
sy += dy;
ix = (int) sx;
iy = (int) sy;
if (m_Grid[ix][iy].objectid != m_Grid[ix][iy].bridge)
return -1;
dh = 0;
if (m_Grid[ix][iy].bridge != -1)
{
if (toret == -1)
toret = m_Grid[ix][iy].bridge;
else if (m_Grid[ix][iy].bridge != toret)
return -1;
}
else
{
curheight = m_Grid[ix][iy].height;
dh = lastheight - curheight;
dh = dh < 0 ? dh*-1.0f : dh;
dh /= 10.0f;
if (dh >= 0.5f)
return -1;
}
lastheight = m_Grid[ix][iy].height;
curweight += (1.0f + dh);
// if (CheckSquare (ix,iy,WHEELINDEX) == -1)
// return -1;
// curweight += tempweight;
}
return toret;
}
void CRailGraph::CalcLinks (int min,int max,int range,WorkCallBack callback)
{
int i,size;
int *temp;
bool flag;
ClearLinks ();
flag = false;
gos_PushCurrentHeap (g_RailHeap);
temp = new int[m_Nodes.size ()];
gos_PopCurrentHeap ();
size = m_Nodes.size ();
for (i=0;i<size;i++)
{
if (m_Nodes[i])
{
temp[i] = m_Nodes[i]->ID ();
flag = true;
}
else
temp[i] = -1;
}
if (!flag)
return;
CalcLinks (temp,size,min,max,range,callback);
}
void CRailGraph::BlockLink (int node1,int node2)
{
int size,i;
size = m_BlockedLinks.size ();
for (i=0;i<size;i++)
{
if (m_BlockedLinks[i] == Stuff::Point3D (-1,-1,-1))
break;
}
m_BlockedLinks[i] = Stuff::Point3D ((float) node1,(float) node2,0.0f);
}
void CRailGraph::RemoveBlock (int node)
{
int i,size;
size = m_BlockedLinks.size ();
for (i=0;i<size;i++)
{
if (m_BlockedLinks[i].x == node)
m_BlockedLinks[i] = Stuff::Point3D (-1,-1,-1);
if (m_BlockedLinks[i].y == node)
m_BlockedLinks[i] = Stuff::Point3D (-1,-1,-1);
}
}
void CRailLink::ConstructLinkObjectStream(int id,Stuff::MemoryStream *stream,Stuff::NotationFile *model_file)
{
char entryname[25],notename[25];
int id1,id2;
float weight[NUM_MOVE_TYPES][2];
float calcweight[NUM_MOVE_TYPES][2];
float allweight;
bool jump;
bool Tracked;
bool Flyer;
bool Wheeled;
bool Legged;
bool Hover;
int bridge;
bool water;
int moveflags[2];
Verify (id>=0);
Verify (id<MAXLINKS);
sprintf (entryname,"Link%d",id);
id1 = -1;
id2 = -1;
Page *link_page = model_file->FindPage(entryname);
if (link_page)
{
Check_Object(link_page);
link_page->GetEntry("Node1",&id1,false);
link_page->GetEntry("Node2",&id2,false);
}
Verify (id1>=-1);
Verify (id2>=-1);
if ((id1==-1) || (id2 == -1))
{
*stream << id1;
*stream << id2;
return;
}
Check_Object(link_page);
int i;
if (link_page->GetEntry ("Weight",&allweight,false))
for (i=0;i<NUM_MOVE_TYPES;i++)
{
weight[i][0] = allweight;
weight[i][1] = allweight;
calcweight[i][0] = allweight;
calcweight[i][1] = allweight;
}
else
for (i=0;i<NUM_MOVE_TYPES;i++)
{
weight[i][0] = 1.0f;
weight[i][1] = 1.0f;
calcweight[i][0] = 1.0f;
calcweight[i][1] = 1.0f;
}
for (i=0;i<NUM_MOVE_TYPES;i++)
{
sprintf (notename,"Weight%d",i);
if (link_page->GetEntry(notename,&(weight[i][0]),false))
{
weight[i][1] = weight[i][0];
}
else
{
sprintf (notename,"Weight%dd0",i);
link_page->GetEntry(notename,&(weight[i][0]),false);
sprintf (notename,"Weight%dd1",i);
link_page->GetEntry(notename,&(weight[i][1]),false);
}
Verify (weight[i][0] >= -1.0f);
Verify (weight[i][1] >= -1.0f);
}
for (i=0;i<NUM_MOVE_TYPES;i++)
{
sprintf (notename,"CalcWeight%d",i);
if (link_page->GetEntry(notename,&(calcweight[i][0]),false))
{
calcweight[i][1] = calcweight[i][0];
}
else
{
sprintf (notename,"CalcWeight%dd0",i);
link_page->GetEntry(notename,&(calcweight[i][0]),false);
sprintf (notename,"CalcWeight%dd1",i);
link_page->GetEntry(notename,&(calcweight[i][1]),false);
}
Verify (calcweight[i][0] >= -1.0f);
Verify (calcweight[i][1] >= -1.0f);
}
moveflags[0] = 0;
if (link_page->GetEntry("UsageFlags",&moveflags[0],false))
{
moveflags[1] = moveflags[0];
}
else if (link_page->GetEntry ("UsageFlags0",&moveflags[0],false))
{
link_page->GetEntry ("UsageFlags1",&moveflags[1],false);
}
else
{
Tracked = false;
Flyer = false;
Wheeled = false;
Legged = true;
Hover = false;
jump = false;
water = false;
link_page->GetEntry("Jump",&jump,false);
link_page->GetEntry("Tracked",&Tracked,false);
link_page->GetEntry("Flyer",&Flyer,false);
link_page->GetEntry("Wheeled",&Wheeled,false);
link_page->GetEntry("Legged",&Legged,false);
link_page->GetEntry("Hover",&Hover,false);
link_page->GetEntry("Water",&water,false);
moveflags[0] |= jump ? JUMPFLAG : 0;
moveflags[0] |= Tracked ? TRACKFLAG : 0;
moveflags[0] |= Flyer ? FLYFLAG : 0;
moveflags[0] |= Wheeled ? WHEELFLAG : 0;
moveflags[0] |= Legged ? LEGFLAG : 0;
moveflags[0] |= Hover ? HOVERFLAG : 0;
moveflags[0] |= water ? WATERFLAG : 0;
moveflags[1] = moveflags[0];
}
bridge = -1;
link_page->GetEntry("Bridge",&bridge,false);
*stream << id1;
*stream << id2;
for (i=0;i<NUM_MOVE_TYPES;i++)
{
*stream << weight[i][0];
*stream << weight[i][1];
}
for (i=0;i<NUM_MOVE_TYPES;i++)
{
*stream << calcweight[i][0];
*stream << calcweight[i][1];
}
*stream << moveflags[0];
*stream << moveflags[1];
*stream << bridge;
}
void CRailLink::DumpNotationFile (Stuff::NotationFile *file)
{
char entryname[25],notename[25];
int i;
sprintf (entryname,"Link%d",m_ID);
Page *link_page = file->SetPage(entryname);
Check_Object(link_page);
Verify (m_Loc[0]);
Verify (m_Loc[1]);
link_page->SetEntry ("Node1",(int) m_Loc[0]->ID ());
link_page->SetEntry ("Node2",(int) m_Loc[1]->ID ());
#if 0
if (m_Loc[0] != 0)
{
link_page->SetEntry ("Node1",(int) m_Loc[0]->ID ());
}
if (m_Loc[1] != 0)
{
link_page->SetEntry ("Node2",(int) m_Loc[1]->ID ());
}
#endif
for (i=0;i<NUM_MOVE_TYPES;i++)
{
sprintf (notename,"Weight%dd0",i,0);
link_page->SetEntry (notename,m_Weight[i][0]);
sprintf (notename,"Weight%dd1",i,1);
link_page->SetEntry (notename,m_Weight[i][1]);
}
for (i=0;i<NUM_MOVE_TYPES;i++)
{
sprintf (notename,"CalcWeight%dd0",i);
link_page->SetEntry (notename,m_CalcedWeight[i][0]);
sprintf (notename,"CalcWeight%dd1",i);
link_page->SetEntry (notename,m_CalcedWeight[i][1]);
}
link_page->SetEntry ("UsageFlags0",m_UsageFlags[0]);
link_page->SetEntry ("UsageFlags1",m_UsageFlags[1]);
link_page->SetEntry ("Bridge",m_Bridge);
}
CRailLink::CRailLink (Stuff::MemoryStream *stream,CRailGraph *parent)
{
int t1,t2;
int i;
Check_Pointer(this);
Check_Object(stream);
Check_Pointer (parent);
*stream >> t1;
*stream >> t2;
m_LocID[0] = t1;
m_LocID[1] = t2;
if ((t1 == -1) || (t2 == -1))
return;
m_Loc[0] = parent->Node (t1);
m_Loc[1] = parent->Node (t2);
for (i=0;i<NUM_MOVE_TYPES;i++)
{
*stream >> m_Weight[i][0];
*stream >> m_Weight[i][1];
}
for (i=0;i<NUM_MOVE_TYPES;i++)
{
*stream >> m_CalcedWeight[i][0];
*stream >> m_CalcedWeight[i][1];
}
*stream >> m_UsageFlags[0];
*stream >> m_UsageFlags[1];
*stream >> m_Bridge;
}