// Copyright (C) 1999-2000 Id Software, Inc. // // g_misc.c /* * Portions Copyright (C) 2000-2001 Tim Angus * * This program is free software; you can redistribute it and/or modify it * under the terms of the OSML - Open Source Modification License v1.0 as * described in the file COPYING which is distributed with this source * code. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #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_null (0 0.5 0) (-4 -4 -4) (4 4 4) Used as a positional target for calculations in the utilities (spotlights, etc), but removed during gameplay. */ void SP_info_null( gentity_t *self ) { G_FreeEntity( self ); } /*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 ); } /*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) linear Non-displayed light. "light" overrides the default 300 intensity. Linear checbox gives linear falloff instead of inverse square Lights pointed at a target will be spotlights. "radius" overrides the default 64 unit radius of a spotlight at the target point. */ void SP_light( gentity_t *self ) { G_FreeEntity( self ); } /* ================================================================================= TELEPORTERS ================================================================================= */ 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( player->client->sess.sessionTeam != TEAM_SPECTATOR ) { 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( player->client->sess.sessionTeam != TEAM_SPECTATOR ) 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( player->client->sess.sessionTeam != TEAM_SPECTATOR ) 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) "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 } //=========================================================== void locateCamera( gentity_t *ent ) { vec3_t dir; gentity_t *target; gentity_t *owner; owner = G_PickTarget( ent->target ); if( !owner ) { G_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.powerups = 0; } else ent->s.powerups = 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.0f * 256; } /* ====================================================================== NEAT EFFECTS AND STUFF FOR TREMULOUS ====================================================================== */ //TA: use function for spriter void SP_use_spriter( gentity_t *self, gentity_t *other, gentity_t *activator ) { //toggle EF_NODRAW if( self->s.eFlags & EF_NODRAW ) self->s.eFlags &= ~EF_NODRAW; else self->s.eFlags |= EF_NODRAW; } //TA: spawn function for spriter void SP_misc_spriter( gentity_t *self ) { vec3_t accel; G_SetOrigin( self, self->s.origin ); //set a bunch of stuff to be visible client side VectorCopy( self->acceleration, self->s.origin2 ); self->s.time = (int)self->speed; self->s.time2 = (int)self->wait; self->s.powerups = (int)self->random; self->s.angles2[ 0 ] = self->physicsBounce; self->s.modelindex = self->pos1[ 0 ]; self->s.modelindex2 = self->pos1[ 1 ]; self->s.legsAnim = self->pos2[ 0 ]; self->s.torsoAnim = self->pos2[ 1 ]; if( self->count > 0 ) self->s.angles2[ 1 ] = ( 1000 / self->count ); else self->s.angles2[ 1 ] = 1000; //add the shader to the client precache list self->s.weapon = G_ShaderIndex( self->targetShaderName ); if( self->spawnflags & 1 ) self->s.eFlags |= EF_OVERDRAW_OFF; if( self->spawnflags & ( 1 << 1 ) ) self->s.eFlags |= EF_REAL_LIGHT; if( self->spawnflags & ( 1 << 2 ) ) self->s.eFlags |= EF_NODRAW; self->use = SP_use_spriter; self->s.eType = ET_SPRITER; trap_LinkEntity( self ); } //TA: use function for anim model void SP_use_anim_model( gentity_t *self, gentity_t *other, gentity_t *activator ) { if( self->spawnflags & 1 ) { //if spawnflag 1 is set //toggle EF_NODRAW if( self->s.eFlags & EF_NODRAW ) self->s.eFlags &= ~EF_NODRAW; else self->s.eFlags |= EF_NODRAW; } else { //if the animation loops then toggle the animation //toggle EF_MOVER_STOP if( self->s.eFlags & EF_MOVER_STOP ) self->s.eFlags &= ~EF_MOVER_STOP; else self->s.eFlags |= EF_MOVER_STOP; } } //TA: spawn function for anim model void SP_misc_anim_model( gentity_t *self ) { self->s.powerups = (int)self->animation[ 0 ]; self->s.weapon = (int)self->animation[ 1 ]; self->s.torsoAnim = (int)self->animation[ 2 ]; self->s.legsAnim = (int)self->animation[ 3 ]; self->s.angles2[ 0 ] = self->pos2[ 0 ]; //add the model to the client precache list self->s.modelindex = G_ModelIndex( self->model ); self->use = SP_use_anim_model; self->s.eType = ET_ANIMMAPOBJ; trap_LinkEntity( self ); } //TA: use function for light flares void SP_use_light_flare( gentity_t *self, gentity_t *other, gentity_t *activator ) { self->s.eFlags ^= EF_NODRAW; } //TA: finds an empty spot radius units from origin static void findEmptySpot( vec3_t origin, float radius, vec3_t spot ) { int i, j, k; vec3_t delta, test, total; trace_t tr; VectorClear( total ); //54(!) traces to test for empty spots for( i = -1; i <= 1; i++ ) { for( j = -1; j <= 1; j++ ) { for( k = -1; k <= 1; k++ ) { VectorSet( delta, ( i * radius ), ( j * radius ), ( k * radius ) ); VectorAdd( origin, delta, test ); trap_Trace( &tr, test, NULL, NULL, test, -1, MASK_SOLID ); if( !tr.allsolid ) { trap_Trace( &tr, test, NULL, NULL, origin, -1, MASK_SOLID ); VectorScale( delta, tr.fraction, delta ); VectorAdd( total, delta, total ); } } } } VectorNormalize( total ); VectorScale( total, radius, total ); VectorAdd( origin, total, spot ); } //TA: spawn function for light flares void SP_misc_light_flare( gentity_t *self ) { self->s.eType = ET_LIGHTFLARE; self->s.modelindex = G_ShaderIndex( self->targetShaderName ); VectorCopy( self->pos2, self->s.origin2 ); //try to find a spot near to the flare which is empty. This //is used to facilitate visibility testing findEmptySpot( self->s.origin, 8.0f, self->s.angles2 ); self->use = SP_use_light_flare; G_SpawnFloat( "speed", "200", &self->speed ); self->s.time = self->speed; G_SpawnInt( "mindist", "0", &self->s.generic1 ); if( self->spawnflags & 1 ) self->s.eFlags |= EF_NODRAW; trap_LinkEntity( self ); }