// g_weapon.c // perform the server side effects of a weapon firing #include "g_local.h" #include "g_functions.h" #include "anims.h" #include "b_local.h" #include "objectives.h" #include "..\cgame\cg_text.h" extern vmCvar_t cg_thirdPerson; static vec3_t forward, vright, up; static vec3_t muzzle; #define BORG_ADAPT_NUM_HITS 10 extern int killPlayerTimer; void FX_BorgShield( vec3_t position, vec3_t dir ); void FX_BorgHit( vec3_t origin, vec3_t dir ); extern void CGCam_Shake( float intensity, int duration ); extern void G_SetEnemy( gentity_t *self, gentity_t *enemy ); void drop_charge(gentity_t *ent, vec3_t start, vec3_t dir); void ViewHeightFix( gentity_t *ent ); qboolean LogAccuracyHit( gentity_t *target, gentity_t *attacker ); extern void CG_ChangeWeapon( int num ); extern qboolean G_PointInBounds( vec3_t point, vec3_t mins, vec3_t maxs ); extern qboolean G_BoxInBounds( vec3_t point, vec3_t mins, vec3_t maxs, vec3_t boundsMins, vec3_t boundsMaxs ); void W_TraceSetStart( gentity_t *ent, vec3_t start, vec3_t mins, vec3_t maxs ) {//make sure our start point isn't on the other side of a wall trace_t tr; vec3_t entMins; vec3_t entMaxs; VectorAdd( ent->currentOrigin, ent->mins, entMins ); VectorAdd( ent->currentOrigin, ent->maxs, entMaxs ); if ( G_BoxInBounds( start, mins, maxs, entMins, entMaxs ) ) { return; } if ( !ent->client ) { return; } gi.trace( &tr, ent->client->renderInfo.eyePoint, mins, maxs, start, ent->s.number, MASK_SOLID|CONTENTS_SHOTCLIP ); if ( tr.startsolid || tr.allsolid ) { return; } if ( tr.fraction < 1.0f ) { VectorCopy( tr.endpos, start ); } else { //trace was cleah } } //--------------------------------------------------------- qboolean W_CheckBorgAdapt( gentity_t *attacker, int weapon, gentity_t *traceEnt, vec3_t endpos ) //--------------------------------------------------------- { if ( !traceEnt ) return qtrue; if ( traceEnt->client && traceEnt->client->race == RACE_BORG && traceEnt->NPC && traceEnt->NPC->aiFlags&NPCAI_SHIELDS ) { // Adapt info is stored under the players persistent client info if ( g_entities[0].client ) { int *borgAdaptHits = g_entities[0].client->ps.borgAdaptHits; //FIXME: use damage accumulated rather than number of hits? borgAdaptHits[weapon]++; if(borgAdaptHits[weapon] >= BORG_ADAPT_NUM_HITS) { //They've adapted if(borgAdaptHits[weapon] == BORG_ADAPT_NUM_HITS) { G_UseTargets2(attacker, attacker->owner, "adapted"); } vec3_t dir; VectorSubtract( attacker->currentOrigin, endpos, dir ); VectorNormalize( dir ); FX_BorgShield( endpos, dir ); traceEnt->s.powerups |= ( 1 << PW_BORG_SHIELD ); traceEnt->client->ps.powerups[PW_BORG_SHIELD] = level.time + 50; //FIXME: Add this to cgs.media and play this on the cgame side G_Sound(traceEnt, G_SoundIndex("sound/enemies/borg/borgshield.wav")); //make them still react to absorbed shots, but no anim GEntity_PainFunc( traceEnt, attacker, -1 ); return qfalse; } else { //Hit them vec3_t backward; VectorScale(forward, -1, backward); FX_BorgHit( endpos , backward ); return qtrue; } } } return qtrue; } /* ---------------------------------------------- PLAYER WEAPONS ---------------------------------------------- ---------------------------------------------- PHASER ---------------------------------------------- */ #define PHASER_WEAK_DAMAGE 1 #define PHASER_DAMAGE 4 #define PHASER_ALT_DAMAGE 10 //--------------------------------------------------------- void WP_FirePhaser( gentity_t *ent, qboolean alt_fire ) //--------------------------------------------------------- { trace_t tr; vec3_t start, end; gentity_t *traceEnt; qboolean do_damage = qtrue, weak = qfalse; // Make sure that the damage trace corresponds with the trace done for the actual effect or weird things could happen if ( ent->s.number == 0 ) VectorCopy( ent->client->renderInfo.eyePoint, start ); else VectorCopy( muzzle, start ); VectorMA( start, -8, forward, start ); VectorMA( start, weaponData[WP_PHASER].range, forward, end); // Find out who we've hit gi.trace ( &tr, start, NULL, NULL, end, ent->s.number, MASK_SHOT); traceEnt = &g_entities[ tr.entityNum ]; AddSoundEvent( ent, tr.endpos, 256, AEL_DISCOVERED ); AddSightEvent( ent, tr.endpos, 512, AEL_DISCOVERED ); // Make sure to check if the Borg have adapted... do_damage = W_CheckBorgAdapt( ent, WP_PHASER, traceEnt, tr.endpos ); if ( ent->client ) { if ( ent->client->ps.ammo[AMMO_PHASER] < 1 ) weak = qtrue; } if ( traceEnt->takedamage ) { if ( LogAccuracyHit( traceEnt, ent ) ) ent->client->ps.persistant[PERS_ACCURACY_HITS]++; if ( do_damage ) { if ( weak ) { // If we are in weak mode, only damage every now and then. if ( (rand() & 1) ) G_Damage( traceEnt, ent, ent, forward, tr.endpos, PHASER_WEAK_DAMAGE, DAMAGE_NO_KNOCKBACK, MOD_PHASER ); } else { if ( alt_fire ) G_Damage( traceEnt, ent, ent, forward, tr.endpos, PHASER_ALT_DAMAGE, DAMAGE_NO_KNOCKBACK, MOD_PHASER_ALT ); else G_Damage( traceEnt, ent, ent, forward, tr.endpos, PHASER_DAMAGE, DAMAGE_NO_KNOCKBACK, MOD_PHASER ); } } } } /* ---------------------------------------------- COMPRESSION RIFLE ---------------------------------------------- */ #define CRIFLE_DAMAGE 25 #define COMPRESSION_SPREAD 100 //--------------------------------------------------------- void WP_FireCompressionRifle ( gentity_t *ent, qboolean alt_fire ) //--------------------------------------------------------- { gentity_t *tent; gentity_t *tent2 = 0; gentity_t *traceEnt; int damage = alt_fire ? CRIFLE_DAMAGE * 6 : CRIFLE_DAMAGE; // do butt-loads of damage for alt trace_t tr; vec3_t start, end; qboolean do_damage = qtrue; qboolean render_impact = qtrue; if ( ent->s.number == 0 && !cg_thirdPerson.integer ) // The trace start will originate at the eye so we can ensure that it hits the crosshair. // This can cause small clipping errors, but it's probably not that big of a deal at this point. VectorCopy( ent->client->renderInfo.eyePoint, start ); else VectorCopy( muzzle, start ); VectorMA( start, -8, forward, start ); VectorMA( start, 8192, forward, end ); gi.trace ( &tr, start, NULL, NULL, end, ent->s.number, MASK_SHOT ); // If the beam hits a skybox, etc. it would look foolish to add in an explosion if ( tr.surfaceFlags & SURF_NOIMPACT ) { render_impact = qfalse; } traceEnt = &g_entities[ tr.entityNum ]; // Render a shot in any case. if ( alt_fire ) tent = G_TempEntity( tr.endpos, EV_COMPRESSION_RIFLE_ALT ); else tent = G_TempEntity( tr.endpos, EV_COMPRESSION_RIFLE ); // Only add in impact stuff when told to do so if ( render_impact ) { // send bullet impact if ( traceEnt->takedamage && traceEnt->client ) { // Create a simple impact type mark tent2 = G_TempEntity( tr.endpos, EV_COMPRESSION_RIFLE_HIT ); tent->s.otherEntityNum = traceEnt->s.number; do_damage = W_CheckBorgAdapt( ent, WP_COMPRESSION_RIFLE, traceEnt, tr.endpos ); if( LogAccuracyHit( traceEnt, ent ) ) { ent->client->ps.persistant[PERS_ACCURACY_HITS]++; } } else { if ( alt_fire) { // Do a discreet miss tent2 = G_TempEntity( tr.endpos, EV_COMPRESSION_RIFLE_ALT_MISS ); AddSightEvent( ent, tr.endpos, 256, AEL_SUSPICIOUS ); } else { // Create an explosion tent2 = G_TempEntity( tr.endpos, EV_COMPRESSION_RIFLE_MISS ); // FIXME: Is this really an ok place to add this? if ( ent->s.number == 0 ) { //Add the event AddSoundEvent( ent, tr.endpos, 256, AEL_DISCOVERED ); AddSightEvent( ent, tr.endpos, 512, AEL_DISCOVERED ); } } } } // Stash origins, etc. so that the effect can have access to them. VectorCopy( muzzle, tent->s.origin2 ); if ( render_impact ) { VectorCopy( muzzle, tent2->s.origin2 ); tent2->s.eventParm = tent->s.eventParm = DirToByte( tr.plane.normal ); tent2->s.weapon = tent->s.weapon = ent->s.weapon; } if ( traceEnt->takedamage && do_damage ) { // Do less damage when it's an NPC that is shooting this weapon, this is done so that they can still shoot a lot // but not necessarily kill everything for you. if ( ent->client->ps.clientNum != 0 ) { damage *= 0.5; } if ( alt_fire ) { G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, MOD_SNIPER ); } else { G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_CRIFLE ); } } } /* ---------------------------------------------- IMOD ---------------------------------------------- */ #define IMOD_DAMAGE 24 #define IMOD_ALT_DAMAGE 60 //--------------------------------------------------------- void WP_FireIMOD ( gentity_t *ent, qboolean alt_fire ) //--------------------------------------------------------- { trace_t tr; vec3_t start, end, d_dir; gentity_t *tent; gentity_t *tent2 = 0; gentity_t *traceEnt; qboolean render_impact = qtrue; if ( ent->s.number == 0 && !cg_thirdPerson.integer ) // The trace start will originate at the eye so we can ensure that it hits the crosshair. // This can cause small clipping errors, but it's probably not that big of a deal at this point. VectorCopy( ent->client->renderInfo.eyePoint, start ); else VectorCopy( muzzle, start ); VectorMA( start, -8, forward, start ); VectorMA ( start, weaponData[WP_IMOD].range, forward, end); gi.trace ( &tr, start, NULL, NULL, end, ent->s.number, MASK_SHOT); // If the beam hits a skybox, etc. it would look foolish to add in an explosion if ( tr.surfaceFlags & SURF_NOIMPACT ) { render_impact = qfalse; } traceEnt = &g_entities[ tr.entityNum ]; if ( ent->s.number == 0 ) { //Add the event AddSoundEvent( ent, tr.endpos, 256, AEL_DISCOVERED ); AddSightEvent( ent, tr.endpos, 512, AEL_DISCOVERED ); } // Create the events that will add in the necessary effects if ( alt_fire ) { tent = G_TempEntity( tr.endpos, EV_IMOD_ALTFIRE ); if ( render_impact ) { // send bullet impact if ( traceEnt->takedamage && traceEnt->client ) { tent2 = G_TempEntity( tr.endpos, EV_IMOD_ALTFIRE_HIT ); tent->s.otherEntityNum = traceEnt->s.number; if( LogAccuracyHit( traceEnt, ent ) ) { ent->client->ps.persistant[PERS_ACCURACY_HITS]++; } } else { tent2 = G_TempEntity( tr.endpos, EV_IMOD_ALTFIRE_MISS ); } } } else { tent = G_TempEntity( tr.endpos, EV_IMOD ); if ( render_impact ) { // send bullet impact if ( traceEnt->takedamage && traceEnt->client ) { tent2 = G_TempEntity( tr.endpos, EV_IMOD_HIT ); tent->s.otherEntityNum = traceEnt->s.number; if( LogAccuracyHit( traceEnt, ent ) ) { ent->client->ps.persistant[PERS_ACCURACY_HITS]++; } } else { tent2 = G_TempEntity( tr.endpos, EV_IMOD_MISS ); } } } if ( traceEnt->takedamage) { //For knockback - send them up in air VectorCopy(forward, d_dir); if(d_dir[2] < 0.30f) d_dir[2] = 0.30f; VectorNormalize(d_dir); if ( alt_fire ) G_Damage( traceEnt, ent, ent, d_dir, tr.endpos, IMOD_ALT_DAMAGE*Q_flrand(0.8f, 1.2f), DAMAGE_NO_HIT_LOC|DAMAGE_DEATH_KNOCKBACK|DAMAGE_EXTRA_KNOCKBACK, MOD_IMOD ); else G_Damage( traceEnt, ent, ent, d_dir, tr.endpos, IMOD_DAMAGE*Q_flrand(0.8f, 1.2f), DAMAGE_NO_HIT_LOC|DAMAGE_DEATH_KNOCKBACK|DAMAGE_EXTRA_KNOCKBACK, MOD_IMOD ); } // Stash origins, etc. so the effect can have access to them VectorCopy( muzzle, tent->s.origin2 ); if ( render_impact ) { VectorCopy( muzzle, tent2->s.origin2 ); tent2->s.eventParm = tent->s.eventParm = DirToByte( tr.plane.normal ); tent2->s.weapon = tent->s.weapon = ent->s.weapon; } } /* ---------------------------------------------- SCAVENGER ---------------------------------------------- */ #define SCAV_SPREAD 0.5 #define SCAV_VELOCITY 1500 #define SCAV_DAMAGE 10 #define SCAV_NPC_DAMAGE_EASY 3 #define SCAV_NPC_DAMAGE_NORMAL 7 #define SCAV_NPC_DAMAGE_HARD 9 #define SCAV_ALT_DAMAGE 60 #define SCAV_ALT_SPLASH_RAD 80 #define SCAV_ALT_SPLASH_DAM 60 #define SCAV_ALT_VELOCITY 900 //--------------------------------------------------------- void FireScavengerBullet( gentity_t *ent, vec3_t start, vec3_t dir, qboolean greyscale ) //--------------------------------------------------------- { gentity_t *bolt; int damage = SCAV_DAMAGE; int velocity = SCAV_VELOCITY; bolt = G_Spawn(); bolt->classname = "scav_proj"; bolt->nextthink = level.time + 10000; bolt->e_ThinkFunc = thinkF_G_FreeEntity; bolt->s.eType = ET_MISSILE; if ( greyscale ) { bolt->s.weapon = WP_CHAOTICA_GUARD_GUN; } else { bolt->s.weapon = WP_SCAVENGER_RIFLE; } bolt->owner = ent; // See if we need to do less damage, otherwise you'll be dead instantly if this weapon is used against you if ( ent->client ) { if ( ent->client->ps.clientNum != 0 ) { // Flags effect as being lite version for NPC's bolt->count = 1; // Lowering velocity seemed to help the effect look a bit cooler from 3rd person, that way you see a volley // of bullets racing at you... velocity *= 0.5; } else { // Flags effect as being the full beefy version for the player bolt->count = 0; } } //Do the damages if ( ent->s.number != 0 ) { if ( g_spskill->integer == 0 ) damage = SCAV_NPC_DAMAGE_EASY; else if ( g_spskill->integer == 1 ) damage = SCAV_NPC_DAMAGE_NORMAL; else damage = SCAV_NPC_DAMAGE_HARD; } else { damage = SCAV_DAMAGE; } bolt->damage = damage; bolt->dflags = 0; bolt->splashDamage = 0; bolt->splashRadius = 0; bolt->methodOfDeath = MOD_SCAVENGER; bolt->clipmask = MASK_SHOT; bolt->s.pos.trType = TR_LINEAR ; bolt->s.pos.trTime = level.time; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, velocity, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->currentOrigin); } // Alt-fire... //--------------------------------------------------------- void FireScavengerGrenade( gentity_t *ent, vec3_t start, vec3_t dir ) //--------------------------------------------------------- { gentity_t *grenade; grenade = G_Spawn(); grenade->classname = "scav_grenade"; grenade->nextthink = level.time + 9000; grenade->e_ThinkFunc = thinkF_G_FreeEntity; grenade->s.eType = ET_MISSILE; grenade->s.weapon = WP_SCAVENGER_RIFLE; grenade->owner = ent; grenade->damage = SCAV_ALT_DAMAGE; grenade->dflags = 0; grenade->splashDamage = SCAV_ALT_SPLASH_DAM; grenade->splashRadius = SCAV_ALT_SPLASH_RAD; grenade->methodOfDeath = MOD_GRENADE; grenade->splashMethodOfDeath = MOD_GRENADE_SPLASH; grenade->clipmask = MASK_SHOT; grenade->alt_fire = qtrue; // How 'bout we give this thing a size... VectorSet( grenade->maxs, 1.0f, 1.0f, 1.0f ); VectorScale( grenade->maxs, -1, grenade->mins ); grenade->s.pos.trType = TR_GRAVITY; grenade->s.pos.trTime = level.time; // move a bit on the very first frame VectorCopy( start, grenade->s.pos.trBase ); VectorScale( dir, random() * 100 + SCAV_ALT_VELOCITY, grenade->s.pos.trDelta ); grenade->s.pos.trDelta[2] += 140; SnapVector( grenade->s.pos.trDelta ); // save net bandwidth VectorCopy (start, grenade->currentOrigin); VectorCopy (start, grenade->pos1); } //--------------------------------------------------------- void WP_FireScavenger( gentity_t *ent, qboolean alt_fire, qboolean greyscale ) //--------------------------------------------------------- { vec3_t dir, angles, temp_ang, temp_org; vec3_t start; float offset; VectorCopy( forward, dir ); VectorCopy( muzzle, start ); if ( alt_fire ) { FireScavengerGrenade( ent, start, dir ); } else { if ( ent->s.number == 0 ) { // FIXME: Move in from the side muzzle. But, it might be much better to have it just come from the main barrel... VectorMA( start, -20, forward, start ); VectorMA( start, 1, vright, start ); VectorMA( start, -4, up, start ); } vectoangles( dir, angles ); //Scavengers use their aim values as well as the gun's inherent inaccuracy if ( ent->client && ent->NPC && ent->client->playerTeam == TEAM_SCAVENGERS ) { float xofs, yofs; xofs = angles[0] + ( crandom() * (SCAV_SPREAD+(ent->NPC->stats.aim*0.5)) ); yofs = angles[1] + ( crandom() * (SCAV_SPREAD+(ent->NPC->stats.aim*0.5)) ); VectorSet( temp_ang, xofs, yofs, angles[2] ); } else { VectorSet( temp_ang, angles[0] + (crandom() * SCAV_SPREAD), angles[1] + (crandom() * SCAV_SPREAD), angles[2] ); } AngleVectors( temp_ang, dir, NULL, NULL ); // try to make the shot alternate between barrels offset = Q_irand(0, 1) * 2 + 1; VectorMA( start, offset, vright, temp_org ); VectorMA( temp_org, offset, up, temp_org ); //FIXME if temp_org does not have clear trace to inside the bbox, don't shoot! FireScavengerBullet( ent, temp_org, dir, greyscale ); } } /* ---------------------------------------------- STASIS ---------------------------------------------- */ #define STASIS_VELOCITY 1000 //650 #define STASIS_SPREAD 5.0 //1.8 // Keep the spread relatively small so that you can get multiple projectile impacts when a badie is close #define STASIS_ALT_DAMAGE 40 #define STASIS_ALT_DAMAGE2 20 #define STASIS_ALT_RIGHT_OFS 0.10 #define STASIS_ALT_UP_OFS 0.02 #define MAXRANGE_STASIS 8192 //--------------------------------------------------------- void FireStasisMissile( gentity_t *ent, vec3_t origin, vec3_t dir, int size ) //--------------------------------------------------------- { gentity_t *bolt; bolt = G_Spawn(); bolt->classname = "stasis_projectile"; bolt->nextthink = level.time + 10000; bolt->e_ThinkFunc = thinkF_G_FreeEntity; bolt->s.eType = ET_MISSILE; bolt->svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_STASIS; bolt->owner = ent; bolt->damage = size * 12; bolt->dflags = 0; bolt->splashDamage = 0; bolt->splashRadius = 0; bolt->methodOfDeath = MOD_STASIS; //bolt->splashMethodOfDeath = MOD_ROCKET_SPLASH; bolt->clipmask = MASK_SHOT; bolt->trigger_formation = qfalse; // don't draw tail on first frame // There are going to be a couple of different sized projectiles, so store 'em here bolt->count = size; // How 'bout we give this thing a size... VectorSet( bolt->maxs, 2.0f, 2.0f, 2.0f ); VectorScale( bolt->maxs, -size, bolt->mins ); VectorScale( bolt->maxs, size, bolt->maxs ); bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time; VectorCopy( origin, bolt->s.pos.trBase ); VectorScale( dir, STASIS_VELOCITY + ( 50 * size ), bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy (origin, bolt->currentOrigin); // Used by trails VectorCopy (origin, bolt->pos1 ); VectorCopy (origin, bolt->pos2 ); } //--------------------------------------------------------- void WP_FireStasisMain( gentity_t *ent ) //--------------------------------------------------------- { vec3_t dir, start; vec3_t angles, temp; VectorCopy( forward, dir ); VectorCopy( muzzle, start ); vectoangles( dir, angles ); FireStasisMissile( ent, start, dir, 4 ); VectorCopy( angles, temp ); temp[YAW] += STASIS_SPREAD; AngleVectors( temp, temp, NULL, NULL ); FireStasisMissile( ent, start, temp, 2 ); VectorCopy( angles, temp ); temp[YAW] -= STASIS_SPREAD; AngleVectors( temp, temp, NULL, NULL ); FireStasisMissile( ent, start, temp, 2 ); } void DoSmallStasisBeam(gentity_t *ent, vec3_t start, vec3_t dir) { qboolean do_damage; vec3_t end; trace_t tr; gentity_t *traceEnt; VectorMA(start, MAXRANGE_STASIS, dir, end); gi.trace(&tr, start, NULL, NULL, end, ent->s.number, MASK_SHOT); traceEnt = &g_entities[ tr.entityNum ]; do_damage = W_CheckBorgAdapt( ent, WP_STASIS, traceEnt, tr.endpos ); if ( traceEnt->takedamage && do_damage ) { //For knockback - send them up in air if ( dir[2] < 0.20f ) { dir[2] = 0.20f; } VectorNormalize( dir ); G_Damage(traceEnt, ent, ent, dir, tr.endpos, STASIS_ALT_DAMAGE2, DAMAGE_NO_HIT_LOC, MOD_STASIS ); } } //--------------------------------------------------------- void WP_FireStasisAlt( gentity_t *ent ) //--------------------------------------------------------- { trace_t tr; vec3_t end, d_dir, d_right, d_up={0,0,1}; gentity_t *tent; gentity_t *tent2; gentity_t *traceEnt; qboolean render_impact = qtrue; qboolean do_damage = qtrue; // Find the main impact point VectorMA (muzzle, MAXRANGE_STASIS, forward, end); gi.trace ( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT); // Rendering things like explosions when hitting a sky box would look bad, but you still want to see the beam if ( tr.surfaceFlags & SURF_NOIMPACT ) { render_impact = qfalse; } traceEnt = &g_entities[ tr.entityNum ]; // Why am I doing this when I've got a right and up already? Well, because this is how it is calc'ed on the client side. CrossProduct(forward, d_up, d_right); CrossProduct(d_right, forward, d_up); // Change the "fake up" (0,0,1) to a "real up" (perpendicular to the forward vector). // Fire a shot up and to the right. VectorMA(forward, STASIS_ALT_RIGHT_OFS, d_right, d_dir); VectorMA(d_dir, STASIS_ALT_UP_OFS, d_up, d_dir); DoSmallStasisBeam(ent, muzzle, d_dir); // Fire a shot up and to the left. VectorMA(forward, -STASIS_ALT_RIGHT_OFS, d_right, d_dir); VectorMA(d_dir, STASIS_ALT_UP_OFS, d_up, d_dir); DoSmallStasisBeam(ent, muzzle, d_dir); // Fire a shot a bit down and to the right. VectorMA(forward, 2.0*STASIS_ALT_RIGHT_OFS, d_right, d_dir); VectorMA(d_dir, -0.5*STASIS_ALT_UP_OFS, d_up, d_dir); DoSmallStasisBeam(ent, muzzle, d_dir); // Fire a shot up and to the left. VectorMA(forward, -2.0*STASIS_ALT_RIGHT_OFS, d_right, d_dir); VectorMA(d_dir, -0.5*STASIS_ALT_UP_OFS, d_up, d_dir); DoSmallStasisBeam(ent, muzzle, d_dir); // Main beam tent = G_TempEntity( tr.endpos, EV_STASIS ); // Only add in impact stuff when told to do so if ( render_impact ) { // send bullet impact if ( traceEnt->takedamage && traceEnt->client ) { tent2 = G_TempEntity( tr.endpos, EV_STASIS_HIT ); tent->s.otherEntityNum = traceEnt->s.number; do_damage = W_CheckBorgAdapt( ent, WP_STASIS, traceEnt, tr.endpos ); } else { tent2 = G_TempEntity( tr.endpos, EV_STASIS_MISS ); if ( ent->s.number == 0 ) { //Add the event AddSoundEvent( ent, tr.endpos, 256, AEL_DISCOVERED ); AddSightEvent( ent, tr.endpos, 512, AEL_DISCOVERED ); } } // Stash origins, etc. so that the effects can have access to them VectorCopy( muzzle, tent2->s.origin2 ); tent2->s.eventParm = tent->s.eventParm = DirToByte( tr.plane.normal ); tent2->s.weapon = tent->s.weapon = ent->s.weapon; } if ( traceEnt->takedamage && do_damage ) { //For knockback - send them up in air VectorCopy( forward, d_dir ); if ( d_dir[2] < 0.30f ) { d_dir[2] = 0.30f; } VectorNormalize( d_dir ); G_Damage( traceEnt, ent, ent, d_dir, tr.endpos, STASIS_ALT_DAMAGE, DAMAGE_NO_HIT_LOC, MOD_STASIS ); } // Stash origins, etc. so that the effects can have access to them VectorCopy( muzzle, tent->s.origin2 ); } //--------------------------------------------------------- void WP_FireStasis( gentity_t *ent, qboolean alt_fire ) //--------------------------------------------------------- { // This was moved out of the FireWeapon switch statement below to keep things more consistent if ( alt_fire ) { WP_FireStasisAlt( ent ); } else { WP_FireStasisMain( ent ); } } /* ---------------------------------------------- GRENADE LAUNCHER ---------------------------------------------- */ #define GRENADE_DAMAGE 100 #define GRENADE_SPLASH_RAD 128 #define GRENADE_SPLASH_DAM 90 #define GRENADE_VELOCITY 900 #define GRENADE_TIME 6000 #define GRENADE_ALT_DAMAGE 100 #define GRENADE_ALT_SPLASH_RAD 128 #define GRENADE_ALT_SPLASH_DAM 90 #define GRENADE_ALT_VELOCITY 1600 #define GRENADE_ALT_TIME 3000 #define SHRAPNEL_DAMAGE 30 #define SHRAPNEL_DISTANCE 4096 #define SHRAPNEL_BITS 6 #define SHRAPNEL_RANDOM 3 #define SHRAPNEL_SPREAD 0.75 //--------------------------------------------------------- void grenadeExplode( gentity_t *ent ) //--------------------------------------------------------- { vec3_t normal = { 0, 0, 1 }; vec3_t pos; gentity_t *tent; VectorSet( pos, ent->currentOrigin[0], ent->currentOrigin[1], ent->currentOrigin[2] + 8 ); G_RadiusDamage( ent->currentOrigin, ent->owner, GRENADE_SPLASH_DAM, GRENADE_SPLASH_RAD, NULL, MOD_GRENADE); tent = G_TempEntity( pos, EV_GRENADE_EXPLODE ); VectorCopy( normal, tent->s.origin2 ); G_FreeEntity( ent ); } //--------------------------------------------------------- void WP_FireShrapnel( gentity_t *ent, vec3_t dir ) //--------------------------------------------------------- { vec3_t end, org; trace_t tr; gentity_t *tent; VectorCopy( ent->s.pos.trBase, org ); VectorMA( org, SHRAPNEL_DISTANCE, dir, end ); gi.trace ( &tr, org, NULL, NULL, end, 0, MASK_SHOT ); tent = G_TempEntity( org, EV_GRENADE_SHRAPNEL ); VectorCopy( tr.endpos, tent->s.origin2 ); VectorCopy( tr.plane.normal, tent->pos1 ); } //--------------------------------------------------------- void grenadeSpewShrapnel( gentity_t *ent ) //--------------------------------------------------------- { int i, t, num_bits; vec3_t norm; gentity_t *tent; num_bits = (int)( SHRAPNEL_BITS + random() * SHRAPNEL_RANDOM ); tent = G_TempEntity( ent->currentOrigin, EV_GRENADE_SHRAPNEL_EXPLODE ); VectorCopy( ent->pos1, tent->pos1 ); if ( ent->owner && ent->owner->s.number == 0 ) { //Add the event AddSoundEvent( ent->owner, ent->currentOrigin, 256, AEL_DISCOVERED ); AddSightEvent( ent->owner, ent->currentOrigin, 512, AEL_DISCOVERED ); } G_RadiusDamage( ent->currentOrigin, ent->owner, GRENADE_ALT_SPLASH_DAM, GRENADE_ALT_SPLASH_RAD, NULL, MOD_GRENADE); for ( i = 0; i < num_bits; i++ ) { VectorCopy( ent->pos1, norm ); for ( t = 0; t < 3; t++ ) { norm[t] += crandom() * SHRAPNEL_SPREAD; } VectorNormalize( norm ); WP_FireShrapnel( ent, norm ); } ent->nextthink = level.time + 100; ent->e_ThinkFunc = thinkF_G_FreeEntity; } //--------------------------------------------------------- void WP_FireGrenade( gentity_t *ent, qboolean alt_fire ) //--------------------------------------------------------- { gentity_t *bolt; vec3_t dir, start; VectorCopy( forward, dir ); VectorCopy( muzzle, start ); bolt = G_Spawn(); if ( alt_fire ) { bolt->classname = "grenade_alt_projectile"; bolt->nextthink = level.time + GRENADE_ALT_TIME; // How long 'til she blows bolt->e_ThinkFunc = thinkF_grenadeSpewShrapnel; bolt->alt_fire = qtrue; bolt->s.eFlags |= EF_MISSILE_STICK; VectorScale( dir, GRENADE_ALT_VELOCITY, bolt->s.pos.trDelta ); bolt->s.pos.trType = TR_GRAVITY; bolt->trigger_formation = qtrue; bolt->damage = GRENADE_ALT_DAMAGE; bolt->dflags = 0; bolt->splashDamage = GRENADE_ALT_SPLASH_DAM; bolt->splashRadius = GRENADE_ALT_SPLASH_RAD; } else { bolt->classname = "grenade_projectile"; bolt->nextthink = level.time + GRENADE_TIME; // How long 'til she blows bolt->e_ThinkFunc = thinkF_grenadeExplode; bolt->s.eFlags |= EF_BOUNCE_HALF; VectorScale( dir, GRENADE_VELOCITY, bolt->s.pos.trDelta ); bolt->s.pos.trDelta[2] += 120; bolt->s.pos.trType = TR_GRAVITY; bolt->damage = GRENADE_DAMAGE; bolt->dflags = 0; bolt->splashDamage = GRENADE_SPLASH_DAM; bolt->splashRadius = GRENADE_SPLASH_RAD; // How 'bout we give this thing a size... VectorSet( bolt->mins, -2.0f, -2.0f, -2.0f ); VectorSet( bolt->maxs, 2.0f, 2.0f, 2.0f ); } bolt->s.eType = ET_MISSILE; bolt->svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_GRENADE_LAUNCHER; bolt->owner = bolt->owner = ent; bolt->methodOfDeath = MOD_GRENADE; //bolt->splashMethodOfDeath = MOD_ROCKET_SPLASH; bolt->clipmask = MASK_SHOT; bolt->s.pos.trTime = level.time; // move a bit on the very first frame W_TraceSetStart( ent, start, bolt->mins, bolt->maxs );//make sure our start point isn't on the other side of a wall VectorCopy( start, bolt->s.pos.trBase ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy (start, bolt->currentOrigin); VectorCopy( start, bolt->pos2 ); } /* ---------------------------------------------- TETRION ---------------------------------------------- */ #define TETRION_SPREAD 275 #define TETRION_DAMAGE 4 #define NUM_TETRION_SHOTS 3 #define TETRION_ALT_VELOCITY 1500 #define TETRION_ALT_DAMAGE 40 //--------------------------------------------------------- void FireTetrionBullet( gentity_t *ent, vec3_t start, vec3_t dir ) //--------------------------------------------------------- { gentity_t *tent = 0; gentity_t *tent2 = 0; gentity_t *traceEnt, *hitEnt = NULL; trace_t tr; vec3_t end; float r; float u; qboolean render_impact = qtrue; qboolean do_damage = qtrue; VectorCopy( muzzle, start ); W_TraceSetStart( ent, start, vec3_origin, vec3_origin );//make sure our start point isn't on the other side of a wall // To simulate fast firing, we'll just generate a couple of shots at once for ( int i = 0; i < NUM_TETRION_SHOTS; i ++ ) { // Add some sloppiness to the shot so it's harder to hit when you are far away r = crandom()*TETRION_SPREAD; u = crandom()*TETRION_SPREAD; VectorMA (start, 8192, forward, end); VectorMA (end, r, vright, end); VectorMA (end, u, up, end); gi.trace ( &tr, start, NULL, NULL, end, ent->s.number, MASK_SHOT); // Rendering things like impacts when hitting a sky box would look bad, but you still want to see the tracer if ( tr.surfaceFlags & SURF_NOIMPACT ) { render_impact = qfalse; } traceEnt = &g_entities[ tr.entityNum ]; // Don't always draw a tracer line if ( random() > 0.1 ) { // Create the trace effect tent = G_TempEntity( tr.endpos, EV_TETRION ); // Stash origins, etc. so that effects can have access to startPoints and other useful info. VectorCopy( start, tent->s.origin2 ); tent->s.eventParm = DirToByte( tr.plane.normal ); tent->s.weapon = ent->s.weapon; VectorScale( forward, -1, tent->pos1 ); // Used for trace-back tent->s.otherEntityNum = traceEnt->s.number; } // Make sure we hit something that should generate an impact if ( render_impact ) { // send bullet impact if ( traceEnt->takedamage && traceEnt->client ) { tent2 = G_TempEntity( tr.endpos, EV_TETRION_HIT ); do_damage = W_CheckBorgAdapt( ent, WP_TETRION_DISRUPTOR, traceEnt, tr.endpos ); if( LogAccuracyHit( traceEnt, ent ) ) { hitEnt = traceEnt; } } else { tent2 = G_TempEntity( tr.endpos, EV_TETRION_MISS ); if ( ent->s.number == 0 ) { //Add the event AddSoundEvent( ent, tr.endpos, 256, AEL_DISCOVERED ); AddSightEvent( ent, tr.endpos, 512, AEL_DISCOVERED ); } } // Stash origins, etc. so that effects can have access to startPoints and other useful info. VectorCopy( start, tent2->s.origin2 ); tent2->s.eventParm = DirToByte( tr.plane.normal ); tent2->s.weapon = ent->s.weapon; } // Let's do some damage to whomever or whatever we hit if ( traceEnt->takedamage && do_damage ) { G_Damage( traceEnt, ent, ent, forward, tr.endpos, TETRION_DAMAGE, DAMAGE_NO_KNOCKBACK, MOD_TETRION ); } } // next shot trace if ( hitEnt ) { ent->client->ps.persistant[PERS_ACCURACY_HITS]++; } } //--------------------------------------------------------- void FireTetrionProjectile( gentity_t *ent, vec3_t start, vec3_t dir ) //--------------------------------------------------------- {// Projectile that bounces off surfaces but does not have gravity gentity_t *bolt; bolt = G_Spawn(); bolt->classname = "tetrion_projectile"; bolt->nextthink = level.time + 4000; bolt->e_ThinkFunc = thinkF_G_FreeEntity; bolt->s.eType = ET_MISSILE; bolt->svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.eFlags |= EF_BOUNCE; bolt->bounceCount = Q_irand( 3, 5 ); // Give 'em three to five bounces bolt->alt_fire = qtrue; bolt->s.weapon = WP_TETRION_DISRUPTOR; bolt->damage = TETRION_ALT_DAMAGE; bolt->dflags = 0; bolt->splashDamage = 0; bolt->splashRadius = 0; bolt->owner = ent; bolt->methodOfDeath = MOD_TETRION; //bolt->splashMethodOfDeath = MOD_ROCKET_SPLASH; bolt->clipmask = MASK_SHOT; // How 'bout we give this thing a size... VectorSet( bolt->mins, -2.0f, -2.0f, -2.0f ); VectorSet( bolt->maxs, 2.0f, 2.0f, 2.0f ); bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time; // move a bit on the very first frame W_TraceSetStart( ent, start, bolt->mins, bolt->maxs );//make sure our start point isn't on the other side of a wall VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, TETRION_ALT_VELOCITY, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy (start, bolt->currentOrigin); } //--------------------------------------------------------- void WP_FireTetrionDisruptor( gentity_t *ent, qboolean alt_fire ) //--------------------------------------------------------- { vec3_t dir; vec3_t start; VectorCopy( forward, dir ); VectorCopy( muzzle, start ); VectorNormalize (dir); if ( alt_fire ) { FireTetrionProjectile( ent, start, dir ); } else { FireTetrionBullet( ent, start, dir ); } } /* ---------------------------------------------- DREADNOUGHT ---------------------------------------------- */ #define DREADNOUGHT_DAMAGE 13 #define DN_VELOCITY 1400 #define DREADNOUGHT_ALT_DAMAGE 60 void WP_DreadnoughtBurstThink( gentity_t *ent ); //--------------------------------------------------------- void WP_FireDreadnoughtBeam( gentity_t *ent ) //--------------------------------------------------------- { trace_t tr; vec3_t start, end, mins, maxs; gentity_t *traceEnt; qboolean do_damage = qtrue; // Make sure that the damage trace corresponds with the trace done for the actual effect or weird things could happen if ( ent->s.number == 0 ) VectorCopy( ent->client->renderInfo.eyePoint, start ); else VectorCopy( muzzle, start ); VectorMA( start, weaponData[WP_DREADNOUGHT].range, forward, end ); // Give the beam a bit of meat VectorSet( maxs, 2.0f, 2.0f, 2.0f ); VectorScale( maxs, -1.0f, mins ); // Find out who we've hit gi.trace( &tr, muzzle, mins, maxs, end, ent->s.number, MASK_SHOT ); traceEnt = &g_entities[ tr.entityNum ]; if ( !traceEnt->takedamage && ent->s.number == 0 ) { //Add the event AddSoundEvent( ent, tr.endpos, 256, AEL_DISCOVERED ); AddSightEvent( ent, tr.endpos, 512, AEL_DISCOVERED ); } do_damage = W_CheckBorgAdapt( ent, WP_DREADNOUGHT, traceEnt, tr.endpos ); if ( traceEnt->takedamage ) { if ( LogAccuracyHit( traceEnt, ent ) ) { ent->client->ps.persistant[PERS_ACCURACY_HITS]++; } if ( do_damage ) { G_Damage( traceEnt, ent, ent, forward, tr.endpos, DREADNOUGHT_DAMAGE, 0, MOD_DREADNOUGHT); } } } #define DN_SEARCH_DIST 128 #define DN_SIDE_DIST 128 #define DN_RAND_DEV 24 #define DN_ALT_THINK_TIME 100 #define DN_ALT_SIZE 12 //--------------------------------------------------------- void WP_FireDreadnoughtBurst( gentity_t *ent ) //--------------------------------------------------------- { gentity_t *bolt; vec3_t dir, start; VectorCopy( forward, dir ); VectorCopy( muzzle, start ); bolt = G_Spawn(); bolt->classname = "dn_projectile"; bolt->nextthink = level.time + 200; bolt->e_ThinkFunc = thinkF_WP_DreadnoughtBurstThink; bolt->count = 5; //how many times it can damage something bolt->s.eType = ET_MISSILE; bolt->svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_DREADNOUGHT; bolt->owner = ent; bolt->damage = DREADNOUGHT_ALT_DAMAGE; bolt->dflags = 0; bolt->splashDamage = 0; bolt->splashRadius = 0; bolt->methodOfDeath = MOD_DREADNOUGHT; bolt->clipmask = MASK_SHOT; bolt->alt_fire = qtrue; bolt->fx_time = level.time; bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time; { vec3_t mins={-DN_ALT_SIZE,-DN_ALT_SIZE,-1}, maxs={DN_ALT_SIZE,DN_ALT_SIZE,1}; W_TraceSetStart( ent, start, mins, maxs );//make sure our start point isn't on the other side of a wall } VectorCopy( start, bolt->s.pos.trBase ); VectorCopy( start, bolt->s.origin); //VectorScale( dir, DN_VELOCITY, bolt->s.pos.trDelta ); VectorCopy( forward, bolt->movedir); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->currentOrigin ); VectorCopy( start, bolt->pos1 ); VectorCopy( start, bolt->pos2 ); WP_DreadnoughtBurstThink(bolt); } //--------------------------------------------------------- void WP_DreadnoughtBurstThink( gentity_t *ent ) //--------------------------------------------------------- { static qboolean bAlreadyHere = qfalse; vec3_t startpos, endpos, perp; trace_t tr; gentity_t *traceEnt, *tent; int source; vec3_t dest, mins={-DN_ALT_SIZE,-DN_ALT_SIZE,-1}, maxs={DN_ALT_SIZE,DN_ALT_SIZE,1}; float dot; ent->count--; //dec the count of repeat hits VectorCopy(ent->s.origin, startpos); // Search in a 3-way arc in front of it. VectorMA( startpos, DN_SEARCH_DIST, ent->movedir, endpos ); endpos[0] += crandom() * DN_RAND_DEV; endpos[1] += crandom() * DN_RAND_DEV; endpos[2] += crandom() * DN_RAND_DEV * 0.5f; // unlink this entity, so the next trace will go past it gi.unlinkentity( ent ); if ( ent->enemy ) { source = ent->enemy->s.number; } else if ( ent->owner ) { source = ent->owner->s.number; } else { source = ent->s.number; } gi.trace( &tr, startpos, mins, maxs, endpos, source, MASK_SHOT ); traceEnt = &g_entities[ tr.entityNum ]; if ( traceEnt->takedamage && traceEnt->health > 0 && ent->count > 0 ) { if ( traceEnt->client ) { if ( traceEnt->client->playerTeam == TEAM_STARFLEET || traceEnt->client->noclip ) { // Scale down damage, hurt them, but don't stick on them G_Damage( traceEnt, ent, ent->owner, forward, tr.endpos, ent->damage * 0.5f, 0, MOD_DREADNOUGHT ); goto ignore1; } G_SetEnemy(ent, traceEnt); VectorCopy( ent->s.origin, ent->s.origin2 ); VectorCopy( traceEnt->currentOrigin, ent->s.origin ); gi.linkentity( ent ); VectorNormalize( ent->movedir ); ent->nextthink = level.time + DN_ALT_THINK_TIME; G_Damage( traceEnt, ent, ent->owner, forward, tr.endpos, ent->damage, 0, MOD_DREADNOUGHT ); return; } G_Damage( traceEnt, ent, ent->owner, forward, tr.endpos, ent->damage, 0, MOD_DREADNOUGHT ); } else { if ( tr.fraction < 0.02f || ent->count < 1 ) { // Hit a wall... dot = DotProduct( ent->movedir, tr.plane.normal ); if ( !traceEnt->takedamage && ent->owner && ent->owner->s.number == 0 ) { //Add the event AddSoundEvent( ent->owner, tr.endpos, 256, AEL_DISCOVERED ); AddSightEvent( ent->owner, tr.endpos, 512, AEL_DISCOVERED ); } if ( dot < -0.6f || dot == 0.0f || (tr.surfaceFlags & SURF_NOIMPACT) || ent->count < 1 ) { // Stop. G_FreeEntity( ent ); if ( !(tr.surfaceFlags & SURF_NOIMPACT) ) { tent = G_TempEntity( tr.endpos, EV_DREADNOUGHT_MISS ); // Stash origins, etc. so that the effects can have access to them VectorCopy( startpos, tent->s.origin2 ); tent->s.eventParm = DirToByte( tr.plane.normal ); } return; } else { // Bounce off the surface just a little VectorMA( ent->movedir, -1.25f * dot, tr.plane.normal, ent->movedir ); VectorNormalize( ent->movedir ); // Make sure we store a next think time, else the effect could sit around forever... ent->nextthink = level.time + DN_ALT_THINK_TIME; // this stops a runaway crash bug when the insects break through the corridor in voy16 if (!bAlreadyHere) { bAlreadyHere = qtrue; WP_DreadnoughtBurstThink( ent );// NOTE RECURSION HERE. bAlreadyHere = qfalse; } return; } } VectorCopy( tr.endpos, dest ); } // Didn't hit anything forward. Try some side vectors. perp[0] = ent->movedir[1]; perp[1] = -ent->movedir[0]; perp[2] = ent->movedir[2]; // Search a random interval from the side arc VectorMA( endpos, DN_SIDE_DIST, perp, endpos ); endpos[0] += crandom() * DN_RAND_DEV; endpos[1] += crandom() * DN_RAND_DEV; endpos[2] += crandom() * DN_RAND_DEV * 0.5f; gi.trace( &tr, startpos, mins, maxs, endpos, source, MASK_SHOT ); traceEnt = &g_entities[ tr.entityNum ]; if ( traceEnt->takedamage && traceEnt->health > 0 ) { if ( traceEnt->client ) { if ( traceEnt->client->playerTeam == TEAM_STARFLEET || traceEnt->client->noclip ) { goto ignore1; } G_SetEnemy( ent, traceEnt ); VectorCopy( ent->s.origin, ent->s.origin2 ); VectorCopy( traceEnt->currentOrigin, ent->s.origin ); gi.linkentity( ent ); VectorNormalize( ent->movedir ); ent->nextthink = level.time + DN_ALT_THINK_TIME; G_Damage( traceEnt, ent, ent->owner, forward, tr.endpos, ent->damage, 0, MOD_DREADNOUGHT ); return; } G_Damage( traceEnt, ent, ent->owner, forward, tr.endpos, ent->damage, 0, MOD_DREADNOUGHT ); } // Search a random interval in the opposite direction VectorMA( endpos, -2.0f * DN_SIDE_DIST, perp, endpos ); endpos[0] += crandom() * DN_RAND_DEV; endpos[1] += crandom() * DN_RAND_DEV; endpos[2] += crandom() * DN_RAND_DEV * 0.5f; gi.trace( &tr, startpos, mins, maxs, endpos, source, MASK_SHOT ); traceEnt = &g_entities[ tr.entityNum ]; if ( traceEnt->takedamage && traceEnt->health > 0 ) { if ( traceEnt->client ) { if ( traceEnt->client->playerTeam == TEAM_STARFLEET || traceEnt->client->noclip ) { goto ignore1; } G_SetEnemy(ent, traceEnt); VectorCopy( ent->s.origin, ent->s.origin2 ); VectorCopy( traceEnt->currentOrigin, ent->s.origin ); gi.linkentity( ent ); VectorNormalize( ent->movedir ); ent->nextthink = level.time + DN_ALT_THINK_TIME; G_Damage( traceEnt, ent, ent->owner, forward, tr.endpos, ent->damage, 0, MOD_DREADNOUGHT ); return; } G_Damage( traceEnt, ent, ent->owner, forward, tr.endpos, ent->damage, 0, MOD_DREADNOUGHT ); } ignore1: // We didn't find anything, so move the entity to the middle destination. ent->enemy = NULL; VectorCopy( ent->s.origin, ent->s.origin2 ); VectorCopy( dest, ent->s.origin ); gi.linkentity( ent ); ent->nextthink = level.time + DN_ALT_THINK_TIME; return; } //--------------------------------------------------------- void WP_FireDreadnought( gentity_t *ent, qboolean alt_fire ) //--------------------------------------------------------- { // This was moved out of the FireWeapon switch statement below to keep things more consistent if ( alt_fire ) { WP_FireDreadnoughtBurst( ent ); } else { WP_FireDreadnoughtBeam( ent ); } } /* ---------------------------------------------- QUANTUM BURST ---------------------------------------------- */ #define QUANTUM_VELOCITY 900 #define QUANTUM_DAMAGE 100 #define QUANTUM_SPLASH_DAM 64 #define QUANTUM_SPLASH_RAD 128 #define QUANTUM_ALT_VELOCITY 500 #define QUANTUM_ALT_DAMAGE 125 #define QUANTUM_ALT_SPLASH_DAM 100 #define QUANTUM_ALT_SPLASH_RAD 128 #define QUANTUM_ALT_THINK_TIME 200 #define QUANTUM_ALT_SEARCH_DIST 1024 //--------------------------------------------------------- void FireQuantumBurst( gentity_t *ent, vec3_t start, vec3_t dir ) //--------------------------------------------------------- { gentity_t *bolt; bolt = G_Spawn(); bolt->classname = "quantum_projectile"; bolt->nextthink = level.time + 6000; bolt->e_ThinkFunc = thinkF_G_FreeEntity; bolt->s.eType = ET_MISSILE; bolt->svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_QUANTUM_BURST; bolt->owner = ent; bolt->damage = QUANTUM_DAMAGE; bolt->dflags = 0; bolt->splashDamage = QUANTUM_SPLASH_DAM; bolt->splashRadius = QUANTUM_SPLASH_RAD; bolt->methodOfDeath = MOD_QUANTUM; bolt->splashMethodOfDeath = MOD_QUANTUM_SPLASH; bolt->clipmask = MASK_SHOT; // How 'bout we give this thing a size... VectorSet( bolt->mins, -3.0f, -3.0f, -3.0f ); VectorSet( bolt->maxs, 3.0f, 3.0f, 3.0f ); bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, QUANTUM_VELOCITY, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy (start, bolt->currentOrigin); VectorCopy (start, bolt->pos1); } //--------------------------------------------------------- void FireQuantumBurstAlt( gentity_t *ent, vec3_t start, vec3_t dir ) //--------------------------------------------------------- { gentity_t *bolt; bolt = G_Spawn(); bolt->classname = "quantum_alt_projectile"; // bolt->nextthink = level.time + 6000; // bolt->e_ThinkFunc = thinkF_G_FreeEntity; bolt->nextthink = level.time + 100; bolt->e_ThinkFunc = thinkF_WP_QuantumAltThink; bolt->health = 50; // 10 seconds. bolt->s.eType = ET_MISSILE; bolt->svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_QUANTUM_BURST; bolt->owner = ent; bolt->alt_fire = qtrue; // Things won't work if this doesn't get set!! bolt->damage = QUANTUM_ALT_DAMAGE; bolt->dflags = 0; bolt->splashDamage = QUANTUM_ALT_SPLASH_DAM; bolt->splashRadius = QUANTUM_ALT_SPLASH_RAD; bolt->methodOfDeath = MOD_QUANTUM; bolt->splashMethodOfDeath = MOD_QUANTUM_SPLASH; bolt->clipmask = MASK_SHOT; // How 'bout we give this thing a size... VectorSet( bolt->mins, -3.0f, -3.0f, -3.0f ); VectorSet( bolt->maxs, 3.0f, 3.0f, 3.0f ); bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, QUANTUM_ALT_VELOCITY, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy (start, bolt->currentOrigin); VectorCopy(dir, bolt->movedir); } //--------------------------------------------------------- qboolean SearchTarget( gentity_t *ent, vec3_t start, vec3_t end ) { trace_t tr; gentity_t *traceEnt; vec3_t fwd; gi.trace( &tr, start, NULL, NULL, end, ent->s.number, MASK_SHOT ); traceEnt = &g_entities[ tr.entityNum ]; if( traceEnt->takedamage && traceEnt->client && traceEnt->health > 0 ) { G_SetEnemy(ent, traceEnt); if ( ent->enemy ) { VectorSubtract( ent->enemy->currentOrigin, ent->currentOrigin, fwd ); VectorNormalize( fwd ); VectorScale( fwd, QUANTUM_ALT_VELOCITY, ent->s.pos.trDelta ); VectorCopy( fwd, ent->movedir ); SnapVector( ent->s.pos.trDelta ); // save net bandwidth VectorCopy( ent->currentOrigin, ent->s.pos.trBase ); ent->s.pos.trTime = level.time; ent->nextthink = level.time + QUANTUM_ALT_THINK_TIME; return qtrue; } } return qfalse; } //--------------------------------------------------------- void WP_QuantumAltThink( gentity_t *ent ) { vec3_t start, newdir, targetdir, up={0,0,1}, right, search; float dot, dot2; ent->health--; if( ent->health <= 0 ) { G_FreeEntity( ent ); return; } if ( ent->enemy ) { // Already have a target, start homing. if ( !ent->enemy->inuse || ent->enemy->health <= 0 ) { // No longer target this. ent->enemy = NULL; ent->nextthink = level.time + 1000; ent->health -= 5; return; } VectorSubtract( ent->enemy->currentOrigin, ent->currentOrigin, targetdir ); VectorNormalize( targetdir ); // Now the rocket can't do a 180 in space, so we'll limit the turn to about 45 degrees. dot = DotProduct( targetdir, ent->movedir ); // a dot of 1.0 means right-on-target. if ( dot < 0.0f ) { // Go in the direction opposite, start a 180. CrossProduct( ent->movedir, up, right ); dot2 = DotProduct( targetdir, right ); if ( dot2 > 0 ) { // Turn 45 degrees right. VectorAdd( ent->movedir, right, newdir ); } else { // Turn 45 degrees left. VectorSubtract(ent->movedir, right, newdir); } // Yeah we've adjusted horizontally, but let's split the difference vertically, so we kinda try to move towards it. newdir[2] = ( targetdir[2] + ent->movedir[2] ) * 0.5; VectorNormalize( newdir ); } else if ( dot < 0.7 ) { // Need about one correcting turn. Generate by meeting the target direction "halfway". // Note, this is less than a 45 degree turn, but it is sufficient. We do this because the rocket may have to go UP. VectorAdd( ent->movedir, targetdir, newdir ); VectorNormalize( newdir ); } else { // else adjust to right on target. VectorCopy( targetdir, newdir ); } VectorScale( newdir, QUANTUM_ALT_VELOCITY, ent->s.pos.trDelta ); VectorCopy( newdir, ent->movedir ); SnapVector( ent->s.pos.trDelta ); // save net bandwidth VectorCopy( ent->currentOrigin, ent->s.pos.trBase ); ent->s.pos.trTime = level.time; } else { // Search in front of the missile for targets. VectorCopy( ent->currentOrigin, start ); CrossProduct( ent->movedir, up, right ); // Search straight ahead. VectorMA( start, QUANTUM_ALT_SEARCH_DIST, ent->movedir, search ); if ( SearchTarget( ent, start, search )) return; // Search to the right. VectorMA( search, QUANTUM_ALT_SEARCH_DIST * 0.1f, right, search ); if ( SearchTarget(ent, start, search )) return; // Search to the left. VectorMA( search, -QUANTUM_ALT_SEARCH_DIST * 0.2f, right, search ); if ( SearchTarget( ent, start, search )) return; } ent->nextthink = level.time + QUANTUM_ALT_THINK_TIME; // Nothing at all spectacular happened, continue. return; } //--------------------------------------------------------- void WP_FireQuantumBurst( gentity_t *ent, qboolean alt_fire ) //--------------------------------------------------------- { vec3_t dir, start; VectorCopy( forward, dir ); VectorCopy( muzzle, start ); if ( alt_fire ) { FireQuantumBurstAlt( ent, start, dir ); } else { FireQuantumBurst( ent, start, dir ); } } //--------------------------------------------------------- void WP_TricorderScan (gentity_t *ent, qboolean alt_fire) //--------------------------------------------------------- { static int sound_debounce_time; if ( sound_debounce_time < level.time ) { if ( alt_fire ) { sound_debounce_time = level.time + Q_irand(500, 1500); } else { sound_debounce_time = level.time + 5000; } } } /* ---------------------------------------------- PROTON GUN ---------------------------------------------- */ #define PROTON_DAMAGE 25 #define COMPRESSION_SPREAD 100 //--------------------------------------------------------- void WP_FireProtonGun ( gentity_t *ent, qboolean alt_fire ) //--------------------------------------------------------- { gentity_t *tent; gentity_t *tent2 = 0; gentity_t *traceEnt; int damage = alt_fire ? PROTON_DAMAGE * 6 : PROTON_DAMAGE; // do butt-loads of damage for alt trace_t tr; vec3_t start, end; qboolean do_damage = qtrue; qboolean render_impact = qtrue; if ( ent->s.number == 0 && !cg_thirdPerson.integer ) // The trace start will originate at the eye so we can ensure that it hits the crosshair. // This can cause small clipping errors, but it's probably not that big of a deal at this point. VectorCopy( ent->client->renderInfo.eyePoint, start ); else VectorCopy( muzzle, start ); VectorMA( start, -8, forward, start ); VectorMA( start, 8192, forward, end ); gi.trace ( &tr, start, NULL, NULL, end, ent->s.number, MASK_SHOT ); // If the beam hits a skybox, etc. it would look foolish to add in an explosion if ( tr.surfaceFlags & SURF_NOIMPACT ) { render_impact = qfalse; } traceEnt = &g_entities[ tr.entityNum ]; // Render a shot in any case. if ( alt_fire ) tent = G_TempEntity( tr.endpos, EV_PROTON_GUN_ALT ); else tent = G_TempEntity( tr.endpos, EV_PROTON_GUN ); // Only add in impact stuff when told to do so if ( render_impact ) { // send bullet impact if ( traceEnt->takedamage && traceEnt->client ) { // Create a simple impact type mark tent2 = G_TempEntity( tr.endpos, EV_PROTON_GUN_HIT ); tent->s.otherEntityNum = traceEnt->s.number; do_damage = W_CheckBorgAdapt( ent, WP_PROTON_GUN, traceEnt, tr.endpos ); if( LogAccuracyHit( traceEnt, ent ) ) { ent->client->ps.persistant[PERS_ACCURACY_HITS]++; } } else { if ( alt_fire) { // Do a discreet miss tent2 = G_TempEntity( tr.endpos, EV_PROTON_GUN_ALT_MISS ); AddSightEvent( ent, tr.endpos, 256, AEL_SUSPICIOUS ); } else { // Create an explosion tent2 = G_TempEntity( tr.endpos, EV_PROTON_GUN_MISS ); // FIXME: Is this really an ok place to add this? if ( ent->s.number == 0 ) { //Add the event AddSoundEvent( ent, tr.endpos, 256, AEL_DISCOVERED ); AddSightEvent( ent, tr.endpos, 512, AEL_DISCOVERED ); } } } } // Stash origins, etc. so that the effect can have access to them. VectorCopy( muzzle, tent->s.origin2 ); if ( render_impact ) { VectorCopy( muzzle, tent2->s.origin2 ); tent2->s.eventParm = tent->s.eventParm = DirToByte( tr.plane.normal ); tent2->s.weapon = tent->s.weapon = ent->s.weapon; } if ( traceEnt->takedamage && do_damage ) { // Do less damage when it's an NPC that is shooting this weapon, this is done so that they can still shoot a lot // but not necessarily kill everything for you. if ( ent->client->ps.clientNum != 0 ) { damage *= 0.5; } if ( alt_fire ) { G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, MOD_SNIPER ); } else { G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_CRIFLE ); } } } /* ---------------------------------------------- BORG WEAPONS ---------------------------------------------- */ #define BORG_PROJ_DAMAGE 10 #define BORG_PROJ_SPEED 500 //--------------------------------------------------------- void WP_FireBorgWeapon( gentity_t *ent ) //--------------------------------------------------------- { gentity_t *bolt; vec3_t dir, start; VectorCopy( forward, dir ); VectorCopy( muzzle, start ); VectorNormalize (dir); bolt = G_Spawn(); bolt->classname = "borg_projectile"; bolt->nextthink = level.time + 10000; bolt->e_ThinkFunc = thinkF_G_FreeEntity; bolt->s.eType = ET_MISSILE; bolt->svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_BORG_WEAPON; bolt->owner = ent; bolt->damage = BORG_PROJ_DAMAGE; bolt->dflags = 0; bolt->splashDamage = 0; bolt->splashRadius = 0; bolt->methodOfDeath = MOD_ENERGY; bolt->clipmask = MASK_SHOT; // How 'bout we give this thing a size... VectorSet( bolt->mins, -2.0f, -2.0f, -2.0f ); VectorSet( bolt->maxs, 2.0f, 2.0f, 2.0f ); bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, BORG_PROJ_SPEED, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy (start, bolt->currentOrigin); } #define BORG_ATTACK_DELAY 100 void CG_DrawNode( vec3_t origin, int type ); //--------------------------------------------------------- void WP_BorgAssimilate(gentity_t *borg) //--------------------------------------------------------- { trace_t tr; vec3_t end; gentity_t *traceEnt; if ( borg->attackDebounceTime > level.time ) return; muzzle[2] -= 8; VectorMA (muzzle, 64, forward, end); gi.trace ( &tr, muzzle, NULL, NULL, end, borg->s.number, MASK_SHOT ); if ( tr.surfaceFlags & SURF_NOIMPACT ) return; traceEnt = &g_entities[ tr.entityNum ]; //FIXME: generate effect and sound if ( traceEnt->takedamage && traceEnt->client && traceEnt->client->ps.stats[STAT_HEALTH] > 0) { //You've been assimilated! float damage = ( g_spskill->integer == 2 ) ? 5.0f : 1.0f; G_Damage( traceEnt, borg, borg, forward, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, MOD_ASSIMILATE ); } //Basically, longer on easy, less on hard int attackDelay = ( ( 2 - ( ( g_spskill->integer ) % 3 ) ) + 1 ) * BORG_ATTACK_DELAY; borg->attackDebounceTime = level.time + attackDelay; } //--------------------------------------------------------- void WP_FireBorgTaser( gentity_t *ent ) //--------------------------------------------------------- { trace_t tr; vec3_t end, d_dir; gentity_t *tent; gentity_t *traceEnt; VectorMA (muzzle, weaponData[WP_BORG_TASER].range, forward, end); gi.trace ( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT); if ( tr.surfaceFlags & SURF_NOIMPACT ) { return; } traceEnt = &g_entities[ tr.entityNum ]; // send bullet impact if ( traceEnt->takedamage && traceEnt->client ) { tent = G_TempEntity( tr.endpos, EV_BTASER_HIT ); } else { tent = G_TempEntity( tr.endpos, EV_BTASER_MISS ); } VectorCopy( muzzle, tent->s.origin2 ); tent->s.eventParm = DirToByte( tr.plane.normal ); tent->s.weapon = ent->s.weapon; if ( ent->NPC_type && Q_stricmp( "satan", ent->NPC_type ) == 0 ) {//satan's robot- more damage, greyscale effect tent->count = 1; } if ( traceEnt->takedamage) { //For knockback - send them up in air VectorCopy(forward, d_dir); if(d_dir[2] < 0.30f) { d_dir[2] = 0.30f; } VectorNormalize(d_dir); G_Damage( traceEnt, ent, ent, d_dir, tr.endpos, 5, 0, MOD_TASER ); } } /* ---------------------------------------------- SCOUT BOT ---------------------------------------------- */ #define BOT_WELDER_RANGE 128 #define BOT_WELDER_DAMAGE_EASY 4 #define BOT_WELDER_DAMAGE_NORMAL 8 #define BOT_WELDER_DAMAGE_HARD 10 #define BOT_WELDER_SPEED_EASY 500 #define BOT_WELDER_SPEED_NORMAL 650 #define BOT_WELDER_SPEED_HARD 800 #define BOT_LASER_DAMAGE 5 #define BOT_LASER_RANGE 1024 //--------------------------------------------------------- void WP_BotWelder( gentity_t *ent ) //--------------------------------------------------------- { gentity_t *bolt; vec3_t dir, start; VectorCopy( forward, dir ); VectorCopy( muzzle, start ); VectorNormalize (dir); bolt = G_Spawn(); bolt->classname = "bot_projectile"; bolt->nextthink = level.time + 10000; bolt->e_ThinkFunc = thinkF_G_FreeEntity; bolt->s.eType = ET_MISSILE; bolt->svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_BOT_WELDER; bolt->owner = ent; if ( g_spskill->integer == 0 ) bolt->damage = BOT_WELDER_DAMAGE_EASY; else if ( g_spskill->integer == 1 ) bolt->damage = BOT_WELDER_DAMAGE_NORMAL; else bolt->damage = BOT_WELDER_DAMAGE_HARD; bolt->dflags = 0; bolt->splashDamage = 0; bolt->splashRadius = 0; bolt->methodOfDeath = MOD_ENERGY; bolt->clipmask = MASK_SHOT; // How 'bout we give this thing a size... // VectorSet( bolt->mins, -2.0f, -2.0f, -2.0f ); // VectorSet( bolt->maxs, 2.0f, 2.0f, 2.0f ); bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); float speed; if ( g_spskill->integer == 0 ) speed = BOT_WELDER_SPEED_EASY; else if ( g_spskill->integer == 1 ) speed = BOT_WELDER_SPEED_NORMAL; else speed = BOT_WELDER_SPEED_HARD; VectorScale( dir, speed, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy (start, bolt->currentOrigin); VectorCopy( start, bolt->pos1 ); } void WP_BotLaser( gentity_t *ent ) //--------------------------------------------------------- {//Short burst laser, works like a long range lightning gun (held fire) trace_t tr; vec3_t end; gentity_t *traceEnt;//, *tent; VectorMA( muzzle, BOT_LASER_RANGE, forward, end ); gi.trace( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT ); if ( tr.entityNum == ENTITYNUM_NONE ) { return; } traceEnt = &g_entities[ tr.entityNum ]; // send bullet impact if ( traceEnt->takedamage && traceEnt->client ) { /* tent = G_TempEntity( tr.endpos, EV_DREADNOUGHT_HIT ); tent->s.otherEntityNum = traceEnt->s.number; tent->s.eventParm = DirToByte( tr.plane.normal ); tent->s.weapon = ent->s.weapon; */ } else if ( !( tr.surfaceFlags & SURF_NOIMPACT ) ) { /* tent = G_TempEntity( tr.endpos, EV_DREADNOUGHT_MISS ); tent->s.eventParm = DirToByte( tr.plane.normal ); */ } if ( traceEnt->takedamage) { G_Damage( traceEnt, ent, ent, forward, tr.endpos, BOT_LASER_DAMAGE, 0, MOD_TARGET_LASER ); } if ( !ent->s.loopSound ) { G_Sound( ent, G_SoundIndex( "sound/weapons/scout_bot/laser_start.wav" ) ); ent->s.loopSound = G_SoundIndex( "sound/weapons/scout_bot/laser_firing.wav" ); } } /* ---------------------------------------------- HUNTER SEEKER ---------------------------------------------- */ #define BOT_ROCKET_DAMAGE 10 #define BOT_ROCKET_SPLASH_RAD 64 #define BOT_ROCKET_SPLASH_DAM 8 #define BOT_ROCKET_SPEED 250 #define BOT_ROCKET_CORRECT_AMT 0.05f // (0.0f - 1.0f) -- higher values are better #define BOT_ROCKET_DRUNK_AMT 0.009f void bot_rocket_think( gentity_t *ent ) //--------------------------------------------------------- { vec3_t newdir, targetdir, dir, org; float dot; if ( ent->owner && ent->owner->enemy ) { // Already have a target, start homing. if ( !ent->owner->enemy->inuse || ent->owner->enemy->health <= 0 ) { // No longer target this. ent->owner = NULL; ent->nextthink = level.time + 1000; return; } AngleVectors( ent->owner->enemy->client->ps.viewangles, dir, NULL, NULL ); dot = DotProduct( dir, ent->movedir ); // If the thing gets behind us, don't track anymore if ( ( dot < -0.5f ) || ( ent->spawnflags & 2 ) ) { VectorCopy( ent->owner->enemy->currentOrigin, org ); org[2] += 22; VectorSubtract( org, ent->currentOrigin, targetdir ); VectorNormalize( targetdir ); // Do some direction correction if ( ent->spawnflags & 2 ) { VectorMA( ent->movedir, BOT_ROCKET_CORRECT_AMT * 4, targetdir, newdir ); } else { VectorMA( ent->movedir, BOT_ROCKET_CORRECT_AMT * (g_spskill->integer + 1), targetdir, newdir ); } // Make the rocket act a bit "drunk" for ( int i = 0; i < 3; i++ ) { newdir[i] += crandom() * BOT_ROCKET_DRUNK_AMT * (g_spskill->integer + 1) * (g_spskill->integer + 1); } VectorNormalize( newdir ); if ( ent->spawnflags & 2 ) { VectorScale( newdir, 400, ent->s.pos.trDelta ); } else { VectorScale( newdir, ( g_spskill->integer ) * 75 + BOT_ROCKET_SPEED, ent->s.pos.trDelta ); } VectorCopy( newdir, ent->movedir ); SnapVector( ent->s.pos.trDelta ); // save net bandwidth VectorCopy( ent->currentOrigin, ent->s.pos.trBase ); ent->s.pos.trTime = level.time; } } ent->nextthink = level.time + 100; } //--------------------------------------------------------- void bot_rocket_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod ) { gentity_t *tent; // Death tent = G_TempEntity(self->currentOrigin, EV_BOT_ROCKET_DIE); VectorAdd( self->currentOrigin, self->movedir, tent->s.origin2 ); // remove the rocket after a bit self->e_ThinkFunc = thinkF_G_FreeEntity; self->nextthink = level.time + 100; } void WP_BotRocket( gentity_t *ent ) //--------------------------------------------------------- {//Rocket projectile gentity_t *bolt; vec3_t start; VectorMA( ent->currentOrigin, 6, forward, start ); VectorMA( start, 2, up, start ); bolt = G_Spawn(); VectorSet( bolt->mins, -4, -4, -4 ); VectorSet( bolt->maxs, 4, 4, 4 ); bolt->classname = "bot_rocket"; bolt->nextthink = level.time + 200; bolt->e_ThinkFunc = thinkF_bot_rocket_think; bolt->s.eType = ET_MISSILE; bolt->svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_BOT_ROCKET; bolt->owner = ent; bolt->damage = BOT_ROCKET_DAMAGE; bolt->dflags = 0; bolt->splashDamage = BOT_ROCKET_SPLASH_DAM; bolt->splashRadius = BOT_ROCKET_SPLASH_RAD; bolt->methodOfDeath = MOD_BOTROCKET; bolt->splashMethodOfDeath = MOD_BOTROCKET_SPLASH; bolt->clipmask = MASK_SHOT; bolt->contents = CONTENTS_SOLID; bolt->takedamage = qtrue; bolt->health = 10; bolt->e_DieFunc = dieF_bot_rocket_die; bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( forward, BOT_ROCKET_SPEED, bolt->s.pos.trDelta ); VectorCopy( forward, bolt->movedir ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->currentOrigin ); VectorCopy( start, bolt->pos1 ); gi.linkentity( bolt ); } void WP_BotTurret( gentity_t * ent ) //--------------------------------------------------------- { vec3_t end; trace_t tr; gentity_t *tr_ent; VectorMA( ent->client->renderInfo.muzzlePoint, 1024, ent->pos2, end ); gi.trace( &tr, ent->client->renderInfo.muzzlePoint, NULL, NULL, end, ent->s.number, MASK_SHOT ); tr_ent = &g_entities[ tr.entityNum ]; G_Damage( tr_ent, ent, ent, ent->pos2, tr.endpos, 3, 0, MOD_TARGET_LASER ); } void WP_HunterSeeker( gentity_t *ent, qboolean alt_fire ) //--------------------------------------------------------- { if ( alt_fire ) WP_BotTurret( ent ); else WP_BotRocket( ent ); } /* ---------------------------------------------- REAVER ---------------------------------------------- */ #define FORGE_PROJECTILE_DAMAGE 10 #define FORGE_PROJECTILE_SPLASH_RAD 64 #define FORGE_PROJECTILE_SPLASH_DAM 8 #define FORGE_PROJECTILE_VELOCITY 750 void WP_ForgeProjectile( gentity_t *ent ) //--------------------------------------------------------- {//Energy projectile weapon gentity_t *bolt; bolt = G_Spawn(); bolt->classname = "forge_projectile"; bolt->nextthink = level.time + 10000; bolt->e_ThinkFunc = thinkF_G_FreeEntity; bolt->s.eType = ET_MISSILE; bolt->svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_FORGE_PROJ; bolt->owner = ent; bolt->damage = FORGE_PROJECTILE_DAMAGE; bolt->dflags = 0; bolt->splashDamage = FORGE_PROJECTILE_SPLASH_DAM; bolt->splashRadius = FORGE_PROJECTILE_SPLASH_RAD; bolt->methodOfDeath = MOD_ENERGY; bolt->splashMethodOfDeath = MOD_ENERGY_SPLASH; bolt->clipmask = MASK_SHOT; // How 'bout we give this thing a size... VectorSet( bolt->mins, -2.0f, -2.0f, -2.0f ); VectorSet( bolt->maxs, 2.0f, 2.0f, 2.0f ); bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time; // move a bit on the very first frame VectorCopy( muzzle, bolt->s.pos.trBase ); VectorScale( forward, FORGE_PROJECTILE_VELOCITY, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( muzzle, bolt->currentOrigin ); } /* ---------------------------------------------- AVATAR ---------------------------------------------- */ #define PSYCHIC_BLAST_DAMAGE 12 #define PSYCHIC_BLAST_SPLASH_DAM 5 #define PSYCHIC_BLAST_SPLASH_RAD 32 #define PSYCHIC_BLAST_VELOCITY 800 void WP_ForgePsychicBlast( gentity_t *ent ) //--------------------------------------------------------- { gentity_t *bolt; bolt = G_Spawn(); bolt->classname = "psychic_projectile"; bolt->nextthink = level.time + 10000; bolt->e_ThinkFunc = thinkF_G_FreeEntity; bolt->s.eType = ET_MISSILE; bolt->svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_FORGE_PSYCH; bolt->owner = ent; bolt->damage = PSYCHIC_BLAST_DAMAGE; bolt->dflags = 0; bolt->splashDamage = PSYCHIC_BLAST_SPLASH_DAM; bolt->splashRadius = PSYCHIC_BLAST_SPLASH_RAD; bolt->methodOfDeath = MOD_ENERGY; bolt->splashMethodOfDeath = MOD_ENERGY_SPLASH; bolt->clipmask = MASK_SHOT; // How 'bout we give this thing a size... VectorSet( bolt->mins, -2.0f, -2.0f, -2.0f ); VectorSet( bolt->maxs, 2.0f, 2.0f, 2.0f ); bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time; // move a bit on the very first frame VectorCopy( muzzle, bolt->s.pos.trBase ); VectorScale( forward, PSYCHIC_BLAST_VELOCITY, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( muzzle, bolt->currentOrigin ); } /* ---------------------------------------------- PARASITE ---------------------------------------------- */ #define PARASITE_DAMAGE 6 #define PARASITE_SPLASH_DAM 4 #define PARASITE_SPLASH_RAD 72 #define PARASITE_VELOCITY 500 #define PARASITE_UPWARD_KICK 200 //--------------------------------------------------------- void WP_ParasiteAcid( gentity_t *ent ) //--------------------------------------------------------- { //shoots acid spurts at player gentity_t *bolt; bolt = G_Spawn(); bolt->classname = "acid_projectile"; bolt->nextthink = level.time + 10000; bolt->e_ThinkFunc = thinkF_G_FreeEntity; bolt->s.eType = ET_MISSILE; bolt->svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_PARASITE; bolt->owner = ent; bolt->damage = PARASITE_DAMAGE; bolt->dflags = 0; bolt->splashDamage = PARASITE_SPLASH_DAM; bolt->splashRadius = PARASITE_SPLASH_RAD; bolt->methodOfDeath = MOD_ACID; bolt->splashMethodOfDeath = MOD_ACID_SPLASH; bolt->clipmask = MASK_SHOT; bolt->s.pos.trType = TR_GRAVITY; bolt->s.pos.trTime = level.time; // move a bit on the very first frame VectorCopy( muzzle, bolt->s.pos.trBase ); VectorScale( forward, PARASITE_VELOCITY, bolt->s.pos.trDelta ); bolt->s.pos.trDelta[2] += PARASITE_UPWARD_KICK; // give the spurt a bit of an extra upward thrust so it'll travel a bit further. SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( muzzle, bolt->currentOrigin ); } /* ---------------------------------------------- STASIS ALIEN ---------------------------------------------- */ #define STASIS_ALIEN_VELOCITY 450 #define STASIS_ALIEN_DAMAGE 5 //--------------------------------------------------------- void FireStasisGuyAttack( gentity_t *ent ) //--------------------------------------------------------- { gentity_t *bolt; vec3_t dir, start; VectorSet( dir, 0,0,1 ); // up // Move the shot up by his hands VectorMA( muzzle, 24, dir, start ); VectorCopy( forward, dir ); VectorNormalize (dir); bolt = G_Spawn(); bolt->classname = "stasis_alien_proj"; bolt->nextthink = level.time + 10000; bolt->e_ThinkFunc = thinkF_G_FreeEntity; bolt->s.eType = ET_MISSILE; bolt->svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_STASIS_ATTACK; bolt->owner = ent; bolt->damage = STASIS_ALIEN_DAMAGE; bolt->dflags = 0; bolt->splashDamage = 0; bolt->splashRadius = 0; bolt->methodOfDeath = MOD_STASIS; bolt->clipmask = MASK_SHOT; // How 'bout we give this thing a size... VectorSet( bolt->mins, -4.0f, -4.0f, -4.0f ); VectorSet( bolt->maxs, 4.0f, 4.0f, 4.0f ); bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time; VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, STASIS_ALIEN_VELOCITY, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy (start, bolt->currentOrigin); } //--------------------------------------------------------- extern void Q3_SetVar( int taskID, int entID, const char *type_name, const char *data ); void G_HypoUsedHack( gentity_t *ent ) { //HACKHACKHACK //switch back to scav rifle after one use if ( ent->client->ps.stats[STAT_WEAPONS]&(1<client->ps.stats[STAT_WEAPONS] &= ~( 1 << WP_BLUE_HYPO ); ent->client->ps.stats[STAT_WEAPONS] &= ~( 1 << WP_RED_HYPO ); //clear this variable Q3_SetVar( -1, 0, "whichhypo", "NULL" ); //HACKHACKHACK } void WP_SprayBlueHypo( gentity_t *ent ) { gentity_t *tr_ent; trace_t tr; vec3_t mins, maxs, end; // Move out to the end of the nozzle //VectorMA( muzzle, 20, forward, muzzle ); //VectorMA( muzzle, 4, vright, muzzle ); VectorMA( muzzle, 24, forward, end ); VectorSet( maxs, 6, 6, 6 ); VectorScale( maxs, -1, mins ); gi.trace ( &tr, muzzle, mins, maxs, end, ent->s.number, MASK_SHOT ); G_HypoUsedHack( ent ); if ( tr.entityNum >= ENTITYNUM_WORLD ) { gentity_t *t_ent; // Create the effect -- thought something was needed here, but apparently not. VectorMA( muzzle, 20, forward, muzzle ); VectorMA( muzzle, 4, vright, muzzle ); t_ent = G_TempEntity( muzzle, EV_HYPO_PUFF ); t_ent->s.eventParm = qfalse; VectorCopy( forward, t_ent->pos1 ); return; } tr_ent = &g_entities[tr.entityNum]; // This should only work on things that have a client if ( tr_ent->client ) { G_Damage( tr_ent, ent, ent, forward, tr.endpos, 0, DAMAGE_NO_DAMAGE, MOD_UNKNOWN ); //GEntity_PainFunc( tr_ent, ent, 1 ); } } //--------------------------------------------------------- void WP_SprayRedHypo( gentity_t *ent ) { gentity_t *tr_ent; trace_t tr; vec3_t mins, maxs, end; // Move out to the end of the nozzle //VectorMA( muzzle, 20, forward, muzzle ); //VectorMA( muzzle, 4, vright, muzzle ); VectorMA( muzzle, 24, forward, end ); VectorSet( maxs, 6, 6, 6 ); VectorScale( maxs, -1, mins ); gi.trace ( &tr, muzzle, mins, maxs, end, ent->s.number, MASK_SHOT ); G_HypoUsedHack( ent ); if ( tr.entityNum >= ENTITYNUM_WORLD ) { gentity_t *t_ent; // Create the effect -- thought something was needed here, but apparently not. VectorMA( muzzle, 20, forward, muzzle ); VectorMA( muzzle, 4, vright, muzzle ); t_ent = G_TempEntity( muzzle, EV_HYPO_PUFF ); t_ent->s.eventParm = qtrue; VectorCopy( forward, t_ent->pos1 ); return; } tr_ent = &g_entities[tr.entityNum]; // This should only work on things that have a client if ( tr_ent && tr_ent->client ) { if ( tr_ent->NPC_type && tr_ent->NPC_type[0] && Q_stricmp( tr_ent->NPC_type, "hirogenalpha" ) == 0 ) {//If scavboss, just fire his target2 G_UseTargets2( tr_ent, tr_ent, tr_ent->target2 ); tr_ent->target2 = NULL; //so he doesn't repeat himself if ( tr_ent->NPC ) { tr_ent->NPC->localState = 4; } //take some health away tr_ent->health -= 25; if ( tr_ent->health < 1 ) {//but don't kill him tr_ent->health = 1; } } else { //knock them out, no deathscript, no deathtarget tr_ent->behaviorSet[BSET_DEATH] = NULL; tr_ent->behaviorSet[BSET_FFDEATH] = NULL; tr_ent->target = NULL; tr_ent->health = 0; if ( tr_ent->client->playerTeam == TEAM_STARFLEET ) {//"killed" a teammate, you lose killPlayerTimer = level.time + 5000; statusTextIndex = IGT_YOUCAUSEDDEATHOFTEAMMATE; } player_die( tr_ent, ent, ent, 0, MOD_KNOCKOUT ); //G_Damage ( tr_ent, NULL, NULL, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_TELEFRAG ); } } } //--------------------------------------------------------- void WP_SprayVoyagerHypo( gentity_t *ent ) { gentity_t *tr_ent; trace_t tr; vec3_t mins, maxs, end; VectorMA( muzzle, 32, forward, end ); VectorSet( maxs, 6, 6, 6 ); VectorScale( maxs, -1, mins ); gi.trace ( &tr, muzzle, mins, maxs, end, ent->s.number, MASK_SHOT ); if ( tr.entityNum >= ENTITYNUM_WORLD ) { gentity_t *t_ent; // Create the effect -- thought something was needed here, but apparently not. VectorMA( muzzle, 20, forward, muzzle ); VectorMA( muzzle, 4, vright, muzzle ); t_ent = G_TempEntity( muzzle, EV_HYPO_PUFF ); t_ent->s.eventParm = qfalse; VectorCopy( forward, t_ent->pos1 ); return; } tr_ent = &g_entities[tr.entityNum]; // Not exactly sure the best way to do this, but ah well... if ( tr_ent && tr_ent->health > 0 ) { if ( tr_ent->health > tr_ent->max_health - 20 ) { tr_ent->health = tr_ent->max_health; } else { tr_ent->health += 20; } } } //--------------------------------------------------------- void WP_KnifeSwing( gentity_t *ent ) { gentity_t *tr_ent; trace_t tr; vec3_t mins, maxs, end; VectorMA( muzzle, 64, forward, end ); VectorSet( maxs, 6, 6, 6 ); VectorScale( maxs, -1, mins ); gi.trace ( &tr, muzzle, mins, maxs, end, ent->s.number, MASK_SHOT ); if ( tr.entityNum >= ENTITYNUM_WORLD ) { return; } tr_ent = &g_entities[tr.entityNum]; if ( tr_ent && tr_ent->takedamage ) { G_Sound( tr_ent, G_SoundIndex( va("sound/enemies/harvester/stab%d", Q_irand(1, 3)) ) ); G_Damage( tr_ent, ent, ent, forward, tr.endpos, (g_spskill->integer*2)+1, DAMAGE_NO_KNOCKBACK, MOD_MELEE ); } } #define PALADIN_VELOCITY 700 #define PALADIN_DAMAGE 4 //--------------------------------------------------------- void WP_PaladinShot( gentity_t *ent ) { gentity_t *bolt; bolt = G_Spawn(); bolt->classname = "paladin_proj"; bolt->nextthink = level.time + 10000; bolt->e_ThinkFunc = thinkF_G_FreeEntity; bolt->s.eType = ET_MISSILE; bolt->s.weapon = WP_PALADIN; bolt->owner = ent; bolt->damage = PALADIN_DAMAGE*(g_spskill->integer+1); bolt->dflags = 0; bolt->splashDamage = 0; bolt->splashRadius = 0; bolt->methodOfDeath = MOD_SCAVENGER; bolt->clipmask = MASK_SHOT; bolt->s.pos.trType = TR_LINEAR ; bolt->s.pos.trTime = level.time; // move a bit on the very first frame VectorCopy( muzzle, bolt->s.pos.trBase ); VectorScale( forward, PALADIN_VELOCITY, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( muzzle, bolt->currentOrigin); } #define DESPERADO_DAMAGE 3 //--------------------------------------------------------- void WP_DesperadoShot( gentity_t *ent ) { gentity_t *tent; gentity_t *tent2 = 0; gentity_t *traceEnt; trace_t tr; vec3_t start, end; qboolean do_damage = qtrue; qboolean render_impact = qtrue; VectorCopy( muzzle, start ); VectorMA( start, -8, forward, start ); VectorMA( start, 8192, forward, end ); gi.trace ( &tr, start, NULL, NULL, end, ent->s.number, MASK_SHOT ); // If the beam hits a skybox, etc. it would look foolish to add in an explosion if ( tr.surfaceFlags & SURF_NOIMPACT ) render_impact = qfalse; traceEnt = &g_entities[ tr.entityNum ]; tent = G_TempEntity( tr.endpos, EV_RIFLE ); // Only add in impact stuff when told to do so if ( render_impact ) { // send bullet impact if ( traceEnt->takedamage && traceEnt->client ) { // Create a simple impact type mark tent2 = G_TempEntity( tr.endpos, EV_RIFLE_HIT ); tent->s.otherEntityNum = traceEnt->s.number; } else { // Do a discreet miss tent2 = G_TempEntity( tr.endpos, EV_RIFLE_MISS ); } } // Stash origins, etc. so that the effect can have access to them. VectorCopy( muzzle, tent->s.origin2 ); if ( render_impact ) { VectorCopy( muzzle, tent2->s.origin2 ); tent2->s.eventParm = tent->s.eventParm = DirToByte( tr.plane.normal ); tent2->s.weapon = tent->s.weapon = ent->s.weapon; } if ( traceEnt->takedamage && do_damage ) G_Damage( traceEnt, ent, ent, forward, tr.endpos, DESPERADO_DAMAGE*(g_spskill->integer+1), 0, MOD_CRIFLE ); } //--------------------------------------------------------- void AddLeanOfs(gentity_t *ent, vec3_t point) //--------------------------------------------------------- { if(ent->client) { if(ent->client->ps.leanofs) { vec3_t right; //add leaning offset AngleVectors(ent->client->ps.viewangles, NULL, right, NULL); VectorMA(point, (float)ent->client->ps.leanofs, right, point); } } } //--------------------------------------------------------- void ViewHeightFix(gentity_t *ent) //--------------------------------------------------------- { //FIXME: this is hacky and doesn't need to be here. Was only put here to make up //for the times a crouch anim would be used but not actually crouching. //When we start calcing eyepos (SPOT_HEAD) from the tag_eyes, we won't need //this (or viewheight at all?) if ( !ent->client || !ent->NPC ) return; if ( ent->client->ps.stats[STAT_HEALTH] <= 0 ) return;//dead if ( ( ent->client->ps.legsAnim&~ANIM_TOGGLEBIT) == BOTH_CROUCH1IDLE || (ent->client->ps.legsAnim&~ANIM_TOGGLEBIT) == BOTH_CROUCH1 || (ent->client->ps.legsAnim&~ANIM_TOGGLEBIT ) == BOTH_CROUCH1WALK ) { if ( ent->client->ps.viewheight!=ent->client->crouchheight + STANDARD_VIEWHEIGHT_OFFSET ) ent->client->ps.viewheight = ent->client->crouchheight + STANDARD_VIEWHEIGHT_OFFSET; } else { if ( ent->client->ps.viewheight!=ent->client->standheight + STANDARD_VIEWHEIGHT_OFFSET ) ent->client->ps.viewheight = ent->client->standheight + STANDARD_VIEWHEIGHT_OFFSET; } } /* =============== 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; } //--------------------------------------------------------- void CalcMuzzlePoint( gentity_t *ent, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint, float lead_in ) //--------------------------------------------------------- { if((ent->client->ps.weapon != WP_BORG_ASSIMILATOR) && (!lead_in)) //&& ent->s.number != 0 {//Not players or melee if( ent->client ) { if ( ent->client->renderInfo.mPCalcTime >= level.time - FRAMETIME*2 ) {//Our muzz point was calced no more than 2 frames ago VectorCopy(ent->client->renderInfo.muzzlePoint, muzzlePoint); return; } } } VectorCopy( ent->currentOrigin, muzzlePoint ); switch( ent->s.weapon ) { case WP_IMOD: muzzlePoint[2] += ent->client->ps.viewheight;//By eyes muzzlePoint[2] -= 13; VectorMA( muzzlePoint, 26, forward, muzzlePoint ); VectorMA( muzzlePoint, 6, vright, muzzlePoint ); break; case WP_COMPRESSION_RIFLE: case WP_RED_HYPO: case WP_BLUE_HYPO: case WP_VOYAGER_HYPO: case WP_TETRION_DISRUPTOR: case WP_PROTON_GUN: ViewHeightFix(ent); muzzlePoint[2] += ent->client->ps.viewheight;//By eyes muzzlePoint[2] -= 16; VectorMA( muzzlePoint, 28, forward, muzzlePoint ); VectorMA( muzzlePoint, 6, vright, muzzlePoint ); break; case WP_STASIS: case WP_QUANTUM_BURST: case WP_GRENADE_LAUNCHER: ViewHeightFix(ent); muzzlePoint[2] += ent->client->ps.viewheight;//By eyes muzzlePoint[2] -= 2; break; case WP_SCAVENGER_RIFLE: case WP_CHAOTICA_GUARD_GUN: ViewHeightFix(ent); muzzlePoint[2] += ent->client->ps.viewheight;//By eyes muzzlePoint[2] -= 1; if ( ent->s.number == 0 ) VectorMA( muzzlePoint, 12, forward, muzzlePoint ); // player, don't set this any lower otherwise the projectile will impact immediately when your back is to a wall else VectorMA( muzzlePoint, 2, forward, muzzlePoint ); // NPC, don't set too far forward otherwise the projectile can go through doors VectorMA( muzzlePoint, 1, vright, muzzlePoint ); break; case WP_BORG_TASER: case WP_BORG_WEAPON: case WP_PHASER: if(ent->NPC!=NULL && (ent->client->ps.torsoAnim&~ANIM_TOGGLEBIT == TORSO_WEAPONREADY2 || ent->client->ps.torsoAnim&~ANIM_TOGGLEBIT == BOTH_ATTACK2))//Sniper pose { ViewHeightFix(ent); muzzle[2] += ent->client->ps.viewheight;//By eyes } else { muzzlePoint[2] += 16; } VectorMA( muzzlePoint, 8, forward, muzzlePoint ); VectorMA( muzzlePoint, 16, vright, muzzlePoint ); break; case WP_BORG_ASSIMILATOR: ViewHeightFix(ent); muzzlePoint[2] += ent->maxs[2];//ent->client->ps.viewheight;//By eyes VectorMA( muzzlePoint, 8, forward, muzzlePoint ); VectorMA( muzzlePoint, -4, vright, muzzlePoint ); break; case WP_PARASITE: muzzlePoint[2] -= 23; break; case WP_BOT_WELDER: muzzlePoint[2] -= 8; //Near the naughty-bits break; case WP_BOT_ROCKET: break; } AddLeanOfs(ent, muzzlePoint); } //--------------------------------------------------------- void FireWeapon( gentity_t *ent, qboolean alt_fire ) //--------------------------------------------------------- { // track shots taken for accuracy tracking. ent->client->ps.persistant[PERS_ACCURACY_SHOTS]++; // set aiming directions AngleVectors (ent->client->ps.viewangles, forward, vright, up); ent->alt_fire = alt_fire; CalcMuzzlePoint ( ent, forward, vright, up, muzzle , 0); // fire the specific weapon switch( ent->s.weapon ) { // Player weapons //----------------- case WP_PHASER: WP_FirePhaser( ent, alt_fire ); break; case WP_COMPRESSION_RIFLE: WP_FireCompressionRifle( ent, alt_fire ); break; case WP_IMOD: WP_FireIMOD( ent, alt_fire ); break; case WP_SCAVENGER_RIFLE: WP_FireScavenger( ent, alt_fire, qfalse ); break; case WP_CHAOTICA_GUARD_GUN: WP_FireScavenger( ent, qfalse, qtrue ); break; case WP_STASIS: WP_FireStasis( ent, alt_fire ); break; case WP_GRENADE_LAUNCHER: WP_FireGrenade( ent, alt_fire ); break; case WP_TETRION_DISRUPTOR: WP_FireTetrionDisruptor( ent, alt_fire ); break; case WP_DREADNOUGHT: WP_FireDreadnought( ent, alt_fire ); break; case WP_QUANTUM_BURST: WP_FireQuantumBurst( ent, alt_fire ); break; case WP_TRICORDER: WP_TricorderScan( ent, alt_fire ); break; case WP_PROTON_GUN: WP_FireProtonGun( ent, qfalse );//no alt-fire break; // Borg Weapons //----------------- case WP_BORG_ASSIMILATOR: case WP_BORG_DRILL: WP_BorgAssimilate( ent ); break; case WP_BORG_TASER: WP_FireBorgTaser( ent ); break; case WP_BORG_WEAPON: WP_FireBorgWeapon( ent ); break; // Scout Bot //----------------- case WP_BOT_WELDER: WP_BotWelder( ent ); break; /* case WP_BOT_LASER: WP_BotLaser( ent ); break; */ // Hunter Seeker //----------------- case WP_BOT_ROCKET: WP_HunterSeeker( ent, alt_fire ); break; // Reaver //----------------- case WP_FORGE_PROJ: WP_ForgeProjectile( ent ); break; // Avatar //----------------- case WP_FORGE_PSYCH: WP_ForgePsychicBlast( ent ); break; // Parasite //----------------- case WP_PARASITE: WP_ParasiteAcid( ent ); break; // Melee Placeholder, doesn't do much of anything as you can tell //----------------- case WP_MELEE: break; // Stasis Bad Boy //----------------- case WP_STASIS_ATTACK: FireStasisGuyAttack( ent ); break; // Fun hypo "weapons" //----------------- case WP_BLUE_HYPO: WP_SprayBlueHypo( ent ); break; case WP_RED_HYPO: WP_SprayRedHypo( ent ); break; case WP_VOYAGER_HYPO: WP_SprayVoyagerHypo( ent ); break; // Holo-character weapons //----------------- case WP_PALADIN: WP_PaladinShot( ent ); break; case WP_DESPERADO: WP_DesperadoShot( ent ); break; case WP_KLINGON_BLADE: case WP_IMPERIAL_BLADE: WP_KnifeSwing( ent ); break; default: break; } } //DETPACK //--------------------------------------------------------- void UseCharge (int entityNum) //--------------------------------------------------------- { gentity_t *ent; if(entityNum < 0 || entityNum >= ENTITYNUM_WORLD) { return; } ent = &g_entities[entityNum]; if(!ent || !ent->client) { return; } if(ent->client->ps.eFlags & EF_PLANTED_CHARGE) { gentity_t *found = NULL; G_Sound(ent, G_SoundIndex("sound/weapons/detpacklatch.wav")); while((found = G_Find(found, FOFS(classname), "charge")) != NULL) { if(found->owner == ent) { VectorCopy( found->currentOrigin, found->s.origin ); found->e_ThinkFunc = thinkF_ExplodeDeath; found->nextthink = level.time + 500; G_Sound(found, G_SoundIndex("sound/weapons/detpackfire.wav")); //Fixme: this should really wake up enemies in the area } } ent->client->ps.eFlags &= ~EF_PLANTED_CHARGE; } else { AngleVectors (ent->client->ps.viewangles, forward, vright, up); CalcMuzzlePoint ( ent, forward, vright, up, muzzle, 0 ); VectorNormalize( forward ); VectorMA(muzzle, -4, forward, muzzle); drop_charge (ent, muzzle, forward); ent->client->ps.eFlags |= EF_PLANTED_CHARGE; } }