sof2-sdk/code/game/g_misc.c
2002-05-24 00:00:00 +00:00

595 lines
16 KiB
C

// Copyright (C) 2001-2002 Raven Software.
//
// g_misc.c
#include "g_local.h"
/*QUAKED func_group (0 0 0) ?
Used to group brushes together just for editor convenience. They are turned into normal brushes by the utilities.
*/
/*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)
Used as a positional target for in-game calculation, like jumppad targets.
target_position does the same thing
*/
void SP_info_notnull( gentity_t *self )
{
G_SetOrigin( self, self->s.origin );
}
/*
=================================================================================
TeleportPlayer
=================================================================================
*/
void TeleportPlayer ( gentity_t *player, vec3_t origin, vec3_t angles )
{
gentity_t *tent;
// use temp events at source and destination to prevent the effect
// from getting dropped by a second player event
if ( !G_IsClientSpectating ( player->client ) )
{
tent = G_TempEntity( player->client->ps.origin, EV_PLAYER_TELEPORT_OUT );
tent->s.clientNum = player->s.clientNum;
tent = G_TempEntity( origin, EV_PLAYER_TELEPORT_IN );
tent->s.clientNum = player->s.clientNum;
}
// unlink to make sure it can't possibly interfere with G_KillBox
trap_UnlinkEntity (player);
VectorCopy ( origin, player->client->ps.origin );
player->client->ps.origin[2] += 1;
// spit the player out
AngleVectors( angles, player->client->ps.velocity, NULL, NULL );
VectorScale( player->client->ps.velocity, 400, player->client->ps.velocity );
player->client->ps.pm_time = 160; // hold time
player->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
// toggle the teleport bit so the client knows to not lerp
player->client->ps.eFlags ^= EF_TELEPORT_BIT;
// set angles
SetClientViewAngle( player, angles );
// kill anything at the destination
if ( !G_IsClientSpectating ( player->client ) )
{
G_KillBox (player);
}
// save results of pmove
BG_PlayerStateToEntityState( &player->client->ps, &player->s, qtrue );
// use the precise origin for linking
VectorCopy( player->client->ps.origin, player->r.currentOrigin );
if ( !G_IsClientSpectating ( player->client ) )
{
trap_LinkEntity (player);
}
}
/*QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16)
Point teleporters at these.
Now that we don't have teleport destination pads, this is just
an info_notnull
*/
void SP_misc_teleporter_dest( gentity_t *ent )
{
}
//===========================================================
/*QUAKED misc_model (1 0 0) (-16 -16 -16) (16 16 16) RMG
this model is inserted into the bsp file
"model" arbitrary .md3 file to display
*/
void SP_misc_model( gentity_t *ent )
{
#if 0
ent->s.modelindex = G_ModelIndex( ent->model );
VectorSet (ent->mins, -16, -16, -16);
VectorSet (ent->maxs, 16, 16, 16);
trap_LinkEntity (ent);
G_SetOrigin( ent, ent->s.origin );
VectorCopy( ent->s.angles, ent->s.apos.trBase );
#else
G_FreeEntity( ent );
#endif
}
/*QUAKED misc_G2model (1 0 0) (-16 -16 -16) (16 16 16)
"model" arbitrary .glm file to display
*/
void SP_misc_G2model( gentity_t *ent ) {
#if 0
char name1[200] = "models/players/kyle/modelmp.glm";
trap_G2API_InitGhoul2Model(&ent->s, name1, G_ModelIndex( name1 ), 0, 0, 0, 0);
trap_G2API_SetBoneAnim(ent->s.ghoul2, 0, "model_root", 0, 12, BONE_ANIM_OVERRIDE_LOOP, 1.0f, level.time, -1, -1);
ent->s.radius = 150;
// VectorSet (ent->mins, -16, -16, -16);
// VectorSet (ent->maxs, 16, 16, 16);
trap_LinkEntity (ent);
G_SetOrigin( ent, ent->s.origin );
VectorCopy( ent->s.angles, ent->s.apos.trBase );
#else
G_FreeEntity( ent );
#endif
}
//===========================================================
void locateCamera( gentity_t *ent )
{
vec3_t dir;
gentity_t *target;
gentity_t *owner;
owner = G_PickTarget( ent->target );
if ( !owner )
{
Com_Printf( "Couldn't find target for misc_partal_surface\n" );
G_FreeEntity( ent );
return;
}
ent->r.ownerNum = owner->s.number;
// frame holds the rotate speed
if ( owner->spawnflags & 1 )
{
ent->s.frame = 25;
}
else if ( owner->spawnflags & 2 )
{
ent->s.frame = 75;
}
// swing camera ?
if ( owner->spawnflags & 4 )
{
// set to 0 for no rotation at all
ent->s.gametypeitems = 0;
}
else
{
ent->s.gametypeitems = 1;
}
// clientNum holds the rotate offset
ent->s.clientNum = owner->s.clientNum;
VectorCopy( owner->s.origin, ent->s.origin2 );
// see if the portal_camera has a target
target = G_PickTarget( owner->target );
if ( target ) {
VectorSubtract( target->s.origin, owner->s.origin, dir );
VectorNormalize( dir );
} else {
G_SetMovedir( owner->s.angles, dir );
}
ent->s.eventParm = DirToByte( dir );
}
/*QUAKED misc_portal_surface (0 0 1) (-8 -8 -8) (8 8 8)
The portal surface nearest this entity will show a view from the targeted misc_portal_camera, or a mirror view if untargeted.
This must be within 64 world units of the surface!
*/
void SP_misc_portal_surface(gentity_t *ent)
{
VectorClear( ent->r.mins );
VectorClear( ent->r.maxs );
trap_LinkEntity (ent);
ent->r.svFlags = SVF_PORTAL;
ent->s.eType = ET_PORTAL;
if ( !ent->target )
{
VectorCopy( ent->s.origin, ent->s.origin2 );
}
else
{
ent->think = locateCamera;
ent->nextthink = level.time + 100;
}
}
/*QUAKED misc_portal_camera (0 0 1) (-8 -8 -8) (8 8 8) slowrotate fastrotate noswing
The target for a misc_portal_director. You can set either angles or target another entity to determine the direction of view.
"roll" an angle modifier to orient the camera around the target vector;
*/
void SP_misc_portal_camera(gentity_t *ent)
{
float roll;
VectorClear( ent->r.mins );
VectorClear( ent->r.maxs );
trap_LinkEntity (ent);
G_SpawnFloat( "roll", "0", &roll );
ent->s.clientNum = roll/360.0 * 256;
}
/*QUAKED misc_bsp (1 0 0) (-16 -16 -16) (16 16 16)
"bspmodel" arbitrary .bsp file to display
*/
void SP_misc_bsp(gentity_t *ent)
{
char temp[MAX_QPATH];
char *out;
float newAngle;
int tempint;
G_SpawnFloat( "angle", "0", &newAngle );
if (newAngle != 0.0)
{
ent->s.angles[1] = newAngle;
}
// don't support rotation any other way
ent->s.angles[0] = 0.0;
ent->s.angles[2] = 0.0;
G_SpawnString("bspmodel", "", &out);
ent->s.eFlags = EF_PERMANENT;
// Mainly for debugging
G_SpawnInt( "spacing", "0", &tempint);
ent->s.time2 = tempint;
G_SpawnInt( "flatten", "0", &tempint);
ent->s.time = tempint;
Com_sprintf(temp, MAX_QPATH, "#%s", out);
trap_SetBrushModel( ent, temp ); // SV_SetBrushModel -- sets mins and maxs
G_BSPIndex(temp);
level.mNumBSPInstances++;
Com_sprintf(temp, MAX_QPATH, "%d-", level.mNumBSPInstances);
VectorCopy(ent->s.origin, level.mOriginAdjust);
level.mRotationAdjust = ent->s.angles[1];
level.mTargetAdjust = temp;
level.hasBspInstances = qtrue;
level.mBSPInstanceDepth++;
G_SpawnString("filter", "", &out);
strcpy(level.mFilter, out);
G_SpawnString("teamfilter", "", &out);
strcpy(level.mTeamFilter, out);
VectorCopy( ent->s.origin, ent->s.pos.trBase );
VectorCopy( ent->s.origin, ent->r.currentOrigin );
VectorCopy( ent->s.angles, ent->s.apos.trBase );
VectorCopy( ent->s.angles, ent->r.currentAngles );
ent->s.eType = ET_MOVER;
trap_LinkEntity (ent);
trap_SetActiveSubBSP(ent->s.modelindex);
G_SpawnEntitiesFromString(qtrue);
trap_SetActiveSubBSP(-1);
level.mBSPInstanceDepth--;
level.mFilter[0] = level.mTeamFilter[0] = 0;
if ( g_debugRMG.integer )
{
G_SpawnDebugCylinder ( ent->s.origin, ent->s.time2, &g_entities[0], 2000, COLOR_WHITE );
if ( ent->s.time )
{
G_SpawnDebugCylinder ( ent->s.origin, ent->s.time, &g_entities[0], 2000, COLOR_RED );
}
}
}
/*QUAKED terrain (1.0 1.0 1.0) ?
Terrain entity
It will stretch to the full height of the brush
numPatches - integer number of patches to split the terrain brush into (default 200)
terxels - integer number of terxels on a patch side (default 4) (2 <= count <= 8)
seed - integer seed for random terrain generation (default 0)
textureScale - float scale of texture (default 0.005)
heightMap - name of heightmap data image to use
terrainDef - defines how the game textures the terrain (file is base/ext_data/*.terrain - default is grassyhills)
instanceDef - defines which bsp instances appear
miscentDef - defines which client models spawn on the terrain (file is base/ext_data/*.miscents)
densityMap - how dense the client models are packed
*/
void SP_terrain(gentity_t *ent)
{
char temp[MAX_INFO_STRING];
char final[MAX_QPATH];
char seed[MAX_QPATH];
char missionType[MAX_QPATH];
char soundSet[MAX_QPATH];
int shaderNum, i;
char *value;
int terrainID;
VectorClear (ent->s.angles);
trap_SetBrushModel( ent, ent->model );
// Get the shader from the top of the brush
// shaderNum = gi.CM_GetShaderNum(s.modelindex);
shaderNum = 0;
if (RMG.integer)
{
// Grab the default terrain file from the RMG cvar
trap_Cvar_VariableStringBuffer("RMG_terrain", temp, MAX_QPATH);
Com_sprintf(final, MAX_QPATH, "%s", temp);
AddSpawnField("terrainDef", temp);
trap_Cvar_VariableStringBuffer("RMG_instances", temp, MAX_QPATH);
Com_sprintf(final, MAX_QPATH, "%s", temp);
AddSpawnField("instanceDef", temp);
trap_Cvar_VariableStringBuffer("RMG_miscents", temp, MAX_QPATH);
Com_sprintf(final, MAX_QPATH, "%s", temp);
AddSpawnField("miscentDef", temp);
trap_Cvar_VariableStringBuffer("RMG_seed", seed, MAX_QPATH);
trap_Cvar_VariableStringBuffer("RMG_mission", missionType, MAX_QPATH);
trap_Cvar_VariableStringBuffer("RMG_soundset", soundSet, MAX_QPATH);
trap_SetConfigstring(CS_AMBIENT_SOUNDSETS, soundSet );
}
// Arbitrary (but sane) limits to the number of terxels
// if((mTerxels < MIN_TERXELS) || (mTerxels > MAX_TERXELS))
{
// Com_printf("G_Terrain: terxels out of range - defaulting to 4\n");
// mTerxels = 4;
}
// Get info required for the common init
temp[0] = 0;
G_SpawnString("heightmap", "", &value);
Info_SetValueForKey(temp, "heightMap", value);
G_SpawnString("numpatches", "400", &value);
Info_SetValueForKey(temp, "numPatches", va("%d", atoi(value)));
G_SpawnString("terxels", "4", &value);
Info_SetValueForKey(temp, "terxels", va("%d", atoi(value)));
Info_SetValueForKey(temp, "seed", seed);
Info_SetValueForKey(temp, "minx", va("%f", ent->r.mins[0]));
Info_SetValueForKey(temp, "miny", va("%f", ent->r.mins[1]));
Info_SetValueForKey(temp, "minz", va("%f", ent->r.mins[2]));
Info_SetValueForKey(temp, "maxx", va("%f", ent->r.maxs[0]));
Info_SetValueForKey(temp, "maxy", va("%f", ent->r.maxs[1]));
Info_SetValueForKey(temp, "maxz", va("%f", ent->r.maxs[2]));
Info_SetValueForKey(temp, "modelIndex", va("%d", ent->s.modelindex));
G_SpawnString("terraindef", "grassyhills", &value);
Info_SetValueForKey(temp, "terrainDef", value);
G_SpawnString("instancedef", "", &value);
Info_SetValueForKey(temp, "instanceDef", value);
G_SpawnString("miscentdef", "", &value);
Info_SetValueForKey(temp, "miscentDef", value);
Info_SetValueForKey(temp, "missionType", missionType);
for(i = 0; i < MAX_INSTANCE_TYPES; i++)
{
trap_Cvar_VariableStringBuffer(va("RMG_instance%d", i), final, MAX_QPATH);
if(strlen(final))
{
Info_SetValueForKey(temp, va("inst%d", i), final);
}
}
// Set additional data required on the client only
G_SpawnString("densitymap", "", &value);
Info_SetValueForKey(temp, "densityMap", value);
Info_SetValueForKey(temp, "shader", va("%d", shaderNum));
G_SpawnString("texturescale", "0.005", &value);
Info_SetValueForKey(temp, "texturescale", va("%f", atof(value)));
// Initialise the common aspects of the terrain
terrainID = trap_CM_RegisterTerrain(temp);
// SetCommon(common);
Info_SetValueForKey(temp, "terrainId", va("%d", terrainID));
// Let the entity know if it is random generated or not
// SetIsRandom(common->GetIsRandom());
// Let the game remember everything
level.landScapes[terrainID] = ent;
// Send all the data down to the client
trap_SetConfigstring(CS_TERRAINS + terrainID, temp);
// Make sure the contents are properly set
ent->r.contents = CONTENTS_TERRAIN;
ent->r.svFlags = SVF_NOCLIENT;
ent->s.eFlags = EF_PERMANENT;
ent->s.eType = ET_TERRAIN;
// Hook into the world so physics will work
trap_LinkEntity(ent);
// If running RMG then initialize the terrain and handle team skins
if ( RMG.integer )
{
trap_RMG_Init(terrainID);
if ( level.gametypeData->teams )
{
char temp[MAX_QPATH];
// Red team change from RMG ?
trap_GetConfigstring ( CS_GAMETYPE_REDTEAM, temp, MAX_QPATH );
if ( Q_stricmp ( temp, level.gametypeTeam[TEAM_RED] ) )
{
level.gametypeTeam[TEAM_RED] = trap_VM_LocalStringAlloc ( temp );
}
// Blue team change from RMG ?
trap_GetConfigstring ( CS_GAMETYPE_BLUETEAM, temp, MAX_QPATH );
if ( Q_stricmp ( temp, level.gametypeTeam[TEAM_BLUE] ) )
{
level.gametypeTeam[TEAM_BLUE] = trap_VM_LocalStringAlloc ( temp );
}
}
}
}
/*
=================================================================================
G_DebugCylinderThink
=================================================================================
*/
void G_DebugCylinderThink ( gentity_t* ent )
{
vec3_t vec1;
vec3_t vec2;
ent->nextthink = level.time + 1000;
VectorCopy ( ent->parent->client->ps.origin, vec1 );
VectorCopy ( ent->r.currentOrigin, vec2 );
vec1[2] = 0;
vec2[2] = 0;
// IF we are too far away then kill it
if ( Distance ( vec1, vec2 ) > ent->speed )
{
trap_UnlinkEntity ( ent );
return;
}
trap_LinkEntity ( ent );
}
/*
=================================================================================
G_SpawnDebugCylinder
=================================================================================
*/
void G_SpawnDebugCylinder ( vec3_t origin, float radius, gentity_t* clientent, float viewRadius, int colorIndex )
{
gentity_t* ent;
if ( !g_cheats.integer )
{
return;
}
ent = G_Spawn ( );
ent->s.eType = ET_DEBUG_CYLINDER;
ent->s.time = colorIndex;
ent->s.time2 = (int) radius;
ent->parent = clientent;
ent->speed = viewRadius;
VectorCopy ( origin, ent->s.origin );
VectorCopy ( origin, ent->r.currentOrigin );
ent->nextthink = level.time + 1000;
ent->think = G_DebugCylinderThink;
trap_LinkEntity ( ent );
}
void fx_think( gentity_t *ent )
{
int time;
// play fx
G_PlayEffect( ent->health, ent->s.origin, ent->pos1 );
if( ent->count >= 0 ) // stops it from rolling over, yeah kinda lame...
{
ent->count--;
}
if( !ent->count ) // effect is suppose to play mCount times then dissapear
{
G_FreeEntity(ent);
return;
}
// calc next play time
time = (ent->wait + flrand(0.0f, ent->random)) * 1000; // need it in milliseconds
ent->think = fx_think;
ent->nextthink = level.time + time;
}
/*QUAKED fx_play_effect (.2 .5 .8) (-8 -8 -8) (8 8 8) START_OFF
Plays specified effect file
"effect" name of .efx file
"wait" seconds between triggerings, default 0.3
"random" wait variance in seconds, default 0
"target" direction of effect, default up
"count" plays effect this many times then deletes itself, default -1 = infinite
START_OFF fx starts off
*/
void SP_fx_play_effect(gentity_t *ent)
{
gentity_t *target;
char *fxName;
G_SetOrigin( ent, ent->s.origin );
G_SpawnString("effect", "", &fxName);
ent->health = G_EffectIndex(fxName);
if (ent->wait == 0.0)
{
ent->wait = 0.3;
}
target = G_Find(0, FOFS(targetname), ent->target);
if (target)
{
VectorSubtract( target->s.origin, ent->s.origin, ent->pos1 );
VectorNormalize( ent->pos1 );
// find angles
vectoangles( ent->pos1, ent->r.currentAngles );
// copy over to other angles
VectorCopy( ent->r.currentAngles, ent->s.angles );
VectorCopy( ent->r.currentAngles, ent->s.apos.trBase );
}
else
{
ent->pos1[0] = ent->pos1[1] = 0.0;
ent->pos1[2] = 1.0;
}
ent->think = fx_think;
ent->nextthink = level.time + 100;
}