diff options
author | Tim Angus <tim@ngus.net> | 2001-01-03 18:26:58 +0000 |
---|---|---|
committer | Tim Angus <tim@ngus.net> | 2001-01-03 18:26:58 +0000 |
commit | 5d27565dc8162000b75e41bf315f18b3e8015ac7 (patch) | |
tree | 5a30f46118e6baa4703045a9c2d3f02f144fcd8b /src/game/g_weapon.c | |
parent | 25e294467350aef8726c235cc908aba5aa4d091f (diff) |
1.27 import
Diffstat (limited to 'src/game/g_weapon.c')
-rw-r--r-- | src/game/g_weapon.c | 800 |
1 files changed, 800 insertions, 0 deletions
diff --git a/src/game/g_weapon.c b/src/game/g_weapon.c new file mode 100644 index 00000000..c5af5293 --- /dev/null +++ b/src/game/g_weapon.c @@ -0,0 +1,800 @@ +// Copyright (C) 1999-2000 Id Software, Inc. +// +// g_weapon.c +// perform the server side effects of a weapon firing + +/* + * 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 GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * 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. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* To assertain which portions are licensed under the GPL and which are + * licensed by Id Software, Inc. please run a diff between the equivalent + * versions of the "Tremulous" modification and the unmodified "Quake3" + * game source code. + */ + +#include "g_local.h" + +static float s_quadFactor; +static vec3_t forward, right, up; +static vec3_t muzzle; + +//TA: for horizontal changes in muzzle point +static int hmuzzle = 10; + +#define NUM_NAILSHOTS 10 + +/* +================ +G_BounceProjectile +================ +*/ +void G_BounceProjectile( vec3_t start, vec3_t impact, vec3_t dir, vec3_t endout ) { + vec3_t v, newv; + float dot; + + VectorSubtract( impact, start, v ); + dot = DotProduct( v, dir ); + VectorMA( v, -2*dot, dir, newv ); + + VectorNormalize(newv); + VectorMA(impact, 8192, newv, endout); +} + +/* +====================================================================== + +GAUNTLET + +====================================================================== +*/ + +void Weapon_Gauntlet( gentity_t *ent ) { + +} + +/* +=============== +CheckGauntletAttack +=============== +*/ +qboolean CheckGauntletAttack( gentity_t *ent ) { + trace_t tr; + vec3_t end; + gentity_t *tent; + gentity_t *traceEnt; + int damage; + + // set aiming directions + AngleVectors (ent->client->ps.viewangles, forward, right, up); + + CalcMuzzlePoint ( ent, forward, right, up, muzzle ); + + VectorMA (muzzle, 32, forward, end); + + trap_Trace (&tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT); + if ( tr.surfaceFlags & SURF_NOIMPACT ) { + return qfalse; + } + + traceEnt = &g_entities[ tr.entityNum ]; + + // send blood impact + if ( traceEnt->takedamage && traceEnt->client ) { + tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT ); + tent->s.otherEntityNum = traceEnt->s.number; + tent->s.eventParm = DirToByte( tr.plane.normal ); + tent->s.weapon = ent->s.weapon; + } + + if ( !traceEnt->takedamage) { + return qfalse; + } + + /*if (ent->client->ps.powerups[PW_QUAD] ) { + G_AddEvent( ent, EV_POWERUP_QUAD, 0 ); + s_quadFactor = g_quadfactor.value; + } else*/ { + s_quadFactor = 1; + } + + damage = 50 * s_quadFactor; + G_Damage( traceEnt, ent, ent, forward, tr.endpos, + damage, 0, MOD_GAUNTLET ); + + return qtrue; +} + + +/* +====================================================================== + +MACHINEGUN + +====================================================================== +*/ + +/* +====================== +SnapVectorTowards + +Round a vector to integers for more efficient network +transmission, but make sure that it rounds towards a given point +rather than blindly truncating. This prevents it from truncating +into a wall. +====================== +*/ +void SnapVectorTowards( vec3_t v, vec3_t to ) { + int i; + + for ( i = 0 ; i < 3 ; i++ ) { + if ( to[i] <= v[i] ) { + v[i] = (int)v[i]; + } else { + v[i] = (int)v[i] + 1; + } + } +} + +#define MACHINEGUN_SPREAD 200 +#define MACHINEGUN_DAMAGE 7 +#define MACHINEGUN_TEAM_DAMAGE 5 // wimpier MG in teamplay + +#define CHAINGUN_SPREAD 1200 +#define CHAINGUN_DAMAGE 14 + +void Bullet_Fire (gentity_t *ent, float spread, int damage, int mod ) { + trace_t tr; + vec3_t end; + float r; + float u; + gentity_t *tent; + gentity_t *traceEnt; + int i, passent; + + damage *= s_quadFactor; + + r = random() * M_PI * 2.0f; + u = sin(r) * crandom() * spread * 16; + r = cos(r) * crandom() * spread * 16; + VectorMA (muzzle, 8192*16, forward, end); + VectorMA (end, r, right, end); + VectorMA (end, u, up, end); + + passent = ent->s.number; + for (i = 0; i < 10; i++) { + + trap_Trace (&tr, muzzle, NULL, NULL, end, passent, MASK_SHOT); + if ( tr.surfaceFlags & SURF_NOIMPACT ) { + return; + } + + traceEnt = &g_entities[ tr.entityNum ]; + + // snap the endpos to integers, but nudged towards the line + SnapVectorTowards( tr.endpos, muzzle ); + + // send bullet impact + if ( traceEnt->takedamage && traceEnt->client ) { + tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_FLESH ); + tent->s.eventParm = traceEnt->s.number; + if( LogAccuracyHit( traceEnt, ent ) ) { + ent->client->accuracy_hits++; + } + } else { + tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_WALL ); + tent->s.eventParm = DirToByte( tr.plane.normal ); + } + tent->s.otherEntityNum = ent->s.number; + + if ( traceEnt->takedamage) { + G_Damage( traceEnt, ent, ent, forward, tr.endpos, + damage, 0, MOD_MACHINEGUN); + } + break; + } +} + + +/* +====================================================================== + +BFG + +====================================================================== +*/ + +void BFG_Fire ( gentity_t *ent ) { + gentity_t *m; + + m = fire_bfg (ent, muzzle, forward); + m->damage *= s_quadFactor; + m->splashDamage *= s_quadFactor; + +// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics +} + + +/* +====================================================================== + +SHOTGUN + +====================================================================== +*/ + +// DEFAULT_SHOTGUN_SPREAD and DEFAULT_SHOTGUN_COUNT are in bg_public.h, because +// client predicts same spreads +#define DEFAULT_SHOTGUN_DAMAGE 10 + +qboolean ShotgunPellet( vec3_t start, vec3_t end, gentity_t *ent ) { + trace_t tr; + int damage, i, passent; + gentity_t *traceEnt; + vec3_t tr_start, tr_end; + + passent = ent->s.number; + VectorCopy( start, tr_start ); + VectorCopy( end, tr_end ); + for (i = 0; i < 10; i++) { + trap_Trace (&tr, tr_start, NULL, NULL, tr_end, passent, MASK_SHOT); + traceEnt = &g_entities[ tr.entityNum ]; + + // send bullet impact + if ( tr.surfaceFlags & SURF_NOIMPACT ) { + return qfalse; + } + + if ( traceEnt->takedamage) { + damage = DEFAULT_SHOTGUN_DAMAGE * s_quadFactor; + G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_SHOTGUN); + if( LogAccuracyHit( traceEnt, ent ) ) { + return qtrue; + } + } + return qfalse; + } + return qfalse; +} + +// this should match CG_ShotgunPattern +void ShotgunPattern( vec3_t origin, vec3_t origin2, int seed, gentity_t *ent ) { + int i; + float r, u; + vec3_t end; + vec3_t forward, right, up; + int oldScore; + qboolean hitClient = qfalse; + + // derive the right and up vectors from the forward vector, because + // the client won't have any other information + VectorNormalize2( origin2, forward ); + PerpendicularVector( right, forward ); + CrossProduct( forward, right, up ); + + oldScore = ent->client->ps.persistant[PERS_SCORE]; + + // generate the "random" spread pattern + for ( i = 0 ; i < DEFAULT_SHOTGUN_COUNT ; i++ ) { + r = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD * 16; + u = Q_crandom( &seed ) * DEFAULT_SHOTGUN_SPREAD * 16; + VectorMA( origin, 8192 * 16, forward, end); + VectorMA (end, r, right, end); + VectorMA (end, u, up, end); + if( ShotgunPellet( origin, end, ent ) && !hitClient ) { + hitClient = qtrue; + ent->client->accuracy_hits++; + } + } +} + + +void weapon_supershotgun_fire (gentity_t *ent) { + gentity_t *tent; + + // send shotgun blast + tent = G_TempEntity( muzzle, EV_SHOTGUN ); + VectorScale( forward, 4096, tent->s.origin2 ); + SnapVector( tent->s.origin2 ); + tent->s.eventParm = rand() & 255; // seed for spread pattern + tent->s.otherEntityNum = ent->s.number; + + ShotgunPattern( tent->s.pos.trBase, tent->s.origin2, tent->s.eventParm, ent ); +} + + +/* +====================================================================== + +GRENADE LAUNCHER + +====================================================================== +*/ + +void weapon_grenadelauncher_fire (gentity_t *ent) { + gentity_t *m; + + // extra vertical velocity + forward[2] += 0.2f; + VectorNormalize( forward ); + + m = fire_grenade (ent, muzzle, forward); + m->damage *= s_quadFactor; + m->splashDamage *= s_quadFactor; + +// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics +} + +/* +====================================================================== + +ROCKET + +====================================================================== +*/ + +void Weapon_RocketLauncher_Fire (gentity_t *ent) { + gentity_t *m; + + m = fire_rocket (ent, muzzle, forward); + m->damage *= s_quadFactor; + m->splashDamage *= s_quadFactor; + +// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics +} + + +/* +====================================================================== + +FLAME THROWER + +====================================================================== +*/ + +void Weapon_Flamer_Fire (gentity_t *ent) { + gentity_t *m; + + m = fire_flamer (ent, muzzle, forward); + m->damage *= s_quadFactor; + m->splashDamage *= s_quadFactor; + +// VectorAdd( m->s.pos.trDelta, ent->client->ps.velocity, m->s.pos.trDelta ); // "real" physics +} + +/* +====================================================================== + +RAILGUN + +====================================================================== +*/ + + +/* +================= +weapon_railgun_fire +================= +*/ +#define MAX_RAIL_HITS 4 +void weapon_railgun_fire (gentity_t *ent) { + vec3_t end; + trace_t trace; + gentity_t *tent; + gentity_t *traceEnt; + int damage; + int i; + int hits; + int unlinked; + int passent; + gentity_t *unlinkedEntities[MAX_RAIL_HITS]; + + damage = 100 * s_quadFactor; + + VectorMA (muzzle, 8192, forward, end); + + // trace only against the solids, so the railgun will go through people + unlinked = 0; + hits = 0; + passent = ent->s.number; + do { + trap_Trace (&trace, muzzle, NULL, NULL, end, passent, MASK_SHOT ); + if ( trace.entityNum >= ENTITYNUM_MAX_NORMAL ) { + break; + } + traceEnt = &g_entities[ trace.entityNum ]; + if ( traceEnt->takedamage ) { + if( LogAccuracyHit( traceEnt, ent ) ) { + hits++; + } + G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN); + } + if ( trace.contents & CONTENTS_SOLID ) { + break; // we hit something solid enough to stop the beam + } + // unlink this entity, so the next trace will go past it + trap_UnlinkEntity( traceEnt ); + unlinkedEntities[unlinked] = traceEnt; + unlinked++; + } while ( unlinked < MAX_RAIL_HITS ); + + // link back in any entities we unlinked + for ( i = 0 ; i < unlinked ; i++ ) { + trap_LinkEntity( unlinkedEntities[i] ); + } + + // the final trace endpos will be the terminal point of the rail trail + + // snap the endpos to integers to save net bandwidth, but nudged towards the line + SnapVectorTowards( trace.endpos, muzzle ); + + // send railgun beam effect + tent = G_TempEntity( trace.endpos, EV_RAILTRAIL ); + + // set player number for custom colors on the railtrail + tent->s.clientNum = ent->s.clientNum; + + VectorCopy( muzzle, tent->s.origin2 ); + // move origin a bit to come closer to the drawn gun muzzle + VectorMA( tent->s.origin2, 4, right, tent->s.origin2 ); + VectorMA( tent->s.origin2, -1, up, tent->s.origin2 ); + + // no explosion at end if SURF_NOIMPACT, but still make the trail + if ( trace.surfaceFlags & SURF_NOIMPACT ) { + tent->s.eventParm = 255; // don't make the explosion at the end + } else { + tent->s.eventParm = DirToByte( trace.plane.normal ); + } + tent->s.clientNum = ent->s.clientNum; + + // give the shooter a reward sound if they have made two railgun hits in a row + if ( hits == 0 ) { + // complete miss + ent->client->accurateCount = 0; + } else { + // check for "impressive" reward sound + ent->client->accurateCount += hits; + if ( ent->client->accurateCount >= 2 ) { + ent->client->accurateCount -= 2; + ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++; + // add the sprite over the player's head + //ent->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); + ent->client->ps.eFlags |= EF_AWARD_IMPRESSIVE; + ent->client->rewardTime = level.time + REWARD_SPRITE_TIME; + } + ent->client->accuracy_hits++; + } + +} + + +/* +====================================================================== + +GRAPPLING HOOK + +====================================================================== +*/ + +void Weapon_GrapplingHook_Fire (gentity_t *ent) +{ + if (!ent->client->fireHeld && !ent->client->hook) + fire_grapple (ent, muzzle, forward); + + ent->client->fireHeld = qtrue; +} + +void Weapon_HookFree (gentity_t *ent) +{ + ent->parent->client->hook = NULL; + ent->parent->client->ps.pm_flags &= ~PMF_GRAPPLE_PULL; + G_FreeEntity( ent ); +} + +void Weapon_HookThink (gentity_t *ent) +{ + if (ent->enemy) { + vec3_t v, oldorigin; + + VectorCopy(ent->r.currentOrigin, oldorigin); + v[0] = ent->enemy->r.currentOrigin[0] + (ent->enemy->r.mins[0] + ent->enemy->r.maxs[0]) * 0.5; + v[1] = ent->enemy->r.currentOrigin[1] + (ent->enemy->r.mins[1] + ent->enemy->r.maxs[1]) * 0.5; + v[2] = ent->enemy->r.currentOrigin[2] + (ent->enemy->r.mins[2] + ent->enemy->r.maxs[2]) * 0.5; + SnapVectorTowards( v, oldorigin ); // save net bandwidth + + G_SetOrigin( ent, v ); + } + + VectorCopy( ent->r.currentOrigin, ent->parent->client->ps.grapplePoint); +} + +/* +====================================================================== + +LIGHTNING GUN + +====================================================================== +*/ + + +void Weapon_LightningFire( gentity_t *ent ) { + trace_t tr; + vec3_t end; + gentity_t *traceEnt, *tent; + int damage, i, passent; + + damage = 8 * s_quadFactor; + + passent = ent->s.number; + for (i = 0; i < 10; i++) { + VectorMA( muzzle, LIGHTNING_RANGE, forward, end ); + + trap_Trace( &tr, muzzle, NULL, NULL, end, passent, MASK_SHOT ); + + if ( tr.entityNum == ENTITYNUM_NONE ) { + return; + } + + traceEnt = &g_entities[ tr.entityNum ]; + + if ( traceEnt->takedamage) { + G_Damage( traceEnt, ent, ent, forward, tr.endpos, + damage, 0, MOD_LIGHTNING); + } + + if ( traceEnt->takedamage && traceEnt->client ) { + tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT ); + tent->s.otherEntityNum = traceEnt->s.number; + tent->s.eventParm = DirToByte( tr.plane.normal ); + tent->s.weapon = ent->s.weapon; + if( LogAccuracyHit( traceEnt, ent ) ) { + ent->client->accuracy_hits++; + } + } else if ( !( tr.surfaceFlags & SURF_NOIMPACT ) ) { + tent = G_TempEntity( tr.endpos, EV_MISSILE_MISS ); + tent->s.eventParm = DirToByte( tr.plane.normal ); + } + + break; + } +} + + +//====================================================================== + +/* +====================================================================== + +BUILD GUN + +====================================================================== +*/ + + +///////build weapons +/* +=============== +Weapon_Abuild_Fire +=============== +*/ +void Weapon_Abuild_Fire( gentity_t *ent ) +{ + G_AddPredictableEvent( ent, EV_MENU, MN_ABUILD ); +} + +/* +=============== +Weapon_Hbuild_Fire +=============== +*/ +void Weapon_Hbuild_Fire( gentity_t *ent ) +{ + G_AddPredictableEvent( ent, EV_MENU, MN_HBUILD ); +} +///////build weapons + +/* +=============== +Weapon_Venom_Fire +=============== +*/ +void Weapon_Venom_Fire( gentity_t *ent ) { + trace_t tr; + vec3_t end; + gentity_t *tent; + gentity_t *traceEnt; + + // set aiming directions + AngleVectors (ent->client->ps.viewangles, forward, right, up); + + CalcMuzzlePoint ( ent, forward, right, up, muzzle ); + + VectorMA (muzzle, 32, forward, end); + + trap_Trace (&tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT); + if ( tr.surfaceFlags & SURF_NOIMPACT ) { + return; + } + + traceEnt = &g_entities[ tr.entityNum ]; + + // send blood impact + if ( traceEnt->takedamage && traceEnt->client ) { + tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT ); + tent->s.otherEntityNum = traceEnt->s.number; + tent->s.eventParm = DirToByte( tr.plane.normal ); + tent->s.weapon = ent->s.weapon; + } + + if ( traceEnt->takedamage) { + G_Damage( traceEnt, ent, ent, forward, tr.endpos, 50, 0, MOD_VENOM ); + } + +} + +//====================================================================== + +/* +=============== +LogAccuracyHit +=============== +*/ +qboolean LogAccuracyHit( gentity_t *target, gentity_t *attacker ) { + if( !target->takedamage ) { + return qfalse; + } + + if ( target == attacker ) { + return qfalse; + } + + if( !target->client ) { + return qfalse; + } + + if( !attacker->client ) { + return qfalse; + } + + if( target->client->ps.stats[STAT_HEALTH] <= 0 ) { + return qfalse; + } + + if ( OnSameTeam( target, attacker ) ) { + return qfalse; + } + + return qtrue; +} + + +/* +=============== +CalcMuzzlePoint + +set muzzle location relative to pivoting eye +=============== +*/ +void CalcMuzzlePoint ( gentity_t *ent, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint ) { + VectorCopy( ent->s.pos.trBase, muzzlePoint ); + muzzlePoint[2] += ent->client->ps.viewheight; + VectorMA( muzzlePoint, 8, forward, muzzlePoint ); + + //TA: move the muzzle a bit with BFG + if( ent->s.weapon == WP_BFG ) + { + VectorMA( muzzlePoint, hmuzzle, right, muzzlePoint ); + hmuzzle = -hmuzzle; + } + + // snap to integer coordinates for more efficient network bandwidth usage + SnapVector( muzzlePoint ); +} + +/* +=============== +CalcMuzzlePointOrigin + +set muzzle location relative to pivoting eye +=============== +*/ +void CalcMuzzlePointOrigin ( gentity_t *ent, vec3_t origin, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint ) { + VectorCopy( ent->s.pos.trBase, muzzlePoint ); + muzzlePoint[2] += ent->client->ps.viewheight; + VectorMA( muzzlePoint, 14, forward, muzzlePoint ); + // snap to integer coordinates for more efficient network bandwidth usage + SnapVector( muzzlePoint ); +} + + +/* +=============== +FireWeapon +=============== +*/ +void FireWeapon( gentity_t *ent ) { + /*if (ent->client->ps.powerups[PW_QUAD] ) { + s_quadFactor = g_quadfactor.value; + } else*/ { + s_quadFactor = 1; + } + + // track shots taken for accuracy tracking. Grapple is not a weapon and gauntet is just not tracked + if( ent->s.weapon != WP_GRAPPLING_HOOK && ent->s.weapon != WP_GAUNTLET ) { + ent->client->accuracy_shots++; + } + + // set aiming directions + AngleVectors (ent->client->ps.viewangles, forward, right, up); + + CalcMuzzlePointOrigin ( ent, ent->client->oldOrigin, forward, right, up, muzzle ); + + // fire the specific weapon + switch( ent->s.weapon ) { + case WP_GAUNTLET: + Weapon_Gauntlet( ent ); + break; + case WP_LIGHTNING: + Weapon_LightningFire( ent ); + break; + case WP_SHOTGUN: + weapon_supershotgun_fire( ent ); + break; + case WP_MACHINEGUN: + if ( g_gametype.integer != GT_TEAM ) { + Bullet_Fire( ent, MACHINEGUN_SPREAD, MACHINEGUN_DAMAGE, MOD_MACHINEGUN ); + } else { + Bullet_Fire( ent, MACHINEGUN_SPREAD, MACHINEGUN_TEAM_DAMAGE, MOD_MACHINEGUN ); + } + break; + case WP_CHAINGUN: + Bullet_Fire( ent, CHAINGUN_SPREAD, CHAINGUN_DAMAGE, MOD_CHAINGUN ); + break; + case WP_GRENADE_LAUNCHER: + weapon_grenadelauncher_fire( ent ); + break; + case WP_ROCKET_LAUNCHER: + Weapon_RocketLauncher_Fire( ent ); + break; + case WP_FLAMER: + Weapon_Flamer_Fire( ent ); + break; + case WP_RAILGUN: + weapon_railgun_fire( ent ); + break; + case WP_BFG: + BFG_Fire( ent ); + break; + case WP_GRAPPLING_HOOK: + Weapon_GrapplingHook_Fire( ent ); + break; + case WP_VENOM: + Weapon_Venom_Fire( ent ); + break; + case WP_ABUILD: + Weapon_Abuild_Fire( ent ); + break; + case WP_HBUILD: + Weapon_Hbuild_Fire( ent ); + break; + case WP_SCANNER: //scanner doesn't "fire" + default: +// FIXME G_Error( "Bad ent->s.weapon" ); + break; + } +} + |