Initial commit.

This commit is contained in:
Jim Gray
2013-04-04 14:32:05 -07:00
parent ba5c81da32
commit d71d53e8ec
2180 changed files with 1393544 additions and 1 deletions

480
code/RMG/RM_Area.cpp Normal file
View File

@@ -0,0 +1,480 @@
/************************************************************************************************
*
* Copyright (C) 2001-2002 Raven Software
*
* RM_Area.cpp
*
************************************************************************************************/
#include "../server/exe_headers.h"
#include "rm_headers.h"
#ifdef _WIN32
#pragma optimize("p", on)
#endif
/************************************************************************************************
* CRMArea::CRMArea
* constructor
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
CRMArea::CRMArea (
float spacingRadius,
float paddingSize,
float confineRadius,
vec3_t confineOrigin,
vec3_t lookAtOrigin,
bool flatten,
int symmetric
)
{
mMoveCount = 0;
mAngle = 0;
mCollision = true;
mConfineRadius = confineRadius;
mPaddingSize = paddingSize;
mSpacingRadius = spacingRadius;
mFlatten = flatten;
mLookAt = true;
mLockOrigin = false;
mSymmetric = symmetric;
mRadius = spacingRadius;
VectorCopy ( confineOrigin, mConfineOrigin );
VectorCopy ( lookAtOrigin, mLookAtOrigin );
}
/************************************************************************************************
* CRMArea::LookAt
* Angle the area towards the given point
*
* inputs:
* lookat - the origin to look at
*
* return:
* the angle in radians that was calculated
*
************************************************************************************************/
float CRMArea::LookAt ( vec3_t lookat )
{
if (mLookAt)
{ // this area orients itself towards a point
vec3_t a;
VectorCopy ( lookat, mLookAtOrigin );
VectorSubtract ( lookat, mOrigin, a );
mAngle = atan2 ( a[1], a[0] );
}
return mAngle;
}
/************************************************************************************************
* CRMArea::Mirror
* Mirrors the area to the other side of the map. This includes mirroring the confine origin
* and lookat origin
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
void CRMArea::Mirror ( void )
{
mOrigin[0] = -mOrigin[0];
mOrigin[1] = -mOrigin[1];
mConfineOrigin[0] = -mConfineOrigin[0];
mConfineOrigin[1] = -mConfineOrigin[1];
mLookAtOrigin[0] = -mLookAtOrigin[0];
mLookAtOrigin[1] = -mLookAtOrigin[1];
}
/************************************************************************************************
* CRMAreaManager::CRMAreaManager
* constructor
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
CRMAreaManager::CRMAreaManager ( const vec3_t mins, const vec3_t maxs)
{
VectorCopy ( mins, mMins );
VectorCopy ( maxs, mMaxs );
mWidth = mMaxs[0] - mMins[0];
mHeight = mMaxs[1] - mMins[1];
}
/************************************************************************************************
* CRMAreaManager::~CRMAreaManager
* Removes all managed areas
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
CRMAreaManager::~CRMAreaManager ( )
{
int i;
for ( i = mAreas.size() - 1; i >=0; i -- )
{
delete mAreas[i];
}
mAreas.clear();
}
/************************************************************************************************
* CRMAreaManager::MoveArea
* Moves an area within the area manager thus shifting any other areas as needed
*
* inputs:
* area - area to be moved
* origin - new origin to attempt to move to
*
* return:
* none
*
************************************************************************************************/
void CRMAreaManager::MoveArea ( CRMArea* movedArea, vec3_t origin)
{
int index;
int size;
// Increment the addcount (this is for infinite protection)
movedArea->AddMoveCount ();
// Infinite recursion prevention
if ( movedArea->GetMoveCount() > 250 )
{
// assert ( 0 );
movedArea->EnableCollision ( false );
return;
}
// First set the area's origin, This may cause it to be in collision with
// another area but that will get fixed later
movedArea->SetOrigin ( origin );
// when symmetric we want to ensure that no instances end up on the "other" side of the imaginary diaganol that cuts the map in two
// mSymmetric tells us which side of the map is legal
if ( movedArea->GetSymmetric ( ) )
{
const vec3pair_t& bounds = TheRandomMissionManager->GetLandScape()->GetBounds();
vec3_t point;
vec3_t dir;
vec3_t tang;
bool push;
float len;
VectorSubtract( movedArea->GetOrigin(), bounds[0], point );
VectorSubtract( bounds[1], bounds[0], dir );
VectorNormalize(dir);
dir[2] = 0;
point[2] = 0;
VectorMA( bounds[0], DotProduct(point, dir), dir, tang );
VectorSubtract ( movedArea->GetOrigin(), tang, dir );
dir[2] = 0;
push = false;
len = VectorNormalize(dir);
if ( len < movedArea->GetRadius ( ) )
{
if ( movedArea->GetLockOrigin ( ) )
{
movedArea->EnableCollision ( false );
return;
}
VectorMA ( point, (movedArea->GetSpacingRadius() - len) + TheRandomMissionManager->GetLandScape()->irand(10,movedArea->GetSpacingRadius()), dir, point );
origin[0] = point[0] + bounds[0][0];
origin[1] = point[1] + bounds[0][1];
movedArea->SetOrigin ( origin );
}
switch ( movedArea->GetSymmetric ( ) )
{
case SYMMETRY_TOPLEFT:
if ( origin[1] > origin[0] )
{
movedArea->Mirror ( );
}
break;
case SYMMETRY_BOTTOMRIGHT:
if ( origin[1] < origin[0] )
{
movedArea->Mirror ( );
}
break;
default:
// unknown symmetry type
assert ( 0 );
break;
}
}
// Confine to area unless we are being pushed back by the same guy who pushed us last time (infinite loop)
if ( movedArea->GetConfineRadius() )
{
if ( movedArea->GetMoveCount() < 25 )
{
vec3_t cdiff;
float cdist;
VectorSubtract ( movedArea->GetOrigin(), movedArea->GetConfineOrigin(), cdiff );
cdiff[2] = 0;
cdist = VectorLength ( cdiff );
if ( cdist + movedArea->GetSpacingRadius() > movedArea->GetConfineRadius() )
{
cdist = movedArea->GetConfineRadius() - movedArea->GetSpacingRadius();
VectorNormalize ( cdiff );
VectorMA ( movedArea->GetConfineOrigin(), cdist, cdiff, movedArea->GetOrigin());
}
}
else
{
index = 0;
}
}
// See if it fell off the world in the x direction
if ( movedArea->GetOrigin()[0] + movedArea->GetSpacingRadius() > mMaxs[0] )
movedArea->GetOrigin()[0] = mMaxs[0] - movedArea->GetSpacingRadius() - (TheRandomMissionManager->GetLandScape()->irand(10,200));
else if ( movedArea->GetOrigin()[0] - movedArea->GetSpacingRadius() < mMins[0] )
movedArea->GetOrigin()[0] = mMins[0] + movedArea->GetSpacingRadius() + (TheRandomMissionManager->GetLandScape()->irand(10,200));
// See if it fell off the world in the y direction
if ( movedArea->GetOrigin()[1] + movedArea->GetSpacingRadius() > mMaxs[1] )
movedArea->GetOrigin()[1] = mMaxs[1] - movedArea->GetSpacingRadius() - (TheRandomMissionManager->GetLandScape()->irand(10,200));
else if ( movedArea->GetOrigin()[1] - movedArea->GetSpacingRadius() < mMins[1] )
movedArea->GetOrigin()[1] = mMins[1] + movedArea->GetSpacingRadius() + (TheRandomMissionManager->GetLandScape()->irand(10,200));
// Look at what we need to look at
movedArea->LookAt ( movedArea->GetLookAtOrigin() );
// Dont collide against things that have no collision
// if ( !movedArea->IsCollisionEnabled ( ) )
// {
// return;
// }
// See if its colliding
for(index = 0, size = mAreas.size(); index < size; index ++ )
{
CRMArea *area = mAreas[index];
vec3_t diff;
vec3_t newOrigin;
float dist;
float targetdist;
// Skip the one that was moved in the first place
if ( area == movedArea )
{
continue;
}
if ( area->GetLockOrigin ( ) && movedArea->GetLockOrigin( ) )
{
continue;
}
// Dont collide against things that have no collision
if ( !area->IsCollisionEnabled ( ) )
{
continue;
}
// Grab the distance between the two
// only want the horizontal distance -- dmv
//dist = Distance ( movedArea->GetOrigin ( ), area->GetOrigin ( ));
vec3_t maOrigin;
vec3_t aOrigin;
VectorCopy(movedArea->GetOrigin(), maOrigin);
VectorCopy(area->GetOrigin(), aOrigin);
maOrigin[2] = aOrigin[2] = 0;
dist = Distance ( maOrigin, aOrigin );
targetdist = movedArea->GetSpacingRadius() + area->GetSpacingRadius() + maximum(movedArea->GetPaddingSize(),area->GetPaddingSize());
if ( dist == 0 )
{
area->GetOrigin()[0] += (50 * (float)(TheRandomMissionManager->GetLandScape()->irand(0,99))/100.0f);
area->GetOrigin()[1] += (50 * (float)(TheRandomMissionManager->GetLandScape()->irand(0,99))/100.0f);
VectorCopy(area->GetOrigin(), aOrigin);
aOrigin[2] = 0;
dist = Distance ( maOrigin, aOrigin );
}
// Are they are enough apart?
if ( dist >= targetdist )
{
continue;
}
// Dont move a step if locked
if ( area->GetLockOrigin ( ) )
{
MoveArea ( area, area->GetOrigin ( ) );
continue;
}
// we got a collision, move the guy we hit
VectorSubtract ( area->GetOrigin(), movedArea->GetOrigin(), diff );
diff[2] = 0;
VectorNormalize ( diff );
// Push by the difference in the distance and no-collide radius
VectorMA ( area->GetOrigin(), targetdist - dist + 1 , diff, newOrigin );
// Move the area now
MoveArea ( area, newOrigin );
}
}
/************************************************************************************************
* CRMAreaManager::CreateArea
* Creates an area and adds it to the list of managed areas
*
* inputs:
* none
*
* return:
* a pointer to the newly added area class
*
************************************************************************************************/
CRMArea* CRMAreaManager::CreateArea (
vec3_t origin,
float spacingRadius,
int spacingLine,
float paddingSize,
float confineRadius,
vec3_t confineOrigin,
vec3_t lookAtOrigin,
bool flatten,
bool collide,
bool lockorigin,
int symmetric
)
{
CRMArea* area = new CRMArea ( spacingRadius, paddingSize, confineRadius, confineOrigin, lookAtOrigin, flatten, symmetric );
if ( lockorigin || spacingLine )
{
area->LockOrigin ( );
}
if (origin[0] != lookAtOrigin[0] || origin[1] != lookAtOrigin[1])
area->EnableLookAt(true);
// First add the area to the list
mAreas.push_back ( area );
area->EnableCollision(collide);
// Set the real radius which is used for center line detection
if ( spacingLine )
{
area->SetRadius ( spacingRadius + (spacingLine - 1) * spacingRadius );
}
// Now move the area around
MoveArea ( area, origin );
if ( (origin[0] != lookAtOrigin[0] || origin[1] != lookAtOrigin[1]) )
{
int i;
vec3_t linedir;
vec3_t dir;
vec3_t up = {0,0,1};
vec3_t zerodvec;
VectorClear(zerodvec);
VectorSubtract ( lookAtOrigin, origin, dir );
VectorNormalize ( dir );
dir[2] = 0;
CrossProduct ( dir, up, linedir );
for ( i = 0; i < spacingLine - 1; i ++ )
{
CRMArea* linearea;
vec3_t lineorigin;
linearea = new CRMArea ( spacingRadius, paddingSize, 0, zerodvec, zerodvec, false, symmetric );
linearea->LockOrigin ( );
linearea->EnableCollision(collide);
VectorMA ( origin, spacingRadius + (spacingRadius * 2 * i), linedir, lineorigin );
mAreas.push_back ( linearea );
MoveArea ( linearea, lineorigin );
linearea = new CRMArea ( spacingRadius, paddingSize, 0, zerodvec, zerodvec, false, symmetric );
linearea->LockOrigin ( );
linearea->EnableCollision(collide);
VectorMA ( origin, -spacingRadius - (spacingRadius * 2 * i), linedir, lineorigin );
mAreas.push_back ( linearea );
MoveArea ( linearea, lineorigin );
}
}
// Return it for convienience
return area;
}
/************************************************************************************************
* CRMAreaManager::EnumArea
* Allows for enumeration through the area list. If an invalid index is given then NULL will
* be returned;
*
* inputs:
* index - current enumeration index
*
* return:
* requested area class pointer or NULL if the index was invalid
*
************************************************************************************************/
CRMArea* CRMAreaManager::EnumArea ( const int index )
{
// This isnt an assertion case because there is no size method for
// the area manager so the areas are enumerated until NULL is returned.
if ( index < 0 || index >= mAreas.size ( ) )
{
return NULL;
}
return mAreas[index];
}
#ifdef _WIN32
#pragma optimize("p", off)
#endif

99
code/RMG/RM_Area.h Normal file
View File

@@ -0,0 +1,99 @@
/************************************************************************************************
*
* Copyright (C) 2001-2002 Raven Software
*
* RM_Area.h
*
************************************************************************************************/
#pragma once
#if !defined(RM_AREA_H_INC)
#define RM_AREA_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including RM_Area.h")
#endif
class CRMArea
{
private:
float mPaddingSize;
float mSpacingRadius;
float mConfineRadius;
float mRadius;
float mAngle;
int mMoveCount;
vec3_t mOrigin;
vec3_t mConfineOrigin;
vec3_t mLookAtOrigin;
bool mCollision;
bool mFlatten;
bool mLookAt;
bool mLockOrigin;
int mSymmetric;
public:
CRMArea ( float spacing, float padding, float confine, vec3_t confineOrigin, vec3_t lookAtOrigin, bool flatten = true, int symmetric = 0 );
void Mirror ( void );
void SetOrigin(vec3_t origin) { VectorCopy ( origin, mOrigin ); }
void SetAngle(float angle) { mAngle = angle; }
void SetSymmetric(int sym) { mSymmetric = sym; }
void EnableCollision(bool e) { mCollision = e; }
void EnableLookAt(bool la) {mLookAt = la; }
float LookAt(vec3_t lookat);
void LockOrigin( void ) { mLockOrigin = true; }
void AddMoveCount() { mMoveCount++; }
void ClearMoveCount() { mMoveCount=0; }
float GetPaddingSize() { return mPaddingSize; }
float GetSpacingRadius() { return mSpacingRadius; }
float GetRadius() { return mRadius; }
float GetConfineRadius() { return mConfineRadius; }
float GetAngle() { return mAngle; }
int GetMoveCount() { return mMoveCount; }
vec_t* GetOrigin() { return mOrigin; }
vec_t* GetConfineOrigin() { return mConfineOrigin; }
vec_t* GetLookAtOrigin() { return mLookAtOrigin; }
bool GetLookAt() { return mLookAt;}
bool GetLockOrigin() { return mLockOrigin; }
int GetSymmetric() { return mSymmetric; }
void SetRadius(float r) { mRadius = r; }
bool IsCollisionEnabled(){ return mCollision; }
bool IsFlattened (){ return mFlatten; }
};
typedef vector<CRMArea*> rmAreaVector_t;
class CRMAreaManager
{
private:
rmAreaVector_t mAreas;
vec3_t mMins;
vec3_t mMaxs;
float mWidth;
float mHeight;
public:
CRMAreaManager ( const vec3_t mins, const vec3_t maxs );
~CRMAreaManager ( );
CRMArea* CreateArea ( vec3_t origin, float spacing, int spacingline, float padding, float confine, vec3_t confineOrigin, vec3_t lookAtOrigin, bool flatten=true, bool collide=true, bool lockorigin=false, int symmetric=0);
void MoveArea ( CRMArea* area, vec3_t origin);
CRMArea* EnumArea ( const int index );
// void CreateMap ( void );
};
#endif

71
code/RMG/RM_Headers.h Normal file
View File

@@ -0,0 +1,71 @@
#pragma once
#if !defined(RM_HEADERS_H_INC)
#define RM_HEADERS_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including RM_Headers.h")
#endif
#pragma warning (push, 3)
#include <vector>
#include <list>
#pragma warning (pop)
using namespace std;
#if !defined(GENERICPARSER2_H_INC)
#include "../game/genericparser2.h"
#endif
#if !defined(CM_LOCAL_H_INC)
#include "../qcommon/cm_local.h"
#endif
#define MAX_INSTANCE_TRIES 5
// on a symmetric map which corner is the first node
typedef enum
{
SYMMETRY_NONE,
SYMMETRY_TOPLEFT,
SYMMETRY_BOTTOMRIGHT
} symmetry_t;
#if !defined(CM_TERRAINMAP_H_INC)
#include "../qcommon/cm_terrainmap.h"
#endif
#if !defined(RM_AREA_H_INC)
#include "RM_Area.h"
#endif
#if !defined(RM_PATH_H_INC)
#include "RM_Path.h"
#endif
#if !defined(RM_OBJECTIVE_H_INC)
#include "RM_Objective.h"
#endif
#if !defined(RM_INSTANCEFILE_H_INC)
#include "RM_InstanceFile.h"
#endif
#if !defined(RM_INSTANCE_H_INC)
#include "RM_Instance.h"
#endif
#if !defined(RM_MISSION_H_INC)
#include "RM_Mission.h"
#endif
#if !defined(RM_MANAGER_H_INC)
#include "RM_Manager.h"
#endif
#if !defined(RM_TERRAIN_H_INC)
#include "RM_Terrain.h"
#endif
#endif

191
code/RMG/RM_Instance.cpp Normal file
View File

@@ -0,0 +1,191 @@
#include "../server/exe_headers.h"
#include "rm_headers.h"
#include "../qcommon/cm_terrainmap.h"
/************************************************************************************************
* CRMInstance::CRMInstance
* constructs a instnace object using the given parser group
*
* inputs:
* instance: parser group containing information about the instance
*
* return:
* none
*
************************************************************************************************/
CRMInstance::CRMInstance ( CGPGroup *instGroup, CRMInstanceFile& instFile )
{
mObjective = NULL;
mSpacingRadius = 0;
mFlattenRadius = 0;
mFilter[0] = mTeamFilter[0] = 0;
mArea = NULL;
mAutomapSymbol = 0;
mEntityID = 0;
mSide = 0;
mMirror = 0;
mFlattenHeight = 66;
mSpacingLine = 0;
mSurfaceSprites = true;
mLockOrigin = false;
}
/************************************************************************************************
* CRMInstance::PreSpawn
* Prepares the instance for spawning by flattening the ground under it
*
* inputs:
* landscape: landscape the instance will be spawned on
*
* return:
* true: spawn preparation successful
* false: spawn preparation failed
*
************************************************************************************************/
bool CRMInstance::PreSpawn ( CRandomTerrain* terrain, qboolean IsServer )
{
vec3_t origin;
CArea area;
VectorCopy(GetOrigin(), origin);
if (mMirror)
{
origin[0] = TheRandomMissionManager->GetLandScape()->GetBounds()[0][0] + TheRandomMissionManager->GetLandScape()->GetBounds()[1][0] - origin[0];
origin[1] = TheRandomMissionManager->GetLandScape()->GetBounds()[0][1] + TheRandomMissionManager->GetLandScape()->GetBounds()[1][1] - origin[1];
}
const vec3_t& terxelSize = terrain->GetLandScape()->GetTerxelSize ( );
const vec3pair_t& bounds = terrain->GetLandScape()->GetBounds();
// Align the instance to the center of a terxel
origin[0] = bounds[0][0] + (int)((origin[0] - bounds[0][0] + terxelSize[0] / 2) / terxelSize[0]) * terxelSize[0];
origin[1] = bounds[0][1] + (int)((origin[1] - bounds[0][1] + terxelSize[1] / 2) / terxelSize[1]) * terxelSize[1];
// This is BAD - By copying the mirrored origin back into the instance, you've now mirrored the original instance
// so when anything from this point on looks at the instance they'll be looking at a mirrored version but will be expecting the original
// so later in the spawn functions the instance will be re-mirrored, because it thinks the mInstances have not been changed
// VectorCopy(origin, GetOrigin());
// Flatten the area below the instance
if ( GetFlattenRadius() )
{
area.Init( origin, GetFlattenRadius(), 0.0f, AT_NONE, 0, 0 );
terrain->GetLandScape()->FlattenArea( &area, mFlattenHeight | (mSurfaceSprites?0:0x80), false, true, true );
}
return true;
}
/************************************************************************************************
* CRMInstance::PostSpawn
* Finishes the spawn by linking any objectives into the world that are associated with it
*
* inputs:
* landscape: landscape the instance was spawned on
*
* return:
* true: post spawn successfull
* false: post spawn failed
*
************************************************************************************************/
bool CRMInstance::PostSpawn ( CRandomTerrain* terrain, qboolean IsServer )
{
if ( mObjective )
{
return mObjective->Link ( );
}
return true;
}
#ifndef DEDICATED
void CRMInstance::DrawAutomapSymbol()
{
// draw proper symbol on map for instance
switch (GetAutomapSymbol())
{
default:
case AUTOMAP_NONE:
if (HasObjective())
CM_TM_AddObjective(GetOrigin()[0], GetOrigin()[1], GetSide());
break;
case AUTOMAP_BLD:
CM_TM_AddBuilding(GetOrigin()[0], GetOrigin()[1], GetSide());
if (HasObjective())
CM_TM_AddObjective(GetOrigin()[0], GetOrigin()[1], GetSide());
break;
case AUTOMAP_OBJ:
CM_TM_AddObjective(GetOrigin()[0], GetOrigin()[1], GetSide());
break;
case AUTOMAP_START:
CM_TM_AddStart(GetOrigin()[0], GetOrigin()[1], GetSide());
break;
case AUTOMAP_END:
CM_TM_AddEnd(GetOrigin()[0], GetOrigin()[1], GetSide());
break;
case AUTOMAP_ENEMY:
if (HasObjective())
CM_TM_AddObjective(GetOrigin()[0], GetOrigin()[1]);
if (1 == Cvar_VariableIntegerValue("rmg_automapshowall"))
CM_TM_AddNPC(GetOrigin()[0], GetOrigin()[1], false);
break;
case AUTOMAP_FRIEND:
if (HasObjective())
CM_TM_AddObjective(GetOrigin()[0], GetOrigin()[1]);
if (1 == Cvar_VariableIntegerValue("rmg_automapshowall"))
CM_TM_AddNPC(GetOrigin()[0], GetOrigin()[1], true);
break;
case AUTOMAP_WALL:
CM_TM_AddWallRect(GetOrigin()[0], GetOrigin()[1], GetSide());
break;
}
}
#endif // !DEDICATED
/************************************************************************************************
* CRMInstance::Preview
* Renderings debug information about the instance
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
void CRMInstance::Preview ( const vec3_t from )
{
/* CEntity *tent;
// Add a cylindar for the whole settlement
tent = G_TempEntity( GetOrigin(), EV_DEBUG_CYLINDER );
VectorCopy( GetOrigin(), tent->s.origin2 );
tent->s.pos.trBase[2] += 40;
tent->s.origin2[2] += 50;
tent->s.time = 1050 + ((int)(GetSpacingRadius())<<16);
tent->s.time2 = GetPreviewColor ( );
G_AddTempEntity(tent);
// Origin line
tent = G_TempEntity( GetOrigin ( ), EV_DEBUG_LINE );
VectorCopy( GetOrigin(), tent->s.origin2 );
tent->s.origin2[2] += 400;
tent->s.time = 1050;
tent->s.weapon = 10;
tent->s.time2 = (255<<24) + (255<<16) + (255<<8) + 255;
G_AddTempEntity(tent);
if ( GetFlattenRadius ( ) )
{
// Add a cylindar for the whole settlement
tent = G_TempEntity( GetOrigin(), EV_DEBUG_CYLINDER );
VectorCopy( GetOrigin(), tent->s.origin2 );
tent->s.pos.trBase[2] += 40;
tent->s.origin2[2] += 50;
tent->s.time = 1050 + ((int)(GetFlattenRadius ( ))<<16);
tent->s.time2 = (255<<24) + (80<<16) +(80<<8) + 80;
G_AddTempEntity(tent);
}
*/
}

122
code/RMG/RM_Instance.h Normal file
View File

@@ -0,0 +1,122 @@
#pragma once
#if !defined(RM_INSTANCE_H_INC)
#define RM_INSTANCE_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including RM_Instance.h")
#endif
#if !defined(CM_LANDSCAPE_H_INC)
#include "../qcommon/cm_landscape.h"
#endif
enum CRMAutomapSymbol
{
AUTOMAP_NONE = 0,
AUTOMAP_BLD = 1,
AUTOMAP_OBJ = 2,
AUTOMAP_START= 3,
AUTOMAP_END = 4,
AUTOMAP_ENEMY= 5,
AUTOMAP_FRIEND=6,
AUTOMAP_WALL=7
};
class CRMInstance
{
protected:
char mFilter[MAX_QPATH]; // filter of entities inside of this
char mTeamFilter[MAX_QPATH]; // team specific filter
vec3pair_t mBounds; // Bounding box for instance itself
CRMArea* mArea; // Position of the instance
CRMObjective* mObjective; // Objective associated with this instance
// optional instance specific strings for objective
string mMessage; // message outputed when objective is completed
string mDescription; // description of objective
string mInfo; // more info for objective
float mSpacingRadius; // Radius to space instances with
float mFlattenRadius; // Radius to flatten under instances
int mSpacingLine; // Line of spacing radius's, forces locket
bool mLockOrigin; // Origin cant move
bool mSurfaceSprites; // allow surface sprites under instance?
int mAutomapSymbol; // show which symbol on automap 0=none
int mEntityID; // id of entity spawned
int mSide; // blue or red side
int mMirror; // mirror origin, angle
int mFlattenHeight; // height to flatten land
public:
CRMInstance ( CGPGroup* instance, CRMInstanceFile& instFile);
virtual ~CRMInstance ( ) { }
virtual bool IsValid ( ) { return true; }
virtual bool PreSpawn ( CRandomTerrain* terrain, qboolean IsServer );
virtual bool Spawn ( CRandomTerrain* terrain, qboolean IsServer ) { return false; }
virtual bool PostSpawn ( CRandomTerrain* terrain, qboolean IsServer );
virtual void Preview ( const vec3_t from );
virtual void SetArea ( CRMAreaManager* amanager, CRMArea* area ) { mArea = area; }
virtual void SetFilter ( const char *filter ) { strcpy(mFilter, filter); }
virtual void SetTeamFilter ( const char *teamFilter ) { strcpy(mTeamFilter, teamFilter); }
void SetObjective ( CRMObjective* obj ) { mObjective = obj; }
CRMObjective* GetObjective (void) {return mObjective;}
bool HasObjective () {return mObjective != NULL;}
int GetAutomapSymbol () {return mAutomapSymbol;}
void DrawAutomapSymbol ();
const char* GetMessage(void) { return mMessage.c_str(); }
const char* GetDescription(void){ return mDescription.c_str(); }
const char* GetInfo(void) { return mInfo.c_str(); }
void SetMessage(const char* msg) { mMessage = msg; }
void SetDescription(const char* desc) { mDescription = desc; }
void SetInfo(const char* info) { mInfo = info; }
void SetSide(int side) {mSide = side;}
int GetSide ( ) {return mSide;}
// NOTE: should consider making SetMirror also set all other variables that need flipping
// like the origin and Side, etc... Otherwise an Instance may have had it's origin flipped
// but then later will have mMirror set to false, but the origin is still flipped. So any functions
// that look at the instance later will see mMirror set to false, but not realize the origin has ALREADY been flipped
virtual void SetMirror(int mirror) { mMirror = mirror;}
int GetMirror ( ) { return mMirror;}
virtual bool GetSurfaceSprites ( ) { return mSurfaceSprites; }
virtual bool GetLockOrigin ( ) { return mLockOrigin; }
virtual int GetSpacingLine ( ) { return mSpacingLine; }
virtual int GetPreviewColor ( ) { return 0; }
virtual float GetSpacingRadius ( ) { return mSpacingRadius; }
virtual float GetFlattenRadius ( ) { return mFlattenRadius; }
const char *GetFilter ( ) { return mFilter; }
const char *GetTeamFilter ( ) { return mTeamFilter; }
CRMArea& GetArea ( ) { return *mArea; }
vec_t* GetOrigin ( ) {return mArea->GetOrigin(); }
float GetAngle ( ) {return mArea->GetAngle();}
void SetAngle(float ang ) { mArea->SetAngle(ang);}
const vec3pair_t& GetBounds(void) const { return(mBounds); }
void SetFlattenHeight ( int height ) { mFlattenHeight = height; }
int GetFlattenHeight ( void ) { return mFlattenHeight; }
void SetSpacingRadius (float spacing) { mSpacingRadius = spacing; }
};
typedef list<CRMInstance*>::iterator rmInstanceIter_t;
typedef list<CRMInstance*> rmInstanceList_t;
#endif

View File

@@ -0,0 +1,200 @@
/************************************************************************************************
*
* RM_InstanceFile.cpp
*
* implements the CRMInstanceFile class. This class provides functionality to load
* and create instances from an instance file. First call Open to open the instance file and
* then use CreateInstance to create new instances. When finished call Close to cleanup.
*
************************************************************************************************/
#include "../server/exe_headers.h"
#include "rm_headers.h"
//#include "rm_instance_npc.h"
#include "rm_instance_bsp.h"
#include "rm_instance_random.h"
#include "rm_instance_group.h"
#include "rm_instance_void.h"
/************************************************************************************************
* CRMInstanceFile::CRMInstanceFile
* constructor
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
CRMInstanceFile::CRMInstanceFile ( )
{
mInstances = NULL;
}
/************************************************************************************************
* CRMInstanceFile::~CRMInstanceFile
* Destroys the instance file by freeing the parser
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
CRMInstanceFile::~CRMInstanceFile ( )
{
Close ( );
}
/************************************************************************************************
* CRMInstanceFile::Open
* Opens the given instance file and prepares it for use in instance creation
*
* inputs:
* instance: Name of instance to open. Note that the root path will be automatically
* added and shouldnt be included in the given name
*
* return:
* true: instance file successfully loaded
* false: instance file could not be loaded for some reason
*
************************************************************************************************/
bool CRMInstanceFile::Open ( const char* instance )
{
char instanceDef[MAX_QPATH];
CGPGroup *basegroup;
// Build the filename
Com_sprintf(instanceDef, MAX_QPATH, "ext_data/rmg/%s.instance", instance );
#ifndef FINAL_BUILD
// Debug message
Com_Printf("CM_Terrain: Loading and parsing instanceDef %s.....\n", instance);
#endif
// Parse the text file using the generic parser
if(!Com_ParseTextFile(instanceDef, mParser ))
{
Com_sprintf(instanceDef, MAX_QPATH, "ext_data/arioche/%s.instance", instance );
if(!Com_ParseTextFile(instanceDef, mParser ))
{
Com_Printf(va("CM_Terrain: Could not open instance file '%s'\n", instanceDef));
return false;
}
}
// The whole file....
basegroup = mParser.GetBaseParseGroup();
// The root { } struct
mInstances = basegroup->GetSubGroups();
// The "instances" { } structure
mInstances = mInstances->GetSubGroups ( );
return true;
}
/************************************************************************************************
* CRMInstanceFile::Close
* Closes an open instance file
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
void CRMInstanceFile::Close ( void )
{
// If not open then dont close it
if ( NULL == mInstances )
{
return;
}
mParser.Clean();
mInstances = NULL;
}
/************************************************************************************************
* CRMInstanceFile::CreateInstance
* Creates an instance (to be freed by caller) using the given instance name.
*
* inputs:
* name: Name of the instance to read from the instance file
*
* return:
* NULL: instance could not be read from the instance file
* NON-NULL: instance created and returned for further use
*
************************************************************************************************/
CRMInstance* CRMInstanceFile::CreateInstance ( const char* name )
{
static int instanceID = 0;
CGPGroup* group;
CRMInstance* instance;
// Make sure we were loaded
assert ( mInstances );
// Search through the instances for the one with the given name
for ( group = mInstances; group; group = group->GetNext ( ) )
{
// Skip it if the name doesnt match
if ( stricmp ( name, group->FindPairValue ( "name", "" ) ) )
{
continue;
}
// Handle the various forms of instance types
if ( !stricmp ( group->GetName ( ), "bsp" ) )
{
instance = new CRMBSPInstance ( group, *this );
}
else if ( !stricmp ( group->GetName ( ), "npc" ) )
{
// instance = new CRMNPCInstance ( group, *this );
continue;
}
else if ( !stricmp ( group->GetName ( ), "group" ) )
{
instance = new CRMGroupInstance ( group, *this );
}
else if ( !stricmp ( group->GetName ( ), "random" ) )
{
instance = new CRMRandomInstance ( group, *this );
}
else if ( !stricmp ( group->GetName ( ), "void" ) )
{
instance = new CRMVoidInstance ( group, *this );
}
else
{
continue;
}
// If the instance isnt valid after being created then delete it
if ( !instance->IsValid ( ) )
{
delete instance;
return NULL;
}
// The instance was successfully created so return it
return instance;
}
#ifndef FINAL_BUILD
// The instance wasnt found in the file so report it
Com_Printf(va("WARNING: Instance '%s' was not found in the active instance file\n", name ));
#endif
return NULL;
}

View File

@@ -0,0 +1,28 @@
#pragma once
#if !defined(RM_INSTANCEFILE_H_INC)
#define RM_INSTANCEFILE_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including RM_InstanceFile.h")
#endif
class CRMInstance;
class CRMInstanceFile
{
public:
CRMInstanceFile ( );
~CRMInstanceFile ( );
bool Open ( const char* instance );
void Close ( void );
CRMInstance* CreateInstance ( const char* name );
protected:
CGenericParser2 mParser;
CGPGroup* mInstances;
};
#endif

View File

@@ -0,0 +1,294 @@
/************************************************************************************************
*
* RM_Instance_BSP.cpp
*
* Implements the CRMBSPInstance class. This class is reponsible for parsing a
* bsp instance as well as spawning it into a landscape.
*
************************************************************************************************/
#include "../server/exe_headers.h"
#include "../qcommon/cm_local.h"
#include "../server/server.h"
#include "rm_headers.h"
#include "rm_instance_bsp.h"
#include "../client/vmachine.h"
/************************************************************************************************
* CRMBSPInstance::CRMBSPInstance
* constructs a building instance object using the given parser group
*
* inputs:
* instance: parser group containing information about the building instance
*
* return:
* none
*
************************************************************************************************/
CRMBSPInstance::CRMBSPInstance(CGPGroup *instGroup, CRMInstanceFile& instFile) : CRMInstance ( instGroup, instFile )
{
strcpy(mBsp, instGroup->FindPairValue("file", ""));
mAngleVariance = DEG2RAD(atof(instGroup->FindPairValue("anglevariance", "0")));
mBaseAngle = DEG2RAD(atof(instGroup->FindPairValue("baseangle", "0")));
mAngleDiff = DEG2RAD(atof(instGroup->FindPairValue("anglediff", "0")));
mSpacingRadius = atof( instGroup->FindPairValue ( "spacing", "100" ) );
mSpacingLine = atoi( instGroup->FindPairValue ( "spacingline", "0" ) );
mSurfaceSprites = (!Q_stricmp ( instGroup->FindPairValue ( "surfacesprites", "no" ), "yes")) ? true : false;
mLockOrigin = (!Q_stricmp ( instGroup->FindPairValue ( "lockorigin", "no" ), "yes")) ? true : false;
mFlattenRadius = atof( instGroup->FindPairValue ( "flatten", "0" ) );
mHoleRadius = atof( instGroup->FindPairValue ( "hole", "0" ) );
const char * automapSymName = instGroup->FindPairValue ( "automap_symbol", "building" );
if (0 == strcmpi(automapSymName, "none")) mAutomapSymbol = AUTOMAP_NONE ;
else if (0 == strcmpi(automapSymName, "building")) mAutomapSymbol = AUTOMAP_BLD ;
else if (0 == strcmpi(automapSymName, "objective")) mAutomapSymbol = AUTOMAP_OBJ ;
else if (0 == strcmpi(automapSymName, "start")) mAutomapSymbol = AUTOMAP_START;
else if (0 == strcmpi(automapSymName, "end")) mAutomapSymbol = AUTOMAP_END ;
else if (0 == strcmpi(automapSymName, "enemy")) mAutomapSymbol = AUTOMAP_ENEMY;
else if (0 == strcmpi(automapSymName, "friend")) mAutomapSymbol = AUTOMAP_FRIEND;
else if (0 == strcmpi(automapSymName, "wall")) mAutomapSymbol = AUTOMAP_WALL;
else mAutomapSymbol = atoi( automapSymName );
// optional instance objective strings
SetMessage(instGroup->FindPairValue("objective_message",""));
SetDescription(instGroup->FindPairValue("objective_description",""));
SetInfo(instGroup->FindPairValue("objective_info",""));
mBounds[0][0] = 0;
mBounds[0][1] = 0;
mBounds[1][0] = 0;
mBounds[1][1] = 0;
mBaseAngle += (TheRandomMissionManager->GetLandScape()->irand(0,mAngleVariance) - mAngleVariance/2);
}
/************************************************************************************************
* CRMBSPInstance::Spawn
* spawns a bsp into the world using the previously aquired origin
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
bool CRMBSPInstance::Spawn ( CRandomTerrain* terrain, qboolean IsServer)
{
#ifndef PRE_RELEASE_DEMO
// TEntity* ent;
float yaw;
char temp[10000];
char *savePtr;
vec3_t origin;
vec3_t notmirrored;
float water_level = terrain->GetLandScape()->GetWaterHeight();
const vec3_t& terxelSize = terrain->GetLandScape()->GetTerxelSize ( );
const vec3pair_t& bounds = terrain->GetLandScape()->GetBounds();
// If this entity somehow lost its collision flag then boot it
if ( !GetArea().IsCollisionEnabled ( ) )
{
return false;
}
// copy out the unmirrored version
VectorCopy(GetOrigin(), notmirrored);
// we want to mirror it before determining the Z value just in case the landscape isn't perfectly mirrored
if (mMirror)
{
GetOrigin()[0] = TheRandomMissionManager->GetLandScape()->GetBounds()[0][0] + TheRandomMissionManager->GetLandScape()->GetBounds()[1][0] - GetOrigin()[0];
GetOrigin()[1] = TheRandomMissionManager->GetLandScape()->GetBounds()[0][1] + TheRandomMissionManager->GetLandScape()->GetBounds()[1][1] - GetOrigin()[1];
}
// Align the instance to the center of a terxel
GetOrigin ( )[0] = bounds[0][0] + (int)((GetOrigin ( )[0] - bounds[0][0] + terxelSize[0] / 2) / terxelSize[0]) * terxelSize[0];
GetOrigin ( )[1] = bounds[0][1] + (int)((GetOrigin ( )[1] - bounds[0][1] + terxelSize[1] / 2) / terxelSize[1]) * terxelSize[1];
// Make sure the bsp is resting on the ground, not below or above it
// NOTE: This check is basically saying "is this instance not a bridge", because when instances are created they are all
// placed above the world's Z boundary, EXCEPT FOR BRIDGES. So this call to GetWorldHeight will move all other instances down to
// ground level except bridges
if ( GetOrigin()[2] > terrain->GetBounds()[1][2] )
{
if( GetFlattenRadius() )
{
terrain->GetLandScape()->GetWorldHeight ( GetOrigin(), GetBounds ( ), false );
GetOrigin()[2] += 5;
}
else if (IsServer)
{ // if this instance does not flatten the ground around it, do a trace to more accurately determine its Z value
trace_t tr;
vec3_t end;
vec3_t start;
VectorCopy(GetOrigin(), end);
VectorCopy(GetOrigin(), start);
// start the trace below the top height of the landscape
start[2] = TheRandomMissionManager->GetLandScape()->GetBounds()[1][2] - 1;
// end the trace at the bottom of the world
end[2] = MIN_WORLD_COORD;
memset ( &tr, 0, sizeof ( tr ) );
SV_Trace( &tr, start, vec3_origin, vec3_origin, end, ENTITYNUM_NONE, CONTENTS_TERRAIN|CONTENTS_SOLID, G2_NOCOLLIDE, 0); //qfalse, 0, 10 );
if( !(tr.contents & CONTENTS_TERRAIN) || (tr.fraction == 1.0) )
{
if ( 0 )
assert(0); // this should never happen
// restore the unmirrored origin
VectorCopy( notmirrored, GetOrigin() );
// don't spawn
return false;
}
// assign the Z-value to wherever it hit the terrain
GetOrigin()[2] = tr.endpos[2];
// lower it a little, otherwise the bottom of the instance might be exposed if on some weird sloped terrain
GetOrigin()[2] -= 16; // FIXME: would it be better to use a number related to the instance itself like 1/5 it's height or something...
}
}
else
{
terrain->GetLandScape()->GetWorldHeight ( GetOrigin(), GetBounds ( ), true );
}
// save away the origin
VectorCopy(GetOrigin(), origin);
// make sure not to spawn if in water
if (!HasObjective() && GetOrigin()[2] < water_level)
return false;
// restore the origin
VectorCopy(origin, GetOrigin());
if (mMirror)
{ // change blue things to red for symmetric maps
if (strlen(mFilter) > 0)
{
char * blue = strstr(mFilter,"blue");
if (blue)
{
blue[0] = (char) 0;
strcat(mFilter, "red");
SetSide(SIDE_RED);
}
}
if (strlen(mTeamFilter) > 0)
{
char * blue = strstr(mTeamFilter,"blue");
if (blue)
{
strcpy(mTeamFilter, "red");
SetSide(SIDE_RED);
}
}
yaw = RAD2DEG(mArea->GetAngle() + mBaseAngle) + 180;
}
else
{
yaw = RAD2DEG(mArea->GetAngle() + mBaseAngle);
}
/*
if( TheRandomMissionManager->GetMission()->GetSymmetric() )
{
vec3_t diagonal;
vec3_t lineToPoint;
vec3_t mins;
vec3_t maxs;
vec3_t point;
vec3_t vProj;
vec3_t vec;
float distance;
VectorCopy( TheRandomMissionManager->GetLandScape()->GetBounds()[1], maxs );
VectorCopy( TheRandomMissionManager->GetLandScape()->GetBounds()[0], mins );
VectorCopy( GetOrigin(), point );
mins[2] = maxs[2] = point[2] = 0;
VectorSubtract( point, mins, lineToPoint );
VectorSubtract( maxs, mins, diagonal);
VectorNormalize(diagonal);
VectorMA( mins, DotProduct(lineToPoint, diagonal), diagonal, vProj);
VectorSubtract(point, vProj, vec );
distance = VectorLength(vec);
// if an instance is too close to the imaginary diagonal that cuts the world in half, don't spawn it
// otherwise you can get overlapping instances
if( distance < GetSpacingRadius() )
{
#ifdef _DEBUG
mAutomapSymbol = AUTOMAP_END;
#endif
if( !HasObjective() )
{
return false;
}
}
}
*/
// Spawn in the bsp model
sprintf(temp,
"{\n"
"\"classname\" \"misc_bsp\"\n"
"\"bspmodel\" \"%s\"\n"
"\"origin\" \"%f %f %f\"\n"
"\"angles\" \"0 %f 0\"\n"
"\"filter\" \"%s\"\n"
"\"teamfilter\" \"%s\"\n"
"\"spacing\" \"%d\"\n"
"\"flatten\" \"%d\"\n"
"}\n",
mBsp,
GetOrigin()[0], GetOrigin()[1], GetOrigin()[2],
AngleNormalize360(yaw),
mFilter,
mTeamFilter,
(int)GetSpacingRadius(),
(int)GetFlattenRadius()
);
if (IsServer)
{ // only allow for true spawning on the server
savePtr = sv.entityParsePoint;
sv.entityParsePoint = temp;
// VM_Call( cgvm, GAME_SPAWN_RMG_ENTITY );
// char *s;
int bufferSize = 1024;
char buffer[1024];
// s = COM_Parse( (const char **)&sv.entityParsePoint );
Q_strncpyz( buffer, sv.entityParsePoint, bufferSize );
if ( sv.entityParsePoint && sv.entityParsePoint[0] )
{
ge->GameSpawnRMGEntity(sv.entityParsePoint);
}
sv.entityParsePoint = savePtr;
}
#ifndef DEDICATED
DrawAutomapSymbol();
#endif
Com_DPrintf( "RMG: Building '%s' spawned at (%f %f %f)\n", mBsp, GetOrigin()[0], GetOrigin()[1], GetOrigin()[2] );
// now restore the instances un-mirrored origin
// NOTE: all this origin flipping, setting the side etc... should be done when mMirror is set
// because right after this function is called, mMirror is set to 0 but all the instance data is STILL MIRRORED -- not good
VectorCopy(notmirrored, GetOrigin());
#endif // PRE_RELEASE_DEMO
return true;
}

View File

@@ -0,0 +1,35 @@
#pragma once
#if !defined(RM_INSTANCE_BSP_H_INC)
#define RM_INSTANCE_BSP_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including RM_Instance_BSP.h")
#endif
class CRMBSPInstance : public CRMInstance
{
private:
char mBsp[MAX_QPATH];
float mAngleVariance;
float mBaseAngle;
float mAngleDiff;
float mHoleRadius;
public:
CRMBSPInstance ( CGPGroup *instance, CRMInstanceFile& instFile );
virtual int GetPreviewColor ( ) { return (255<<24)+255; }
virtual float GetHoleRadius ( ) { return mHoleRadius; }
virtual bool Spawn ( CRandomTerrain* terrain, qboolean IsServer );
const char* GetModelName (void) const { return(mBsp); }
float GetAngleDiff (void) const { return(mAngleDiff); }
bool GetAngularType (void) const { return(mAngleDiff != 0.0f); }
};
#endif

View File

@@ -0,0 +1,343 @@
/************************************************************************************************
*
* RM_Instance_Group.cpp
*
* Implements the CRMGroupInstance class. This class is reponsible for parsing a
* group instance as well as spawning it into a landscape.
*
************************************************************************************************/
#include "../server/exe_headers.h"
#include "rm_headers.h"
#include "rm_instance_group.h"
/************************************************************************************************
* CRMGroupInstance::CRMGroupInstance
* constructur
*
* inputs:
* settlementID: ID of the settlement being created
*
* return:
* none
*
************************************************************************************************/
CRMGroupInstance::CRMGroupInstance ( CGPGroup *instGroup, CRMInstanceFile& instFile )
: CRMInstance ( instGroup, instFile )
{
// Grab the padding and confine radius
mPaddingSize = atof ( instGroup->FindPairValue ( "padding", va("%i", TheRandomMissionManager->GetMission()->GetDefaultPadding() ) ) );
mConfineRadius = atof ( instGroup->FindPairValue ( "confine", "0" ) );
const char * automapSymName = instGroup->FindPairValue ( "automap_symbol", "none" );
if (0 == strcmpi(automapSymName, "none")) mAutomapSymbol = AUTOMAP_NONE ;
else if (0 == strcmpi(automapSymName, "building")) mAutomapSymbol = AUTOMAP_BLD ;
else if (0 == strcmpi(automapSymName, "objective")) mAutomapSymbol = AUTOMAP_OBJ ;
else if (0 == strcmpi(automapSymName, "start")) mAutomapSymbol = AUTOMAP_START;
else if (0 == strcmpi(automapSymName, "end")) mAutomapSymbol = AUTOMAP_END ;
else if (0 == strcmpi(automapSymName, "enemy")) mAutomapSymbol = AUTOMAP_ENEMY;
else if (0 == strcmpi(automapSymName, "friend")) mAutomapSymbol = AUTOMAP_FRIEND;
else mAutomapSymbol = atoi( automapSymName );
// optional instance objective strings
SetMessage(instGroup->FindPairValue("objective_message",""));
SetDescription(instGroup->FindPairValue("objective_description",""));
SetInfo(instGroup->FindPairValue("objective_info",""));
// Iterate through the sub groups to determine the instances which make up the group
instGroup = instGroup->GetSubGroups ( );
while ( instGroup )
{
CRMInstance* instance;
const char* name;
int mincount;
int maxcount;
int count;
float minrange;
float maxrange;
// Make sure only instances are specified as sub groups
assert ( 0 == stricmp ( instGroup->GetName ( ), "instance" ) );
// Grab the name
name = instGroup->FindPairValue ( "name", "" );
// Grab the range information
minrange = atof(instGroup->FindPairValue ( "minrange", "0" ) );
maxrange = atof(instGroup->FindPairValue ( "maxrange", "0" ) );
// Grab the count information and randomly generate a count value
mincount = atoi(instGroup->FindPairValue ( "mincount", "1" ) );
maxcount = atoi(instGroup->FindPairValue ( "maxcount", "1" ) );
count = mincount;
if ( maxcount > mincount )
{
count += (TheRandomMissionManager->GetLandScape()->irand(0, maxcount-mincount));
}
// For each count create and add the instance
for ( ; count ; count -- )
{
// Create the instance
instance = instFile.CreateInstance ( name );
// Skip this instance if it couldnt be created for some reason. The CreateInstance
// method will report an error so no need to do so here.
if ( NULL == instance )
{
continue;
}
// Set the min and max range for the instance
instance->SetFilter(mFilter);
instance->SetTeamFilter(mTeamFilter);
// Add the instance to the list
mInstances.push_back ( instance );
}
// Next sub group
instGroup = instGroup->GetNext ( );
}
}
/************************************************************************************************
* CRMGroupInstance::~CRMGroupInstance
* Removes all buildings and inhabitants
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
CRMGroupInstance::~CRMGroupInstance(void)
{
// Cleanup
RemoveInstances ( );
}
/************************************************************************************************
* CRMGroupInstance::SetFilter
* Sets a filter used to exclude instances
*
* inputs:
* filter: filter name
*
* return:
* none
*
************************************************************************************************/
void CRMGroupInstance::SetFilter( const char *filter )
{
rmInstanceIter_t it;
CRMInstance::SetFilter(filter);
for(it = mInstances.begin(); it != mInstances.end(); it++)
{
(*it)->SetFilter(filter);
}
}
/************************************************************************************************
* CRMGroupInstance::SetTeamFilter
* Sets the filter used to exclude team based instances
*
* inputs:
* teamFilter: filter name
*
* return:
* none
*
************************************************************************************************/
void CRMGroupInstance::SetTeamFilter( const char *teamFilter )
{
rmInstanceIter_t it;
CRMInstance::SetTeamFilter(teamFilter);
for(it = mInstances.begin(); it != mInstances.end(); it++)
{
(*it)->SetTeamFilter(teamFilter);
}
}
/************************************************************************************************
* CRMGroupInstance::SetMirror
* Sets the flag to mirror an instance on map
*
* inputs:
* mirror
*
* return:
* none
*
************************************************************************************************/
void CRMGroupInstance::SetMirror(int mirror)
{
rmInstanceIter_t it;
CRMInstance::SetMirror(mirror);
for(it = mInstances.begin(); it != mInstances.end(); it++)
{
(*it)->SetMirror(mirror);
}
}
/************************************************************************************************
* CRMGroupInstance::RemoveInstances
* Removes all instances associated with the group
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
void CRMGroupInstance::RemoveInstances ( )
{
rmInstanceIter_t it;
for(it = mInstances.begin(); it != mInstances.end(); it++)
{
delete *it;
}
mInstances.clear();
}
/************************************************************************************************
* CRMGroupInstance::PreSpawn
* Prepares the group for spawning by
*
* inputs:
* landscape: landscape to calculate the position within
* instance: instance to calculate the position for
*
* return:
* none
*
************************************************************************************************/
bool CRMGroupInstance::PreSpawn ( CRandomTerrain* terrain, qboolean IsServer )
{
rmInstanceIter_t it;
for(it = mInstances.begin(); it != mInstances.end(); it++ )
{
CRMInstance* instance = *it;
instance->SetFlattenHeight ( mFlattenHeight );
// Add the instance to the landscape now
instance->PreSpawn ( terrain, IsServer );
}
return CRMInstance::PreSpawn ( terrain, IsServer );
}
/************************************************************************************************
* CRMGroupInstance::Spawn
* Adds the group instance to the given landscape using the specified origin. All sub instances
* will be added to the landscape within their min and max range from the origin.
*
* inputs:
* landscape: landscape to add the instance group to
* origin: origin of the instance group
*
* return:
* none
*
************************************************************************************************/
bool CRMGroupInstance::Spawn ( CRandomTerrain* terrain, qboolean IsServer )
{
rmInstanceIter_t it;
// Spawn all the instances associated with this group
for(it = mInstances.begin(); it != mInstances.end(); it++)
{
CRMInstance* instance = *it;
instance->SetSide(GetSide()); // which side owns it?
// Add the instance to the landscape now
instance->Spawn ( terrain, IsServer );
}
#ifndef DEDICATED
DrawAutomapSymbol();
#endif
return true;
}
/************************************************************************************************
* CRMGroupInstance::Preview
* Renders debug information for the instance
*
* inputs:
* from: point to render the preview from
*
* return:
* none
*
************************************************************************************************/
void CRMGroupInstance::Preview ( const vec3_t from )
{
rmInstanceIter_t it;
CRMInstance::Preview ( from );
// Render all the instances
for(it = mInstances.begin(); it != mInstances.end(); it++)
{
CRMInstance* instance = *it;
instance->Preview ( from );
}
}
/************************************************************************************************
* CRMGroupInstance::SetArea
* Overidden to make sure the groups area doesnt eat up any room. The collision on the
* groups area will be turned off
*
* inputs:
* area: area to set
*
* return:
* none
*
************************************************************************************************/
void CRMGroupInstance::SetArea ( CRMAreaManager* amanager, CRMArea* area )
{
rmInstanceIter_t it;
bool collide = area->IsCollisionEnabled ( );
// Disable collision
area->EnableCollision ( false );
// Do what really needs to get done
CRMInstance::SetArea ( amanager, area );
// Prepare for spawn by calculating all the positions of the sub instances
// and flattening the ground below them.
for(it = mInstances.begin(); it != mInstances.end(); it++ )
{
CRMInstance *instance = *it;
CRMArea *newarea;
vec3_t origin;
// Drop it in the center of the group for now
origin[0] = GetOrigin()[0];
origin[1] = GetOrigin()[1];
origin[2] = 2500;
// Set the area of position
newarea = amanager->CreateArea ( origin, instance->GetSpacingRadius(), instance->GetSpacingLine(), mPaddingSize, mConfineRadius, GetOrigin(), GetOrigin(), instance->GetFlattenRadius()?true:false, collide, instance->GetLockOrigin(), area->GetSymmetric ( ) );
instance->SetArea ( amanager, newarea );
}
}

View File

@@ -0,0 +1,41 @@
#pragma once
#if !defined(RM_INSTANCE_GROUP_H_INC)
#define RM_INSTANCE_GROUP_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including RM_Instance_Group.h")
#endif
class CRMGroupInstance : public CRMInstance
{
protected:
rmInstanceList_t mInstances;
float mConfineRadius;
float mPaddingSize;
public:
CRMGroupInstance( CGPGroup* instGroup, CRMInstanceFile& instFile);
~CRMGroupInstance();
virtual bool PreSpawn ( CRandomTerrain* terrain, qboolean IsServer );
virtual bool Spawn ( CRandomTerrain* terrain, qboolean IsServer );
virtual void Preview ( const vec3_t from );
virtual void SetFilter ( const char *filter );
virtual void SetTeamFilter ( const char *teamFilter );
virtual void SetArea ( CRMAreaManager* amanager, CRMArea* area );
virtual int GetPreviewColor ( ) { return (255<<24)+(255<<8); }
virtual float GetSpacingRadius ( ) { return 0; }
virtual float GetFlattenRadius ( ) { return 0; }
virtual void SetMirror(int mirror);
protected:
void RemoveInstances ( );
};
#endif

View File

@@ -0,0 +1,187 @@
/************************************************************************************************
*
* RM_Instance_Random.cpp
*
* Implements the CRMRandomInstance class. This class is reponsible for parsing a
* random instance as well as spawning it into a landscape.
*
************************************************************************************************/
#include "../server/exe_headers.h"
#include "rm_headers.h"
#include "rm_instance_random.h"
/************************************************************************************************
* CRMRandomInstance::CRMRandomInstance
* constructs a random instance by choosing one of the sub instances and creating it
*
* inputs:
* instGroup: parser group containing infromation about this instance
* instFile: reference to an open instance file for creating sub instances
*
* return:
* none
*
************************************************************************************************/
CRMRandomInstance::CRMRandomInstance ( CGPGroup *instGroup, CRMInstanceFile& instFile )
: CRMInstance ( instGroup, instFile )
{
CGPGroup* group;
CGPGroup* groups[MAX_RANDOM_INSTANCES];
int numGroups;
// Build a list of the groups one can be chosen
for ( numGroups = 0, group = instGroup->GetSubGroups ( );
group;
group = group->GetNext ( ) )
{
// If this isnt an instance group then skip it
if ( stricmp ( group->GetName ( ), "instance" ) )
{
continue;
}
int multiplier = atoi(group->FindPairValue ( "multiplier", "1" ));
for ( ; multiplier > 0 && numGroups < MAX_RANDOM_INSTANCES; multiplier -- )
{
groups[numGroups++] = group;
}
}
// No groups, no instance
if ( !numGroups )
{
// Initialize this now
mInstance = NULL;
Com_Printf ( "WARNING: No sub instances specified for random instance '%s'\n", group->FindPairValue ( "name", "unknown" ) );
return;
}
// Now choose a group to parse
instGroup = groups[TheRandomMissionManager->GetLandScape()->irand(0,numGroups-1)];
// Create the child instance now. If the instance create fails then the
// IsValid routine will return false and this instance wont be added
mInstance = instFile.CreateInstance ( instGroup->FindPairValue ( "name", "" ) );
mInstance->SetFilter(mFilter);
mInstance->SetTeamFilter(mTeamFilter);
mAutomapSymbol = mInstance->GetAutomapSymbol();
SetMessage(mInstance->GetMessage());
SetDescription(mInstance->GetDescription());
SetInfo(mInstance->GetInfo());
}
/************************************************************************************************
* CRMRandomInstance::~CRMRandomInstance
* Deletes the sub instance
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
CRMRandomInstance::~CRMRandomInstance(void)
{
if ( mInstance )
{
delete mInstance;
}
}
void CRMRandomInstance::SetMirror(int mirror)
{
CRMInstance::SetMirror(mirror);
if (mInstance)
{
mInstance->SetMirror(mirror);
}
}
void CRMRandomInstance::SetFilter( const char *filter )
{
CRMInstance::SetFilter(filter);
if (mInstance)
{
mInstance->SetFilter(filter);
}
}
void CRMRandomInstance::SetTeamFilter( const char *teamFilter )
{
CRMInstance::SetTeamFilter(teamFilter);
if (mInstance)
{
mInstance->SetTeamFilter(teamFilter);
}
}
/************************************************************************************************
* CRMRandomInstance::PreSpawn
* Prepares for the spawn of the random instance
*
* inputs:
* landscape: landscape object this instance will be spawned on
*
* return:
* true: preparation successful
* false: preparation failed
*
************************************************************************************************/
bool CRMRandomInstance::PreSpawn ( CRandomTerrain* terrain, qboolean IsServer )
{
assert ( mInstance );
mInstance->SetFlattenHeight ( GetFlattenHeight( ) );
return mInstance->PreSpawn ( terrain, IsServer );
}
/************************************************************************************************
* CRMRandomInstance::Spawn
* Spawns the instance onto the landscape
*
* inputs:
* landscape: landscape object this instance will be spawned on
*
* return:
* true: spawn successful
* false: spawn failed
*
************************************************************************************************/
bool CRMRandomInstance::Spawn ( CRandomTerrain* terrain, qboolean IsServer )
{
mInstance->SetObjective(GetObjective());
mInstance->SetSide(GetSide());
if ( !mInstance->Spawn ( terrain, IsServer ) )
{
return false;
}
return true;
}
/************************************************************************************************
* CRMRandomInstance::SetArea
* Forwards the given area off to the internal instance
*
* inputs:
* area: area to be set
*
* return:
* none
*
************************************************************************************************/
void CRMRandomInstance::SetArea ( CRMAreaManager* amanager, CRMArea* area )
{
CRMInstance::SetArea ( amanager, area );
mInstance->SetArea ( amanager, mArea );
}

View File

@@ -0,0 +1,40 @@
#pragma once
#if !defined(RM_INSTANCE_RANDOM_H_INC)
#define RM_INSTANCE_RANDOM_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including RM_Instance_Random.h")
#endif
#define MAX_RANDOM_INSTANCES 64
class CRMRandomInstance : public CRMInstance
{
protected:
CRMInstance* mInstance;
public:
CRMRandomInstance ( CGPGroup* instGroup, CRMInstanceFile& instFile );
~CRMRandomInstance ( );
virtual bool IsValid ( ) { return mInstance==NULL?false:true; }
virtual int GetPreviewColor ( ) { return mInstance->GetPreviewColor ( ); }
virtual float GetSpacingRadius ( ) { return mInstance->GetSpacingRadius ( ); }
virtual int GetSpacingLine ( ) { return mInstance->GetSpacingLine ( ); }
virtual float GetFlattenRadius ( ) { return mInstance->GetFlattenRadius ( ); }
virtual bool GetLockOrigin ( ) { return mInstance->GetLockOrigin ( ); }
virtual void SetFilter ( const char *filter );
virtual void SetTeamFilter ( const char *teamFilter );
virtual void SetArea ( CRMAreaManager* amanager, CRMArea* area );
virtual void SetMirror (int mirror);
virtual bool PreSpawn ( CRandomTerrain* terrain, qboolean IsServer );
virtual bool Spawn ( CRandomTerrain* terrain, qboolean IsServer );
};
#endif

View File

@@ -0,0 +1,53 @@
/************************************************************************************************
*
* RM_Instance_Void.cpp
*
* Implements the CRMVoidInstance class. This class just adds a void into the
* area manager to help space things out.
*
************************************************************************************************/
#include "../server/exe_headers.h"
#include "rm_headers.h"
#include "rm_instance_void.h"
/************************************************************************************************
* CRMVoidInstance::CRMVoidInstance
* constructs a void instance
*
* inputs:
* instGroup: parser group containing infromation about this instance
* instFile: reference to an open instance file for creating sub instances
*
* return:
* none
*
************************************************************************************************/
CRMVoidInstance::CRMVoidInstance ( CGPGroup *instGroup, CRMInstanceFile& instFile )
: CRMInstance ( instGroup, instFile )
{
mSpacingRadius = atof( instGroup->FindPairValue ( "spacing", "0" ) );
mFlattenRadius = atof( instGroup->FindPairValue ( "flatten", "0" ) );
}
/************************************************************************************************
* CRMVoidInstance::SetArea
* Overidden to make sure the void area doesnt continually.
*
* inputs:
* area: area to set
*
* return:
* none
*
************************************************************************************************/
void CRMVoidInstance::SetArea ( CRMAreaManager* amanager, CRMArea* area )
{
// Disable collision
area->EnableCollision ( false );
// Do what really needs to get done
CRMInstance::SetArea ( amanager, area );
}

View File

@@ -0,0 +1,18 @@
#pragma once
#if !defined(RM_INSTANCE_VOID_H_INC)
#define RM_INSTANCE_VOID_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including RM_Instance_Void.h")
#endif
class CRMVoidInstance : public CRMInstance
{
public:
CRMVoidInstance ( CGPGroup* instGroup, CRMInstanceFile& instFile );
virtual void SetArea ( CRMAreaManager* amanager, CRMArea* area );
};
#endif

402
code/RMG/RM_Manager.cpp Normal file
View File

@@ -0,0 +1,402 @@
/************************************************************************************************
*
* RM_Manager.cpp
*
* Implements the CRMManager class. The CRMManager class manages the arioche system.
*
************************************************************************************************/
#include "../server/exe_headers.h"
#include "rm_headers.h"
#include "../server/server.h"
CRMObjective *CRMManager::mCurObjective=0;
/************************************************************************************************
* TheRandomMissionManager
* Pointer to only active CRMManager class
*
************************************************************************************************/
CRMManager *TheRandomMissionManager;
/************************************************************************************************
* CRMManager::CRMManager
* constructor
*
* inputs:
*
* return:
*
************************************************************************************************/
CRMManager::CRMManager(void)
{
mLandScape = NULL;
mTerrain = NULL;
mMission = NULL;
mCurPriority = 1;
mUseTimeLimit = false;
}
/************************************************************************************************
* CRMManager::~CRMManager
* destructor
*
* inputs:
*
* return:
*
************************************************************************************************/
CRMManager::~CRMManager(void)
{
#ifndef FINAL_BUILD
Com_Printf ("... Shutting down TheRandomMissionManager\n");
#endif
#ifndef DEDICATED
CM_TM_Free();
#endif
if (mMission)
{
delete mMission;
mMission = NULL;
}
}
/************************************************************************************************
* CRMManager::SetLandscape
* Sets the landscape and terrain object used to load a mission
*
* inputs:
* landscape - landscape object
*
* return:
* none
*
************************************************************************************************/
void CRMManager::SetLandScape(CCMLandScape *landscape)
{
mLandScape = landscape;
mTerrain = landscape->GetRandomTerrain();
}
/************************************************************************************************
* CRMManager::LoadMission
* Loads the mission using the mission name stored in the ar_mission cvar
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
bool CRMManager::LoadMission ( qboolean IsServer )
{
#ifndef PRE_RELEASE_DEMO
char instances[MAX_QPATH];
char mission[MAX_QPATH];
char course[MAX_QPATH];
char map[MAX_QPATH];
char temp[MAX_QPATH];
#ifndef FINAL_BUILD
Com_Printf ("--------- Random Mission Manager ---------\n\n");
Com_Printf ("RMG version : 0.01\n\n");
#endif
if (!mTerrain)
{
return false;
}
// Grab the arioche variables
Cvar_VariableStringBuffer("rmg_usetimelimit", temp, MAX_QPATH);
if (strcmpi(temp, "yes") == 0)
{
mUseTimeLimit = true;
}
Cvar_VariableStringBuffer("rmg_instances", instances, MAX_QPATH);
Cvar_VariableStringBuffer("RMG_mission", temp, MAX_QPATH);
Cvar_VariableStringBuffer("rmg_map", map, MAX_QPATH);
sprintf(mission, "%s_%s", temp, map);
Cvar_VariableStringBuffer("rmg_course", course, MAX_QPATH);
// dump existing mission, if any
if (mMission)
{
delete mMission;
mMission = NULL;
}
// Create a new mission file
mMission = new CRMMission ( mTerrain );
// Load the mission using the arioche variables
if ( !mMission->Load ( mission, instances, course ) )
{
return false;
}
if (mUseTimeLimit)
{
Cvar_Set("rmg_timelimit", va("%d", mMission->GetTimeLimit()));
}
else
{
Cvar_Set("rmg_timelimit", "0");
}
if (IsServer)
{ // set the names of the teams
CGenericParser2 parser;
//CGPGroup* root;
Cvar_VariableStringBuffer("RMG_terrain", temp, MAX_QPATH);
/*
// Create the parser for the mission file
if(Com_ParseTextFile(va("ext_data/rmg/%s.teams", temp), parser))
{
root = parser.GetBaseParseGroup()->GetSubGroups();
if (0 == stricmp(root->GetName(), "teams"))
{
SV_SetConfigstring( CS_GAMETYPE_REDTEAM, root->FindPairValue ( "red", "marine" ));
SV_SetConfigstring( CS_GAMETYPE_BLUETEAM, root->FindPairValue ( "blue", "thug" ));
}
parser.Clean();
}
*/
//rww - This is single player, no such thing.
}
// Must have a valid landscape before we can spawn the mission
assert ( mLandScape );
#ifndef FINAL_BUILD
Com_Printf ("------------------------------------------\n");
#endif
return true;
#else
return false;
#endif // PRE_RELEASE_DEMO
}
/************************************************************************************************
* CRMManager::IsMissionComplete
* Determines whether or not all the arioche objectives have been met
*
* inputs:
* none
*
* return:
* true: all objectives have been completed
* false: one or more of the objectives has not been met
*
************************************************************************************************/
bool CRMManager::IsMissionComplete(void)
{
if ( NULL == mMission->GetCurrentObjective ( ) )
{
return true;
}
return false;
}
/************************************************************************************************
* CRMManager::HasTimeExpired
* Determines whether or not the time limit (if one) has expired
*
* inputs:
* none
*
* return:
* true: time limit has expired
* false: time limit has not expired
*
************************************************************************************************/
bool CRMManager::HasTimeExpired(void)
{
/* if (mMission->GetTimeLimit() == 0 || !mUseTimeLimit)
{ // no time limit set
return false;
}
if (mMission->GetTimeLimit() * 1000 * 60 > level.time - level.startTime)
{ // we are still under our time limit
return false;
}
// over our time limit!
return true;*/
return false;
}
/************************************************************************************************
* CRMManager::UpdateStatisticCvars
* Updates the statistic cvars with data from the game
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
void CRMManager::UpdateStatisticCvars ( void )
{
/* // No player set then nothing more to do
if ( mPlayer )
{
float accuracy;
// Calculate the accuracy
accuracy = (float)mPlayer->client->ps.persistant[PERS_SHOTS_HIT];
accuracy /= (float)mPlayer->client->ps.persistant[PERS_SHOTS];
accuracy *= 100.0f;
// set the accuracy cvar
gi.Cvar_Set ( "ar_pl_accuracy", va("%d%%",(int)accuracy) );
// Set the # of kills cvar
gi.Cvar_Set ( "ar_kills", va("%d", mPlayer->client->ps.persistant[PERS_SCORE] ) );
int hours;
int mins;
int seconds;
int tens;
int millisec = (level.time - level.startTime);
seconds = millisec / 1000;
hours = seconds / (60 * 60);
seconds -= (hours * 60 * 60);
mins = seconds / 60;
seconds -= mins * 60;
tens = seconds / 10;
seconds -= tens * 10;
gi.Cvar_Set ( "ar_duration", va("%dhr %dmin %dsec", hours, mins, seconds ) );
WpnID wpnID = TheWpnSysMgr().GetFavoriteWeapon ( );
gi.Cvar_Set ( "ar_fav_wp", CWeaponSystem::GetWpnName ( wpnID ) );
// show difficulty
char difficulty[MAX_QPATH];
gi.Cvar_VariableStringBuffer("g_skill", difficulty, MAX_QPATH);
strupr(difficulty);
gi.Cvar_Set ( "ar_diff", va("&GENERIC_%s&",difficulty) );
// compute rank
float compositeRank = 1;
int rankMax = 3; // max rank less 1
float timeRank = mUseTimeLimit ? (1.0f - (mins / (float)mMission->GetTimeLimit())) : 0;
float killRank = mPlayer->client->ps.persistant[PERS_SCORE] / (float)GetCharacterManager().GetAllSize();
killRank = (killRank > 0) ? killRank : 1.0f;
float accuRank = (accuracy > 0) ? accuracy*0.01f : 1.0f;
float weapRank = 1.0f - CWeaponSystem::GetRank(wpnID);
compositeRank = ((timeRank + killRank + accuRank + weapRank) / 3.0f) * rankMax + 1;
if (compositeRank > 4)
compositeRank = 4;
gi.Cvar_Set ( "ar_rank", va("&RMG_RANK%d&",((int)compositeRank)) );
}*/
}
/************************************************************************************************
* CRMManager::CompleteMission
* Does end-of-mission stuff (pause game, end screen, return to menu)
* <Description> *
* Input *
* <Variable>: <Description> *
* Output / Return *
* <Variable>: <Description> *
************************************************************************************************/
void CRMManager::CompleteMission(void)
{
UpdateStatisticCvars ( );
mMission->CompleteMission();
}
/************************************************************************************************
* CRMManager::FailedMission
* Does end-of-mission stuff (pause game, end screen, return to menu)
* <Description> *
* Input *
* TimeExpired: indicates if the reason failed was because of time
* Output / Return *
* <Variable>: <Description> *
************************************************************************************************/
void CRMManager::FailedMission(bool TimeExpired)
{
UpdateStatisticCvars ( );
mMission->FailedMission(TimeExpired);
}
/************************************************************************************************
* CRMManager::CompleteObjective
* Marks the given objective as completed
*
* inputs:
* obj: objective to set as completed
*
* return:
* none
*
************************************************************************************************/
void CRMManager::CompleteObjective ( CRMObjective *obj )
{
assert ( obj );
mMission->CompleteObjective ( obj );
}
/************************************************************************************************
* CRMManager::Preview
* previews the random mission genration information
*
* inputs:
* from: origin being previed from
*
* return:
* none
*
************************************************************************************************/
void CRMManager::Preview ( const vec3_t from )
{
// Dont bother if we havent reached our timer yet
/* if ( level.time < mPreviewTimer )
{
return;
}
// Let the mission do all the previewing
mMission->Preview ( from );
// Another second
mPreviewTimer = level.time + 1000;*/
}
/************************************************************************************************
* CRMManager::Preview
* previews the random mission genration information
*
* inputs:
* from: origin being previed from
*
* return:
* none
*
************************************************************************************************/
bool CRMManager::SpawnMission ( qboolean IsServer )
{
// Spawn the mission
mMission->Spawn ( mTerrain, IsServer );
return true;
}

55
code/RMG/RM_Manager.h Normal file
View File

@@ -0,0 +1,55 @@
#pragma once
#if !defined(RM_MANAGER_H_INC)
#define RM_MANAGER_H_INC
#if !defined(CM_LANDSCAPE_H_INC)
#include "../qcommon/cm_landscape.h"
#endif
class CRMManager
{
private:
CRMMission* mMission;
CCMLandScape* mLandScape;
CRandomTerrain* mTerrain;
int mPreviewTimer;
int mCurPriority;
bool mUseTimeLimit;
void UpdateStatisticCvars ( void );
public:
// Constructors
CRMManager (void);
~CRMManager (void);
bool LoadMission ( qboolean IsServer );
bool SpawnMission ( qboolean IsServer );
// Accessors
void SetLandScape (CCMLandScape *landscape);
void SetCurPriority (int priority) { mCurPriority = priority; }
CRandomTerrain* GetTerrain (void) { return mTerrain; }
CCMLandScape* GetLandScape (void) { return mLandScape; }
CRMMission* GetMission (void) { return mMission; }
int GetCurPriority (void) { return mCurPriority; }
void Preview ( const vec3_t from );
bool IsMissionComplete (void);
bool HasTimeExpired (void);
void CompleteObjective ( CRMObjective *obj );
void CompleteMission (void);
void FailedMission (bool TimeExpired);
// eek
static CRMObjective *mCurObjective;
};
extern CRMManager* TheRandomMissionManager;
#endif // RANDOMMISSION_H_INC

1930
code/RMG/RM_Mission.cpp Normal file

File diff suppressed because it is too large Load Diff

129
code/RMG/RM_Mission.h Normal file
View File

@@ -0,0 +1,129 @@
#pragma once
#if !defined(RM_MISSION_H_INC)
#define RM_MISSION_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including RM_Mission.h")
#endif
// maximum random choices
#define MAX_RANDOM_CHOICES 100
typedef vector<int> rmIntVector_t;
class CRMMission
{
private:
rmObjectiveList_t mObjectives;
rmInstanceList_t mInstances;
CRMInstanceFile mInstanceFile;
CRMObjective* mCurrentObjective;
bool mValidNodes;
bool mValidPaths;
bool mValidRivers;
bool mValidWeapons;
bool mValidAmmo;
bool mValidObjectives;
bool mValidInstances;
int mTimeLimit;
int mMaxInstancePosition;
// npc multipliers
float mAccuracyMultiplier;
float mHealthMultiplier;
// % chance that RMG pickup is actually spawned
float mPickupHealth;
float mPickupArmor;
float mPickupAmmo;
float mPickupWeapon;
float mPickupEquipment;
string mDescription;
string mExitScreen;
string mTimeExpiredScreen;
// symmetric landscape style
symmetry_t mSymmetric;
// if set to 1 in the mission file, adds an extra connecting path in symmetric maps
// to ensure both sides actually do connect
int mBackUpPath;
int mDefaultPadding;
CRMAreaManager* mAreaManager;
CRMPathManager* mPathManager;
CRandomTerrain* mLandScape;
public:
CRMMission ( CRandomTerrain* );
~CRMMission ( );
bool Load ( const char* name, const char* instances, const char* difficulty );
bool Spawn ( CRandomTerrain* terrain, qboolean IsServer );
void Preview ( const vec3_t from );
CRMObjective* FindObjective ( const char* name );
CRMObjective* GetCurrentObjective ( ) { return mCurrentObjective; }
void CompleteMission (void);
void FailedMission (bool TimeExpired);
void CompleteObjective ( CRMObjective* ojective );
int GetTimeLimit (void) { return mTimeLimit; }
int GetMaxInstancePosition (void) { return mMaxInstancePosition; }
const char* GetDescription (void) { return mDescription.c_str(); }
const char* GetExitScreen (void) { return mExitScreen.c_str(); }
int GetSymmetric (void) { return mSymmetric; }
int GetBackUpPath (void) { return mBackUpPath; }
int GetDefaultPadding (void) { return mDefaultPadding; }
// void CreateMap ( void );
bool DenyPickupHealth () {return mLandScape->flrand(0.0f,1.0f) > mPickupHealth;}
bool DenyPickupArmor () {return mLandScape->flrand(0.0f,1.0f) > mPickupArmor;}
bool DenyPickupAmmo () {return mLandScape->flrand(0.0f,1.0f) > mPickupAmmo;}
bool DenyPickupWeapon () {return mLandScape->flrand(0.0f,1.0f) > mPickupWeapon;}
bool DenyPickupEquipment () {return mLandScape->flrand(0.0f,1.0f) > mPickupEquipment;}
private:
// void PurgeUnlinkedTriggers ( );
// void PurgeTrigger ( CEntity* trigger );
void MirrorPos (vec3_t pos);
CGPGroup* ParseRandom ( CGPGroup* random );
bool ParseOrigin ( CGPGroup* originGroup, vec3_t origin, vec3_t lookat, int* flattenHeight );
bool ParseNodes ( CGPGroup* group );
bool ParsePaths ( CGPGroup *paths);
bool ParseRivers ( CGPGroup *rivers);
void PlaceBridges ();
void PlaceWallInstance(CRMInstance* instance, float xpos, float ypos, float zpos, int x, int y, float angle);
bool ParseDifficulty ( CGPGroup* difficulty, CGPGroup *parent );
bool ParseWeapons ( CGPGroup* weapons );
bool ParseAmmo ( CGPGroup* ammo );
bool ParseOutfit ( CGPGroup* outfit );
bool ParseObjectives ( CGPGroup* objectives );
bool ParseInstance ( CGPGroup* instance );
bool ParseInstances ( CGPGroup* instances );
bool ParseInstancesOnPath ( CGPGroup* group );
bool ParseWallRect ( CGPGroup* group, int side);
// void SpawnNPCTriggers ( CCMLandScape* landscape );
// void AttachNPCTriggers ( CCMLandScape* landscape );
};
#endif

134
code/RMG/RM_Objective.cpp Normal file
View File

@@ -0,0 +1,134 @@
/************************************************************************************************
*
* RM_Objective.cpp
*
* Implements the CRMObjective class. This class is reponsible for parsing an objective
* from the mission file as well as linking the objective into the world.
*
************************************************************************************************/
#include "../server/exe_headers.h"
#include "rm_headers.h"
/************************************************************************************************
* CRMObjective::CRMObjective
* Constructs a random mission objective and fills in the default properties
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
CRMObjective::CRMObjective ( CGPGroup* group )
{
SetPriority(atoi(group->FindPairValue("priority", "0")));
SetMessage( group->FindPairValue("message",va("Objective %i Completed", GetPriority()) ) );
SetDescription(group->FindPairValue("description",va("Objective %i", GetPriority()) ) );
SetInfo(group->FindPairValue("info",va("Info %i", GetPriority()) ) );
SetTrigger(group->FindPairValue("trigger",""));
SetName(group->GetName());
/* const char * soundPath = group->FindPairValue("completed_sound", "" );
if (soundPath)
mCompleteSoundID = G_SoundIndex(soundPath);
*/
mCompleted = false;
mOrderIndex = -1;
// If no priority was specified for this objective then its active by default.
if ( GetPriority ( ) )
{
mActive = false;
}
else
{
mActive = true;
}
}
/************************************************************************************************
* CRMObjective::FindRandomTrigger
* Searches the entitySystem form a random arioche trigger that matches the objective name
*
* inputs:
* none
*
* return:
* trigger: a random trigger or NULL if one couldnt be found
*
************************************************************************************************/
/*CTriggerAriocheObjective* CRMObjective::FindRandomTrigger ( )
{
CEntity* search;
CEntity* triggers[20];
int numTriggers;
// Start off the first trigger
numTriggers = 0;
search = entitySystem->GetEntityFromClassname ( NULL, "trigger_arioche_objective" );
// Make a list of triggers
while ( numTriggers < 20 && search )
{
CTriggerAriocheObjective* trigger = (CTriggerAriocheObjective*) search;
// Move on to the next trigger
search = entitySystem->GetEntityFromClassname ( search, "trigger_arioche_objective" );
// See if this trigger is already in use
if ( trigger->GetObjective ( ) )
{
continue;
}
// If the objective names dont match then ignore this trigger
if ( stricmp ( trigger->GetObjectiveName ( ), GetTrigger() ) )
{
continue;
}
// Add the trigger to the list
triggers[numTriggers++] = trigger;
}
// If no matching triggers then just return NULL
if ( 0 == numTriggers )
{
return NULL;
}
// Return a random choice from the trigger list
return (CTriggerAriocheObjective*)triggers[TheRandomMissionManager->GetLandScape()->irand(0,numTriggers-1)];
}
*/
/************************************************************************************************
* CRMObjective::Link
* Links the objective into the world using the current state of the world to determine
* where it should link
*
* inputs:
* none
*
* return:
* true: objective successfully linked
* false: objective failed to link
*
************************************************************************************************/
bool CRMObjective::Link ( )
{
/* CTriggerAriocheObjective* trigger;
// Look for a random trigger to associate this objective to.
trigger = FindRandomTrigger ( );
if ( NULL != trigger )
{
trigger->SetObjective ( this );
}
*/
return true;
}

65
code/RMG/RM_Objective.h Normal file
View File

@@ -0,0 +1,65 @@
#pragma once
#if !defined(RM_OBJECTIVE_H_INC)
#define RM_OBJECTIVE_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including RM_Objective.h")
#endif
class CRMObjective
{
protected:
bool mCompleted; // Is objective completed?
bool mActive; // set to false if the objective requires another objective to be met first
int mPriority; // sequence in which objectives need to be completed
int mOrderIndex; // objective index in ui
int mCompleteSoundID; // sound for when objective is finished
string mMessage; // message outputed when objective is completed
string mDescription; // description of objective
string mInfo; // more info for objective
string mName; // name of objective
string mTrigger; // trigger associated with objective
public:
CRMObjective(CGPGroup *group);
~CRMObjective(void) {}
bool Link (void);
bool IsCompleted (void) const { return mCompleted; }
bool IsActive (void) const { return mActive; }
void Activate (void) { mActive = true; }
void Complete (bool comp) { mCompleted = comp;}
// Get methods
int GetPriority(void){return mPriority;}
int GetOrderIndex(void) { return mOrderIndex; }
const char* GetMessage(void) { return mMessage.c_str(); }
const char* GetDescription(void) { return mDescription.c_str(); }
const char* GetInfo(void) { return mInfo.c_str(); }
const char* GetName(void) { return mName.c_str(); }
const char* GetTrigger(void) { return mTrigger.c_str(); }
int CompleteSoundID() { return mCompleteSoundID; };
// Set methods
void SetPriority(int priority){mPriority = priority;}
void SetOrderIndex(int order) { mOrderIndex = order; }
void SetMessage(const char* msg) { mMessage = msg; }
void SetDescription(const char* desc) { mDescription = desc; }
void SetInfo(const char* info) { mInfo = info; }
void SetName(const char* name) { mName = name; }
void SetTrigger(const char* name) { mTrigger = name; }
private:
// CTriggerAriocheObjective* FindRandomTrigger ( );
};
typedef list<CRMObjective *>::iterator rmObjectiveIter_t;
typedef list<CRMObjective *> rmObjectiveList_t;
#endif

721
code/RMG/RM_Path.cpp Normal file
View File

@@ -0,0 +1,721 @@
/************************************************************************************************
*
* Copyright (C) 2001-2002 Raven Software
*
* RM_Path.cpp
*
************************************************************************************************/
#include "../server/exe_headers.h"
#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);
}

223
code/RMG/RM_Path.h Normal file
View File

@@ -0,0 +1,223 @@
/************************************************************************************************
*
* Copyright (C) 2001-2002 Raven Software
*
* RM_Path.h
*
************************************************************************************************/
#pragma once
#if !defined(RM_PATH_H_INC)
#define RM_PATH_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including RM_Path.h")
#endif
#if !defined(CM_RANDOMTERRAIN_H_INC)
#include "../qcommon/cm_randomterrain.h"
#endif
class CRMPathManager;
// directions you can proceed from cells
enum ERMDir
{
DIR_FIRST= 0,
DIR_N = 0,
DIR_NE,
DIR_E ,
DIR_SE,
DIR_S ,
DIR_SW,
DIR_W ,
DIR_NW,
DIR_MAX,
DIR_ALL = 255
};
#define HALF_DIR_MAX (DIR_MAX/2)
class CRMNode
{
private:
string mName; // name of node - "" if not used yet
vec3_t mPos; // where node is
int mPathID[DIR_MAX]; // path id's that lead from this node
bool mAreaPointPlaced; // false if no area point here yet.
int mFlattenHeight;
public:
CRMNode ( );
bool IsLocation() {return strlen(mName.c_str())>0;};
const char* GetName ( ) { return mName.c_str(); }
vec3_t& GetPos ( ) { return mPos; }
const float PathExist( const int dir) { return (mPathID[dir % DIR_MAX] != -1); };
const float GetPath ( const int dir) { return mPathID[dir % DIR_MAX]; };
bool AreaPoint() {return mAreaPointPlaced;};
void SetName ( const char* name ) { mName = name; }
void SetPos ( const vec3_t& v ) { VectorCopy ( v, mPos ); }
void SetPath( const int dir, const int id) { mPathID[dir % DIR_MAX] = id; };
void SetAreaPoint(bool ap) {mAreaPointPlaced = ap;};
void SetFlattenHeight(int flattenHeight) {mFlattenHeight = flattenHeight; }
int GetFlattenHeight() {return mFlattenHeight; }
};
typedef vector<CRMNode*> rmNodeVector_t;
// named spots on the map, should be placed into nodes
class CRMLoc
{
private:
string mName; // name of location
int mMinDepth;
int mMaxDepth;
int mMinPaths;
int mMaxPaths;
bool mPlaced; // location has been placed at a node
public:
CRMLoc (const char *name, const int min_depth, const int max_depth, const int min_paths =1, const int max_paths=1 )
: mMinDepth(min_depth), mMaxDepth(max_depth), mPlaced(false), mMinPaths(min_paths), mMaxPaths(max_paths)
{ mName = name; };
const char* GetName ( ) { return mName.c_str(); }
void SetName ( const char* name ) { mName = name; }
int MinDepth() {return mMinDepth;};
void SetMinDepth(const int deep) {mMinDepth = deep;};
int MaxDepth() {return mMaxDepth;};
void SetMaxDepth(const int deep) {mMaxDepth = deep;};
int MinPaths() {return mMinPaths;};
void SetMinPaths(const int paths) {mMinPaths = paths;};
int MaxPaths() {return mMaxPaths;};
void SetMaxPaths(const int paths) {mMaxPaths = paths;};
bool Placed() { return mPlaced; };
void SetPlaced(bool p) { mPlaced = p;};
};
typedef vector<CRMLoc*> rmLocVector_t;
// cells are used for figuring out node connections / paths
struct CRMCell
{
private:
int border;
int wall;
public:
CRMCell() { border = 0; wall = DIR_ALL; };
int Border() {return border;};
int Wall() {return wall;};
bool Border(const int dir) { return (border & (1<<dir))!=0; };
bool Wall(const int dir) { return (wall & (1<<dir))!=0; };
void SetBorder(const int dir) { border |= (1<<dir); };
void SetWall(const int dir) { wall |= (1<<dir); };
void RemoveWall(const int dir) { wall &= ~(1<<dir); };
};
typedef vector<CRMCell> rmCellVector_t;
class CRMPathManager
{
public:
int mXNodes; // number of nodes in the x dimension
int mYNodes; // number of nodes in the y dimension
private:
rmLocVector_t mLocations; // location, named spots to be placed at nodes
rmNodeVector_t mNodes; // nodes, spots on map that *may* be connected by paths
rmCellVector_t mCells; // array of cells for doing path generation
int mPathCount;
int mRiverCount;
int mMaxDepth; // deepest any location wants to be
int mDepth; // current depth
bool mCrossed; // used to indicate if paths crossed the imaginary diagonal that cuts symmetric maps in half
// path style
int mPathPoints;
float mPathMinWidth;
float mPathMaxWidth;
float mPathDepth;
float mPathDeviation;
float mPathBreadth;
// river style
int mRiverDepth;
int mRiverPoints;
float mRiverMinWidth;
float mRiverMaxWidth;
float mRiverBedDepth;
float mRiverDeviation;
float mRiverBreadth;
string mRiverBridge;
vec3_t mRiverPos;
static int neighbor_x[DIR_MAX];
static int neighbor_y[DIR_MAX];
CRandomTerrain* mTerrain;
public:
CRMPathManager ( CRandomTerrain* terrain );
~CRMPathManager ( );
void ClearCells (int x_nodes, int y_nodes);
bool CreateArray ( const int x_nodes, const int y_nodes );
CRMNode* FindNodeByName ( const char* name );
CRMNode* Node ( const int x, const int y ) {return mNodes[x + y*mXNodes];};
void CreateLocation ( const char* name, const int min_depth, int max_depth, const int min_paths =1, int max_paths =1 );
vec3_t& GetNodePos ( const int x, const int y ) { return mNodes[x + y*mXNodes]->GetPos(); };
void SetNodePos ( const int x, const int y, const vec3_t& pos) { mNodes[x + y*mXNodes]->SetPos(pos); };
int GetPathCount () {return mPathCount;};
int GetRiverCount () {return mRiverCount;};
float GetRiverDepth () {return mRiverBedDepth;};
float GetPathDepth () {return mPathDepth;};
const char *GetBridgeName () {return mRiverBridge.c_str();};
vec3_t& GetRiverPos ( const int x, const int y );
CRMCell& Cell ( const int x, const int y ) {return mCells[x + y*mXNodes];};
CRMCell& RiverCell ( const int x, const int y ) {return mCells[x + y*(mXNodes+1)];};
void PlaceLocation ( const int x, const int y );
void PathVisit ( const int x, const int y );
void RiverVisit ( const int x, const int y );
void SetPathStyle ( const int points = 10,
const float minwidth = 0.01f,
const float maxwidth = 0.05f,
const float depth = 0.3f,
const float deviation = 0.2f,
const float breadth = 5);
void SetRiverStyle ( const int depth = 5,
const int points = 10,
const float minwidth = 0.01,
const float maxwidth = 0.03,
const float beddepth = 0.0f,
const float deviation = 0.25f,
const float breadth = 7,
string bridge_name = "");
void GeneratePaths ( symmetry_t symmetric = SYMMETRY_NONE );
void GenerateRivers ( );
};
#endif

533
code/RMG/RM_Terrain.cpp Normal file
View File

@@ -0,0 +1,533 @@
#include "../server/exe_headers.h"
#include "../client/client.h"
#include "../qcommon/cm_local.h"
#include "../renderer/tr_types.h"
#include "rm_headers.h"
//#include "../qcommon/q_imath.h"
#pragma optimize("", off)
void R_LoadDataImage ( const char *name, byte **pic, int *width, int *height);
void R_InvertImage ( byte *data, int width, int height, int depth);
void R_Resample ( byte *source, int swidth, int sheight, byte *dest, int dwidth, int dheight, int components);
void RE_GetModelBounds (refEntity_t *refEnt, vec3_t bounds1, vec3_t bounds2);
static CRMLandScape *rm_landscape;
static CCMLandScape *origin_land;
CRMLandScape::CRMLandScape(void)
{
common = NULL;
mDensityMap = NULL;
}
CRMLandScape::~CRMLandScape(void)
{
if(mDensityMap)
{
Z_Free(mDensityMap);
mDensityMap = NULL;
}
}
void CCGHeightDetails::AddModel(const CRandomModel *hd)
{
if(mNumModels < MAX_RANDOM_MODELS)
{
mTotalFrequency += hd->GetFrequency();
mModels[mNumModels++] = *hd;
}
}
void CRMLandScape::AddModel(const int height, int maxheight, const CRandomModel *hd)
{
int i;
if(maxheight > HEIGHT_RESOLUTION)
{
maxheight = HEIGHT_RESOLUTION;
}
for(i = height; hd->GetModel() && (i < maxheight); i++)
{
mHeightDetails[i].AddModel(hd);
}
}
void CRMLandScape::LoadMiscentDef(const char *td)
{
char miscentDef[MAX_QPATH];
CGenericParser2 parse;
CGPGroup *basegroup, *classes, *items, *model;
CGPValue *pair;
Com_sprintf(miscentDef, MAX_QPATH, "ext_data/RMG/%s.miscents", Info_ValueForKey(td, "miscentDef"));
Com_DPrintf("CG_Terrain: Loading and parsing miscentDef %s.....\n", Info_ValueForKey(td, "miscentDef"));
if(!Com_ParseTextFile(miscentDef, parse))
{
Com_sprintf(miscentDef, MAX_QPATH, "ext_data/arioche/%s.miscents", Info_ValueForKey(td, "miscentDef"));
if(!Com_ParseTextFile(miscentDef, parse))
{
Com_Printf("Could not open %s\n", miscentDef);
return;
}
}
// The whole file....
basegroup = parse.GetBaseParseGroup();
// The root { } struct
classes = basegroup->GetSubGroups();
while(classes)
{
items = classes->GetSubGroups();
while(items)
{
if(!stricmp(items->GetName(), "miscent"))
{
int height, maxheight;
// Height must exist - the rest are optional
height = atol(items->FindPairValue("height", "0"));
maxheight = atol(items->FindPairValue("maxheight", "255"));
model = items->GetSubGroups();
while(model)
{
if(!stricmp(model->GetName(), "model"))
{
CRandomModel hd;
// Set defaults
hd.SetModel("");
hd.SetFrequency(1.0f);
hd.SetMinScale(1.0f);
hd.SetMaxScale(1.0f);
pair = model->GetPairs();
while(pair)
{
if(!stricmp(pair->GetName(), "name"))
{
hd.SetModel(pair->GetTopValue());
}
else if(!stricmp(pair->GetName(), "frequency"))
{
hd.SetFrequency((float)atof(pair->GetTopValue()));
}
else if(!stricmp(pair->GetName(), "minscale"))
{
hd.SetMinScale((float)atof(pair->GetTopValue()));
}
else if(!stricmp(pair->GetName(), "maxscale"))
{
hd.SetMaxScale((float)atof(pair->GetTopValue()));
}
pair = (CGPValue *)pair->GetNext();
}
AddModel(height, maxheight, &hd);
}
model = (CGPGroup *)model->GetNext();
}
}
items = (CGPGroup *)items->GetNext();
}
classes = (CGPGroup *)classes->GetNext();
}
Com_ParseTextFileDestroy(parse);
}
void CG_Decrease(byte *work, float lerp, int *info)
{
int val;
val = *work - origin_land->irand(2, 5);
*work = (byte)Com_Clamp(1, 255, val);
}
void CRMLandScape::CreateRandomDensityMap(byte *density, int width, int height, int seed)
{
// int i, border, inc;
int x, y, count;
// byte *work, *work2;
CArea *area;
vec3_t derxelSize, pos;
ivec3_t dmappos;
byte *hm_map = common->GetHeightMap();
int hm_width = common->GetRealWidth();
int hm_height = common->GetRealHeight();
int xpos, ypos, dx, dy;
byte *densityPos = density;
bool foundUneven;
// Init to linear spread
memset(density, 0, width * height);
/* // Make more prevalent towards the edges
border = Com_Clamp(6, 12, (width + height) >> 4);
for(i = 0; i < border; i++)
{
inc = (border - i + 1) * 9;
// Top line
work = density + i + (i * width);
for(x = i; x < width - i; x++, work++)
{
*work += (byte)common->irand(inc >> 1, inc);
}
// Left and right edges
work = density + i + ((i + 1) * width);
work2 = density + (width - i) + ((i + 1) * width);
for(y = i + 1; y < height - i - 2; y++, work += width, work2 += width)
{
*work += (byte)common->irand(inc >> 1, inc);
*work2 += (byte)common->irand(inc >> 1, inc);
}
// Bottom line
work = density + i + ((height - i - 1) * width);
for(x = i; x < width - i; x++, work++)
{
*work += (byte)common->irand(inc >> 1, inc);
}
}
*/
count = 0;
for(y=0;y<height;y++)
{
for(x=0;x<width;x++,densityPos++)
{
xpos = (x * hm_width / width);
ypos = (y * hm_height / height);
ypos = hm_height - ypos - 1;
if (hm_map[ypos*hm_width + xpos] < 150)
{
continue;
}
foundUneven = false;
for(dx=-4;(dx<=4 && !foundUneven);dx++)
{
for(dy=-4;(dy<=4 && !foundUneven);dy++)
{
if (dx == 0 && dy == 0)
{
continue;
}
if ((xpos+dx) >= 0 && (xpos+dx) < hm_width && (ypos+dy) >= 0 && (ypos+dy) < hm_height)
{
if (hm_map[(ypos+dy)*hm_width + (xpos+dx)] < 190)
{
*densityPos = 205;
count++;
foundUneven = true;
}
}
}
}
}
}
/* FILE *FH;
FH = fopen("c:\o.raw", "wb");
fwrite(hm_map, 1, common->GetRealWidth() * common->GetRealHeight(), FH);
fclose(FH);
FH = fopen("c:\d.raw", "wb");
fwrite(density, 1, width*height, FH);
fclose(FH);
*/
// Reduce severely for any settlements/buildings/objectives
VectorScale(common->GetSize(), 1.0f / width, derxelSize);
origin_land = common;
area = common->GetFirstArea();
while(area)
{
// Skip group types since they encompass to much open area
if ( area->GetType ( ) == AT_GROUP )
{
area = common->GetNextArea();
continue;
}
VectorSubtract(area->GetPosition(), common->GetMins(), pos);
VectorInverseScaleVector(pos, derxelSize, dmappos);
// Damn upside down gensurf
dmappos[1] = height - dmappos[1];
count = ceilf(area->GetRadius() / derxelSize[1]);
while(count > 0)
{
CM_CircularIterate(density, width, height, dmappos[0], dmappos[1], 0, count, NULL, CG_Decrease);
count--;
}
area = common->GetNextArea();
}
}
void CRMLandScape::LoadDensityMap(const char *td)
{
char densityMap[MAX_QPATH];
byte *imageData;
int iWidth, iHeight, seed;
char *ptr;
// Fill in with default values
mDensityMap = (byte *)Z_Malloc(common->GetBlockCount(), TAG_R_TERRAIN, qfalse);
memset(mDensityMap, 128, common->GetBlockCount());
// Load in density map (if any)
Com_sprintf(densityMap, MAX_QPATH, "%s", Info_ValueForKey(td, "densityMap"));
if(strlen(densityMap))
{
Com_DPrintf("CG_Terrain: Loading density map %s.....\n", densityMap);
R_LoadDataImage(densityMap, &imageData, &iWidth, &iHeight);
if(imageData)
{
if(strstr(densityMap, "density_"))
{
seed = strtoul(Info_ValueForKey(td, "seed"),&ptr,10);
CreateRandomDensityMap(imageData, iWidth, iHeight, seed);
}
R_Resample(imageData, iWidth, iHeight, mDensityMap, common->GetBlockWidth(), common->GetBlockHeight(), 1);
R_InvertImage(mDensityMap, common->GetBlockWidth(), common->GetBlockHeight(), 1);
Z_Free(imageData);
}
}
}
CRandomModel *CCGHeightDetails::GetRandomModel(CCMLandScape *land)
{
int seek, i;
seek = land->irand(0, mTotalFrequency);
for(i = 0; i < mNumModels; i++)
{
seek -= mModels[i].GetFrequency();
if(seek <= 0)
{
return(mModels + i);
}
}
assert(0);
return(NULL);
}
#ifndef DEDICATED
void CRMLandScape::Sprinkle(CCMPatch *patch, CCGHeightDetails *hd, int level)
{
int i, count, px, py;
float density;
vec3_t origin, scale, angles, bounds[2];
refEntity_t refEnt;
CRandomModel *rm;
CArea area;
// int areaTypes[] = { AT_BSP, AT_OBJECTIVE };
// TCGMiscEnt *data = (TCGMiscEnt *)cl.mSharedMemory;
// TCGTrace *td = (TCGTrace *)cl.mSharedMemory;
// memset(&refEnt, 0, sizeof(refEntity_t));
px = patch->GetHeightMapX() / common->GetTerxels();
py = patch->GetHeightMapY() / common->GetTerxels();
// Get a number -5.3f to 5.3f
density = (mDensityMap[px + (common->GetBlockWidth() * py)] - 128) / 24.0f;
// ..and multiply that into the count
count = Round(common->GetPatchScalarSize() * hd->GetAverageFrequency() * powf(2.0f, density) * 0.001);
for(i = 0; i < count; i++)
{
if(!common->irand(0, 10))
{
vec3_t temp;
trace_t tr;
float average;
rm = hd->GetRandomModel(common);
refEnt.hModel = re.RegisterModel(rm->GetModelName());
refEnt.frame = 0;
RE_GetModelBounds(&refEnt, bounds[0], bounds[1]);
// Calculate the scale using some magic to help ensure that the
// scales are never too different from eachother. Otherwise you
// could get an entity that is really small on one axis but huge
// on another.
temp[0] = common->flrand(rm->GetMinScale(), rm->GetMaxScale());
temp[1] = common->flrand(rm->GetMinScale(), rm->GetMaxScale());
temp[2] = common->flrand(rm->GetMinScale(), rm->GetMaxScale());
// Average of the three random numbers and divide that by two
average = ( ( temp[0] + temp[1] + temp[2] ) / 3) / 2;
// Add in half of the other two numbers and then subtract half the average to prevent.
// any number from going beyond the range. If all three numbers were the same then
// they would remain unchanged after this calculation.
scale[0] = temp[0] + (temp[1]+temp[2]) / 2 - average;
scale[1] = temp[1] + (temp[0]+temp[2]) / 2 - average;
scale[2] = temp[2] + (temp[0]+temp[1]) / 2 - average;
angles[0] = 0.0f;
angles[1] = common->flrand((float)-M_PI, (float)M_PI);
angles[2] = 0.0f;
VectorCopy(patch->GetMins(), origin);
origin[0] += common->flrand(0.0f, common->GetPatchWidth());
origin[1] += common->flrand(0.0f, common->GetPatchHeight());
// Get above world height
float slope = common->GetWorldHeight(origin, bounds, true);
if (slope > 1.33)
{ // spot has too steep of a slope
continue;
}
if(origin[2] < common->GetWaterHeight())
{
continue;
}
// very that we aren't dropped too low
if (origin[2] < common->CalcWorldHeight(level))
{
continue;
}
// Hack-ariffic, don't allow them to drop below the big player clip brush.
if (origin[2] < 1280 )
{
continue;
}
// FIXME: shouldn't be using a hard-coded 1280 number, only allow to spawn if inside player clip brush?
// if( !(CONTENTS_PLAYERCLIP & VM_Call( cgvm, CG_POINT_CONTENTS )) )
// {
// continue;
// }
// Simple radius check for buildings
/* area.Init(origin, VectorLength(bounds[0]));
if(common->AreaCollision(&area, areaTypes, sizeof(areaTypes) / sizeof(int)))
{
continue;
}*/
// Make sure there is no architecture around - doesn't work for ents though =(
/*
memset(td, sizeof(*td), 0);
VectorCopy(origin, td->mStart);
VectorCopy(bounds[0], td->mMins);
VectorCopy(bounds[1], td->mMaxs);
VectorCopy(origin, td->mEnd);
td->mSkipNumber = -1;
td->mMask = MASK_PLAYERSOLID;
*/
SV_Trace(&tr, origin, bounds[0], bounds[1], origin, -1, (CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_BODY|CONTENTS_TERRAIN));
/*
VM_Call( cgvm, CG_TRACE );
if(td->mResult.surfaceFlags & SURF_NOMISCENTS)
{
continue;
}
if(td->mResult.startsolid)
{
// continue;
}
*/
if (tr.surfaceFlags & SURF_NOMISCENTS)
{
continue;
}
if (tr.startsolid)
{
// continue;
}
// Get minimum height of area
common->GetWorldHeight(origin, bounds, false);
// Account for relative origin
origin[2] -= bounds[0][2] * scale[2];
origin[2] -= common->flrand(2.0, (bounds[1][2] - bounds[0][2]) / 4);
//rwwFIXMEFIXME: Do this properly
// Spawn the client model
/*
strcpy(data->mModel, rm->GetModelName());
VectorCopy(origin, data->mOrigin);
VectorCopy(angles, data->mAngles);
VectorCopy(scale, data->mScale);
VM_Call( cgvm, CG_MISC_ENT);
*/
mModelCount++;
}
}
}
#endif // !DEDICATED
void CRMLandScape::SpawnPatchModels(CCMPatch *patch)
{
int i;
CCGHeightDetails *hd;
// Rand_Init(10);
#ifndef DEDICATED
for(i = 0; i < 4; i++)
{
hd = mHeightDetails + patch->GetHeight(i);
if(hd->GetNumModels())
{
Sprinkle(patch, hd, patch->GetHeight(i));
}
}
#endif // !DEDICATED
}
void SpawnPatchModelsWrapper(CCMPatch *patch, void *userdata)
{
CRMLandScape *landscape = (CRMLandScape *)userdata;
landscape->SpawnPatchModels(patch);
}
void RM_CreateRandomModels(int terrainId, const char *terrainInfo)
{
CRMLandScape *landscape;
landscape = rm_landscape = new CRMLandScape;
landscape->SetCommon(cmg.landScape);
Com_DPrintf("CG_Terrain: Creating random models.....\n");
landscape->LoadMiscentDef(terrainInfo);
landscape->LoadDensityMap(terrainInfo);
landscape->ClearModelCount();
CM_TerrainPatchIterate(landscape->GetCommon(), SpawnPatchModelsWrapper, landscape);
Com_DPrintf(".....%d random client models spawned\n", landscape->GetModelCount());
}
void RM_InitTerrain(void)
{
rm_landscape = NULL;
}
void RM_ShutdownTerrain(void)
{
CRMLandScape *landscape;
landscape = rm_landscape;
if(landscape)
{
// CM_ShutdownTerrain(i);
delete landscape;
rm_landscape = NULL;
}
}
// end
#pragma optimize("", on)

97
code/RMG/RM_Terrain.h Normal file
View File

@@ -0,0 +1,97 @@
#pragma once
#if !defined(RM_TERRAIN_H_INC)
#define RM_TERRAIN_H_INC
#define MAX_RANDOM_MODELS 8
class CRandomModel
{
private:
char mModelName[MAX_QPATH];
float mFrequency;
float mMinScale;
float mMaxScale;
public:
CRandomModel(void) { }
~CRandomModel(void) { }
// Accessors
const bool GetModel( void ) const { return(!!strlen(mModelName)); }
const char *GetModelName( void ) const { return(mModelName); }
void SetModel(const char *name) { Com_sprintf(mModelName, MAX_QPATH, "%s.md3", name); }
const float GetFrequency(void) const { return(mFrequency); }
void SetFrequency(const float freq) { mFrequency = freq; }
const float GetMinScale(void) const { return(mMinScale); }
void SetMinScale(const float minscale) { mMinScale = minscale; }
const float GetMaxScale(void) const { return(mMaxScale); }
void SetMaxScale(const float maxscale) { mMaxScale = maxscale; }
};
class CCGHeightDetails
{
private:
int mNumModels;
int mTotalFrequency;
CRandomModel mModels[MAX_RANDOM_MODELS];
public:
// Constructors
CCGHeightDetails( void ) { memset(this, 0, sizeof(*this)); }
~CCGHeightDetails( void ) { }
// Accessors
const int GetNumModels(void) const { return(mNumModels); }
const int GetAverageFrequency(void) const { return(mTotalFrequency / mNumModels); }
// Prototypes
void AddModel(const CRandomModel *hd);
CRandomModel *GetRandomModel(CCMLandScape *land);
};
class CCGPatch
{
private:
class CCMLandScape *owner;
class CCGLandScape *localowner;
CCMPatch *common;
public:
};
class CRMLandScape
{
private:
CCMLandScape *common;
byte *mDensityMap; // Data image of model densities
int mModelCount; // Count of spawned client models
CCGHeightDetails mHeightDetails[HEIGHT_RESOLUTION]; // Array of info specific to height
public:
CRMLandScape(void);
~CRMLandScape(void);
// Accessors
void SetCommon(CCMLandScape *landscape) { common = landscape; }
const CCMLandScape *GetCommon( void ) const { return(common); }
const thandle_t GetCommonId( void ) const { return(common->GetTerrainId()); }
const int GetTerxels(void) const { return(common->GetTerxels()); }
const int GetRealWidth(void) const { return(common->GetRealWidth()); }
const float GetPatchScalarSize(void) const { return(common->GetPatchScalarSize()); }
const CCGHeightDetails *GetHeightDetail(int height) const { return(mHeightDetails + height); }
void ClearModelCount(void) { mModelCount = 0; }
const int GetModelCount(void) const { return(mModelCount); }
// Prototypes
void SetShaders(const int height, const qhandle_t shader);
void AddModel(const int height, int maxheight, const CRandomModel *hd);
void LoadMiscentDef(const char *td);
void LoadDensityMap(const char *td);
void SpawnPatchModels(CCMPatch *patch);
void Sprinkle(CCMPatch *patch, CCGHeightDetails *hd, int level);
void CreateRandomDensityMap(byte *imageData, int width, int height, int seed);
};
void RM_CreateRandomModels(int terrainId, const char *terrainInfo);
void RM_InitTerrain(void);
void RM_ShutdownTerrain(void);
#endif // RM_TERRAIN_H_INC