jedi-academy/code/RMG/RM_Instance_BSP.cpp
2013-04-23 16:54:20 +10:00

294 lines
9.7 KiB
C++

/************************************************************************************************
*
* 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 == 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;
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;
}