723 lines
19 KiB
C++
723 lines
19 KiB
C++
//Anything above this #include will be ignored by the compiler
|
|
#include "../qcommon/exe_headers.h"
|
|
|
|
/************************************************************************************************
|
|
*
|
|
* Copyright (C) 2001-2002 Raven Software
|
|
*
|
|
* RM_Path.cpp
|
|
*
|
|
************************************************************************************************/
|
|
|
|
#include "RM_Headers.h"
|
|
|
|
#define max(a,b) (((a) > (b)) ? (a) : (b))
|
|
|
|
/************************************************************************************************
|
|
* CRMNode::CRMNode
|
|
* constructor
|
|
*
|
|
* inputs:
|
|
* none
|
|
*
|
|
* return:
|
|
* none
|
|
*
|
|
************************************************************************************************/
|
|
CRMNode::CRMNode ( )
|
|
{
|
|
int i;
|
|
|
|
mFlattenHeight = -1;
|
|
|
|
mPos[0] = 0;
|
|
mPos[1] = 0;
|
|
mPos[2] = 0;
|
|
|
|
// no paths
|
|
for (i = 0; i < DIR_MAX; i++)
|
|
mPathID[i] = -1;
|
|
|
|
mAreaPointPlaced = false;
|
|
}
|
|
|
|
|
|
/************************************************************************************************
|
|
* CRMPathManager::CRMPathManager
|
|
* constructor
|
|
*
|
|
* inputs:
|
|
* none
|
|
*
|
|
* return:
|
|
* none
|
|
*
|
|
************************************************************************************************/
|
|
CRMPathManager::CRMPathManager ( CRandomTerrain* terrain )
|
|
: mXNodes(0), mYNodes(0), mPathCount(0), mRiverCount(0), mMaxDepth(0), mDepth(0),
|
|
mPathPoints(10), mPathMinWidth(0.02f), mPathMaxWidth(0.04f), mPathDepth(0.3f), mPathDeviation(0.03f), mPathBreadth(5),
|
|
mRiverDepth(5), mRiverPoints(10), mRiverMinWidth(0.01f), mRiverMaxWidth(0.02f), mRiverBedDepth(1), mRiverDeviation(0.01f), mRiverBreadth(7),
|
|
mTerrain(terrain), mCrossed(false)
|
|
{
|
|
}
|
|
|
|
CRMPathManager::~CRMPathManager ( )
|
|
{
|
|
int i,j;
|
|
|
|
for ( i = mLocations.size() - 1; i >=0; i-- )
|
|
{
|
|
if (mLocations[i])
|
|
delete mLocations[i];
|
|
}
|
|
mLocations.clear();
|
|
|
|
for ( j = mNodes.size() - 1; j >=0; j-- )
|
|
{
|
|
if (mNodes[j])
|
|
delete mNodes[j];
|
|
}
|
|
mNodes.clear();
|
|
}
|
|
|
|
void CRMPathManager::CreateLocation ( const char* name, const int min_depth, int max_depth, const int min_paths, int max_paths )
|
|
{
|
|
int i;
|
|
|
|
// sanity checks -- dmv
|
|
if( max_paths < min_paths )
|
|
{
|
|
Com_Printf("[CreateLocation()] ERROR : max_paths < min_paths :: set max_paths = min_paths\n" );
|
|
max_paths = min_paths;
|
|
}
|
|
if( max_depth < min_depth )
|
|
{
|
|
Com_Printf("[CreateLocation()] ERROR : max_depth < min_depth :: set max_depth = min_depth\n" );
|
|
max_depth = min_depth;
|
|
}
|
|
|
|
for (i = mLocations.size()-1; i>=0; --i)
|
|
if ( !stricmp ( name, mLocations[i]->GetName ( ) ) )
|
|
{
|
|
mLocations[i]->SetMinDepth(min_depth);
|
|
mLocations[i]->SetMaxDepth(max_depth);
|
|
mLocations[i]->SetMinPaths(min_paths);
|
|
mLocations[i]->SetMaxPaths(max_paths);
|
|
return;
|
|
}
|
|
|
|
CRMLoc* pLoc= new CRMLoc(name, min_depth, max_depth, min_paths, max_paths);
|
|
mLocations.push_back(pLoc);
|
|
mMaxDepth = max(mMaxDepth, max_depth);
|
|
}
|
|
|
|
void CRMPathManager::ClearCells(int x_nodes, int y_nodes)
|
|
{
|
|
int x,y;
|
|
|
|
// clear cell array - used for generating paths
|
|
CRMCell empty;
|
|
for (x=0; x < x_nodes * y_nodes; x++)
|
|
{
|
|
if (x >= mCells.size())
|
|
mCells.push_back(empty);
|
|
else
|
|
mCells[x] = empty;
|
|
}
|
|
|
|
// set borders of world
|
|
for (y = 0; y < y_nodes; y++)
|
|
{
|
|
mCells[y * x_nodes].SetBorder(DIR_W );
|
|
mCells[y * x_nodes].SetBorder(DIR_SW );
|
|
mCells[y * x_nodes].SetBorder(DIR_NW );
|
|
mCells[y * x_nodes + x_nodes-1].SetBorder( DIR_E );
|
|
mCells[y * x_nodes + x_nodes-1].SetBorder( DIR_NE );
|
|
mCells[y * x_nodes + x_nodes-1].SetBorder( DIR_SE );
|
|
}
|
|
|
|
for (x = 0; x < x_nodes; x++)
|
|
{
|
|
mCells[x].SetBorder( DIR_N );
|
|
mCells[x].SetBorder( DIR_NE );
|
|
mCells[x].SetBorder( DIR_NW );
|
|
mCells[(y_nodes-1) * x_nodes + x].SetBorder( DIR_S );
|
|
mCells[(y_nodes-1) * x_nodes + x].SetBorder( DIR_SE );
|
|
mCells[(y_nodes-1) * x_nodes + x].SetBorder( DIR_SW );
|
|
}
|
|
}
|
|
|
|
/************************************************************************************************
|
|
* CRMPathManager::CreateArray
|
|
* Create array of nodes that are spaced over the landscape.
|
|
* Create array of cells, which is used to determine how nodes are connected.
|
|
*
|
|
* inputs:
|
|
* x_nodes, y_nodes - how many nodes in each dimension to layout
|
|
*
|
|
* return:
|
|
* true if the node array was created, false if we have a problem
|
|
*
|
|
************************************************************************************************/
|
|
bool CRMPathManager::CreateArray(const int x_nodes, const int y_nodes)
|
|
{
|
|
mXNodes = x_nodes;
|
|
mYNodes = y_nodes;
|
|
|
|
// fill node array with positions that are spaced over the landscape
|
|
int x,y;
|
|
|
|
// dump existing nodes
|
|
for ( x = mNodes.size() - 1; x >=0; x-- )
|
|
{
|
|
if (mNodes[x])
|
|
delete mNodes[x];
|
|
}
|
|
mNodes.clear();
|
|
mNodes.resize(mXNodes * mYNodes, NULL);
|
|
|
|
// add a small amount of random jitter to spots chosen
|
|
float x_rnd = 0.2f / (mXNodes+1);
|
|
float y_rnd = 0.2f / (mYNodes+1);
|
|
|
|
for (x = 0; x < mXNodes; x++)
|
|
{
|
|
float cell_x = (x + 1.0f) / (mXNodes+1);
|
|
// float cell_x = (x + 2.0f) / (mXNodes+3);
|
|
|
|
for (y = 0; y < mYNodes; y++)
|
|
{
|
|
vec3_t pos;
|
|
CRMNode * pnode = new CRMNode();
|
|
mNodes[x + y*mXNodes] = pnode;
|
|
|
|
float cell_y = (y + 1.0f) / (mYNodes+1);
|
|
// float cell_y = (y + 2.0f) / (mYNodes+3);
|
|
|
|
pos[0] = TheRandomMissionManager->GetLandScape()->flrand(cell_x - x_rnd, cell_x + x_rnd);
|
|
pos[1] = TheRandomMissionManager->GetLandScape()->flrand(cell_y - y_rnd, cell_y + y_rnd);
|
|
pos[2] = 0;
|
|
|
|
SetNodePos(x, y, pos);
|
|
}
|
|
}
|
|
|
|
ClearCells(mXNodes, mYNodes);
|
|
|
|
return true;
|
|
}
|
|
|
|
// neighbor offsets - easy way to turn a direction into the array position for a neighboring cell or node
|
|
int CRMPathManager::neighbor_x[DIR_MAX] = { 0, 1, 1, 1, 0,-1,-1,-1};
|
|
int CRMPathManager::neighbor_y[DIR_MAX] = {-1,-1, 0, 1, 1, 1, 0,-1};
|
|
|
|
/************************************************************************************************
|
|
* CRMPathManager::PlaceLocation
|
|
* This method is used to determine if a named location should be placed at this node.
|
|
*
|
|
* inputs:
|
|
* c_x, c_y - cell to examine
|
|
*
|
|
* return:
|
|
* none
|
|
*
|
|
************************************************************************************************/
|
|
void CRMPathManager::PlaceLocation(const int c_x, const int c_y)
|
|
{
|
|
if ( !Node(c_x,c_y)->IsLocation() )
|
|
{ // not currently a location
|
|
|
|
// how many paths lead to this cell?
|
|
int count_paths = 0;
|
|
int i;
|
|
|
|
for (i = 0; i<DIR_MAX; i++)
|
|
if (Node(c_x,c_y)->PathExist(i))
|
|
count_paths++;
|
|
|
|
int deepest_depth = -1;
|
|
int deepest_loc = -1;
|
|
for (i = mLocations.size()-1; i>=0; --i)
|
|
{
|
|
if (!mLocations[i]->Placed() && // node has not been placed
|
|
mLocations[i]->MinDepth() <= mDepth && // our current depth is in the proper range
|
|
mLocations[i]->MaxDepth() >= mDepth &&
|
|
mLocations[i]->MinPaths() <= count_paths && // our path count is in the proper range
|
|
mLocations[i]->MaxPaths() >= count_paths &&
|
|
mLocations[i]->MaxDepth() > deepest_depth) // and this is the deepest location of the ones that match
|
|
{
|
|
deepest_loc = i;
|
|
deepest_depth = mLocations[i]->MaxDepth();
|
|
}
|
|
}
|
|
|
|
if (deepest_loc >= 0 && deepest_loc < mLocations.size())
|
|
{ // found a location to place at this node / cell
|
|
const char * name = mLocations[deepest_loc]->GetName();
|
|
Node(c_x,c_y)->SetName(name);
|
|
mLocations[deepest_loc]->SetPlaced(true);
|
|
|
|
// need a new max depth
|
|
int max_depth = -1;
|
|
for (i = mLocations.size()-1; i>=0; --i)
|
|
{
|
|
// figure out new max depth based on the max depth of unplaced locations
|
|
if (!mLocations[i]->Placed() && // node has not been placed
|
|
mLocations[i]->MaxDepth() > max_depth) // and this is the deepest
|
|
{
|
|
max_depth = mLocations[i]->MaxDepth();
|
|
}
|
|
}
|
|
mMaxDepth = max_depth;
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************************************
|
|
* CRMPathManager::PathVisit
|
|
* This method is called recursively to create a network of nodes connected with paths.
|
|
*
|
|
* inputs:
|
|
* c_x, c_y - cell to visit
|
|
*
|
|
* return:
|
|
* none
|
|
*
|
|
************************************************************************************************/
|
|
void CRMPathManager::PathVisit(const int c_x, const int c_y)
|
|
{
|
|
// does this cell have any neighbors with all walls intact?
|
|
int i,off;
|
|
|
|
// look at neighbors in random order
|
|
off = TheRandomMissionManager->GetLandScape()->irand(DIR_FIRST, DIR_MAX-1);
|
|
|
|
++mDepth; // track our depth of recursion
|
|
|
|
for (i = DIR_FIRST; i<DIR_MAX && mDepth <= mMaxDepth; i++)
|
|
{
|
|
int d = (i + off) % DIR_MAX;
|
|
if ( !Cell(c_x, c_y).Border(d) )
|
|
{ // we can move this way, since no border
|
|
int new_c_x = c_x + neighbor_x[d];
|
|
int new_c_y = c_y + neighbor_y[d];
|
|
if (Cell(new_c_x,new_c_y).Wall() == DIR_ALL)
|
|
{ // we have a new cell that has not been visited!
|
|
int new_dir;
|
|
// d is the direction relative to the current cell
|
|
// new_dir is the direction relative to the next cell (N becomes S, NE becomes SW, etc...)
|
|
if( d < HALF_DIR_MAX )
|
|
{
|
|
new_dir = d + HALF_DIR_MAX;
|
|
}
|
|
else
|
|
{
|
|
new_dir = d - HALF_DIR_MAX;
|
|
}
|
|
|
|
// knock down walls
|
|
Cell(c_x,c_y).RemoveWall(d);
|
|
Cell(new_c_x,new_c_y).RemoveWall(new_dir); //DIR_MAX - d);
|
|
|
|
// set path id
|
|
Node(c_x, c_y)->SetPath(d, mPathCount);
|
|
Node(new_c_x, new_c_y)->SetPath(new_dir, mPathCount); //DIR_MAX - d, mPathCount);
|
|
|
|
// create path between cells
|
|
mTerrain->CreatePath( mPathCount++,
|
|
-1,
|
|
0,
|
|
mPathPoints,
|
|
GetNodePos(c_x,c_y)[0],
|
|
GetNodePos(c_x,c_y)[1],
|
|
GetNodePos(new_c_x,new_c_y)[0],
|
|
GetNodePos(new_c_x,new_c_y)[1],
|
|
mPathMinWidth,
|
|
mPathMaxWidth,
|
|
mPathDepth,
|
|
mPathDeviation,
|
|
mPathBreadth );
|
|
|
|
// flatten a small spot
|
|
CArea area;
|
|
float flat_radius = mPathMaxWidth *
|
|
fabs(TheRandomMissionManager->GetLandScape()->GetBounds()[1][0] - TheRandomMissionManager->GetLandScape()->GetBounds()[0][0]);
|
|
area.Init( GetNodePos(c_x,c_y), flat_radius, 0.0f, AT_NONE, 0, 0 );
|
|
TheRandomMissionManager->GetLandScape()->FlattenArea(&area, 255 * mPathDepth, false, true, true );
|
|
|
|
// recurse
|
|
PathVisit(new_c_x, new_c_y);
|
|
}
|
|
}
|
|
}
|
|
|
|
--mDepth;
|
|
|
|
// NOTE: *whoop* hack alert, the first time this is reached, it should be the very last placed node.
|
|
if( !mCrossed && TheRandomMissionManager->GetMission()->GetSymmetric() &&
|
|
TheRandomMissionManager->GetMission()->GetBackUpPath() )
|
|
{
|
|
mCrossed = true;
|
|
|
|
int directionSet[3][3] = {DIR_NW,DIR_W,DIR_SW,DIR_N,-1,DIR_S,DIR_NE,DIR_E,DIR_SE};
|
|
int ncx = (mXNodes-1)-c_x;
|
|
int ncy = (mYNodes-1)-c_y;
|
|
|
|
int x_delta = ncx - c_x;
|
|
int y_delta = ncy - c_y;
|
|
|
|
if( x_delta < -1 )
|
|
{
|
|
x_delta = -1;
|
|
}
|
|
else if( x_delta > 1 )
|
|
{
|
|
x_delta = 1;
|
|
}
|
|
|
|
if( y_delta < -1 )
|
|
{
|
|
y_delta = -1;
|
|
}
|
|
else if( y_delta > 1 )
|
|
{
|
|
y_delta = 1;
|
|
}
|
|
|
|
// make sure the mirror is actually in a different position than then un-mirrored node
|
|
if( x_delta || y_delta )
|
|
{
|
|
|
|
int d = directionSet[x_delta][y_delta];
|
|
int new_dir;
|
|
// d is the direction relative to the current cell
|
|
// new_dir is the direction relative to the next cell (N becomes S, NE becomes SW, etc...)
|
|
if( d < HALF_DIR_MAX )
|
|
{
|
|
new_dir = d + HALF_DIR_MAX;
|
|
}
|
|
else
|
|
{
|
|
new_dir = d - HALF_DIR_MAX;
|
|
}
|
|
|
|
//NOTE: Knocking down these walls will cause instances to be created on this new artificial path
|
|
// Since this path could span more than just the normal 1 cell, these walls being knocked down are not exactly correct... but get the job done
|
|
|
|
// knock down walls
|
|
Cell(c_x,c_y).RemoveWall(d);
|
|
Cell(ncx,ncy).RemoveWall(new_dir); //DIR_MAX - d);
|
|
|
|
// set path id
|
|
Node(c_x, c_y)->SetPath(d, mPathCount);
|
|
Node(ncx, ncy)->SetPath(new_dir, mPathCount); //DIR_MAX - d, mPathCount);
|
|
|
|
// create an artificial path that crosses over to connect the symmetric and non-symmetric map parts
|
|
mTerrain->CreatePath( mPathCount++,
|
|
-1,
|
|
0,
|
|
mPathPoints,
|
|
GetNodePos(c_x,c_y)[0],
|
|
GetNodePos(c_x,c_y)[1],
|
|
GetNodePos(ncx,ncy)[0],
|
|
GetNodePos(ncx,ncy)[1],
|
|
mPathMinWidth,
|
|
mPathMaxWidth,
|
|
mPathDepth,
|
|
mPathDeviation,
|
|
mPathBreadth );
|
|
|
|
}
|
|
}
|
|
|
|
PlaceLocation(c_x, c_y);
|
|
}
|
|
|
|
|
|
/************************************************************************************************
|
|
* CRMPathManager::FindNodeByName
|
|
* Finds the managed node with the matching case-insensivity name
|
|
*
|
|
* inputs:
|
|
* name - name of the node to find
|
|
*
|
|
* return:
|
|
* a pointer to the found node or NULL if the node couldn't be found
|
|
*
|
|
************************************************************************************************/
|
|
CRMNode* CRMPathManager::FindNodeByName ( const char* name )
|
|
{
|
|
int j;
|
|
|
|
for ( j = mNodes.size() - 1; j >=0; j-- )
|
|
{
|
|
if ( !stricmp ( name, mNodes[j]->GetName ( ) ) )
|
|
return mNodes[j];
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/************************************************************************************************
|
|
* CRMPathManager::SetPathStyle
|
|
* sets style for all paths
|
|
*
|
|
* inputs:
|
|
* settings for paths that are created
|
|
*
|
|
* return:
|
|
* none
|
|
*
|
|
************************************************************************************************/
|
|
void CRMPathManager::SetPathStyle (
|
|
const int points,
|
|
const float minwidth,
|
|
const float maxwidth,
|
|
const float depth,
|
|
const float deviation,
|
|
const float breadth
|
|
)
|
|
{
|
|
// save path style
|
|
mPathPoints = points ;
|
|
mPathMinWidth = minwidth;
|
|
mPathMaxWidth = maxwidth;
|
|
mPathDepth = depth ;
|
|
mPathDeviation= deviation;
|
|
mPathBreadth = breadth ;
|
|
}
|
|
|
|
/************************************************************************************************
|
|
* CRMPathManager::SetRiverStyle
|
|
* sets style for all rivers
|
|
*
|
|
* inputs:
|
|
* settings for river paths that are created
|
|
*
|
|
* return:
|
|
* none
|
|
*
|
|
************************************************************************************************/
|
|
void CRMPathManager::SetRiverStyle (const int depth,
|
|
const int points,
|
|
const float minwidth,
|
|
const float maxwidth,
|
|
const float beddepth,
|
|
const float deviation,
|
|
const float breadth,
|
|
string bridge_name)
|
|
{
|
|
// save river style
|
|
mRiverDepth = depth;
|
|
mRiverPoints = points ;
|
|
mRiverMinWidth = minwidth;
|
|
mRiverMaxWidth = maxwidth;
|
|
mRiverBedDepth = beddepth ;
|
|
mRiverDeviation= deviation;
|
|
mRiverBreadth = breadth ;
|
|
mRiverBridge = bridge_name;
|
|
}
|
|
|
|
vec3_t& CRMPathManager::GetRiverPos( const int x, const int y )
|
|
{
|
|
mRiverPos[0] = (float)(x + 1.0f) / (float)(mXNodes+2);
|
|
mRiverPos[1] = (float)(y + 1.0f) / (float)(mYNodes+2);
|
|
return mRiverPos;
|
|
}
|
|
|
|
void CRMPathManager::RiverVisit(const int c_x, const int c_y)
|
|
{
|
|
// does this cell have any neighbors with all walls intact?
|
|
int i,off;
|
|
|
|
// look at neighbors in random order
|
|
off = TheRandomMissionManager->GetLandScape()->irand(DIR_FIRST, DIR_MAX-1);
|
|
|
|
++mDepth; // track our depth of recursion
|
|
|
|
for (i = DIR_FIRST; i<DIR_MAX && mDepth <= mMaxDepth; i+=2)
|
|
{
|
|
int d = (i + off) % DIR_MAX;
|
|
if ( !Cell(c_x, c_y).Border(d) )
|
|
{ // we can move this way, since no border
|
|
int new_c_x = c_x + neighbor_x[d];
|
|
int new_c_y = c_y + neighbor_y[d];
|
|
if (RiverCell(new_c_x,new_c_y).Wall() == DIR_ALL)
|
|
{ // we have a new cell that has not been visited!
|
|
|
|
int new_dir;
|
|
// d is the direction relative to the current cell
|
|
// new_dir is the direction relative to the next cell (N becomes S, NE becomes SW, etc...)
|
|
if( d < HALF_DIR_MAX )
|
|
{
|
|
new_dir = d + HALF_DIR_MAX;
|
|
}
|
|
else
|
|
{
|
|
new_dir = d - HALF_DIR_MAX;
|
|
}
|
|
// knock down walls
|
|
RiverCell(c_x,c_y).RemoveWall(d);
|
|
RiverCell(new_c_x,new_c_y).RemoveWall(new_dir); //DIR_MAX - d);
|
|
|
|
// create river between cells
|
|
mTerrain->CreatePath ( mPathCount++,
|
|
-1,
|
|
0,
|
|
mRiverPoints,
|
|
GetRiverPos(c_x,c_y)[0],
|
|
GetRiverPos(c_x,c_y)[1],
|
|
GetRiverPos(new_c_x,new_c_y)[0],
|
|
GetRiverPos(new_c_x,new_c_y)[1],
|
|
mRiverMinWidth,
|
|
mRiverMaxWidth,
|
|
mRiverBedDepth,
|
|
mRiverDeviation,
|
|
mRiverBreadth );
|
|
|
|
// flatten a small spot
|
|
CArea area;
|
|
float flat_radius = mRiverMinWidth *
|
|
fabs(TheRandomMissionManager->GetLandScape()->GetBounds()[1][0] - TheRandomMissionManager->GetLandScape()->GetBounds()[0][0]);
|
|
area.Init( GetRiverPos(c_x,c_y), flat_radius, 0.0f, AT_NONE, 0, 0 );
|
|
TheRandomMissionManager->GetLandScape()->FlattenArea (&area, 255 * mRiverBedDepth, false, true, true );
|
|
|
|
// recurse
|
|
RiverVisit(new_c_x, new_c_y);
|
|
}
|
|
}
|
|
}
|
|
|
|
// --mDepth;
|
|
}
|
|
|
|
/************************************************************************************************
|
|
* CRMPathManager::GenerateRivers
|
|
* Creates a river which intersects the main path
|
|
*
|
|
* inputs:
|
|
* none
|
|
*
|
|
* return:
|
|
* none
|
|
*
|
|
************************************************************************************************/
|
|
void CRMPathManager::GenerateRivers ()
|
|
{
|
|
if (mRiverBedDepth == 1)
|
|
// no rivers
|
|
return;
|
|
|
|
mMaxDepth = mRiverDepth;
|
|
mDepth = 0;
|
|
|
|
int cell_x = 0;
|
|
int cell_y = 0;
|
|
|
|
// choose starting cell along an edge
|
|
int edge = TheRandomMissionManager->GetLandScape()->irand(0, 7);
|
|
switch ( edge )
|
|
{
|
|
case 0:
|
|
cell_x = mXNodes / 2; cell_y = 0;
|
|
break;
|
|
case 1:
|
|
cell_x = mXNodes; cell_y = 0;
|
|
break;
|
|
case 2:
|
|
cell_x = mXNodes; cell_y = mYNodes / 2;
|
|
break;
|
|
case 3:
|
|
cell_x = mXNodes; cell_y = mYNodes;
|
|
break;
|
|
case 4:
|
|
cell_x = mXNodes / 2; cell_y = mYNodes;
|
|
break;
|
|
case 5:
|
|
cell_x = 0; cell_y = mYNodes;
|
|
break;
|
|
case 6:
|
|
cell_x = 0; cell_y = mYNodes / 2;
|
|
break;
|
|
case 7:
|
|
cell_x = 0; cell_y = 0;
|
|
break;
|
|
}
|
|
|
|
ClearCells(mXNodes+1, mYNodes+1);
|
|
|
|
mRiverCount = mPathCount;
|
|
|
|
// visit the first cell
|
|
RiverVisit(cell_x,cell_y);
|
|
|
|
mRiverCount = mPathCount - mRiverCount;
|
|
|
|
return;
|
|
}
|
|
|
|
/************************************************************************************************
|
|
* CRMPathManager::GeneratePaths
|
|
* Creates all paths
|
|
*
|
|
* inputs:
|
|
* none
|
|
*
|
|
* return:
|
|
* none
|
|
*
|
|
************************************************************************************************/
|
|
void CRMPathManager::GeneratePaths ( symmetry_t symmetric )
|
|
{
|
|
int cell_x = 0;
|
|
int cell_y = 0;
|
|
|
|
switch ( symmetric )
|
|
{
|
|
case SYMMETRY_TOPLEFT:
|
|
cell_x = mXNodes-1;
|
|
cell_y = 0;
|
|
break;
|
|
|
|
case SYMMETRY_BOTTOMRIGHT:
|
|
cell_x = 0;
|
|
cell_y = mYNodes-1;
|
|
break;
|
|
|
|
default:
|
|
case SYMMETRY_NONE:
|
|
// choose starting cell along an edge
|
|
switch ( TheRandomMissionManager->GetLandScape()->irand(0, 7) )
|
|
{
|
|
case 0:
|
|
cell_x = mXNodes / 2;
|
|
break;
|
|
case 1:
|
|
cell_x = mXNodes-1;
|
|
break;
|
|
case 2:
|
|
cell_x = mXNodes-1; cell_y = mYNodes / 2;
|
|
break;
|
|
case 3:
|
|
cell_x = mXNodes-1; cell_y = mYNodes-1;
|
|
break;
|
|
case 4:
|
|
cell_x = mXNodes / 2; cell_y = mYNodes-1;
|
|
break;
|
|
case 5:
|
|
cell_y = mYNodes-1;
|
|
break;
|
|
case 6:
|
|
cell_y = mYNodes / 2;
|
|
break;
|
|
default:
|
|
case 7:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// visit the first cell
|
|
PathVisit(cell_x,cell_y);
|
|
}
|
|
|