Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

g_misc.c (13207B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 //
     23 // g_misc.c
     24 
     25 #include "g_local.h"
     26 
     27 
     28 /*QUAKED func_group (0 0 0) ?
     29 Used to group brushes together just for editor convenience.  They are turned into normal brushes by the utilities.
     30 */
     31 
     32 
     33 /*QUAKED info_camp (0 0.5 0) (-4 -4 -4) (4 4 4)
     34 Used as a positional target for calculations in the utilities (spotlights, etc), but removed during gameplay.
     35 */
     36 void SP_info_camp( gentity_t *self ) {
     37 	G_SetOrigin( self, self->s.origin );
     38 }
     39 
     40 
     41 /*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4)
     42 Used as a positional target for calculations in the utilities (spotlights, etc), but removed during gameplay.
     43 */
     44 void SP_info_null( gentity_t *self ) {
     45 	G_FreeEntity( self );
     46 }
     47 
     48 
     49 /*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)
     50 Used as a positional target for in-game calculation, like jumppad targets.
     51 target_position does the same thing
     52 */
     53 void SP_info_notnull( gentity_t *self ){
     54 	G_SetOrigin( self, self->s.origin );
     55 }
     56 
     57 
     58 /*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) linear
     59 Non-displayed light.
     60 "light" overrides the default 300 intensity.
     61 Linear checbox gives linear falloff instead of inverse square
     62 Lights pointed at a target will be spotlights.
     63 "radius" overrides the default 64 unit radius of a spotlight at the target point.
     64 */
     65 void SP_light( gentity_t *self ) {
     66 	G_FreeEntity( self );
     67 }
     68 
     69 
     70 
     71 /*
     72 =================================================================================
     73 
     74 TELEPORTERS
     75 
     76 =================================================================================
     77 */
     78 
     79 void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles ) {
     80 	gentity_t	*tent;
     81 
     82 	// use temp events at source and destination to prevent the effect
     83 	// from getting dropped by a second player event
     84 	if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
     85 		tent = G_TempEntity( player->client->ps.origin, EV_PLAYER_TELEPORT_OUT );
     86 		tent->s.clientNum = player->s.clientNum;
     87 
     88 		tent = G_TempEntity( origin, EV_PLAYER_TELEPORT_IN );
     89 		tent->s.clientNum = player->s.clientNum;
     90 	}
     91 
     92 	// unlink to make sure it can't possibly interfere with G_KillBox
     93 	trap_UnlinkEntity (player);
     94 
     95 	VectorCopy ( origin, player->client->ps.origin );
     96 	player->client->ps.origin[2] += 1;
     97 
     98 	// spit the player out
     99 	AngleVectors( angles, player->client->ps.velocity, NULL, NULL );
    100 	VectorScale( player->client->ps.velocity, 400, player->client->ps.velocity );
    101 	player->client->ps.pm_time = 160;		// hold time
    102 	player->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
    103 
    104 	// toggle the teleport bit so the client knows to not lerp
    105 	player->client->ps.eFlags ^= EF_TELEPORT_BIT;
    106 
    107 	// set angles
    108 	SetClientViewAngle( player, angles );
    109 
    110 	// kill anything at the destination
    111 	if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
    112 		G_KillBox (player);
    113 	}
    114 
    115 	// save results of pmove
    116 	BG_PlayerStateToEntityState( &player->client->ps, &player->s, qtrue );
    117 
    118 	// use the precise origin for linking
    119 	VectorCopy( player->client->ps.origin, player->r.currentOrigin );
    120 
    121 	if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
    122 		trap_LinkEntity (player);
    123 	}
    124 }
    125 
    126 
    127 /*QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16)
    128 Point teleporters at these.
    129 Now that we don't have teleport destination pads, this is just
    130 an info_notnull
    131 */
    132 void SP_misc_teleporter_dest( gentity_t *ent ) {
    133 }
    134 
    135 
    136 //===========================================================
    137 
    138 /*QUAKED misc_model (1 0 0) (-16 -16 -16) (16 16 16)
    139 "model"		arbitrary .md3 file to display
    140 */
    141 void SP_misc_model( gentity_t *ent ) {
    142 
    143 #if 0
    144 	ent->s.modelindex = G_ModelIndex( ent->model );
    145 	VectorSet (ent->mins, -16, -16, -16);
    146 	VectorSet (ent->maxs, 16, 16, 16);
    147 	trap_LinkEntity (ent);
    148 
    149 	G_SetOrigin( ent, ent->s.origin );
    150 	VectorCopy( ent->s.angles, ent->s.apos.trBase );
    151 #else
    152 	G_FreeEntity( ent );
    153 #endif
    154 }
    155 
    156 //===========================================================
    157 
    158 void locateCamera( gentity_t *ent ) {
    159 	vec3_t		dir;
    160 	gentity_t	*target;
    161 	gentity_t	*owner;
    162 
    163 	owner = G_PickTarget( ent->target );
    164 	if ( !owner ) {
    165 		G_Printf( "Couldn't find target for misc_partal_surface\n" );
    166 		G_FreeEntity( ent );
    167 		return;
    168 	}
    169 	ent->r.ownerNum = owner->s.number;
    170 
    171 	// frame holds the rotate speed
    172 	if ( owner->spawnflags & 1 ) {
    173 		ent->s.frame = 25;
    174 	} else if ( owner->spawnflags & 2 ) {
    175 		ent->s.frame = 75;
    176 	}
    177 
    178 	// swing camera ?
    179 	if ( owner->spawnflags & 4 ) {
    180 		// set to 0 for no rotation at all
    181 		ent->s.powerups = 0;
    182 	}
    183 	else {
    184 		ent->s.powerups = 1;
    185 	}
    186 
    187 	// clientNum holds the rotate offset
    188 	ent->s.clientNum = owner->s.clientNum;
    189 
    190 	VectorCopy( owner->s.origin, ent->s.origin2 );
    191 
    192 	// see if the portal_camera has a target
    193 	target = G_PickTarget( owner->target );
    194 	if ( target ) {
    195 		VectorSubtract( target->s.origin, owner->s.origin, dir );
    196 		VectorNormalize( dir );
    197 	} else {
    198 		G_SetMovedir( owner->s.angles, dir );
    199 	}
    200 
    201 	ent->s.eventParm = DirToByte( dir );
    202 }
    203 
    204 /*QUAKED misc_portal_surface (0 0 1) (-8 -8 -8) (8 8 8)
    205 The portal surface nearest this entity will show a view from the targeted misc_portal_camera, or a mirror view if untargeted.
    206 This must be within 64 world units of the surface!
    207 */
    208 void SP_misc_portal_surface(gentity_t *ent) {
    209 	VectorClear( ent->r.mins );
    210 	VectorClear( ent->r.maxs );
    211 	trap_LinkEntity (ent);
    212 
    213 	ent->r.svFlags = SVF_PORTAL;
    214 	ent->s.eType = ET_PORTAL;
    215 
    216 	if ( !ent->target ) {
    217 		VectorCopy( ent->s.origin, ent->s.origin2 );
    218 	} else {
    219 		ent->think = locateCamera;
    220 		ent->nextthink = level.time + 100;
    221 	}
    222 }
    223 
    224 /*QUAKED misc_portal_camera (0 0 1) (-8 -8 -8) (8 8 8) slowrotate fastrotate noswing
    225 The target for a misc_portal_director.  You can set either angles or target another entity to determine the direction of view.
    226 "roll" an angle modifier to orient the camera around the target vector;
    227 */
    228 void SP_misc_portal_camera(gentity_t *ent) {
    229 	float	roll;
    230 
    231 	VectorClear( ent->r.mins );
    232 	VectorClear( ent->r.maxs );
    233 	trap_LinkEntity (ent);
    234 
    235 	G_SpawnFloat( "roll", "0", &roll );
    236 
    237 	ent->s.clientNum = roll/360.0 * 256;
    238 }
    239 
    240 /*
    241 ======================================================================
    242 
    243   SHOOTERS
    244 
    245 ======================================================================
    246 */
    247 
    248 void Use_Shooter( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
    249 	vec3_t		dir;
    250 	float		deg;
    251 	vec3_t		up, right;
    252 
    253 	// see if we have a target
    254 	if ( ent->enemy ) {
    255 		VectorSubtract( ent->enemy->r.currentOrigin, ent->s.origin, dir );
    256 		VectorNormalize( dir );
    257 	} else {
    258 		VectorCopy( ent->movedir, dir );
    259 	}
    260 
    261 	// randomize a bit
    262 	PerpendicularVector( up, dir );
    263 	CrossProduct( up, dir, right );
    264 
    265 	deg = crandom() * ent->random;
    266 	VectorMA( dir, deg, up, dir );
    267 
    268 	deg = crandom() * ent->random;
    269 	VectorMA( dir, deg, right, dir );
    270 
    271 	VectorNormalize( dir );
    272 
    273 	switch ( ent->s.weapon ) {
    274 	case WP_GRENADE_LAUNCHER:
    275 		fire_grenade( ent, ent->s.origin, dir );
    276 		break;
    277 	case WP_ROCKET_LAUNCHER:
    278 		fire_rocket( ent, ent->s.origin, dir );
    279 		break;
    280 	case WP_PLASMAGUN:
    281 		fire_plasma( ent, ent->s.origin, dir );
    282 		break;
    283 	}
    284 
    285 	G_AddEvent( ent, EV_FIRE_WEAPON, 0 );
    286 }
    287 
    288 
    289 static void InitShooter_Finish( gentity_t *ent ) {
    290 	ent->enemy = G_PickTarget( ent->target );
    291 	ent->think = 0;
    292 	ent->nextthink = 0;
    293 }
    294 
    295 void InitShooter( gentity_t *ent, int weapon ) {
    296 	ent->use = Use_Shooter;
    297 	ent->s.weapon = weapon;
    298 
    299 	RegisterItem( BG_FindItemForWeapon( weapon ) );
    300 
    301 	G_SetMovedir( ent->s.angles, ent->movedir );
    302 
    303 	if ( !ent->random ) {
    304 		ent->random = 1.0;
    305 	}
    306 	ent->random = sin( M_PI * ent->random / 180 );
    307 	// target might be a moving object, so we can't set movedir for it
    308 	if ( ent->target ) {
    309 		ent->think = InitShooter_Finish;
    310 		ent->nextthink = level.time + 500;
    311 	}
    312 	trap_LinkEntity( ent );
    313 }
    314 
    315 /*QUAKED shooter_rocket (1 0 0) (-16 -16 -16) (16 16 16)
    316 Fires at either the target or the current direction.
    317 "random" the number of degrees of deviance from the taget. (1.0 default)
    318 */
    319 void SP_shooter_rocket( gentity_t *ent ) {
    320 	InitShooter( ent, WP_ROCKET_LAUNCHER );
    321 }
    322 
    323 /*QUAKED shooter_plasma (1 0 0) (-16 -16 -16) (16 16 16)
    324 Fires at either the target or the current direction.
    325 "random" is the number of degrees of deviance from the taget. (1.0 default)
    326 */
    327 void SP_shooter_plasma( gentity_t *ent ) {
    328 	InitShooter( ent, WP_PLASMAGUN);
    329 }
    330 
    331 /*QUAKED shooter_grenade (1 0 0) (-16 -16 -16) (16 16 16)
    332 Fires at either the target or the current direction.
    333 "random" is the number of degrees of deviance from the taget. (1.0 default)
    334 */
    335 void SP_shooter_grenade( gentity_t *ent ) {
    336 	InitShooter( ent, WP_GRENADE_LAUNCHER);
    337 }
    338 
    339 
    340 #ifdef MISSIONPACK
    341 static void PortalDie (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod) {
    342 	G_FreeEntity( self );
    343 	//FIXME do something more interesting
    344 }
    345 
    346 
    347 void DropPortalDestination( gentity_t *player ) {
    348 	gentity_t	*ent;
    349 	vec3_t		snapped;
    350 
    351 	// create the portal destination
    352 	ent = G_Spawn();
    353 	ent->s.modelindex = G_ModelIndex( "models/powerups/teleporter/tele_exit.md3" );
    354 
    355 	VectorCopy( player->s.pos.trBase, snapped );
    356 	SnapVector( snapped );
    357 	G_SetOrigin( ent, snapped );
    358 	VectorCopy( player->r.mins, ent->r.mins );
    359 	VectorCopy( player->r.maxs, ent->r.maxs );
    360 
    361 	ent->classname = "hi_portal destination";
    362 	ent->s.pos.trType = TR_STATIONARY;
    363 
    364 	ent->r.contents = CONTENTS_CORPSE;
    365 	ent->takedamage = qtrue;
    366 	ent->health = 200;
    367 	ent->die = PortalDie;
    368 
    369 	VectorCopy( player->s.apos.trBase, ent->s.angles );
    370 
    371 	ent->think = G_FreeEntity;
    372 	ent->nextthink = level.time + 2 * 60 * 1000;
    373 
    374 	trap_LinkEntity( ent );
    375 
    376 	player->client->portalID = ++level.portalSequence;
    377 	ent->count = player->client->portalID;
    378 
    379 	// give the item back so they can drop the source now
    380 	player->client->ps.stats[STAT_HOLDABLE_ITEM] = BG_FindItem( "Portal" ) - bg_itemlist;
    381 }
    382 
    383 
    384 static void PortalTouch( gentity_t *self, gentity_t *other, trace_t *trace) {
    385 	gentity_t	*destination;
    386 
    387 	// see if we will even let other try to use it
    388 	if( other->health <= 0 ) {
    389 		return;
    390 	}
    391 	if( !other->client ) {
    392 		return;
    393 	}
    394 //	if( other->client->ps.persistant[PERS_TEAM] != self->spawnflags ) {
    395 //		return;
    396 //	}
    397 
    398 	if ( other->client->ps.powerups[PW_NEUTRALFLAG] ) {		// only happens in One Flag CTF
    399 		Drop_Item( other, BG_FindItemForPowerup( PW_NEUTRALFLAG ), 0 );
    400 		other->client->ps.powerups[PW_NEUTRALFLAG] = 0;
    401 	}
    402 	else if ( other->client->ps.powerups[PW_REDFLAG] ) {		// only happens in standard CTF
    403 		Drop_Item( other, BG_FindItemForPowerup( PW_REDFLAG ), 0 );
    404 		other->client->ps.powerups[PW_REDFLAG] = 0;
    405 	}
    406 	else if ( other->client->ps.powerups[PW_BLUEFLAG] ) {	// only happens in standard CTF
    407 		Drop_Item( other, BG_FindItemForPowerup( PW_BLUEFLAG ), 0 );
    408 		other->client->ps.powerups[PW_BLUEFLAG] = 0;
    409 	}
    410 
    411 	// find the destination
    412 	destination = NULL;
    413 	while( (destination = G_Find(destination, FOFS(classname), "hi_portal destination")) != NULL ) {
    414 		if( destination->count == self->count ) {
    415 			break;
    416 		}
    417 	}
    418 
    419 	// if there is not one, die!
    420 	if( !destination ) {
    421 		if( self->pos1[0] || self->pos1[1] || self->pos1[2] ) {
    422 			TeleportPlayer( other, self->pos1, self->s.angles );
    423 		}
    424 		G_Damage( other, other, other, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_TELEFRAG );
    425 		return;
    426 	}
    427 
    428 	TeleportPlayer( other, destination->s.pos.trBase, destination->s.angles );
    429 }
    430 
    431 
    432 static void PortalEnable( gentity_t *self ) {
    433 	self->touch = PortalTouch;
    434 	self->think = G_FreeEntity;
    435 	self->nextthink = level.time + 2 * 60 * 1000;
    436 }
    437 
    438 
    439 void DropPortalSource( gentity_t *player ) {
    440 	gentity_t	*ent;
    441 	gentity_t	*destination;
    442 	vec3_t		snapped;
    443 
    444 	// create the portal source
    445 	ent = G_Spawn();
    446 	ent->s.modelindex = G_ModelIndex( "models/powerups/teleporter/tele_enter.md3" );
    447 
    448 	VectorCopy( player->s.pos.trBase, snapped );
    449 	SnapVector( snapped );
    450 	G_SetOrigin( ent, snapped );
    451 	VectorCopy( player->r.mins, ent->r.mins );
    452 	VectorCopy( player->r.maxs, ent->r.maxs );
    453 
    454 	ent->classname = "hi_portal source";
    455 	ent->s.pos.trType = TR_STATIONARY;
    456 
    457 	ent->r.contents = CONTENTS_CORPSE | CONTENTS_TRIGGER;
    458 	ent->takedamage = qtrue;
    459 	ent->health = 200;
    460 	ent->die = PortalDie;
    461 
    462 	trap_LinkEntity( ent );
    463 
    464 	ent->count = player->client->portalID;
    465 	player->client->portalID = 0;
    466 
    467 //	ent->spawnflags = player->client->ps.persistant[PERS_TEAM];
    468 
    469 	ent->nextthink = level.time + 1000;
    470 	ent->think = PortalEnable;
    471 
    472 	// find the destination
    473 	destination = NULL;
    474 	while( (destination = G_Find(destination, FOFS(classname), "hi_portal destination")) != NULL ) {
    475 		if( destination->count == ent->count ) {
    476 			VectorCopy( destination->s.pos.trBase, ent->pos1 );
    477 			break;
    478 		}
    479 	}
    480 
    481 }
    482 #endif