/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. Copyright (C) 2000-2006 Tim Angus This file is part of Tremulous. Tremulous 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 of the License, or (at your option) any later version. Tremulous 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 Tremulous; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "g_local.h" #define MISSILE_PRESTEP_TIME 50 /* ================ G_BounceMissile ================ */ void G_BounceMissile( gentity_t *ent, trace_t *trace ) { vec3_t velocity; float dot; int hitTime; // reflect the velocity on the trace plane hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction; BG_EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity ); dot = DotProduct( velocity, trace->plane.normal ); VectorMA( velocity, -2 * dot, trace->plane.normal, ent->s.pos.trDelta ); if( ent->s.eFlags & EF_BOUNCE_HALF ) { //ROTAXfun if(!strcmp(ent->classname,"mine")) { VectorScale( ent->s.pos.trDelta, 0.00, ent->s.pos.trDelta ); if( VectorLength( ent->s.pos.trDelta ) < 40 ) { G_SetOrigin( ent, trace->endpos ); return; } } else { VectorScale( ent->s.pos.trDelta, 0.65, ent->s.pos.trDelta ); } //VectorScale( ent->s.pos.trDelta, 0.65, ent->s.pos.trDelta ); // check for stop if( trace->plane.normal[ 2 ] > 0.2 && VectorLength( ent->s.pos.trDelta ) < 40 ) { G_SetOrigin( ent, trace->endpos ); return; } } VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin ); VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase ); ent->s.pos.trTime = level.time; } /* ================ G_ExplodeMissile Explode a missile without an impact ================ */ void G_ExplodeMissile( gentity_t *ent ) { vec3_t dir; vec3_t origin; BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); SnapVector( origin ); G_SetOrigin( ent, origin ); // we don't have a valid direction, so just point straight up dir[ 0 ] = dir[ 1 ] = 0; dir[ 2 ] = 1; ent->s.eType = ET_GENERAL; //TA: tired... can't be fucked... hack if( ent->s.weapon != WP_LOCKBLOB_LAUNCHER && ent->s.weapon != WP_FLAMER ) G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( dir ) ); ent->freeAfterEvent = qtrue; // splash damage if( ent->splashDamage ) G_RadiusDamage( ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent, ent->splashMethodOfDeath ); trap_LinkEntity( ent ); } void AHive_ReturnToHive( gentity_t *self ); /* ================ G_MissileImpact ================ */ void G_MissileImpact( gentity_t *ent, trace_t *trace ) { gentity_t *other, *attacker; qboolean returnAfterDamage = qfalse; vec3_t dir; other = &g_entities[ trace->entityNum ]; attacker = &g_entities[ ent->r.ownerNum ]; // check for bounce if( !other->takedamage && ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) && strcmp( ent->classname, "acidbomb" ) )//ROTAXfun pokud je to acidbomba, pouzije vlastni zvuk a ne granatovej { G_BounceMissile( ent, trace ); //only play a sound if requested if( !( ent->s.eFlags & EF_NO_BOUNCE_SOUND ) ) G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } if( !strcmp( ent->classname, "grenade" ) ) { //grenade doesn't explode on impact G_BounceMissile( ent, trace ); //only play a sound if requested if( !( ent->s.eFlags & EF_NO_BOUNCE_SOUND ) ) G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } else if( !strcmp( ent->classname, "mine" ) )//ROTAXfun { //grenade doesn't explode on impact G_BounceMissile( ent, trace ); //only play a sound if requested if( !( ent->s.eFlags & EF_NO_BOUNCE_SOUND ) ) G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } else if( !strcmp( ent->classname, "flare" ) )//ROTAXfun { //grenade doesn't explode on impact G_BounceMissile( ent, trace ); //only play a sound if requested if( !( ent->s.eFlags & EF_NO_BOUNCE_SOUND ) ) G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } else if( !strcmp( ent->classname, "lockblob" ) ) { if( other->client && other->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) { other->client->ps.stats[ STAT_STATE ] |= SS_BLOBLOCKED; other->client->lastLockTime = level.time; AngleVectors( other->client->ps.viewangles, dir, NULL, NULL ); other->client->ps.stats[ STAT_VIEWLOCK ] = DirToByte( dir ); } } else if( !strcmp( ent->classname, "slowblob" ) ) { if( other->client && other->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) { other->client->ps.stats[ STAT_STATE ] |= SS_SLOWLOCKED; other->client->lastSlowTime = level.time; AngleVectors( other->client->ps.viewangles, dir, NULL, NULL ); other->client->ps.stats[ STAT_VIEWLOCK ] = DirToByte( dir ); } } else if( !strcmp( ent->classname, "acidbomb" ) )//ROTAXfun { //bomb doesn't explode on impact G_BounceMissile( ent, trace ); //only play a sound if requested if( !( ent->s.eFlags & EF_NO_BOUNCE_SOUND ) ) G_AddEvent( ent, EV_ACIDBOMB_BOUNCE, 0 ); return; } else if( !strcmp( ent->classname, "hive" ) ) { if( other->s.eType == ET_BUILDABLE && other->s.modelindex == BA_A_HIVE ) { if( !ent->parent ) G_Printf( S_COLOR_YELLOW "WARNING: hive entity has no parent in G_MissileImpact\n" ); else ent->parent->active = qfalse; G_FreeEntity( ent ); return; } else { //prevent collision with the client when returning ent->r.ownerNum = other->s.number; ent->think = AHive_ReturnToHive; ent->nextthink = level.time + FRAMETIME; //only damage humans if( other->client && other->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) returnAfterDamage = qtrue; else return; } } else if ( other->s.eType == ET_BUILDABLE && other->s.modelindex == BA_A_BAMBOO && ( !strcmp( ent->classname, "lcannon" ) || !strcmp( ent->classname, "pulse" ) ) )//ROTAXfun (bamboo + luci, prifle) { G_BounceMissile( ent, trace ); return; } // impact damage if( other->takedamage ) { // FIXME: wrong damage direction? if( ent->damage ) { vec3_t velocity; BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); if( VectorLength( velocity ) == 0 ) velocity[ 2 ] = 1; // stepped on a grenade G_Damage( other, ent, attacker, velocity, ent->s.origin, ent->damage, 0, ent->methodOfDeath ); } } if( returnAfterDamage ) return; // is it cheaper in bandwidth to just remove this ent and create a new // one, rather than changing the missile into the explosion? if( other->takedamage && other->client ) { G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); ent->s.otherEntityNum = other->s.number; } else if( trace->surfaceFlags & SURF_METALSTEPS ) G_AddEvent( ent, EV_MISSILE_MISS_METAL, DirToByte( trace->plane.normal ) ); else G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); ent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact ent->s.eType = ET_GENERAL; SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); // save net bandwidth G_SetOrigin( ent, trace->endpos ); // splash damage (doesn't apply to person directly hit) if( ent->splashDamage ) G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, other, ent->splashMethodOfDeath ); trap_LinkEntity( ent ); } /* ================ G_RunMissile ================ */ void G_RunMissile( gentity_t *ent ) { vec3_t origin; trace_t tr; int passent; // get current position BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); // ignore interactions with the missile owner passent = ent->r.ownerNum; // trace a line from the previous position to the current position trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask ); if( tr.startsolid || tr.allsolid ) { // make sure the tr.entityNum is set to the entity we're stuck in trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent, ent->clipmask ); tr.fraction = 0; } else VectorCopy( tr.endpos, ent->r.currentOrigin ); ent->r.contents = CONTENTS_SOLID; //trick trap_LinkEntity into... trap_LinkEntity( ent ); ent->r.contents = 0; //...encoding bbox information if( tr.fraction != 1 ) { // never explode or bounce on sky if( tr.surfaceFlags & SURF_NOIMPACT ) { // If grapple, reset owner if( ent->parent && ent->parent->client && ent->parent->client->hook == ent ) ent->parent->client->hook = NULL; G_FreeEntity( ent ); return; } G_MissileImpact( ent, &tr ); if( ent->s.eType != ET_MISSILE ) return; // exploded } // check think function after bouncing G_RunThink( ent ); } //ROTAXfun /* ================ G_ProcessMine If an enemy is close to the entity, go boom! ================ */ void G_ProcessMine(gentity_t *ent) { int i, total_entities, entityList[MAX_GENTITIES]; vec3_t range, mins, maxs; gentity_t *target; // Set the next time to run this check (can be overwritten below) ent->nextthink = level.time + MINE_CHECK_FREQUENCY; // Grab all entities around us VectorSet(range, MINE_DETECT, MINE_DETECT, MINE_DETECT); VectorAdd(ent->s.origin, range, maxs); VectorSubtract(ent->s.origin, range, mins); total_entities = trap_EntitiesInBox(mins, maxs, entityList, MAX_GENTITIES); // Loop entities looking for an enemy body for(i=0; iclient && target->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS) { if (G_Visible( ent, target )) { // Found an enemy, boom time! ent->nextthink = level.time + MINE_BOOM_TIME; ent->think = G_ExplodeMissile; return; } } } } //ROTAXfun /* ================ G_ProcessFlare If an player is close to the entity, hurt! ================ */ void G_ProcessFlare(gentity_t *ent) { int i, total_entities, entityList[MAX_GENTITIES]; vec3_t range, mins, maxs; gentity_t *target; if (level.time > ent->s.time + 30000) { ent->nextthink = level.time + 100; ent->think = G_ExplodeMissile; return; } // Set the next time to run this check (can be overwritten below) ent->nextthink = level.time + FLARE_CHECK_FREQUENCY; // Grab all entities around us VectorSet(range, FLARE_DETECT, FLARE_DETECT, FLARE_DETECT); VectorAdd(ent->s.origin, range, maxs); VectorSubtract(ent->s.origin, range, mins); total_entities = trap_EntitiesInBox(mins, maxs, entityList, MAX_GENTITIES); // Loop entities looking for an enemy body for(i=0; iclient) { if (G_Visible( ent, target )) { // Found an enemy, hurt time! G_Damage( target, ent, ent->parent, NULL, ent->s.origin, ent->damage, 0, ent->methodOfDeath ); //ent->nextthink = level.time + MINE_BOOM_TIME; //ent->think = G_ExplodeMissile; return; } } } } //============================================================================= /* ================= fire_flamer ================= */ gentity_t *fire_flamer( gentity_t *self, vec3_t start, vec3_t dir ) { gentity_t *bolt; vec3_t pvel; VectorNormalize (dir); bolt = G_Spawn(); bolt->classname = "flame"; bolt->nextthink = level.time + FLAMER_LIFETIME; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_FLAMER; bolt->s.generic1 = self->s.generic1; //weaponMode bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = FLAMER_DMG; bolt->splashDamage = FLAMER_DMG; bolt->splashRadius = FLAMER_RADIUS; bolt->methodOfDeath = MOD_FLAMER; bolt->splashMethodOfDeath = MOD_FLAMER_SPLASH; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; bolt->r.mins[ 0 ] = bolt->r.mins[ 1 ] = bolt->r.mins[ 2 ] = -15.0f; bolt->r.maxs[ 0 ] = bolt->r.maxs[ 1 ] = bolt->r.maxs[ 2 ] = 15.0f; bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( self->client->ps.velocity, FLAMER_LAG, pvel ); VectorMA( pvel, FLAMER_SPEED, dir, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin ); return bolt; } //============================================================================= /* ================= fire_blaster ================= */ gentity_t *fire_blaster( gentity_t *self, vec3_t start, vec3_t dir ) { gentity_t *bolt; VectorNormalize (dir); bolt = G_Spawn(); bolt->classname = "blaster"; bolt->nextthink = level.time + 10000; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_BLASTER; bolt->s.generic1 = self->s.generic1; //weaponMode bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = BLASTER_DMG; bolt->splashDamage = 0; bolt->splashRadius = 0; bolt->methodOfDeath = MOD_BLASTER; bolt->splashMethodOfDeath = MOD_BLASTER; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, BLASTER_SPEED, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin ); return bolt; } //============================================================================= /* ================= fire_pulseRifle ================= */ gentity_t *fire_pulseRifle( gentity_t *self, vec3_t start, vec3_t dir, int i )//ROTAXfun { gentity_t *bolt; VectorNormalize (dir); bolt = G_Spawn(); bolt->classname = "pulse"; bolt->nextthink = level.time + 10000; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_PULSE_RIFLE; bolt->s.generic1 = self->s.generic1; //weaponMode bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = PRIFLE_DMG; bolt->splashDamage = 0; bolt->splashRadius = 0; bolt->methodOfDeath = MOD_PRIFLE; bolt->splashMethodOfDeath = MOD_PRIFLE; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; //ROTAXfun if (i == 1) bolt->s.pos.trType = TR_LINEAR; else if (i == 2) bolt->s.pos.trType = TR_GRAVITY; else if (i == 3) bolt->s.pos.trType = TR_BUOYANCY; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, PRIFLE_SPEED, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin ); return bolt; } //============================================================================= /* ================= fire_luciferCannon ================= */ gentity_t *fire_luciferCannon( gentity_t *self, vec3_t start, vec3_t dir, int damage, int radius ) { gentity_t *bolt; int localDamage = (int)( ceil( ( (float)damage / (float)LCANNON_TOTAL_CHARGE ) * (float)LCANNON_DAMAGE ) ); VectorNormalize( dir ); bolt = G_Spawn( ); bolt->classname = "lcannon"; if( damage == LCANNON_TOTAL_CHARGE ) bolt->nextthink = level.time; else bolt->nextthink = level.time + 10000; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_LUCIFER_CANNON; bolt->s.generic1 = self->s.generic1; //weaponMode bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = localDamage; bolt->splashDamage = localDamage / 2; bolt->splashRadius = radius; bolt->methodOfDeath = MOD_LCANNON; bolt->splashMethodOfDeath = MOD_LCANNON_SPLASH; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, LCANNON_SPEED, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin ); return bolt; } /* ================= launch_grenade ================= */ /*void ooo(gentity_t *ent,gentity_t *ent2,int i) {G_TeamCommand( PTE_HUMANS, va( "print \"Overmind ^3DAMAGED^7 by ^1TEAMMATE^7 ^7\n\"" )); }*/ gentity_t *launch_grenade( gentity_t *self, vec3_t start, vec3_t dir ) { /* gentity_t *bolt; qboolean noExplode = qfalse; bolt = G_Spawn(); // no self->client for shooter_grenade's if( self->client) { // TTimo // was: if( g_gametype.integer < GT_WOLF && grenadeWPID == WP_DYNAMITE || grenadeWPID == WP_DYNAMITE2) { // gcc: suggest parentheses around && within || noExplode = qtrue; } else { bolt->nextthink = level.time + 2500; } if(!noExplode) bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN | SVF_BROADCAST; bolt->s.weapon = WP_GRENADE; bolt->r.ownerNum = self->s.number; bolt->parent = self; // JPW NERVE -- commented out bolt->damage and bolt->splashdamage, override with G_GetWeaponDamage() // so it works with different netgame balance. didn't uncomment bolt->damage on dynamite 'cause its so *special* bolt->damage = GRENADE_DAMAGE; // overridden for dynamite bolt->splashDamage = GRENADE_DAMAGE; // jpw trap_SendServerCommand( self-g_entities, "cp \"^1Dynamite ^3is set but ^1NOT ^3armed\""); // differentiate non-armed dynamite with non-pulsing dlight bolt->classname = "grenade"; bolt->damage = 0; // bolt->splashDamage = 300; bolt->splashRadius = 400; bolt->methodOfDeath = MOD_GRENADE; bolt->splashMethodOfDeath = MOD_GRENADE; bolt->s.eFlags = (EF_BOUNCE | EF_BOUNCE_HALF); // EF_BOUNCE_HEAVY; // dynamite is shootable // JPW NERVE only in single player bolt->health = 5; bolt->takedamage = qtrue; bolt->pain = ooo; // jpw bolt->r.contents = CONTENTS_CORPSE; // (player can walk through) // nope - this causes the dynamite to impact on the players bb when he throws it. // will try setting it when it settles // bolt->r.ownerNum = ENTITYNUM_WORLD; // (SA) make the world the owner of the dynamite, so the player can shoot it without modifying the bullet code to ignore players id for hits // small target cube VectorSet(bolt->r.mins, -12, -12, 0); VectorCopy(bolt->r.mins, bolt->r.absmin); VectorSet(bolt->r.maxs, 12, 12, 20); VectorCopy(bolt->r.maxs, bolt->r.absmax); // JPW NERVE -- blast radius proportional to damage // jpw bolt->clipmask = MASK_SHOT; bolt->s.pos.trType = TR_GRAVITY; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorCopy( dir, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy (start, bolt->r.currentOrigin); return bolt; */ gentity_t *bolt; VectorNormalize( dir ); bolt = G_Spawn( ); bolt->classname = "grenade"; bolt->nextthink = level.time + 5000; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_GRENADE; bolt->s.eFlags = EF_BOUNCE_HALF; bolt->s.generic1 = WPM_PRIMARY; //weaponMode bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = GRENADE_DAMAGE; bolt->splashDamage = GRENADE_DAMAGE; bolt->splashRadius = GRENADE_RANGE; bolt->methodOfDeath = MOD_GRENADE; bolt->splashMethodOfDeath = MOD_GRENADE; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; bolt->r.mins[ 0 ] = bolt->r.mins[ 1 ] = bolt->r.mins[ 2 ] = -3.0f; bolt->r.maxs[ 0 ] = bolt->r.maxs[ 1 ] = bolt->r.maxs[ 2 ] = 3.0f; bolt->s.time = level.time; /*bolt->health = 5; bolt->takedamage = qtrue; bolt->r.contents = CONTENTS_BODY; //bolt->clipmask = MASK_PLAYERSOLID; bolt->pain = ooo;*/ bolt->s.pos.trType = TR_GRAVITY; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, GRENADE_SPEED, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin ); return bolt; } //ROTAXfun gentity_t *launch_mine( gentity_t *self, vec3_t start, vec3_t dir ) { gentity_t *bolt; VectorNormalize( dir ); bolt = G_Spawn( ); bolt->classname = "mine"; bolt->nextthink = level.time + MINE_INIT_TIME; bolt->think = G_ProcessMine; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_MINE; bolt->s.eFlags = EF_BOUNCE_HALF; bolt->s.generic1 = WPM_PRIMARY; //weaponMode bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = MINE_DAMAGE; bolt->splashDamage = MINE_DAMAGE; bolt->splashRadius = MINE_RANGE; bolt->methodOfDeath = MOD_MINE; bolt->splashMethodOfDeath = MOD_MINE; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; bolt->r.mins[ 0 ] = bolt->r.mins[ 1 ] = bolt->r.mins[ 2 ] = -3.0f; bolt->r.maxs[ 0 ] = bolt->r.maxs[ 1 ] = bolt->r.maxs[ 2 ] = 3.0f; bolt->s.time = level.time; bolt->s.pos.trType = TR_GRAVITY; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, MINE_SPEED, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin ); return bolt; } //ROTAXfun gentity_t *launch_flare( gentity_t *self, vec3_t start, vec3_t dir ) { gentity_t *bolt; VectorNormalize( dir ); bolt = G_Spawn( ); bolt->classname = "flare"; bolt->nextthink = level.time + 200; bolt->think = G_ProcessFlare; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_FLARE; bolt->s.eFlags = EF_BOUNCE_HALF; bolt->s.generic1 = WPM_PRIMARY; //weaponMode bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = FLARE_DAMAGE; bolt->splashDamage = 0; bolt->splashRadius = 0; bolt->methodOfDeath = MOD_FLARE; bolt->splashMethodOfDeath = MOD_FLARE; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; bolt->r.mins[ 0 ] = bolt->r.mins[ 1 ] = bolt->r.mins[ 2 ] = -3.0f; bolt->r.maxs[ 0 ] = bolt->r.maxs[ 1 ] = bolt->r.maxs[ 2 ] = 3.0f; bolt->s.time = level.time; bolt->s.pos.trType = TR_GRAVITY; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, FLARE_SPEED, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin ); return bolt; } //============================================================================= /* ================ AHive_ReturnToHive Adjust the trajectory to point towards the hive ================ */ void AHive_ReturnToHive( gentity_t *self ) { vec3_t dir; trace_t tr; if( !self->parent ) { G_Printf( S_COLOR_YELLOW "WARNING: AHive_ReturnToHive called with no self->parent\n" ); return; } trap_UnlinkEntity( self->parent ); trap_Trace( &tr, self->r.currentOrigin, self->r.mins, self->r.maxs, self->parent->r.currentOrigin, self->r.ownerNum, self->clipmask ); trap_LinkEntity( self->parent ); if( tr.fraction < 1.0f ) { //if can't see hive then disperse VectorCopy( self->r.currentOrigin, self->s.pos.trBase ); self->s.pos.trType = TR_STATIONARY; self->s.pos.trTime = level.time; self->think = G_ExplodeMissile; self->nextthink = level.time + 2000; self->parent->active = qfalse; //allow the parent to start again } else { VectorSubtract( self->parent->r.currentOrigin, self->r.currentOrigin, dir ); VectorNormalize( dir ); //change direction towards the hive VectorScale( dir, HIVE_SPEED, self->s.pos.trDelta ); SnapVector( self->s.pos.trDelta ); // save net bandwidth VectorCopy( self->r.currentOrigin, self->s.pos.trBase ); self->s.pos.trTime = level.time; self->think = G_ExplodeMissile; self->nextthink = level.time + 15000; } } /* ================ AHive_SearchAndDestroy Adjust the trajectory to point towards the target ================ */ void AHive_SearchAndDestroy( gentity_t *self ) { vec3_t dir; trace_t tr; trap_Trace( &tr, self->r.currentOrigin, self->r.mins, self->r.maxs, self->target_ent->r.currentOrigin, self->r.ownerNum, self->clipmask ); //if there is no LOS or the parent hive is too far away or the target is dead or notargeting, return if( tr.entityNum == ENTITYNUM_WORLD || Distance( self->r.currentOrigin, self->parent->r.currentOrigin ) > ( HIVE_RANGE * 5 ) || self->target_ent->health <= 0 || self->target_ent->flags & FL_NOTARGET ) { self->r.ownerNum = ENTITYNUM_WORLD; self->think = AHive_ReturnToHive; self->nextthink = level.time + FRAMETIME; } else { VectorSubtract( self->target_ent->r.currentOrigin, self->r.currentOrigin, dir ); VectorNormalize( dir ); //change direction towards the player VectorScale( dir, HIVE_SPEED, self->s.pos.trDelta ); SnapVector( self->s.pos.trDelta ); // save net bandwidth VectorCopy( self->r.currentOrigin, self->s.pos.trBase ); self->s.pos.trTime = level.time; self->nextthink = level.time + HIVE_DIR_CHANGE_PERIOD; } } /* ================= fire_hive ================= */ gentity_t *fire_hive( gentity_t *self, vec3_t start, vec3_t dir ) { gentity_t *bolt; VectorNormalize ( dir ); bolt = G_Spawn( ); bolt->classname = "hive"; bolt->nextthink = level.time + HIVE_DIR_CHANGE_PERIOD; bolt->think = AHive_SearchAndDestroy; bolt->s.eType = ET_MISSILE; bolt->s.eFlags |= EF_BOUNCE|EF_NO_BOUNCE_SOUND; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_HIVE; bolt->s.generic1 = WPM_PRIMARY; //weaponMode bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = HIVE_DMG; bolt->splashDamage = 0; bolt->splashRadius = 0; bolt->methodOfDeath = MOD_SWARM; bolt->clipmask = MASK_SHOT; bolt->target_ent = self->target_ent; bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, HIVE_SPEED, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin ); return bolt; } //============================================================================= /* ================= fire_lockblob ================= */ gentity_t *fire_lockblob( gentity_t *self, vec3_t start, vec3_t dir ) { gentity_t *bolt; VectorNormalize ( dir ); bolt = G_Spawn( ); bolt->classname = "lockblob"; bolt->nextthink = level.time + 15000; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_LOCKBLOB_LAUNCHER; bolt->s.generic1 = WPM_PRIMARY; //weaponMode bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = 0; bolt->splashDamage = 0; bolt->splashRadius = 0; bolt->methodOfDeath = MOD_UNKNOWN; //doesn't do damage so will never kill bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, 500, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin ); return bolt; } /* ================= fire_slowBlob ================= */ gentity_t *fire_slowBlob( gentity_t *self, vec3_t start, vec3_t dir ) { gentity_t *bolt; VectorNormalize ( dir ); bolt = G_Spawn( ); bolt->classname = "slowblob"; bolt->nextthink = level.time + 15000; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_ABUILD2; bolt->s.generic1 = self->s.generic1; //weaponMode bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = ABUILDER_BLOB_DMG; bolt->splashDamage = 0; bolt->splashRadius = 0; bolt->methodOfDeath = MOD_SLOWBLOB; bolt->splashMethodOfDeath = MOD_SLOWBLOB; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; bolt->s.pos.trType = TR_GRAVITY; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, ABUILDER_BLOB_SPEED, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin ); return bolt; } //ROTAXfun /* ================= fire_acidBomb ================= */ gentity_t *fire_acidBomb( gentity_t *self, vec3_t start, vec3_t dir, int wp ) { gentity_t *bolt; VectorNormalize ( dir ); bolt = G_Spawn( ); bolt->classname = "acidbomb"; bolt->nextthink = level.time + 8000; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = wp; bolt->s.eFlags = EF_BOUNCE_HALF; bolt->s.generic1 = self->s.generic1; //weaponMode bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = ABOMB_DMG; bolt->splashDamage = ABOMB_SPLASH_DMG; bolt->splashRadius = ABOMB_SPLASH_RADIUS; bolt->methodOfDeath = MOD_ABOMB; bolt->splashMethodOfDeath = MOD_ABOMB; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; bolt->r.mins[ 0 ] = bolt->r.mins[ 1 ] = bolt->r.mins[ 2 ] = -10.0f; bolt->r.maxs[ 0 ] = bolt->r.maxs[ 1 ] = bolt->r.maxs[ 2 ] = 10.0f; bolt->s.pos.trType = TR_GRAVITY; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, ABOMB_SPEED, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin ); return bolt; } /* ================= fire_paraLockBlob ================= */ gentity_t *fire_paraLockBlob( gentity_t *self, vec3_t start, vec3_t dir ) { gentity_t *bolt; VectorNormalize ( dir ); bolt = G_Spawn( ); bolt->classname = "lockblob"; bolt->nextthink = level.time + 15000; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_LOCKBLOB_LAUNCHER; bolt->s.generic1 = self->s.generic1; //weaponMode bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = 0; bolt->splashDamage = 0; bolt->splashRadius = 0; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; bolt->s.pos.trType = TR_GRAVITY; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, LOCKBLOB_SPEED, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin ); return bolt; } /* ================= fire_bounceBall ================= */ gentity_t *fire_bounceBall( gentity_t *self, vec3_t start, vec3_t dir ) { gentity_t *bolt; VectorNormalize ( dir ); bolt = G_Spawn( ); bolt->classname = "bounceball"; bolt->nextthink = level.time + 3000; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_ALEVEL3_UPG; bolt->s.generic1 = self->s.generic1; //weaponMode bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = LEVEL3_BOUNCEBALL_DMG; bolt->splashDamage = 0; bolt->splashRadius = 0; bolt->methodOfDeath = MOD_LEVEL3_BOUNCEBALL; bolt->splashMethodOfDeath = MOD_LEVEL3_BOUNCEBALL; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; bolt->s.pos.trType = TR_GRAVITY; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, LEVEL3_BOUNCEBALL_SPEED, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin ); /*bolt->s.eFlags |= EF_BOUNCE;*/ return bolt; }