mirror of
https://github.com/UberGames/rpgxEF.git
synced 2024-11-10 07:11:34 +00:00
75ec1fce7b
On the way merged target_postiton with info_notnull Also added a failsafe to target_selfdestruct so it can only be commandspawned Signed-off-by: Harry Young <hendrik.gerritzen@googlemail.com>
685 lines
20 KiB
C
685 lines
20 KiB
C
// Copyright (C) 1999-2000 Id Software, Inc.
|
|
//
|
|
// g_misc.c
|
|
|
|
#include "g_local.h"
|
|
|
|
|
|
/*QUAKED func_group (0 0 0) ?
|
|
-----DESCRIPTION-----
|
|
Used to group brushes together just for editor convenience. They are turned into normal brushes by the utilities.
|
|
|
|
-----SPAWNFLAGS-----
|
|
none
|
|
|
|
-----KEYS-----
|
|
q3map2:
|
|
"_lightmapscale" - set a diffrent lightmapscale for this func group only
|
|
*/
|
|
|
|
|
|
/*QUAKED info_camp (0 0.5 0) (-4 -4 -4) (4 4 4)
|
|
-----DESCRIPTION-----
|
|
Used as a positional target for calculations in the compiler/utilities (spotlights, etc), but removed during gameplay.
|
|
|
|
-----SAPWNFLAGS-----
|
|
none
|
|
|
|
-----KEYS-----
|
|
"targetname" - have whatever is required point at this.
|
|
*/
|
|
// Lol, this is contradictory, should free but instead sets origin... Description sais removed so maybe merge with info_null.
|
|
void SP_info_camp( gentity_t *self ) {
|
|
G_SetOrigin( self, self->s.origin );
|
|
}
|
|
|
|
|
|
/*QUAKED info_null (0 0.5 0) (-4 -4 -4) (4 4 4)
|
|
-----DESCRIPTION-----
|
|
Used as a positional target for calculations in the compiler/utilities (spotlights, etc), but removed during gameplay.
|
|
|
|
-----SAPWNFLAGS-----
|
|
none
|
|
|
|
-----KEYS-----
|
|
"targetname" - have whatever is required point at this.
|
|
*/
|
|
void SP_info_null( gentity_t *self ) {
|
|
G_FreeEntity( self );
|
|
}
|
|
|
|
|
|
/*QUAKED info_notnull (0 0.5 0) (-4 -4 -4) (4 4 4)
|
|
-----DESCRIPTION-----
|
|
Used as a positional target for in-game calculation, like jumppad targets.
|
|
target_position does the same thing
|
|
|
|
-----SPAWNFLAGS-----
|
|
none
|
|
|
|
-----KEYS-----
|
|
"targetname" - have whatever is required point at this.
|
|
*/
|
|
|
|
//these share the spawnfunction with info_notnull
|
|
|
|
/*QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16)
|
|
-----DESCRIPTION-----
|
|
Merely a fancy name for info_notnull.
|
|
was originally used for teleporters but became redundant.
|
|
|
|
-----SPAWNFLAGS-----
|
|
none
|
|
|
|
-----KEYS-----
|
|
"targetname" - have whatever is required point at this.
|
|
*/
|
|
/*QUAKED target_position (0 0.5 0) (-4 -4 -4) (4 4 4)
|
|
-----DESCRIPTION-----
|
|
Merely a fancy name for info_notnull.
|
|
|
|
-----SPAWNFLAGS-----
|
|
none
|
|
|
|
-----KEYS-----
|
|
"targetname" - have whatever is required point at this.
|
|
*/
|
|
void SP_info_notnull( gentity_t *self ){
|
|
if(!Q_stricmp(self->classname, "ref_tag") && !rpg_allowspmaps.integer)
|
|
G_FreeEntity(self);
|
|
|
|
if(strcmp(self->classname, "info_notnull")) {
|
|
self->classname = G_NewString("info_notnull");
|
|
}
|
|
G_SetOrigin( self, self->s.origin );
|
|
}
|
|
|
|
|
|
/*QUAKED light (0 1 0) (-8 -8 -8) (8 8 8) LINEAR NO_INCIDENCE X X NO_GRID NORMALIZED_COLOR FORCE_DISTANCE_ATTENUATION
|
|
-----DESCRIPTION-----
|
|
Light source for the compiler. Will be removed ingame.
|
|
Lights pointed at a target (info_null) will be spotlights.
|
|
|
|
-----SPAWNFLAGS-----
|
|
1: LINEAR - checkbox gives linear falloff instead of inverse square
|
|
2: NO_INCIDENCE - checkbox makes lighting smoother
|
|
4: X - Unknown. Usage not recomended.
|
|
8: X - Unknown. Usage not recomended.
|
|
|
|
q3map2:
|
|
16: NO_GRID - light does not affect the grid
|
|
32: NORMALIZED_COLOR - light color gets normalized by the compiler
|
|
64: FORCE_DISTANCE_ATTENUATION - distance attenuation is enforced
|
|
|
|
-----KEYS-----
|
|
"light" - overrides the default 300 intensity.
|
|
"radius" - overrides the default 64 unit radius of a spotlight at the target point.
|
|
"_color" - light color
|
|
|
|
q3map2:
|
|
"_style" - light style number
|
|
"fade" - Fade factor of light attenuation of linear lights. (Linear lights vanish at light/(fade * 8000).
|
|
"_anglescale" - scales angle attenuation
|
|
"scale" - intensity multiplier
|
|
"_samples" - number of samples to use to get soft shadows from a light
|
|
"_deviance" - position deviance of the samples of a regular light
|
|
"_filterradius" - filter radius for this light
|
|
"_sun" - if 1, this light is an infinite sun light
|
|
"_flareshader" - shader for a flare surface generated by this light
|
|
"_flare" - when set, this light is a flare without a specified shader
|
|
*/
|
|
void SP_light( gentity_t *self ) {
|
|
G_FreeEntity( self );
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
=================================================================================
|
|
|
|
TELEPORTERS
|
|
|
|
=================================================================================
|
|
*/
|
|
|
|
void TransportPlayer( gentity_t *player, vec3_t origin, vec3_t angles, int speed )
|
|
{
|
|
gentity_t *tent = NULL;
|
|
playerState_t *ps = &player->client->ps;
|
|
clientSession_t *sess = &player->client->sess;
|
|
|
|
// use temp events at source and destination to prevent the effect
|
|
// from getting dropped by a second player event
|
|
if ( sess->sessionTeam != TEAM_SPECTATOR /*&& !(ps->eFlags&EF_ELIMINATED)*/ ) {
|
|
vec3_t org;
|
|
|
|
VectorCopy( ps->origin, org );
|
|
org[2] += (ps->viewheight >> 1);
|
|
|
|
tent = G_TempEntity( 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, ps->origin );
|
|
ps->origin[2] += 1;
|
|
|
|
// spit the player out
|
|
AngleVectors( angles, ps->velocity, NULL, NULL );
|
|
VectorScale( ps->velocity, speed, ps->velocity );
|
|
ps->pm_time = 160; // hold time
|
|
ps->pm_flags |= PMF_TIME_KNOCKBACK;
|
|
|
|
// toggle the teleport bit so the client knows to not lerp
|
|
ps->eFlags ^= EF_TELEPORT_BIT;
|
|
|
|
// set angles
|
|
G_Client_SetViewAngle( player, angles );
|
|
|
|
// kill anything at the destination
|
|
if ( sess->sessionTeam != TEAM_SPECTATOR /*&& !(ps->eFlags&EF_ELIMINATED)*/) {
|
|
G_KillBox (player);
|
|
}
|
|
|
|
// save results of pmove
|
|
BG_PlayerStateToEntityState( &player->client->ps, &player->s, qtrue );
|
|
|
|
// use the precise origin for linking
|
|
VectorCopy( ps->origin, player->r.currentOrigin );
|
|
|
|
if ( sess->sessionTeam != TEAM_SPECTATOR /*&& !(ps->eFlags&EF_ELIMINATED)*/) {
|
|
trap_LinkEntity (player);
|
|
}
|
|
}
|
|
|
|
|
|
void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles, tpType_t tpType ) {
|
|
gentity_t *tent;
|
|
playerState_t *ps = &player->client->ps;
|
|
clientSession_t *sess = &player->client->sess;
|
|
|
|
// unlink to make sure it can't possibly interfere with G_KillBox
|
|
trap_UnlinkEntity (player);
|
|
|
|
VectorCopy ( origin, ps->origin );
|
|
|
|
// use temp events at source and destination to prevent the effect
|
|
// from getting dropped by a second player event
|
|
if ( sess->sessionTeam != TEAM_SPECTATOR )
|
|
{
|
|
if ( tpType == TP_BORG )
|
|
{
|
|
// ...we are borg...prepare to be...
|
|
tent = G_TempEntity( origin, EV_BORG_TELEPORT );
|
|
tent->s.clientNum = player->s.clientNum;
|
|
}
|
|
//RPG-X: J2J Added to get fed trans effect without any traveling after beam in
|
|
//TiM: Since the SP teleporter has been coded to only work with Jay's modification,
|
|
//we'll add the second half of the client-side effects (ie materialization) here
|
|
else if( tpType == TP_TRI_TP )
|
|
{
|
|
// probably isn't necessary, but just in case, end the beam out powerup
|
|
ps->powerups[PW_BEAM_OUT] = 0;
|
|
//and add the beam in one
|
|
ps->powerups[PW_QUAD] = level.time + 4000;
|
|
|
|
tent = G_TempEntity( ps->origin, EV_PLAYER_TRANSPORT_IN );
|
|
tent->s.clientNum = player->s.clientNum;
|
|
}
|
|
}
|
|
|
|
// spit the player out
|
|
//TiM - If in a turbolift and moving, get their velocity, perform the rotation
|
|
//calc on it, and then reset it to their velocity.
|
|
if ( tpType == TP_TURBO )
|
|
{
|
|
vec3_t dir;
|
|
vec3_t velAngles;
|
|
float length;
|
|
|
|
VectorCopy( ps->velocity, dir );
|
|
length = VectorLength( dir );
|
|
VectorNormalize( dir );
|
|
vectoangles( dir, velAngles );
|
|
|
|
velAngles[YAW] = AngleNormalize360( velAngles[YAW]+ (angles[YAW] - ps->viewangles[YAW] ) );
|
|
AngleVectors( velAngles, dir, NULL, NULL );
|
|
|
|
VectorScale( dir, length, ps->velocity);
|
|
}
|
|
else {
|
|
//TiM: Set the velocity to 0. So if they were moving b4 the transport, they'll be stopped when they come out.
|
|
//It's a little something called the Heisenberg compensators. ;)
|
|
|
|
//bug-fix. if the velocity is killed in a spectator door teleporter, the player
|
|
//gets wedged in the door. >_<
|
|
if ( sess->sessionTeam != TEAM_SPECTATOR )
|
|
VectorScale( ps->velocity, 0, ps->velocity );
|
|
else
|
|
{
|
|
AngleVectors( angles, ps->velocity, NULL, NULL );
|
|
VectorScale( ps->velocity, 400, ps->velocity );
|
|
ps->pm_time = 160; // hold time
|
|
ps->pm_flags |= PMF_TIME_KNOCKBACK;
|
|
}
|
|
|
|
{//see if we can move the player up one
|
|
vec3_t newOrg;
|
|
trace_t tr;
|
|
|
|
VectorCopy ( origin, newOrg );
|
|
newOrg[2] += 1;
|
|
trap_Trace( &tr, ps->origin, player->r.mins, player->r.maxs, newOrg, ps->clientNum, player->clipmask );
|
|
if ( !tr.allsolid && !tr.startsolid && tr.fraction == 1.0 )
|
|
{
|
|
ps->origin[2] += 1;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// toggle the teleport bit so the client knows to not lerp
|
|
ps->eFlags ^= EF_TELEPORT_BIT;
|
|
|
|
// set angles
|
|
G_Client_SetViewAngle( player, angles );
|
|
|
|
// kill anything at the destination
|
|
if ( sess->sessionTeam != TEAM_SPECTATOR ) {
|
|
if ( G_MoveBox (player) )
|
|
player->r.contents = CONTENTS_NONE;
|
|
}
|
|
|
|
// save results of pmove
|
|
BG_PlayerStateToEntityState( &player->client->ps, &player->s, qtrue );
|
|
|
|
// use the precise origin for linking
|
|
VectorCopy( ps->origin, player->r.currentOrigin );
|
|
|
|
if ( sess->sessionTeam != TEAM_SPECTATOR /*&& !(ps->eFlags&EF_ELIMINATED)*/) {
|
|
trap_LinkEntity (player);
|
|
}
|
|
}
|
|
|
|
//===========================================================
|
|
|
|
/*QUAKED misc_model (1 0 0) (-16 -16 -16) (16 16 16) CAST_SHADOWS CLIP_MODEL FORCE_META
|
|
-----DESCRIPTION-----
|
|
Will just spawn and display it's model.
|
|
Can be hooked up with a brushmodel-entity to move relative to.
|
|
|
|
-----SPAWNFLAGS-----
|
|
q3map2:
|
|
1: CAST_SHADOWS - Model will cast shadows.
|
|
2: CLIP_MODEL - Model will be clipped so noone can pass trough.
|
|
4: FORCE_META - will enforce a meta-compile for .bsp-build even if the compiler wasn't told to do so.
|
|
|
|
-----KEYS-----
|
|
"model" - arbitrary .md3 file to display
|
|
"target" - brushmodel-entity to attach to
|
|
|
|
q3map2:
|
|
"_castShadows" OR "_cs" sets whether the entity casts shadows
|
|
"_receiveShadows" OR "_rs" sets whether the entity receives shadows
|
|
"modelscale" scaling factor for the model to include
|
|
"modelscale_vec" non-uniform scaling vector for the model to include
|
|
"model2" path name of second model to load
|
|
"_frame" frame of model to load
|
|
"_frame2" frame of second model to load
|
|
*/
|
|
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
|
|
}
|
|
|
|
|
|
//===========================================================
|
|
static void setCamera ( gentity_t *ent, int ownernum )
|
|
{
|
|
vec3_t dir;
|
|
gentity_t *target = NULL;
|
|
gentity_t *owner = NULL;
|
|
|
|
ent->r.ownerNum = ownernum;
|
|
|
|
owner = &g_entities[ownernum];
|
|
|
|
//frame holds the rotate speed
|
|
ent->s.frame = 0; //TiM: 0
|
|
|
|
// 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 );
|
|
}
|
|
|
|
void cycleCamera( gentity_t *self )
|
|
{
|
|
gentity_t *orgOwner = NULL;
|
|
gentity_t *owner = NULL;
|
|
|
|
if ( self->r.ownerNum >= 0 && self->r.ownerNum < ENTITYNUM_WORLD )
|
|
{
|
|
orgOwner = &g_entities[self->r.ownerNum];
|
|
}
|
|
|
|
owner = G_Find( orgOwner, FOFS(targetname), self->target );
|
|
|
|
if ( owner == NULL )
|
|
{
|
|
//Uh oh! Not targeted at any ents! Or reached end of list? Which is it?
|
|
//for now assume reached end of list and are cycling
|
|
owner = G_Find( owner, FOFS(targetname), self->target );
|
|
}
|
|
|
|
setCamera( self, owner->s.number );
|
|
|
|
if ( self->think == cycleCamera )
|
|
{
|
|
if ( owner->wait > 0 )
|
|
self->nextthink = level.time + owner->wait;
|
|
else if ( self->wait > 0 )
|
|
self->nextthink = level.time + self->wait;
|
|
else
|
|
self->nextthink = -1; //no auto cycle
|
|
}
|
|
}
|
|
|
|
void misc_portal_use( gentity_t *self, gentity_t *other, gentity_t *activator )
|
|
{
|
|
if(!Q_stricmp(self->swapname, activator->target) && self->wait > 0) { //failsafe in case something slipped up, I'm too tired to be sure ^^
|
|
if(self->nextthink)
|
|
self->nextthink = -1;
|
|
else
|
|
self->nextthink = level.time + self->wait;
|
|
}else{
|
|
cycleCamera( self );
|
|
}
|
|
}
|
|
|
|
void locateCamera( gentity_t *ent ) {
|
|
|
|
gentity_t *owner = NULL;
|
|
owner = G_Find( NULL, FOFS(targetname), ent->target );
|
|
|
|
if(!owner){
|
|
DEVELOPER(G_Printf(S_COLOR_YELLOW "[Entity-Error] Couldn't find target for misc_partal_surface, removing surface so you'll note.\n" ););
|
|
G_FreeEntity( ent );
|
|
return;
|
|
}
|
|
|
|
//let's see if we need cyceling of some sort. Basic requirement: Do we have another camera connected?
|
|
if( G_Find( owner, FOFS(targetname), ent->target)){
|
|
//we do, so do we need to set up for manual cycle or pause?
|
|
if( ent->targetname || (ent->swapname && ent->wait > 0))
|
|
ent->use = misc_portal_use; //there's one of either. Which one will be determined in usefunction.
|
|
|
|
//to set up the autocycle we need wait set on either surface or camera.
|
|
if(ent->wait > 0 || owner->wait > 0){
|
|
if(ent->wait == -1 && owner->wait > 0){ //we need to make sure every camera has an individual wait
|
|
while((owner = G_Find( owner, FOFS(targetname), ent->target)) != NULL){
|
|
if(owner->wait == -1){
|
|
DEVELOPER(G_Printf(S_COLOR_YELLOW "[Entity-Error] One of the tragetet misc_portal_cameras does not have an individual wait. Adapting wait of the first camera found as a default.\n" ););
|
|
owner = G_Find( NULL, FOFS(targetname), ent->target);
|
|
ent->wait = owner->wait; //a camera failed so make sure to have the wait of the first camera ported over to the surface as failsafe
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//make sure we got the right camera at this point
|
|
owner = G_Find( NULL, FOFS(targetname), ent->target);
|
|
|
|
ent->think = cycleCamera;
|
|
if ( owner->wait > 0 )
|
|
ent->nextthink = level.time + owner->wait;
|
|
else if ( ent->wait > 0 )
|
|
ent->nextthink = level.time + ent->wait;
|
|
else
|
|
ent->nextthink = -1; //no auto cycle
|
|
}
|
|
}
|
|
setCamera( ent, owner->s.number );
|
|
}
|
|
|
|
|
|
/*QUAKED misc_portal_surface (0 0 1) (-8 -8 -8) (8 8 8)
|
|
-----DESCRIPTION-----
|
|
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!
|
|
|
|
-----SPAWNFLAGS-----
|
|
none
|
|
|
|
-----KEYS-----
|
|
"target" - misc_portal_camera's to target
|
|
"targetname" - When used, cycles to the next misc_portal_camera it's targeted
|
|
"wait" - makes it auto-cycle between all cameras it's pointed at at intevervals of specified number of seconds. Default = -1 = don't autocycle
|
|
cameras will be cycled through in the order they were created on the map.
|
|
if this and the first camera are -1 there will be no autocycle.
|
|
if this is -1 but the first camera is positive the wait will be adapted as a faulsafe measure should one of the later cameras lack an individual wait.
|
|
"swapname" - will pause/unpause the autocycle. The next cycle will happen aufer "wait" seconds, so wait is required for this.
|
|
requires SELF/NO_ACRIVATOR
|
|
|
|
-----USAGE-----
|
|
Autocycle or manual Cycle usually only makes sence for a survaliance-station or security.
|
|
For a viewscreen or a communications channel make the brush having the portal-texture a func_usable and treat is as described on that entity for VFX-Entities.
|
|
*/
|
|
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->wait > 0)
|
|
ent->wait *= 1000;
|
|
else
|
|
ent->wait = -1;
|
|
|
|
if ( !ent->target ) {
|
|
VectorCopy( ent->s.origin, ent->s.origin2 ); //mirror
|
|
} else {
|
|
ent->think = locateCamera;
|
|
ent->nextthink = level.time + 500; //give cameras time to spawn
|
|
if(ent->targetname && ent->swapname && ent->wait == -1){
|
|
DEVELOPER(G_Printf(S_COLOR_YELLOW "[Entity-Error] Both swapname and wait need to be present on a misc_model_surface to potentionally turn it's autocycle off. NULLing swapname.\n" ););
|
|
ent->swapname = G_NewString("NULL"); //Failsafe: We'll have the usefunction, however we can not use it for pausing as we did not have wait on ent. Set swapname to NULL so we can use wait as a potential failsafe should one of the cameras lack an individual wait.
|
|
}
|
|
}
|
|
}
|
|
|
|
/*QUAKED misc_portal_camera (0 0 1) (-8 -8 -8) (8 8 8) SLOWROTATE FASTROTATE
|
|
-----DESCRIPTION-----
|
|
The target for a misc_portal_surface.
|
|
You can set either angles or target another entity (NOT an info_null or similar) to determine the direction of view.
|
|
|
|
-----SPAWNFLAGS-----
|
|
1: SLOWROTATE - slowly rotates around it's axis of view
|
|
2: FASTROTATE - quickly rotates around it's axis of view
|
|
|
|
-----KEYS-----
|
|
"targetname" - have misc_portal_surface target this
|
|
"roll" - an angle modifier to orient the camera around the target vector. Default is 0.
|
|
"wait" - delay for autocycle misc_portal_surface. Will overwrite theirs. Default is -1 = use surface-value.
|
|
*/
|
|
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;
|
|
if(ent->wait > 0)
|
|
ent->wait *= 1000;
|
|
else
|
|
ent->wait = -1;
|
|
}
|
|
|
|
/*
|
|
======================================================================
|
|
|
|
SHOOTERS
|
|
|
|
======================================================================
|
|
*/
|
|
|
|
void Use_Shooter( gentity_t *ent, gentity_t *other, gentity_t *activator ) {
|
|
vec3_t dir;
|
|
float deg;
|
|
vec3_t up, right;
|
|
|
|
// see if we have a target
|
|
if ( ent->enemy ) {
|
|
VectorSubtract( ent->enemy->r.currentOrigin, ent->s.origin, dir );
|
|
VectorNormalize( dir );
|
|
} else {
|
|
VectorCopy( ent->movedir, dir );
|
|
}
|
|
|
|
// randomize a bit
|
|
PerpendicularVector( up, dir );
|
|
CrossProduct( up, dir, right );
|
|
|
|
deg = crandom() * ent->random;
|
|
VectorMA( dir, deg, up, dir );
|
|
|
|
deg = crandom() * ent->random;
|
|
VectorMA( dir, deg, right, dir );
|
|
|
|
VectorNormalize( dir );
|
|
|
|
switch ( ent->s.weapon ) {
|
|
case WP_8:
|
|
fire_grenade( ent, ent->s.origin, dir );
|
|
break;
|
|
case WP_10:
|
|
fire_rocket( ent, ent->s.origin, dir );
|
|
break;
|
|
case WP_4:
|
|
fire_plasma( ent, ent->s.origin, dir );
|
|
break;
|
|
case WP_9:
|
|
fire_quantum( ent, ent->s.origin, dir );
|
|
break;
|
|
case WP_6:
|
|
fire_comprifle( ent, ent->s.origin, dir );
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static void InitShooter_Finish( gentity_t *ent ) {
|
|
ent->enemy = G_PickTarget( ent->target );
|
|
ent->think = 0;
|
|
ent->nextthink = 0;
|
|
}
|
|
|
|
void InitShooter( gentity_t *ent, int weapon ) {
|
|
ent->use = Use_Shooter;
|
|
ent->s.weapon = weapon;
|
|
|
|
RegisterItem( BG_FindItemForWeapon( (weapon_t)weapon ) );
|
|
|
|
G_SetMovedir( ent->s.angles, ent->movedir );
|
|
|
|
if ( !ent->random ) {
|
|
ent->random = 1.0;
|
|
}
|
|
ent->random = sin( M_PI * ent->random / 180 );
|
|
// target might be a moving object, so we can't set movedir for it
|
|
if ( ent->target ) {
|
|
ent->think = InitShooter_Finish;
|
|
ent->nextthink = level.time + 500;
|
|
}
|
|
trap_LinkEntity( ent );
|
|
}
|
|
|
|
/*QUAKED shooter_rocket (1 0 0) (-16 -16 -16) (16 16 16)
|
|
-----DESCRIPTION-----
|
|
When used fires a rocket at either the target or the current direction.
|
|
|
|
-----SPAWNFLAGS-----
|
|
none
|
|
|
|
-----KEYS-----
|
|
"target" - direction to fire to
|
|
"random" - the number of degrees of deviance from the taget. (1.0 default)
|
|
*/
|
|
void SP_shooter_rocket( gentity_t *ent ) {
|
|
InitShooter( ent, WP_10 );
|
|
}
|
|
|
|
/*QUAKED shooter_plasma (1 0 0) (-16 -16 -16) (16 16 16)
|
|
-----DESCRIPTION-----
|
|
When used fires a plasma-burst at either the target or the current direction.
|
|
|
|
-----SPAWNFLAGS-----
|
|
none
|
|
|
|
-----KEYS-----
|
|
"target" - direction to fire to
|
|
"random" - the number of degrees of deviance from the taget. (1.0 default)
|
|
*/
|
|
void SP_shooter_plasma( gentity_t *ent ) {
|
|
InitShooter( ent, WP_6 ); //TiM : WP_4
|
|
}
|
|
|
|
/*QUAKED shooter_grenade (1 0 0) (-16 -16 -16) (16 16 16)
|
|
-----DESCRIPTION-----
|
|
When used fires a grenade at either the target or the current direction.
|
|
|
|
-----SPAWNFLAGS-----
|
|
none
|
|
|
|
-----KEYS-----
|
|
"target" - direction to fire to
|
|
"random" - the number of degrees of deviance from the taget. (1.0 default)
|
|
*/
|
|
void SP_shooter_grenade( gentity_t *ent ) {
|
|
InitShooter( ent, WP_8);
|
|
}
|
|
|
|
/*QUAKED shooter_torpedo (1 0 0) (-16 -16 -16) (16 16 16)
|
|
-----DESCRIPTION-----
|
|
When used fires a torpedo at either the target or the current direction.
|
|
|
|
-----SPAWNFLAGS-----
|
|
none
|
|
|
|
-----KEYS-----
|
|
"target" - direction to fire to
|
|
"random" - the number of degrees of deviance from the taget. (1.0 default)
|
|
*/
|
|
void SP_shooter_torpedo( gentity_t *ent ) {
|
|
InitShooter( ent, WP_9 );
|
|
}
|