2013-04-19 02:52:48 +00:00
|
|
|
/************************************************************************************************
|
|
|
|
*
|
|
|
|
* 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" );
|
2013-04-23 06:54:20 +00:00
|
|
|
if (0 == Q_strcmpi(automapSymName, "none")) mAutomapSymbol = AUTOMAP_NONE ;
|
|
|
|
else if (0 == Q_strcmpi(automapSymName, "building")) mAutomapSymbol = AUTOMAP_BLD ;
|
|
|
|
else if (0 == Q_strcmpi(automapSymName, "objective")) mAutomapSymbol = AUTOMAP_OBJ ;
|
|
|
|
else if (0 == Q_strcmpi(automapSymName, "start")) mAutomapSymbol = AUTOMAP_START;
|
|
|
|
else if (0 == Q_strcmpi(automapSymName, "end")) mAutomapSymbol = AUTOMAP_END ;
|
|
|
|
else if (0 == Q_strcmpi(automapSymName, "enemy")) mAutomapSymbol = AUTOMAP_ENEMY;
|
|
|
|
else if (0 == Q_strcmpi(automapSymName, "friend")) mAutomapSymbol = AUTOMAP_FRIEND;
|
|
|
|
else if (0 == Q_strcmpi(automapSymName, "wall")) mAutomapSymbol = AUTOMAP_WALL;
|
2013-04-19 02:52:48 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|