static void MissileImpact( gentity_t *ent, trace_t *trace ) { int dirAsByte, impactFlags; const missileAttributes_t *ma = BG_Missile( ent->s.modelindex ); gentity_t *hitEnt = &g_entities[ trace->entityNum ]; gentity_t *attacker = &g_entities[ ent->r.ownerNum ]; // Returns whether damage and hit effects should be done and played. std::function<int(gentity_t*, trace_t*, gentity_t*)> impactFunc; // Check for bounce. if ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) && !HasComponents<HealthComponent>(*hitEnt->entity) ) { BounceMissile( ent, trace ); if ( !( ent->s.eFlags & EF_NO_BOUNCE_SOUND ) ) { G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); } return; } // Call missile specific impact functions. switch( ent->s.modelindex ) { case MIS_GRENADE: impactFunc = ImpactGrenade; break; case MIS_FIREBOMB: impactFunc = ImpactGrenade; break; case MIS_FLAMER: impactFunc = ImpactFlamer; break; case MIS_FIREBOMB_SUB: impactFunc = ImpactFirebombSub; break; case MIS_LOCKBLOB: impactFunc = ImpactLockblock; break; case MIS_SLOWBLOB: impactFunc = ImpactSlowblob; break; case MIS_HIVE: impactFunc = ImpactHive; break; default: impactFunc = DefaultImpactFunc; break; } impactFlags = impactFunc( ent, trace, hitEnt ); // Deal impact damage. if ( !( impactFlags & MIF_NO_DAMAGE ) ) { if ( ent->damage && G_Alive( hitEnt ) ) { vec3_t dir; BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, dir ); if ( VectorNormalize( dir ) == 0 ) { dir[ 2 ] = 1; // stepped on a grenade } int dflags = 0; if ( !ma->doLocationalDamage ) dflags |= DAMAGE_NO_LOCDAMAGE; if ( ma->doKnockback ) dflags |= DAMAGE_KNOCKBACK; hitEnt->entity->Damage(ent->damage * MissileTimeDmgMod(ent), attacker, Vec3::Load(trace->endpos), Vec3::Load(dir), dflags, (meansOfDeath_t)ent->methodOfDeath); } // splash damage (doesn't apply to person directly hit) if ( ent->splashDamage ) { G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage * MissileTimeSplashDmgMod( ent ), ent->splashRadius, hitEnt, ( ma->doKnockback ? DAMAGE_KNOCKBACK : 0 ), ent->splashMethodOfDeath ); } } // Play hit effects and remove the missile. if ( !( impactFlags & MIF_NO_EFFECT ) ) { // Use either the trajectory direction or the surface normal for the hit event. if ( ma->impactFlightDirection ) { vec3_t trajDir; BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, trajDir ); VectorNormalize( trajDir ); dirAsByte = DirToByte( trajDir ); } else { dirAsByte = DirToByte( trace->plane.normal ); } // Add hit event. if ( HasComponents<HealthComponent>(*hitEnt->entity) ) { G_AddEvent( ent, EV_MISSILE_HIT_ENTITY, dirAsByte ); ent->s.otherEntityNum = hitEnt->s.number; } else if ( trace->surfaceFlags & SURF_METAL ) { G_AddEvent( ent, EV_MISSILE_HIT_METAL, dirAsByte ); } else { G_AddEvent( ent, EV_MISSILE_HIT_ENVIRONMENT, dirAsByte ); } ent->freeAfterEvent = true; // HACK: Change over to a general entity at the point of impact. ent->s.eType = ET_GENERAL; // Prevent map models from appearing at impact point. ent->s.modelindex = 0; // Save net bandwith. G_SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); G_SetOrigin( ent, trace->endpos ); trap_LinkEntity( ent ); } // If no impact happened, check if we should continue or free ourselves. else if ( !( impactFlags & MIF_NO_FREE ) ) { G_FreeEntity( ent ); } }
void weapon_railgun_fire (gentity_t *ent) { vec3_t end; #ifdef MISSIONPACK vec3_t impactpoint, bouncedir; #endif trace_t trace; gentity_t *tent; gentity_t *traceEnt; int damage; int i; int hits; int unlinked; int passent; gentity_t *unlinkedEntities[MAX_RAIL_HITS]; damage = 100 * s_quadFactor; VectorMA (g_muzzle, 8192, forward, end); // trace only against the solids, so the railgun will go through people unlinked = 0; hits = 0; passent = ent->s.number; do { trap_Trace (&trace, g_muzzle, NULL, NULL, end, passent, MASK_SHOT ); if ( trace.entityNum >= ENTITYNUM_MAX_NORMAL ) { break; } traceEnt = &g_entities[ trace.entityNum ]; if ( traceEnt->takedamage ) { #ifdef MISSIONPACK if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) { if ( G_InvulnerabilityEffect( traceEnt, forward, trace.endpos, impactpoint, bouncedir ) ) { G_BounceProjectile( g_muzzle, impactpoint, bouncedir, end ); // snap the endpos to integers to save net bandwidth, but nudged towards the line SnapVectorTowards( trace.endpos, g_muzzle ); // send railgun beam effect tent = G_TempEntity( trace.endpos, EV_RAILTRAIL ); // set player number for custom colors on the railtrail tent->s.clientNum = ent->s.clientNum; VectorCopy( g_muzzle, tent->s.origin2 ); // move origin a bit to come closer to the drawn gun muzzle VectorMA( tent->s.origin2, 4, right, tent->s.origin2 ); VectorMA( tent->s.origin2, -1, up, tent->s.origin2 ); tent->s.eventParm = 255; // don't make the explosion at the end // VectorCopy( impactpoint, g_muzzle ); // the player can hit him/herself with the bounced rail passent = ENTITYNUM_NONE; } } else { if( LogAccuracyHit( traceEnt, ent ) ) { hits++; } G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN); } #else if( LogAccuracyHit( traceEnt, ent ) ) { hits++; } G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN); #endif } if ( trace.contents & CONTENTS_SOLID ) { break; // we hit something solid enough to stop the beam } // unlink this entity, so the next trace will go past it trap_UnlinkEntity( traceEnt ); unlinkedEntities[unlinked] = traceEnt; unlinked++; } while ( unlinked < MAX_RAIL_HITS ); // link back in any entities we unlinked for ( i = 0 ; i < unlinked ; i++ ) { trap_LinkEntity( unlinkedEntities[i] ); } // the final trace endpos will be the terminal point of the rail trail // snap the endpos to integers to save net bandwidth, but nudged towards the line SnapVectorTowards( trace.endpos, g_muzzle ); // send railgun beam effect tent = G_TempEntity( trace.endpos, EV_RAILTRAIL ); // set player number for custom colors on the railtrail tent->s.clientNum = ent->s.clientNum; VectorCopy( g_muzzle, tent->s.origin2 ); // move origin a bit to come closer to the drawn gun muzzle VectorMA( tent->s.origin2, 4, right, tent->s.origin2 ); VectorMA( tent->s.origin2, -1, up, tent->s.origin2 ); // no explosion at end if SURF_NOIMPACT, but still make the trail if ( trace.surfaceFlags & SURF_NOIMPACT ) { tent->s.eventParm = 255; // don't make the explosion at the end } else { tent->s.eventParm = DirToByte( trace.plane.normal ); } tent->s.clientNum = ent->s.clientNum; // give the shooter a reward sound if they have made two railgun hits in a row if ( hits == 0 ) { // complete miss ent->client->accurateCount = 0; } else { // check for "impressive" reward sound ent->client->accurateCount += hits; if ( ent->client->accurateCount >= 2 ) { ent->client->accurateCount -= 2; ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++; // add the sprite over the player's head ent->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); ent->client->ps.eFlags |= EF_AWARD_IMPRESSIVE; ent->client->rewardTime = level.time + REWARD_SPRITE_TIME; } ent->client->accuracy_hits++; } }
void W_Fire_Electrobolt_FullInstant( edict_t *self, vec3_t start, vec3_t dir, float maxdamage, float mindamage, int maxknockback, int minknockback, int stun, int range, int minDamageRange, int mod, int timeDelta ) { vec3_t from, end; trace_t tr; edict_t *ignore, *event, *hit; int hit_movetype; int mask; bool missed = true; int dmgflags = 0; #define FULL_DAMAGE_RANGE g_projectile_prestep->value if( GS_Instagib() ) { maxdamage = mindamage = 9999; } VectorMA( start, range, dir, end ); VectorCopy( start, from ); ignore = self; hit = NULL; mask = MASK_SHOT; if( GS_RaceGametype() ) { mask = MASK_SOLID; } clamp_high( mindamage, maxdamage ); clamp_high( minknockback, maxknockback ); clamp_high( minDamageRange, range ); if( minDamageRange <= FULL_DAMAGE_RANGE ) { minDamageRange = FULL_DAMAGE_RANGE + 1; } if( range <= FULL_DAMAGE_RANGE + 1 ) { range = FULL_DAMAGE_RANGE + 1; } tr.ent = -1; while( ignore ) { G_Trace4D( &tr, from, NULL, NULL, end, ignore, mask, timeDelta ); VectorCopy( tr.endpos, from ); ignore = NULL; if( tr.ent == -1 ) { break; } // some entity was touched hit = &game.edicts[tr.ent]; hit_movetype = hit->movetype; // backup the original movetype as the entity may "die" if( hit == world ) { // stop dead if hit the world break; } // allow trail to go through BBOX entities (players, gibs, etc) if( !ISBRUSHMODEL( hit->s.modelindex ) ) { ignore = hit; } if( ( hit != self ) && ( hit->takedamage ) ) { float frac, damage, knockback, dist; dist = DistanceFast( tr.endpos, start ); if( dist <= FULL_DAMAGE_RANGE ) { frac = 0.0f; } else { frac = ( dist - FULL_DAMAGE_RANGE ) / (float)( minDamageRange - FULL_DAMAGE_RANGE ); clamp( frac, 0.0f, 1.0f ); } damage = maxdamage - ( ( maxdamage - mindamage ) * frac ); knockback = maxknockback - ( ( maxknockback - minknockback ) * frac ); G_Damage( hit, self, self, dir, dir, tr.endpos, damage, knockback, stun, dmgflags, mod ); // spawn a impact event on each damaged ent event = G_SpawnEvent( EV_BOLT_EXPLOSION, DirToByte( tr.plane.normal ), tr.endpos ); event->s.firemode = FIRE_MODE_STRONG; if( hit->r.client ) { missed = false; } } if( hit_movetype == MOVETYPE_NONE || hit_movetype == MOVETYPE_PUSH ) { break; } } if( missed && self->r.client ) { G_AwardPlayerMissedElectrobolt( self, mod ); } // send the weapon fire effect event = G_SpawnEvent( EV_ELECTROTRAIL, ENTNUM( self ), start ); VectorScale( dir, 1024, event->s.origin2 ); event->s.firemode = FIRE_MODE_STRONG; #undef FULL_DAMAGE_RANGE }
void G_ExplodeIntoNails(gentity_t * ent) { float angle, angle2; vec3_t forward, forward2, up; vec3_t nailForward, nailRight, nailUp; 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; // Tr3B: don't change the entity type because it is required by the EV_PROJECTILE_* events // the ent->freeAfterEvent = qtrue; will do the same effect //ent->s.eType = ET_GENERAL; G_AddEvent(ent, EV_PROJECTILE_MISS, DirToByte(dir)); ent->freeAfterEvent = qtrue; // splash damage if(ent->splashDamage) { if(G_RadiusDamage(ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent, ent->splashMethodOfDeath)) { g_entities[ent->r.ownerNum].client->accuracy_hits++; } } // create orthogonal vector to main direction PerpendicularVector(dir, forward); //VectorScale(forward, 40, forward); origin[2] += 10; // spread nails in all directions for(angle = 0; angle < 360; angle += 60.0f) { RotatePointAroundVector(forward2, dir, forward, angle); for(angle2 = 0; angle2 < 360; angle2 += 60.0f) { RotatePointAroundVector(up, forward, dir, angle2); VectorAdd(forward2, up, nailForward); VectorNormalize(nailForward); PerpendicularVector(nailRight, nailForward); CrossProduct(nailForward, nailRight, nailUp); #if 1 fire_gravnail(ent, origin, nailForward, nailRight, nailUp); #else VectorAdd(forward2, dir, forward2); fire_clustergrenade(ent, origin, forward2); #endif } } trap_LinkEntity(ent); }
void MSG_WriteDir( msg_t *msg, vec3_t dir ) { MSG_WriteByte( msg, dir ? DirToByte( dir ) : 0 ); }
/* ================ 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_TEAM ] == TEAM_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_TEAM ] == TEAM_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 = G_ExplodeMissile; ent->nextthink = level.time + FRAMETIME; //only damage humans if( other->client && other->client->ps.stats[ STAT_TEAM ] == TEAM_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, DAMAGE_NO_LOCDAMAGE, 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->s.eType == ET_PLAYER || other->s.eType == ET_BUILDABLE ) ) { 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_MissileImpact ================ */ void G_MissileImpact( gentity_t *ent, trace_t *trace ) { gentity_t *other, *attacker; qboolean returnAfterDamage = qfalse; vec3_t dir; float power; 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 ) ) ) { 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, "lockblob" ) ) { if ( other->client && other->client->ps.stats[ STAT_TEAM ] == TEAM_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_TEAM ] == TEAM_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, "hive" ) ) { if ( other->s.eType == ET_BUILDABLE && other->s.modelindex == BA_A_HIVE ) { if ( !ent->parent ) { G_Printf( S_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 = G_ExplodeMissile; ent->nextthink = level.time + FRAMETIME; //only damage humans if ( other->client && other->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) { returnAfterDamage = qtrue; } else { return; } } } power = G_DoMissileTimePowerReduce( ent ); // impact damage if ( other->takedamage ) { // FIXME: wrong damage direction? if ( ent->damage ) { vec3_t dir; BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, dir ); if ( VectorNormalize( dir ) == 0 ) { dir[ 2 ] = 1; // stepped on a grenade } G_Damage( other, ent, attacker, dir, ent->s.origin, ent->damage * power, DAMAGE_NO_LOCDAMAGE, 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->s.eType == ET_PLAYER || other->s.eType == ET_BUILDABLE ) ) { G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); ent->s.otherEntityNum = other->s.number; } else if ( trace->surfaceFlags & SURF_METAL ) { 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 general 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 * power, ent->splashRadius, other, ent->splashMethodOfDeath ); } trap_LinkEntity( ent ); }
void fire_lead( gentity_t *self, vec3_t start, vec3_t dir, int damage ) { trace_t tr; vec3_t end; gentity_t *tent; gentity_t *traceEnt; vec3_t forward, right, up; vec3_t angles; float r, u; qboolean anti_tank_enable = qfalse; r = crandom() * self->random; u = crandom() * self->random; vectoangles( dir, angles ); AngleVectors( angles, forward, right, up ); VectorMA( start, 8192, forward, end ); VectorMA( end, r, right, end ); VectorMA( end, u, up, end ); trap_Trace( &tr, start, NULL, NULL, end, self->s.number, MASK_SHOT ); if ( tr.surfaceFlags & SURF_NOIMPACT ) { return; } traceEnt = &g_entities[ tr.entityNum ]; // snap the endpos to integers, but nudged towards the line SnapVectorTowards( tr.endpos, start ); // send bullet impact if ( traceEnt->takedamage && traceEnt->client ) { tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_FLESH ); tent->s.eventParm = traceEnt->s.number; } else { // Ridah, bullet impact should reflect off surface vec3_t reflect; float dot; tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_WALL ); dot = DotProduct( forward, tr.plane.normal ); VectorMA( forward, -2 * dot, tr.plane.normal, reflect ); VectorNormalize( reflect ); tent->s.eventParm = DirToByte( reflect ); // done. } tent->s.otherEntityNum = self->s.number; if ( traceEnt->takedamage ) { if ( self->s.weapon == WP_SNIPER && traceEnt->s.eType == ET_MOVER && traceEnt->aiName[0] ) { anti_tank_enable = qtrue; } if ( anti_tank_enable ) { self->s.weapon = WP_ROCKET_LAUNCHER; } G_Damage( traceEnt, self, self, forward, tr.endpos, damage, 0, MOD_MACHINEGUN ); if ( anti_tank_enable ) { self->s.weapon = WP_SNIPER; } } }
/* ================ G_MissileImpact impactDamage is how much damage the impact will do to func_explosives ================ */ void G_MissileImpact( gentity_t *ent, trace_t *trace, int impactDamage ) { gentity_t *other; qboolean hitClient = qfalse; vec3_t velocity; int etype; other = &g_entities[trace->entityNum]; // handle func_explosives if ( other->classname && Q_stricmp( other->classname, "func_explosive" ) == 0 ) { // the damage is sufficient to "break" the ent (health == 0 is non-breakable) if ( other->health && impactDamage >= other->health ) { // check for other->takedamage needs to be inside the health check since it is // likely that, if successfully destroyed by the missile, in the next runmissile() // update takedamage would be set to '0' and the func_explosive would not be // removed yet, causing a bounce. if ( other->takedamage ) { BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); G_Damage( other, ent, &g_entities[ent->r.ownerNum], velocity, ent->s.origin, impactDamage, 0, ent->methodOfDeath ); } // its possible of the func_explosive not to die from this and it // should reflect the missile or explode it not vanish into oblivion if ( other->health <= 0 ) { return; } } } // check for bounce if ( !other->takedamage && ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) { G_BounceMissile( ent, trace ); // JPW NERVE -- spotter White Phosphorous rounds shouldn't bounce noise if ( !Q_stricmp( ent->classname,"WP" ) ) { return; } // jpw if ( !Q_stricmp( ent->classname, "flamebarrel" ) ) { G_AddEvent( ent, EV_FLAMEBARREL_BOUNCE, 0 ); } else { G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); } return; } if ( other->takedamage && ent->s.density == 1 ) { G_ExplodeMissilePoisonGas( ent ); return; } // impact damage if ( other->takedamage ) { if ( ent->damage ) { if ( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) { if ( g_entities[ent->r.ownerNum].client ) { g_entities[ent->r.ownerNum].client->ps.persistant[PERS_ACCURACY_HITS]++; } hitClient = qtrue; } BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); if ( VectorLength( velocity ) == 0 ) { velocity[2] = 1; // stepped on a grenade } G_Damage( other, ent, &g_entities[ent->r.ownerNum], velocity, ent->s.origin, ent->damage, 0, ent->methodOfDeath ); } else // if no damage value, then this is a splash damage grenade only { G_BounceMissile( ent, trace ); 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 { // Ridah, try projecting it in the direction it came from, for better decals vec3_t dir; BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, dir ); BG_GetMarkDir( dir, trace->plane.normal, dir ); G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( dir ) ); } ent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact etype = ent->s.eType; 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 ) { if ( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, other, ent->splashMethodOfDeath ) ) { if ( !hitClient && g_entities[ent->r.ownerNum].client ) { g_entities[ent->r.ownerNum].client->ps.persistant[PERS_ACCURACY_HITS]++; } } } trap_LinkEntity( ent ); }
/* =============== CheckPounceAttack =============== */ qboolean CheckPounceAttack( gentity_t *ent ) { trace_t tr; vec3_t end; gentity_t *tent; gentity_t *traceEnt; int damage; vec3_t mins, maxs; VectorSet( mins, -LEVEL3_POUNCE_WIDTH, -LEVEL3_POUNCE_WIDTH, -LEVEL3_POUNCE_WIDTH ); VectorSet( maxs, LEVEL3_POUNCE_WIDTH, LEVEL3_POUNCE_WIDTH, LEVEL3_POUNCE_WIDTH ); if( !ent->client->allowedToPounce ) return qfalse; if( ent->client->ps.groundEntityNum != ENTITYNUM_NONE ) { ent->client->allowedToPounce = qfalse; return qfalse; } if( ent->client->ps.weaponTime ) return qfalse; // set aiming directions AngleVectors( ent->client->ps.viewangles, forward, right, up ); CalcMuzzlePoint( ent, forward, right, up, muzzle ); VectorMA( muzzle, LEVEL3_POUNCE_RANGE, forward, end ); trap_Trace( &tr, ent->s.origin, mins, maxs, end, ent->s.number, MASK_SHOT ); //miss if( tr.fraction >= 1.0 ) return qfalse; if( tr.surfaceFlags & SURF_NOIMPACT ) return qfalse; traceEnt = &g_entities[ tr.entityNum ]; // send blood impact if( traceEnt->takedamage && traceEnt->client ) { tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT ); tent->s.otherEntityNum = traceEnt->s.number; tent->s.eventParm = DirToByte( tr.plane.normal ); tent->s.weapon = ent->s.weapon; tent->s.generic1 = ent->s.generic1; //weaponMode } if( !traceEnt->takedamage ) return qfalse; damage = (int)( ( (float)ent->client->pouncePayload / (float)LEVEL3_POUNCE_SPEED ) * LEVEL3_POUNCE_DMG ); G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, DAMAGE_NO_KNOCKBACK|DAMAGE_NO_LOCDAMAGE, MOD_LEVEL3_POUNCE ); ent->client->ps.weaponTime += LEVEL3_POUNCE_TIME; ent->client->allowedToPounce = qfalse; return qtrue; }
/* =============== CheckVenomAttack =============== */ qboolean CheckVenomAttack( gentity_t *ent ) { trace_t tr; vec3_t end; gentity_t *tent; gentity_t *traceEnt; vec3_t mins, maxs; int damage = LEVEL0_BITE_DMG; VectorSet( mins, -LEVEL0_BITE_WIDTH, -LEVEL0_BITE_WIDTH, -LEVEL0_BITE_WIDTH ); VectorSet( maxs, LEVEL0_BITE_WIDTH, LEVEL0_BITE_WIDTH, LEVEL0_BITE_WIDTH ); // set aiming directions AngleVectors( ent->client->ps.viewangles, forward, right, up ); CalcMuzzlePoint( ent, forward, right, up, muzzle ); VectorMA( muzzle, LEVEL0_BITE_RANGE, forward, end ); trap_Trace( &tr, muzzle, mins, maxs, end, ent->s.number, MASK_SHOT ); if( tr.surfaceFlags & SURF_NOIMPACT ) return qfalse; traceEnt = &g_entities[ tr.entityNum ]; if( !traceEnt->takedamage ) return qfalse; if( !traceEnt->client && !traceEnt->s.eType == ET_BUILDABLE ) return qfalse; //allow bites to work against defensive buildables only if( traceEnt->s.eType == ET_BUILDABLE ) { if( traceEnt->s.modelindex != BA_H_MGTURRET && traceEnt->s.modelindex != BA_H_TESLAGEN ) return qfalse; //hackery damage *= 0.5f; } if( traceEnt->client ) { if( traceEnt->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) return qfalse; if( traceEnt->client->ps.stats[ STAT_HEALTH ] <= 0 ) return qfalse; } // send blood impact if( traceEnt->takedamage && traceEnt->client ) { tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT ); tent->s.otherEntityNum = traceEnt->s.number; tent->s.eventParm = DirToByte( tr.plane.normal ); tent->s.weapon = ent->s.weapon; tent->s.generic1 = ent->s.generic1; //weaponMode } G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, MOD_LEVEL0_BITE ); return qtrue; }
void G_MissileImpact( gentity_t *ent, trace_t *trace ) { gentity_t *other; qboolean hitClient = qfalse; qboolean isKnockedSaber = qfalse; other = &g_entities[trace->entityNum]; // check for bounce if ( !other->takedamage && (ent->bounceCount > 0 || ent->bounceCount == -5) && ( ent->flags & ( FL_BOUNCE | FL_BOUNCE_HALF ) ) ) { int originalBounceCount = ent->bounceCount; G_BounceMissile( ent, trace ); if ( originalBounceCount != ent->bounceCount ) { G_GrenadeBounceEvent ((const gentity_t *)ent); } return; } else if (ent->neverFree && ent->s.weapon == WP_SABER && (ent->flags & FL_BOUNCE_HALF)) { //this is a knocked-away saber if (ent->bounceCount > 0 || ent->bounceCount == -5) { int originalBounceCount = ent->bounceCount; G_BounceMissile( ent, trace ); G_GrenadeBounceEvent ((const gentity_t *)ent); if ( originalBounceCount != ent->bounceCount ) { G_GrenadeBounceEvent ((const gentity_t *)ent); } return; } isKnockedSaber = qtrue; } // I would glom onto the FL_BOUNCE code section above, but don't feel like risking breaking something else if ( (!other->takedamage && (ent->bounceCount > 0 || ent->bounceCount == -5) && ( ent->flags&(FL_BOUNCE_SHRAPNEL) ) ) || ((trace->surfaceFlags&SURF_FORCEFIELD)&&!ent->splashDamage&&!ent->splashRadius&&(ent->bounceCount > 0 || ent->bounceCount == -5)) ) { G_BounceMissile( ent, trace ); if ( ent->bounceCount < 1 ) { ent->flags &= ~FL_BOUNCE_SHRAPNEL; } return; } /* if ( !other->takedamage && ent->s.weapon == WP_THERMAL && !ent->alt_fire ) {//rolling thermal det - FIXME: make this an eFlag like bounce & stick!!! //G_BounceRollMissile( ent, trace ); if ( ent->owner && ent->owner->s.number == 0 ) { G_MissileAddAlerts( ent ); } //gi.linkentity( ent ); return; } */ if ((other->r.contents & CONTENTS_LIGHTSABER) && !isKnockedSaber) { //hit this person's saber, so.. gentity_t *otherOwner = &g_entities[other->r.ownerNum]; if (otherOwner->takedamage && otherOwner->client && otherOwner->client->ps.duelInProgress && otherOwner->client->ps.duelIndex != ent->r.ownerNum) { goto killProj; } } else if (!isKnockedSaber) { if (other->takedamage && other->client && other->client->ps.duelInProgress && other->client->ps.duelIndex != ent->r.ownerNum) { goto killProj; } } if (other->flags & FL_DMG_BY_HEAVY_WEAP_ONLY) { if (ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_ROCKET && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_ROCKET_HOMING && ent->methodOfDeath != MOD_THERMAL && ent->methodOfDeath != MOD_THERMAL_SPLASH && ent->methodOfDeath != MOD_TRIP_MINE_SPLASH && ent->methodOfDeath != MOD_TIMED_MINE_SPLASH && ent->methodOfDeath != MOD_DET_PACK_SPLASH && ent->methodOfDeath != MOD_VEHICLE && ent->methodOfDeath != MOD_CONC && ent->methodOfDeath != MOD_CONC_ALT && ent->methodOfDeath != MOD_SABER && ent->methodOfDeath != MOD_TURBLAST) { vec3_t fwd; if (trace) { VectorCopy(trace->plane.normal, fwd); } else { //oh well AngleVectors(other->r.currentAngles, fwd, NULL, NULL); } G_DeflectMissile(other, ent, fwd); G_MissileBounceEffect(ent, ent->r.currentOrigin, fwd); return; } } if ((other->flags & FL_SHIELDED) && ent->s.weapon != WP_ROCKET_LAUNCHER && ent->s.weapon != WP_THERMAL && ent->s.weapon != WP_TRIP_MINE && ent->s.weapon != WP_DET_PACK && ent->s.weapon != WP_EMPLACED_GUN && ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_TURBLAST && ent->methodOfDeath != MOD_VEHICLE && ent->methodOfDeath != MOD_CONC && ent->methodOfDeath != MOD_CONC_ALT && !(ent->dflags&DAMAGE_HEAVY_WEAP_CLASS) ) {// entity is shielded vec3_t fwd; if (other->client) { AngleVectors(other->client->ps.viewangles, fwd, NULL, NULL); } else { AngleVectors(other->r.currentAngles, fwd, NULL, NULL); } G_DeflectMissile(other, ent, fwd); G_MissileBounceEffect(ent, ent->r.currentOrigin, fwd); return; } // SABERFIXME: make this based on .wpn file? some conc rifles should be able to be deflected... if (other->takedamage && other->client && ent->s.weapon != WP_ROCKET_LAUNCHER && ent->s.weapon != WP_THERMAL && ent->s.weapon != WP_TRIP_MINE && ent->s.weapon != WP_DET_PACK && ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_CONC && ent->methodOfDeath != MOD_CONC_ALT && other->client->saberBlockDebounce < level.time && !isKnockedSaber && WP_SaberCanBlock(other, ent->r.currentOrigin, 0, 0, qtrue, 0)) { //only block one projectile per 200ms (to prevent giant swarms of projectiles being blocked) vec3_t fwd; gentity_t *te; int otherDefLevel = other->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE]; te = G_TempEntity( ent->r.currentOrigin, EV_SABER_BLOCK ); VectorCopy(ent->r.currentOrigin, te->s.origin); VectorCopy(trace->plane.normal, te->s.angles); te->s.eventParm = 0; te->s.weapon = 0;//saberNum te->s.legsAnim = 0;//bladeNum /*if (other->client->ps.velocity[2] > 0 || other->client->pers.cmd.forwardmove || other->client->pers.cmd.rightmove) */ if (other->client->ps.velocity[2] > 0 || other->client->pers.cmd.forwardmove < 0) //now we only do it if jumping or running backward. Should be able to full-on charge. { otherDefLevel -= 1; if (otherDefLevel < 0) { otherDefLevel = 0; } } AngleVectors(other->client->ps.viewangles, fwd, NULL, NULL); // SABERFIXME: Don't make this force based. This code is ugly as f**k anyway other->client->saberBlockDebounce = level.time + (350 - (otherDefLevel*100)); //200; //For jedi AI other->client->ps.saberEventFlags |= SEF_DEFLECTED; if ( other->client->ps.saberActionFlags & (1 << SAF_BLOCKING) && !(other->client->ps.saberActionFlags & ( 1 << SAF_PROJBLOCKING) ) ) { goto killProj; } else if ( other->client->ps.saberActionFlags & SAF_PROJBLOCKING ) { JKG_SaberDeflectMissile(other, ent, fwd); other->client->saberProjBlockTime += 500; // give them a little bit of leeway --eez } return; } else if ((other->r.contents & CONTENTS_LIGHTSABER) && !isKnockedSaber) { //hit this person's saber, so.. gentity_t *otherOwner = &g_entities[other->r.ownerNum]; if (otherOwner->takedamage && otherOwner->client && ent->s.weapon != WP_ROCKET_LAUNCHER && ent->s.weapon != WP_THERMAL && ent->s.weapon != WP_TRIP_MINE && ent->s.weapon != WP_DET_PACK && ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_CONC && ent->methodOfDeath != MOD_CONC_ALT /*&& otherOwner->client->ps.saberBlockTime < level.time*/) { //for now still deflect even if saberBlockTime >= level.time because it hit the actual saber vec3_t fwd; gentity_t *te; int otherDefLevel = otherOwner->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE]; //in this case, deflect it even if we can't actually block it because it hit our saber //WP_SaberCanBlock(otherOwner, ent->r.currentOrigin, 0, 0, qtrue, 0); if (otherOwner->client && otherOwner->client->ps.weaponTime <= 0) { WP_SaberBlockNonRandom(otherOwner, NULL, ent->r.currentOrigin, qtrue); // <-- ??? --eez } te = G_TempEntity( ent->r.currentOrigin, EV_SABER_BLOCK ); VectorCopy(ent->r.currentOrigin, te->s.origin); VectorCopy(trace->plane.normal, te->s.angles); te->s.eventParm = 0; te->s.weapon = 0;//saberNum te->s.legsAnim = 0;//bladeNum /*if (otherOwner->client->ps.velocity[2] > 0 || otherOwner->client->pers.cmd.forwardmove || otherOwner->client->pers.cmd.rightmove)*/ if (otherOwner->client->ps.velocity[2] > 0 || otherOwner->client->pers.cmd.forwardmove < 0) //now we only do it if jumping or running backward. Should be able to full-on charge. { otherDefLevel -= 1; if (otherDefLevel < 0) { otherDefLevel = 0; } } AngleVectors(otherOwner->client->ps.viewangles, fwd, NULL, NULL); otherOwner->client->saberBlockDebounce = level.time + (350 - (otherDefLevel*100));//200; //For jedi AI otherOwner->client->ps.saberEventFlags |= SEF_DEFLECTED; // um...this code is a lil messed up, so i'll replace it with my own --eez if ( otherOwner->client->ps.saberActionFlags & (1 << SAF_BLOCKING) && !(otherOwner->client->ps.saberActionFlags & ( 1 << SAF_PROJBLOCKING )) ) { goto killProj; } else if ( otherOwner->client->ps.saberActionFlags & ( 1 << SAF_PROJBLOCKING ) ) { // SABERFIXME: Write new function for this JKG_SaberDeflectMissile(otherOwner, ent, fwd); otherOwner->client->saberProjBlockTime += 500; // give them a little bit of leeway --eez } return; } } if( ent->genericValue10 ) // nerf this check in order to make grenade bouncing work --eez { vec3_t fwd; if (other->client) { AngleVectors(other->client->ps.viewangles, fwd, NULL, NULL); } else { AngleVectors(other->r.currentAngles, fwd, NULL, NULL); } VectorScale(ent->s.pos.trDelta, 0.2, ent->s.pos.trDelta); G_Damage(other, ent, ent->parent, fwd, ent->s.origin, ent->genericValue9, 0, MOD_THERMAL); G_DeflectMissile(other, ent, fwd); //G_MissileBounceEffect(ent, ent->r.currentOrigin, fwd); return; } // check for sticking if ( !other->takedamage && ( ent->s.eFlags & EF_MISSILE_STICK ) ) { laserTrapStick( ent, trace->endpos, trace->plane.normal ); G_AddEvent( ent, EV_MISSILE_STICK, 0 ); return; } // impact damage if (other->takedamage && !isKnockedSaber) { // FIXME: wrong damage direction? weaponData_t *weapon = GetWeaponData (ent->s.weapon, ent->s.weaponVariation); weaponFireModeStats_t *fireMode = &weapon->firemodes[ent->s.firingMode]; if ( ent->damage || fireMode->damageTypeHandle || fireMode->secondaryDmgHandle ) { vec3_t velocity; hitClient = qtrue; BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); if ( VectorLength( velocity ) == 0 ) { velocity[2] = 1; // stepped on a grenade } if ( fireMode->damageTypeHandle ) { JKG_DoDamage (fireMode->damageTypeHandle, other, ent, &g_entities[ent->r.ownerNum], velocity, ent->r.currentOrigin, 0, ent->methodOfDeath); } else if ( ent->damage ) { G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, ent->r.currentOrigin, ent->damage, 0, ent->methodOfDeath); } if ( fireMode->secondaryDmgHandle ) { JKG_DoDamage (fireMode->secondaryDmgHandle, other, ent, &g_entities[ent->r.ownerNum], velocity, ent->r.currentOrigin, 0, ent->methodOfDeath); } if (other->client) { //What I'm wondering is why this isn't in the NPC pain funcs. But this is what SP does, so whatever. class_t npc_class = other->client->NPC_class; // If we are a robot and we aren't currently doing the full body electricity... if ( npc_class == CLASS_SEEKER || npc_class == CLASS_PROBE || npc_class == CLASS_MOUSE || npc_class == CLASS_GONK || npc_class == CLASS_R2D2 || npc_class == CLASS_R5D2 || npc_class == CLASS_REMOTE || npc_class == CLASS_MARK1 || npc_class == CLASS_MARK2 || //npc_class == CLASS_PROTOCOL ||//no protocol, looks odd npc_class == CLASS_INTERROGATOR || npc_class == CLASS_ATST || npc_class == CLASS_SENTRY ) { // special droid only behaviors if ( other->client->ps.electrifyTime < level.time + 100 ) { // ... do the effect for a split second for some more feedback other->client->ps.electrifyTime = level.time + 450; } //FIXME: throw some sparks off droids,too } } } } killProj: // 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 && !isKnockedSaber ) { { 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 if (ent->s.weapon != G2_MODEL_PART && !isKnockedSaber) { G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); } if (!isKnockedSaber) { 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 ); ent->takedamage = qfalse; if (ent->s.weapon == G2_MODEL_PART) { ent->freeAfterEvent = qfalse; //it will free itself } if(ent->splashRadius && ent->splashDamage && !ent->genericValue10) { G_RadiusDamage(trace->endpos, &g_entities[ent->r.ownerNum], ent->splashDamage, ent->splashRadius, NULL, ent, ent->methodOfDeath); } trap->LinkEntity( (sharedEntity_t *)ent ); }
void G_MissileImpact( gentity_t *ent, trace_t *trace ) { gentity_t *other; qboolean hitClient = qfalse; qboolean isKnockedSaber = qfalse; other = &g_entities[trace->entityNum]; // check for bounce if ( !other->takedamage && (ent->bounceCount > 0 || ent->bounceCount == -5) && ( ent->flags & ( FL_BOUNCE | FL_BOUNCE_HALF ) ) ) { G_BounceMissile( ent, trace ); G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } else if (ent->neverFree && ent->s.weapon == WP_SABER && (ent->flags & FL_BOUNCE_HALF)) { //this is a knocked-away saber if (ent->bounceCount > 0 || ent->bounceCount == -5) { G_BounceMissile( ent, trace ); G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } isKnockedSaber = qtrue; } // I would glom onto the FL_BOUNCE code section above, but don't feel like risking breaking something else if ( (!other->takedamage && (ent->bounceCount > 0 || ent->bounceCount == -5) && ( ent->flags&(FL_BOUNCE_SHRAPNEL) ) ) || ((trace->surfaceFlags&SURF_FORCEFIELD)&&!ent->splashDamage&&!ent->splashRadius&&(ent->bounceCount > 0 || ent->bounceCount == -5)) ) { G_BounceMissile( ent, trace ); if ( ent->bounceCount < 1 ) { ent->flags &= ~FL_BOUNCE_SHRAPNEL; } return; } /* if ( !other->takedamage && ent->s.weapon == WP_THERMAL && !ent->alt_fire ) {//rolling thermal det - FIXME: make this an eFlag like bounce & stick!!! //G_BounceRollMissile( ent, trace ); if ( ent->owner && ent->owner->s.number == 0 ) { G_MissileAddAlerts( ent ); } //gi.linkentity( ent ); return; } */ if ((other->r.contents & CONTENTS_LIGHTSABER) && !isKnockedSaber) { //hit this person's saber, so.. gentity_t *otherOwner = &g_entities[other->r.ownerNum]; if (otherOwner->takedamage && otherOwner->client && otherOwner->client->ps.duelInProgress && otherOwner->client->ps.duelIndex != ent->r.ownerNum) { goto killProj; } } else if (!isKnockedSaber) { if (other->takedamage && other->client && other->client->ps.duelInProgress && other->client->ps.duelIndex != ent->r.ownerNum) { goto killProj; } } if (other->flags & FL_DMG_BY_HEAVY_WEAP_ONLY) { if (ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_ROCKET && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_ROCKET_HOMING && ent->methodOfDeath != MOD_THERMAL && ent->methodOfDeath != MOD_THERMAL_SPLASH && ent->methodOfDeath != MOD_TRIP_MINE_SPLASH && ent->methodOfDeath != MOD_TIMED_MINE_SPLASH && ent->methodOfDeath != MOD_DET_PACK_SPLASH && ent->methodOfDeath != MOD_VEHICLE && ent->methodOfDeath != MOD_CONC && ent->methodOfDeath != MOD_CONC_ALT && ent->methodOfDeath != MOD_SABER && ent->methodOfDeath != MOD_TURBLAST) { vec3_t fwd; if (trace) { VectorCopy(trace->plane.normal, fwd); } else { //oh well AngleVectors(other->r.currentAngles, fwd, NULL, NULL); } G_DeflectMissile(other, ent, fwd); G_MissileBounceEffect(ent, ent->r.currentOrigin, fwd); return; } } if ((other->flags & FL_SHIELDED) && ent->s.weapon != WP_ROCKET_LAUNCHER && ent->s.weapon != WP_THERMAL && ent->s.weapon != WP_TRIP_MINE && ent->s.weapon != WP_DET_PACK && ent->s.weapon != WP_DEMP2 && ent->s.weapon != WP_EMPLACED_GUN && ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_TURBLAST && ent->methodOfDeath != MOD_VEHICLE && ent->methodOfDeath != MOD_CONC && ent->methodOfDeath != MOD_CONC_ALT && !(ent->dflags&DAMAGE_HEAVY_WEAP_CLASS) ) { vec3_t fwd; if (other->client) { AngleVectors(other->client->ps.viewangles, fwd, NULL, NULL); } else { AngleVectors(other->r.currentAngles, fwd, NULL, NULL); } G_DeflectMissile(other, ent, fwd); G_MissileBounceEffect(ent, ent->r.currentOrigin, fwd); return; } if (other->takedamage && other->client && ent->s.weapon != WP_ROCKET_LAUNCHER && ent->s.weapon != WP_THERMAL && ent->s.weapon != WP_TRIP_MINE && ent->s.weapon != WP_DET_PACK && ent->s.weapon != WP_DEMP2 && ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_CONC && ent->methodOfDeath != MOD_CONC_ALT && other->client->ps.saberBlockTime < level.time && !isKnockedSaber && WP_SaberCanBlock(other, ent->r.currentOrigin, 0, 0, qtrue, 0)) { //only block one projectile per 200ms (to prevent giant swarms of projectiles being blocked) vec3_t fwd; gentity_t *te; int otherDefLevel = other->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE]; te = G_TempEntity( ent->r.currentOrigin, EV_SABER_BLOCK ); VectorCopy(ent->r.currentOrigin, te->s.origin); VectorCopy(trace->plane.normal, te->s.angles); te->s.eventParm = 0; /*if (other->client->ps.velocity[2] > 0 || other->client->pers.cmd.forwardmove || other->client->pers.cmd.rightmove) */ if (other->client->ps.velocity[2] > 0 || other->client->pers.cmd.forwardmove < 0) //now we only do it if jumping or running backward. Should be able to full-on charge. { otherDefLevel -= 1; if (otherDefLevel < 0) { otherDefLevel = 0; } } AngleVectors(other->client->ps.viewangles, fwd, NULL, NULL); if (otherDefLevel == FORCE_LEVEL_1) { //if def is only level 1, instead of deflecting the shot it should just die here } else if (otherDefLevel == FORCE_LEVEL_2) { G_DeflectMissile(other, ent, fwd); } else { G_ReflectMissile(other, ent, fwd); } other->client->ps.saberBlockTime = level.time + (350 - (otherDefLevel*100)); //200; //For jedi AI other->client->ps.saberEventFlags |= SEF_DEFLECTED; if (otherDefLevel == FORCE_LEVEL_3) { other->client->ps.saberBlockTime = 0; //^_^ } if (otherDefLevel == FORCE_LEVEL_1) { goto killProj; } return; } else if ((other->r.contents & CONTENTS_LIGHTSABER) && !isKnockedSaber) { //hit this person's saber, so.. gentity_t *otherOwner = &g_entities[other->r.ownerNum]; if (otherOwner->takedamage && otherOwner->client && ent->s.weapon != WP_ROCKET_LAUNCHER && ent->s.weapon != WP_THERMAL && ent->s.weapon != WP_TRIP_MINE && ent->s.weapon != WP_DET_PACK && ent->s.weapon != WP_DEMP2 && ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_CONC && ent->methodOfDeath != MOD_CONC_ALT /*&& otherOwner->client->ps.saberBlockTime < level.time*/) { //for now still deflect even if saberBlockTime >= level.time because it hit the actual saber vec3_t fwd; gentity_t *te; int otherDefLevel = otherOwner->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE]; //in this case, deflect it even if we can't actually block it because it hit our saber //WP_SaberCanBlock(otherOwner, ent->r.currentOrigin, 0, 0, qtrue, 0); if (otherOwner->client && otherOwner->client->ps.weaponTime <= 0) { WP_SaberBlockNonRandom(otherOwner, ent->r.currentOrigin, qtrue); } te = G_TempEntity( ent->r.currentOrigin, EV_SABER_BLOCK ); VectorCopy(ent->r.currentOrigin, te->s.origin); VectorCopy(trace->plane.normal, te->s.angles); te->s.eventParm = 0; /*if (otherOwner->client->ps.velocity[2] > 0 || otherOwner->client->pers.cmd.forwardmove || otherOwner->client->pers.cmd.rightmove)*/ if (otherOwner->client->ps.velocity[2] > 0 || otherOwner->client->pers.cmd.forwardmove < 0) //now we only do it if jumping or running backward. Should be able to full-on charge. { otherDefLevel -= 1; if (otherDefLevel < 0) { otherDefLevel = 0; } } AngleVectors(otherOwner->client->ps.viewangles, fwd, NULL, NULL); if (otherDefLevel == FORCE_LEVEL_1) { //if def is only level 1, instead of deflecting the shot it should just die here } else if (otherDefLevel == FORCE_LEVEL_2) { G_DeflectMissile(otherOwner, ent, fwd); } else { G_ReflectMissile(otherOwner, ent, fwd); } otherOwner->client->ps.saberBlockTime = level.time + (350 - (otherDefLevel*100));//200; //For jedi AI otherOwner->client->ps.saberEventFlags |= SEF_DEFLECTED; if (otherDefLevel == FORCE_LEVEL_3) { otherOwner->client->ps.saberBlockTime = 0; //^_^ } if (otherDefLevel == FORCE_LEVEL_1) { goto killProj; } return; } } // check for sticking if ( !other->takedamage && ( ent->s.eFlags & EF_MISSILE_STICK ) ) { laserTrapStick( ent, trace->endpos, trace->plane.normal ); G_AddEvent( ent, EV_MISSILE_STICK, 0 ); return; } // impact damage if (other->takedamage && !isKnockedSaber) { // FIXME: wrong damage direction? if ( ent->damage ) { vec3_t velocity; qboolean didDmg = qfalse; if( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; hitClient = qtrue; } BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); if ( VectorLength( velocity ) == 0 ) { velocity[2] = 1; // stepped on a grenade } if (ent->s.weapon == WP_BOWCASTER || ent->s.weapon == WP_FLECHETTE || ent->s.weapon == WP_ROCKET_LAUNCHER) { if (ent->s.weapon == WP_FLECHETTE && (ent->s.eFlags & EF_ALT_FIRING)) { ent->think(ent); } else { G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, /*ent->s.origin*/ent->r.currentOrigin, ent->damage, DAMAGE_HALF_ABSORB, ent->methodOfDeath); didDmg = qtrue; } } else { G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, /*ent->s.origin*/ent->r.currentOrigin, ent->damage, 0, ent->methodOfDeath); didDmg = qtrue; } if (didDmg && other && other->client) { //What I'm wondering is why this isn't in the NPC pain funcs. But this is what SP does, so whatever. class_t npc_class = other->client->NPC_class; // If we are a robot and we aren't currently doing the full body electricity... if ( npc_class == CLASS_SEEKER || npc_class == CLASS_PROBE || npc_class == CLASS_MOUSE || npc_class == CLASS_GONK || npc_class == CLASS_R2D2 || npc_class == CLASS_R5D2 || npc_class == CLASS_REMOTE || npc_class == CLASS_MARK1 || npc_class == CLASS_MARK2 || //npc_class == CLASS_PROTOCOL ||//no protocol, looks odd npc_class == CLASS_INTERROGATOR || npc_class == CLASS_ATST || npc_class == CLASS_SENTRY ) { // special droid only behaviors if ( other->client->ps.electrifyTime < level.time + 100 ) { // ... do the effect for a split second for some more feedback other->client->ps.electrifyTime = level.time + 450; } //FIXME: throw some sparks off droids,too } } } if ( ent->s.weapon == WP_DEMP2 ) { //a hit with demp2 decloaks people, disables ships if ( other && other->client && other->client->NPC_class == CLASS_VEHICLE ) { //hit a vehicle if ( other->m_pVehicle //valid vehicle ent && other->m_pVehicle->m_pVehicleInfo//valid stats && (other->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER//always affect speeders ||(other->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER && ent->classname && Q_stricmp("vehicle_proj", ent->classname ) == 0) )//only vehicle ion weapons affect a fighter in this manner && !FighterIsLanded( other->m_pVehicle , &other->client->ps )//not landed && !(other->spawnflags&2) )//and not suspended { //vehicles hit by "ion cannons" lose control if ( other->client->ps.electrifyTime > level.time ) { //add onto it //FIXME: extern the length of the "out of control" time? other->client->ps.electrifyTime += Q_irand(200,500); if ( other->client->ps.electrifyTime > level.time + 4000 ) { //cap it other->client->ps.electrifyTime = level.time + 4000; } } else { //start it //FIXME: extern the length of the "out of control" time? other->client->ps.electrifyTime = level.time + Q_irand(200,500); } } } else if ( other && other->client && other->client->ps.powerups[PW_CLOAKED] ) { Jedi_Decloak( other ); if ( ent->methodOfDeath == MOD_DEMP2_ALT ) { //direct hit with alt disables cloak forever //permanently disable the saboteur's cloak other->client->cloakToggleTime = Q3_INFINITE; } else { //temp disable other->client->cloakToggleTime = level.time + Q_irand( 3000, 10000 ); } } } } killProj: // 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 && !isKnockedSaber ) { 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 if (ent->s.weapon != G2_MODEL_PART && !isKnockedSaber) { G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); } if (!isKnockedSaber) { 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 ); ent->takedamage = qfalse; // splash damage (doesn't apply to person directly hit) if ( ent->splashDamage ) { if( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, other, ent, ent->splashMethodOfDeath ) ) { if( !hitClient && g_entities[ent->r.ownerNum].client ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; } } } if (ent->s.weapon == G2_MODEL_PART) { ent->freeAfterEvent = qfalse; //it will free itself } trap_LinkEntity( ent ); }
//QUAKED target_laser (0 .5 0) (-8 -8 -8) (8 8 8) START_ON RED GREEN BLUE YELLOW ORANGE FAT //When triggered, fires a laser. You can either set a target or a direction. //-------- KEYS -------- //angles: alternate "pitch, yaw, roll" angles method of aiming laser (default 0 0 0). //target : point this to a target_position entity to set the laser's aiming direction. //targetname : the activating trigger points to this. //notsingle : when set to 1, entity will not spawn in Single Player mode //notfree : when set to 1, entity will not spawn in "Free for all" and "Tournament" modes. //notduel : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. (jal: todo) //notteam : when set to 1, entity will not spawn in "Teamplay" and "CTF" modes. //-------- SPAWNFLAGS -------- //START_ON : when set, the laser will start on in the game. //RED : //GREEN : BLUE : //YELLOW : //ORANGE : //FAT : static void target_laser_think( edict_t *self ) { edict_t *ignore; vec3_t start; vec3_t end; trace_t tr; vec3_t point; vec3_t last_movedir; int count; // our lifetime has expired if( self->delay && ( self->wait * 1000 < level.time ) ) { if( self->r.owner && self->r.owner->use ) G_CallUse( self->r.owner, self, self->activator ); G_FreeEdict( self ); return; } if( self->spawnflags & 0x80000000 ) count = 8; else count = 4; if( self->enemy ) { VectorCopy( self->moveinfo.movedir, last_movedir ); VectorMA( self->enemy->r.absmin, 0.5, self->enemy->r.size, point ); VectorSubtract( point, self->s.origin, self->moveinfo.movedir ); VectorNormalize( self->moveinfo.movedir ); if( !VectorCompare( self->moveinfo.movedir, last_movedir ) ) self->spawnflags |= 0x80000000; } ignore = self; VectorCopy( self->s.origin, start ); VectorMA( start, 2048, self->moveinfo.movedir, end ); VectorClear( tr.endpos ); // shut up compiler while( 1 ) { G_Trace( &tr, start, NULL, NULL, end, ignore, MASK_SHOT ); if( tr.fraction == 1 ) break; // hurt it if we can if( ( game.edicts[tr.ent].takedamage ) && !( game.edicts[tr.ent].flags & FL_IMMUNE_LASER ) ) { if( game.edicts[tr.ent].r.client && self->activator->r.client ) { if( !GS_TeamBasedGametype() || game.edicts[tr.ent].s.team != self->activator->s.team ) G_Damage( &game.edicts[tr.ent], self, self->activator, self->moveinfo.movedir, self->moveinfo.movedir, tr.endpos, self->dmg, 1, 0, 0, self->count ); } else { G_Damage( &game.edicts[tr.ent], self, self->activator, self->moveinfo.movedir, self->moveinfo.movedir, tr.endpos, self->dmg, 1, 0, 0, self->count ); } } // if we hit something that's not a monster or player or is immune to lasers, we're done if( !game.edicts[tr.ent].r.client ) { if( self->spawnflags & 0x80000000 ) { edict_t *event; self->spawnflags &= ~0x80000000; event = G_SpawnEvent( EV_LASER_SPARKS, DirToByte( tr.plane.normal ), tr.endpos ); event->s.eventCount = count; event->s.colorRGBA = self->s.colorRGBA; } break; } ignore = &game.edicts[tr.ent]; VectorCopy( tr.endpos, start ); } VectorCopy( tr.endpos, self->s.origin2 ); G_SetBoundsForSpanEntity( self, 8 ); GClip_LinkEntity( self ); self->nextThink = level.time + 1; }
static void SendMeleeHitEvent( gentity_t *attacker, gentity_t *target, trace_t *tr ) { gentity_t *event; vec3_t normal, origin; float mag, radius; if ( !attacker->client ) { return; } if ( target->health <= 0 ) { return; } if ( tr ) { VectorSubtract( tr->endpos, target->s.origin, normal ); } else { VectorSubtract( attacker->client->ps.origin, target->s.origin, normal ); } // Normalize the horizontal components of the vector difference to the "radius" of the bounding box mag = sqrt( normal[ 0 ] * normal[ 0 ] + normal[ 1 ] * normal[ 1 ] ); radius = target->r.maxs[ 0 ] * 1.21f; if ( mag > radius ) { normal[ 0 ] = normal[ 0 ] / mag * radius; normal[ 1 ] = normal[ 1 ] / mag * radius; } // Clamp origin to be within bounding box vertically if ( normal[ 2 ] > target->r.maxs[ 2 ] ) { normal[ 2 ] = target->r.maxs[ 2 ]; } if ( normal[ 2 ] < target->r.mins[ 2 ] ) { normal[ 2 ] = target->r.mins[ 2 ]; } VectorAdd( target->s.origin, normal, origin ); VectorNegate( normal, normal ); VectorNormalize( normal ); event = G_NewTempEntity( origin, EV_WEAPON_HIT_ENTITY ); // normal event->s.eventParm = DirToByte( normal ); // victim event->s.otherEntityNum = target->s.number; // attacker event->s.otherEntityNum2 = attacker->s.number; // weapon event->s.weapon = attacker->s.weapon; // weapon mode event->s.generic1 = attacker->s.generic1; }
/* ================ G_ExplodeMissile Explode a missile without an impact ================ */ void G_ExplodeMissile( gentity_t *ent ) { vec3_t dir; vec3_t origin; qboolean small = qfalse; qboolean zombiespit = qfalse; int etype; 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; etype = ent->s.eType; ent->s.eType = ET_GENERAL; if ( !Q_stricmp( ent->classname, "props_explosion" ) ) { G_AddEvent( ent, EV_MISSILE_MISS_SMALL, DirToByte( dir ) ); small = qtrue; } // JPW NERVE else if ( !Q_stricmp( ent->classname, "air strike" ) ) { G_AddEvent( ent, EV_MISSILE_MISS_LARGE, DirToByte( dir ) ); small = qfalse; } // jpw else if ( !Q_stricmp( ent->classname, "props_explosion_large" ) ) { G_AddEvent( ent, EV_MISSILE_MISS_LARGE, DirToByte( dir ) ); small = qfalse; } else if ( !Q_stricmp( ent->classname, "zombiespit" ) ) { G_AddEvent( ent, EV_SPIT_MISS, DirToByte( dir ) ); zombiespit = qtrue; } else if ( !Q_stricmp( ent->classname, "flamebarrel" ) ) { ent->freeAfterEvent = qtrue; trap_LinkEntity( ent ); return; } else { G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( dir ) ); } ent->freeAfterEvent = qtrue; // splash damage if ( ent->splashDamage ) { if ( G_RadiusDamage( ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent, ent->splashMethodOfDeath ) ) { //----(SA) if ( g_entities[ent->r.ownerNum].client ) { g_entities[ent->r.ownerNum].client->ps.persistant[PERS_ACCURACY_HITS]++; } } } trap_LinkEntity( ent ); if ( etype == ET_MISSILE ) { // DHM - Nerve :: ... in single player anyway if ( g_gametype.integer == GT_SINGLE_PLAYER ) { if ( ent->s.weapon == WP_VENOM_FULL ) { // no default impact smoke zombiespit = qtrue; } else if ( ent->s.weapon == WP_DYNAMITE || ent->s.weapon == WP_DYNAMITE2 ) { // // shot heard round the world... gentity_t *player; player = AICast_FindEntityForName( "player" ); Concussive_fx( player->r.currentOrigin ); } } // JPW NERVE -- big nasty dynamite scoring section else { if ( g_gametype.integer >= GT_WOLF ) { if ( ent->s.weapon == WP_DYNAMITE ) { // do some scoring // check if dynamite is in trigger_objective_info field vec3_t mins, maxs; //static vec3_t range = { 18, 18, 18 }; // NOTE can use this to massage throw distance outside trigger field // TTimo unused int i,num,touch[MAX_GENTITIES]; gentity_t *hit; // NERVE - SMF - made this the actual bounding box of dynamite instead of range VectorAdd( ent->r.currentOrigin, ent->r.mins, mins ); VectorAdd( ent->r.currentOrigin, ent->r.maxs, maxs ); num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES ); VectorAdd( ent->r.currentOrigin, ent->r.mins, mins ); VectorAdd( ent->r.currentOrigin, ent->r.maxs, maxs ); for ( i = 0 ; i < num ; i++ ) { hit = &g_entities[touch[i]]; if ( !hit->target ) { continue; } if ( !( hit->r.contents & CONTENTS_TRIGGER ) ) { continue; } if ( !strcmp( hit->classname,"trigger_objective_info" ) ) { if ( !( hit->spawnflags & ( AXIS_OBJECTIVE | ALLIED_OBJECTIVE ) ) ) { continue; } if ( ( ( hit->spawnflags & AXIS_OBJECTIVE ) && ( ent->s.teamNum == TEAM_BLUE ) ) || ( ( hit->spawnflags & ALLIED_OBJECTIVE ) && ( ent->s.teamNum == TEAM_RED ) ) ) { G_UseTargets( hit,ent ); hit->think = G_FreeEntity; hit->nextthink = level.time + FRAMETIME; if ( ent->parent->client ) { if ( ent->s.teamNum == ent->parent->client->sess.sessionTeam ) { // make sure player hasn't changed teams -- per atvi req AddScore( ent->parent, hit->accuracy ); // set from map, see g_trigger } } } } } } } // give big weapons the shakey shakey if ( ent->s.weapon == WP_DYNAMITE || ent->s.weapon == WP_PANZERFAUST || ent->s.weapon == WP_GRENADE_LAUNCHER || ent->s.weapon == WP_GRENADE_PINEAPPLE || ent->s.weapon == WP_ROCKET_LAUNCHER || ent->s.weapon == WP_MORTAR || ent->s.weapon == WP_ARTY ) { Ground_Shaker( ent->r.currentOrigin, ent->splashDamage * 4 ); } return; } // jpw } if ( !zombiespit ) { gentity_t *Msmoke; Msmoke = G_Spawn(); VectorCopy( ent->r.currentOrigin, Msmoke->s.origin ); if ( small ) { Msmoke->s.density = 1; } Msmoke->think = M_think; Msmoke->nextthink = level.time + FRAMETIME; if ( ent->parent && !Q_stricmp( ent->parent->classname, "props_flamebarrel" ) ) { Msmoke->health = 10; } else { Msmoke->health = 5; } Concussive_fx( Msmoke->s.origin ); } }
/* * G_EdictsAddSnapEffects * add effects based on accumulated info along the server frame */ static void G_SnapEntities( void ) { edict_t *ent; int i; vec3_t dir, origin; for( i = 0, ent = &game.edicts[0]; i < game.numentities; i++, ent++ ) { if( !ent->r.inuse || ( ent->r.svflags & SVF_NOCLIENT ) ) continue; if( ent->s.type == ET_PARTICLES ) // particles use a special configuration { ent->s.frame = ent->particlesInfo.speed; ent->s.modelindex = ent->particlesInfo.shaderIndex; ent->s.modelindex2 = ent->particlesInfo.spread; ent->s.counterNum = ent->particlesInfo.time; ent->s.weapon = ent->particlesInfo.frequency; ent->s.effects = ent->particlesInfo.size & 0xFF; if( ent->particlesInfo.spherical ) ent->s.effects |= ( 1<<8 ); if( ent->particlesInfo.bounce ) ent->s.effects |= ( 1<<9 ); if( ent->particlesInfo.gravity ) ent->s.effects |= ( 1<<10 ); if( ent->particlesInfo.expandEffect ) ent->s.effects |= ( 1<<11 ); if( ent->particlesInfo.shrinkEffect ) ent->s.effects |= ( 1<<11 ); GClip_LinkEntity( ent ); continue; } if( ent->s.type == ET_PLAYER || ent->s.type == ET_CORPSE ) { // this is pretty hackish. We exploit the fact that 0.5 servers *do not* transmit // origin2/old_origin for ET_PLAYER/ET_CORPSE entities, and we use it for sending the player velocity if( !G_ISGHOSTING( ent ) ) { ent->r.svflags |= SVF_TRANSMITORIGIN2; VectorCopy( ent->velocity, ent->s.origin2 ); } else ent->r.svflags &= ~SVF_TRANSMITORIGIN2; } if( ISEVENTENTITY( ent ) || G_ISGHOSTING( ent ) || !ent->takedamage ) continue; // types which can have accumulated damage effects if( ( ent->s.type == ET_GENERIC || ent->s.type == ET_PLAYER || ent->s.type == ET_CORPSE ) ) // doors don't bleed { // Until we get a proper damage saved effect, we accumulate both into the blood fx // so, at least, we don't send 2 entities where we can send one ent->snap.damage_taken += ent->snap.damage_saved; //ent->snap.damage_saved = 0; //spawn accumulated damage if( ent->snap.damage_taken && !( ent->flags & FL_GODMODE ) && HEALTH_TO_INT( ent->health ) > 0 ) { edict_t *event; float damage = ent->snap.damage_taken; if( damage > 120 ) damage = 120; VectorCopy( ent->snap.damage_dir, dir ); VectorNormalize( dir ); VectorAdd( ent->s.origin, ent->snap.damage_at, origin ); if( ent->s.type == ET_PLAYER || ent->s.type == ET_CORPSE ) { event = G_SpawnEvent( EV_BLOOD, DirToByte( dir ), origin ); event->s.damage = HEALTH_TO_INT( damage ); event->s.ownerNum = i; // set owner // ET_PLAYERS can also spawn sound events if( ent->s.type == ET_PLAYER ) { // play an apropriate pain sound if( level.time >= ent->pain_debounce_time ) { // see if it should pain for a FALL or for damage received if( ent->snap.damage_fall ) { ent->pain_debounce_time = level.time + 200; } else if( !G_IsDead( ent ) ) { if( ent->r.client->ps.inventory[POWERUP_SHELL] > 0 ) G_AddEvent( ent, EV_PAIN, PAIN_WARSHELL, true ); else if( ent->health <= 20 ) G_AddEvent( ent, EV_PAIN, PAIN_20, true ); else if( ent->health <= 35 ) G_AddEvent( ent, EV_PAIN, PAIN_30, true ); else if( ent->health <= 60 ) G_AddEvent( ent, EV_PAIN, PAIN_60, true ); else G_AddEvent( ent, EV_PAIN, PAIN_100, true ); ent->pain_debounce_time = level.time + 400; } } } } else { event = G_SpawnEvent( EV_SPARKS, DirToByte( dir ), origin ); event->s.damage = HEALTH_TO_INT( damage ); } } } } }
void G_MissileImpact( gentity_t *ent, trace_t *trace ) { gentity_t *other; qboolean hitClient = qfalse; other = &g_entities[trace->entityNum]; // check for bounce if ( !other->takedamage && (ent->bounceCount > 0 || ent->bounceCount == -5) && ( ent->flags & ( FL_BOUNCE | FL_BOUNCE_HALF ) ) ) { G_BounceMissile( ent, trace ); // G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } if ( other->s.eType == ET_MISSILE && ent->s.weapon == WP_QUANTIZER && other->s.weapon == WP_MORTAR ) {//Quantizer can deflect mortar shots :3 G_DeflectMissile( ent, other, &ent->s.pos.trDelta ); other->r.ownerNum = ent->r.ownerNum; G_FreeEntity( ent ); return; } // impact damage if (other->takedamage) { // FIXME: wrong damage direction? if ( ent->damage ) { vector3 velocity; if( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; hitClient = qtrue; } BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, &velocity ); if ( VectorLength( &velocity ) == 0 ) { velocity.z = 1; // stepped on a grenade } //Raz: The inflictor is the missile, for direct hits from quantizer/RLauncher/etc G_Damage( other, ent, &g_entities[ent->r.ownerNum], ent, NULL/*velocity*/, &ent->s.origin, ent->damage, 0, ent->methodOfDeath ); } } // 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; VectorSnapTowards( &trace->endpos, &ent->s.pos.trBase ); // save net bandwidth G_SetOrigin( ent, &trace->endpos ); // splash damage (doesn't apply to person directly hit) #if 0 if ( ent->splashDamage ) { if( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, other, ent->splashMethodOfDeath ) ) { if( !hitClient ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; } } } #else //QtZ if ( (ent->splashDamage || ent->splashRadius) && G_RadiusDamage( &trace->endpos, ent->parent, (float)ent->splashDamage, (float)ent->splashRadius, other, ent, ent->splashMethodOfDeath ) && !hitClient && g_entities[ent->r.ownerNum].client ) g_entities[ent->r.ownerNum].client->accuracy_hits++; #endif trap->SV_LinkEntity( (sharedEntity_t *)ent ); }
void G_MissileImpacted( gentity_t *ent, gentity_t *other, vec3_t impactPos, vec3_t normal, int hitLoc=HL_NONE ) { // impact damage if ( other->takedamage ) { // FIXME: wrong damage direction? if ( ent->damage ) { vec3_t velocity; EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); if ( VectorLength( velocity ) == 0 ) { velocity[2] = 1; // stepped on a grenade } int damage = ent->damage; if( other->client ) { class_t npc_class = other->client->NPC_class; // If we are a robot and we aren't currently doing the full body electricity... if ( npc_class == CLASS_SEEKER || npc_class == CLASS_PROBE || npc_class == CLASS_MOUSE || npc_class == CLASS_GONK || npc_class == CLASS_R2D2 || npc_class == CLASS_R5D2 || npc_class == CLASS_REMOTE || npc_class == CLASS_MARK1 || npc_class == CLASS_MARK2 || //npc_class == CLASS_PROTOCOL ||//no protocol, looks odd npc_class == CLASS_INTERROGATOR || npc_class == CLASS_ATST || npc_class == CLASS_SENTRY ) { // special droid only behaviors if ( other->client->ps.powerups[PW_SHOCKED] < level.time + 100 ) { // ... do the effect for a split second for some more feedback other->s.powerups |= ( 1 << PW_SHOCKED ); other->client->ps.powerups[PW_SHOCKED] = level.time + 450; } //FIXME: throw some sparks off droids,too } } G_Damage( other, ent, ent->owner, velocity, impactPos, damage, ent->dflags, ent->methodOfDeath, hitLoc); if ( ent->s.weapon == WP_DEMP2 ) {//a hit with demp2 decloaks saboteurs if ( other && other->client && other->client->NPC_class == CLASS_SABOTEUR ) {//FIXME: make this disabled cloak hold for some amount of time? Saboteur_Decloak( other, Q_irand( 3000, 10000 ) ); if ( ent->methodOfDeath == MOD_DEMP2_ALT ) {//direct hit with alt disabled cloak forever if ( other->NPC ) {//permanently disable the saboteur's cloak other->NPC->aiFlags &= ~NPCAI_SHIELDS; } } } } } } // is it cheaper in bandwidth to just remove this ent and create a new // one, rather than changing the missile into the explosion? //G_FreeEntity(ent); if ( (other->takedamage && other->client ) || (ent->s.weapon == WP_FLECHETTE && other->contents&CONTENTS_LIGHTSABER) ) { G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( normal ) ); ent->s.otherEntityNum = other->s.number; } else { G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( normal ) ); ent->s.otherEntityNum = other->s.number; } VectorCopy( normal, ent->pos1 ); if ( ent->owner )//&& ent->owner->s.number == 0 ) { //Add the event AddSoundEvent( ent->owner, ent->currentOrigin, 256, AEL_SUSPICIOUS, qfalse, qtrue ); AddSightEvent( ent->owner, ent->currentOrigin, 512, AEL_DISCOVERED, 75 ); } 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 VectorCopy( impactPos, ent->s.pos.trBase ); G_SetOrigin( ent, impactPos ); // splash damage (doesn't apply to person directly hit) if ( ent->splashDamage ) { G_RadiusDamage( impactPos, ent->owner, ent->splashDamage, ent->splashRadius, other, ent->splashMethodOfDeath ); } if ( ent->s.weapon == WP_NOGHRI_STICK ) { G_SpawnNoghriGasCloud( ent ); } gi.linkentity( ent ); }
/* =============== CheckGrabAttack =============== */ void CheckGrabAttack( gentity_t *ent ) { trace_t tr; vec3_t end, dir; gentity_t *traceEnt; // set aiming directions AngleVectors( ent->client->ps.viewangles, forward, right, up ); CalcMuzzlePoint( ent, forward, right, up, muzzle ); if ( ent->client->ps.weapon == WP_ALEVEL1 ) { VectorMA( muzzle, LEVEL1_GRAB_RANGE, forward, end ); } else if ( ent->client->ps.weapon == WP_ALEVEL1_UPG ) { VectorMA( muzzle, LEVEL1_GRAB_U_RANGE, forward, end ); } trap_Trace( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT ); if ( tr.surfaceFlags & SURF_NOIMPACT ) { return; } traceEnt = &g_entities[ tr.entityNum ]; if ( !traceEnt->takedamage ) { return; } if ( traceEnt->client ) { if ( traceEnt->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) { return; } if ( traceEnt->client->ps.stats[ STAT_HEALTH ] <= 0 ) { return; } if ( !( traceEnt->client->ps.stats[ STAT_STATE ] & SS_GRABBED ) ) { AngleVectors( traceEnt->client->ps.viewangles, dir, NULL, NULL ); traceEnt->client->ps.stats[ STAT_VIEWLOCK ] = DirToByte( dir ); //event for client side grab effect G_AddPredictableEvent( ent, EV_LEV1_GRAB, 0 ); } traceEnt->client->ps.stats[ STAT_STATE ] |= SS_GRABBED; if ( ent->client->ps.weapon == WP_ALEVEL1 ) { traceEnt->client->grabExpiryTime = level.time + LEVEL1_GRAB_TIME; // Update the last combat time. ent->client->lastCombatTime = level.time + LEVEL1_GRAB_TIME; traceEnt->client->lastCombatTime = level.time + LEVEL1_GRAB_TIME; } else if ( ent->client->ps.weapon == WP_ALEVEL1_UPG ) { traceEnt->client->grabExpiryTime = level.time + LEVEL1_GRAB_U_TIME; // Update the last combat time. ent->client->lastCombatTime = level.time + LEVEL1_GRAB_TIME; traceEnt->client->lastCombatTime = level.time + LEVEL1_GRAB_TIME; } } }
/* =========== ClientSpawn Called every time a client is placed fresh in the world: after the first ClientBegin, and after each respawn Initializes all non-persistant parts of playerState ============ */ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles ) { int index; vec3_t spawn_origin, spawn_angles; gclient_t *client; int i; clientPersistant_t saved; clientSession_t savedSess; int persistant[ MAX_PERSISTANT ]; gentity_t *spawnPoint = NULL; int flags; int savedPing; int teamLocal; int eventSequence; char userinfo[ MAX_INFO_STRING ]; vec3_t up = { 0.0f, 0.0f, 1.0f }; int maxAmmo, maxClips; weapon_t weapon; index = ent - g_entities; client = ent->client; teamLocal = client->pers.teamSelection; // only start client if chosen a class and joined a team if( client->pers.classSelection == PCL_NONE && teamLocal == TEAM_NONE ) client->sess.spectatorState = SPECTATOR_FREE; else if( client->pers.classSelection == PCL_NONE ) client->sess.spectatorState = SPECTATOR_LOCKED; if( origin != NULL ) VectorCopy( origin, spawn_origin ); if( angles != NULL ) VectorCopy( angles, spawn_angles ); // find a spawn point // do it before setting health back up, so farthest // ranging doesn't count this client if( client->sess.spectatorState != SPECTATOR_NOT ) { if( teamLocal == TEAM_NONE ) spawnPoint = SelectSpectatorSpawnPoint( spawn_origin, spawn_angles ); else if( teamLocal == TEAM_ALIENS ) spawnPoint = SelectAlienLockSpawnPoint( spawn_origin, spawn_angles ); else if( teamLocal == TEAM_HUMANS ) spawnPoint = SelectHumanLockSpawnPoint( spawn_origin, spawn_angles ); } else { if( spawn == NULL ) { G_Error( "ClientSpawn: spawn is NULL\n" ); return; } spawnPoint = spawn; if( ent != spawn ) { //start spawn animation on spawnPoint G_SetBuildableAnim( spawnPoint, BANIM_SPAWN1, qtrue ); if( spawnPoint->buildableTeam == TEAM_ALIENS ) spawnPoint->clientSpawnTime = ALIEN_SPAWN_REPEAT_TIME; else if( spawnPoint->buildableTeam == TEAM_HUMANS ) spawnPoint->clientSpawnTime = HUMAN_SPAWN_REPEAT_TIME; } } // toggle the teleport bit so the client knows to not lerp flags = ent->client->ps.eFlags & ( EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED ); flags ^= EF_TELEPORT_BIT; G_UnlaggedClear( ent ); // clear everything but the persistant data saved = client->pers; savedSess = client->sess; savedPing = client->ps.ping; for( i = 0; i < MAX_PERSISTANT; i++ ) persistant[ i ] = client->ps.persistant[ i ]; eventSequence = client->ps.eventSequence; memset( client, 0, sizeof( *client ) ); client->pers = saved; client->sess = savedSess; client->ps.ping = savedPing; client->lastkilled_client = -1; for( i = 0; i < MAX_PERSISTANT; i++ ) client->ps.persistant[ i ] = persistant[ i ]; client->ps.eventSequence = eventSequence; // increment the spawncount so the client will detect the respawn client->ps.persistant[ PERS_SPAWN_COUNT ]++; client->ps.persistant[ PERS_SPECSTATE ] = client->sess.spectatorState; client->airOutTime = level.time + 12000; trap_GetUserinfo( index, userinfo, sizeof( userinfo ) ); client->ps.eFlags = flags; //Com_Printf( "ent->client->pers->pclass = %i\n", ent->client->pers.classSelection ); ent->s.groundEntityNum = ENTITYNUM_NONE; ent->client = &level.clients[ index ]; ent->takedamage = qtrue; ent->inuse = qtrue; ent->classname = "player"; ent->r.contents = CONTENTS_BODY; ent->clipmask = MASK_PLAYERSOLID; ent->die = player_die; ent->waterlevel = 0; ent->watertype = 0; ent->flags = 0; // calculate each client's acceleration ent->evaluateAcceleration = qtrue; client->ps.stats[ STAT_WEAPONS ] = 0; client->ps.stats[ STAT_WEAPONS2 ] = 0; client->ps.stats[ STAT_SLOTS ] = 0; client->ps.eFlags = flags; client->ps.clientNum = index; BG_ClassBoundingBox( ent->client->pers.classSelection, ent->r.mins, ent->r.maxs, NULL, NULL, NULL ); if( client->sess.spectatorState == SPECTATOR_NOT ) client->pers.maxHealth = client->ps.stats[ STAT_MAX_HEALTH ] = BG_Class( ent->client->pers.classSelection )->health; else client->pers.maxHealth = client->ps.stats[ STAT_MAX_HEALTH ] = 100; // clear entity values if( ent->client->pers.classSelection == PCL_HUMAN ) { BG_AddWeaponToInventory( WP_BLASTER, client->ps.stats ); BG_AddUpgradeToInventory( UP_MEDKIT, client->ps.stats ); weapon = client->pers.humanItemSelection; } else if( client->sess.spectatorState == SPECTATOR_NOT ) weapon = BG_Class( ent->client->pers.classSelection )->startWeapon; else weapon = WP_NONE; maxAmmo = BG_Weapon( weapon )->maxAmmo; maxClips = BG_Weapon( weapon )->maxClips; BG_AddWeaponToInventory( weapon, client->ps.stats ); client->ps.ammo = maxAmmo; client->ps.clips = maxClips; ent->client->ps.stats[ STAT_CLASS ] = ent->client->pers.classSelection; ent->client->ps.stats[ STAT_TEAM ] = ent->client->pers.teamSelection; ent->client->ps.stats[ STAT_BUILDABLE ] = BA_NONE; ent->client->ps.stats[ STAT_STATE ] = 0; VectorSet( ent->client->ps.grapplePoint, 0.0f, 0.0f, 1.0f ); // health will count down towards max_health ent->health = client->ps.stats[ STAT_HEALTH ] = client->ps.stats[ STAT_MAX_HEALTH ]; //* 1.25; //if evolving scale health if( ent == spawn ) { ent->health *= ent->client->pers.evolveHealthFraction; client->ps.stats[ STAT_HEALTH ] *= ent->client->pers.evolveHealthFraction; } //clear the credits array for( i = 0; i < MAX_CLIENTS; i++ ) ent->credits[ i ] = 0; client->ps.stats[ STAT_STAMINA ] = MAX_STAMINA; G_SetOrigin( ent, spawn_origin ); VectorCopy( spawn_origin, client->ps.origin ); #define UP_VEL 150.0f #define F_VEL 50.0f //give aliens some spawn velocity if( client->sess.spectatorState == SPECTATOR_NOT && client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) { if( ent == spawn ) { //evolution particle system G_AddPredictableEvent( ent, EV_ALIEN_EVOLVE, DirToByte( up ) ); } else { spawn_angles[ YAW ] += 180.0f; AngleNormalize360( spawn_angles[ YAW ] ); if( spawnPoint->s.origin2[ 2 ] > 0.0f ) { vec3_t forward, dir; AngleVectors( spawn_angles, forward, NULL, NULL ); VectorScale( forward, F_VEL, forward ); VectorAdd( spawnPoint->s.origin2, forward, dir ); VectorNormalize( dir ); VectorScale( dir, UP_VEL, client->ps.velocity ); } G_AddPredictableEvent( ent, EV_PLAYER_RESPAWN, 0 ); } } else if( client->sess.spectatorState == SPECTATOR_NOT && client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) { spawn_angles[ YAW ] += 180.0f; AngleNormalize360( spawn_angles[ YAW ] ); } // the respawned flag will be cleared after the attack and jump keys come up client->ps.pm_flags |= PMF_RESPAWNED; trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd ); SetClientViewAngle( ent, spawn_angles ); if( !( client->sess.spectatorState != SPECTATOR_NOT ) ) { trap_LinkEntity( ent ); // force the base weapon up client->ps.weapon = WP_NONE; client->ps.weaponstate = WEAPON_READY; } // don't allow full run speed for a bit client->ps.pm_flags |= PMF_TIME_KNOCKBACK; client->ps.pm_time = 100; client->respawnTime = level.time; client->lastKillTime = level.time; client->inactivityTime = level.time + g_inactivity.integer * 1000; client->latched_buttons = 0; // set default animations client->ps.torsoAnim = TORSO_STAND; client->ps.legsAnim = LEGS_IDLE; if( level.intermissiontime ) MoveClientToIntermission( ent ); else { // fire the targets of the spawn point if( !spawn ) G_UseTargets( spawnPoint, ent ); // select the highest weapon number available, after any // spawn given items have fired client->ps.weapon = 1; for( i = WP_NUM_WEAPONS - 1; i > 0 ; i-- ) { if( BG_InventoryContainsWeapon( i, client->ps.stats ) ) { client->ps.weapon = i; break; } } } // run a client frame to drop exactly to the floor, // initialize animations and other things client->ps.commandTime = level.time - 100; ent->client->pers.cmd.serverTime = level.time; ClientThink( ent-g_entities ); // positively link the client, even if the command times are weird if( client->sess.spectatorState == SPECTATOR_NOT ) { BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue ); VectorCopy( ent->client->ps.origin, ent->r.currentOrigin ); trap_LinkEntity( ent ); } // must do this here so the number of active clients is calculated CalculateRanks( ); // run the presend to set anything else ClientEndFrame( ent ); // clear entity state values BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue ); }
void WP_GrenadeBlow(gentity_t*self) { if(Q_stricmp(self->classname,"emp_grenade")==0 ||Q_stricmp(self->classname,"cryoban_grenade")==0 ||Q_stricmp(self->classname,"flash_grenade")==0) { vec3_t dir={0,0,1}; int entitys[1024]; vec3_t mins,maxs,v; int num=0,i=0,dist=0,mpDamage=7; int e=0; gentity_t*ent; for ( i = 0 ; i < 3 ; i++ ) { mins[i] = self->r.currentOrigin[i] - TD_SPLASH_RAD / 2; maxs[i] = self->r.currentOrigin[i] + TD_SPLASH_RAD / 2; } num = trap_EntitiesInBox(mins,maxs,entitys,MAX_GENTITIES); for ( i = 0 ; i < num ; i++ ) { ent = &g_entities[entitys[ i ]]; if (ent == self) continue; if (!ent->takedamage) continue; if(!ent->inuse || !ent->client) continue; // find the distance from the edge of the bounding box for ( e = 0 ; e < 3 ; e++ ) { if ( self->r.currentOrigin[e] < ent->r.absmin[e] ) { v[e] = ent->r.absmin[e] - self->r.currentOrigin[e]; } else if ( self->r.currentOrigin[e] > ent->r.absmax[e] ) { v[e] = self->r.currentOrigin[e] - ent->r.absmax[e]; } else { v[e] = 0; } } dist = VectorLength( v ); if ( dist >= TD_SPLASH_RAD ) { continue; } if(Q_stricmp(self->classname,"cryoban_grenade") == 0) G_AddEvent(ent, EV_CRYOBAN, DirToByte(dir)); if(Q_stricmp(self->classname,"emp_grenade")==0) { int shield = Q_irand(25,50); int ammo = Q_irand(15,30); G_DodgeDrain(ent, self, 15);//15 for nau ent->client->ps.saberAttackChainCount += mpDamage; if(ent->client->ps.stats[STAT_ARMOR]-shield >= 0) ent->client->ps.stats[STAT_ARMOR]-=shield; else ent->client->ps.stats[STAT_ARMOR]=0; if(ent->client->ps.ammo[weaponData[ent->client->ps.weapon].ammoIndex]-ammo >= 0) ent->client->ps.ammo[weaponData[ent->client->ps.weapon].ammoIndex]-=ammo; else ent->client->ps.ammo[weaponData[ent->client->ps.weapon].ammoIndex]=0; if(Q_stricmp(ent->classname,"item_seeker")==0||Q_stricmp(ent->classname,"item_sentry_gun")==0) { G_Damage( ent, ent, ent, NULL, NULL, 999, 0, MOD_UNKNOWN ); } ent->client->ps.electrifyTime = level.time + Q_irand( 300, 800 ); } else if(Q_stricmp(self->classname,"cryoban_grenade")==0) { ent->client->frozenTime = level.time+FROZEN_TIME; ent->client->ps.userInt3 |= (1 << FLAG_FROZEN); ent->client->ps.userInt1 |= LOCK_UP; ent->client->ps.userInt1 |= LOCK_DOWN; ent->client->ps.userInt1 |= LOCK_RIGHT; ent->client->ps.userInt1 |= LOCK_LEFT; ent->client->viewLockTime = level.time+FROZEN_TIME; ent->client->ps.legsTimer = ent->client->ps.torsoTimer=level.time+FROZEN_TIME; G_SetAnim(ent, NULL, SETANIM_BOTH, WeaponReadyAnim[ent->client->ps.weapon], SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 100); } else if(Q_stricmp(self->classname,"flash_grenade")==0) G_AddEvent( ent, EV_FLASHGRENADE, DirToByte( dir ) ); } self->s.eType = ET_GENERAL; if(Q_stricmp(self->classname,"emp_grenade") == 0) G_AddEvent( self, EV_EMPGRENADE, DirToByte( dir ) ); else if(Q_stricmp(self->classname,"cryoban_grenade") == 0) G_AddEvent( self, EV_CRYOBAN_EXPLODE, DirToByte( dir ) ); self->freeAfterEvent = qtrue; } else if(Q_stricmp(self->classname,"smoke_grenade")==0) { vec3_t dir={0,0,1}; G_AddEvent( self, EV_SMOKEGRENADE, DirToByte( dir ) ); self->freeAfterEvent = qtrue; } else { self->think = G_FreeEntity; self->nextthink = level.time; } }
/* ================ G_MissileImpact ================ */ void G_MissileImpact(gentity_t * ent, trace_t * trace) { gentity_t *other; qboolean hitClient = qfalse; #ifdef MISSIONPACK vec3_t forward, impactpoint, bouncedir; int eFlags; #endif other = &g_entities[trace->entityNum]; // check for bounce if(!other->takedamage && (ent->s.eFlags & (EF_BOUNCE | EF_BOUNCE_HALF))) { G_BounceMissile(ent, trace); G_AddEvent(ent, EV_GRENADE_BOUNCE, 0); return; } if(ent->s.weapon == WP_FLAK_CANNON && ent->s.eType == ET_PROJECTILE2) { G_ExplodeIntoNails(ent); return; } #ifdef MISSIONPACK if(other->takedamage) { if(ent->s.weapon != WP_PROX_LAUNCHER) { if(other->client && other->client->invulnerabilityTime > level.time) { // VectorCopy(ent->s.pos.trDelta, forward); VectorNormalize(forward); if(G_InvulnerabilityEffect(other, forward, ent->s.pos.trBase, impactpoint, bouncedir)) { VectorCopy(bouncedir, trace->plane.normal); eFlags = ent->s.eFlags & EF_BOUNCE_HALF; ent->s.eFlags &= ~EF_BOUNCE_HALF; G_BounceMissile(ent, trace); ent->s.eFlags |= eFlags; } ent->target_ent = other; return; } } } #endif // impact damage if(other->takedamage) { // FIXME: wrong damage direction? if(ent->damage) { vec3_t velocity; if(LogAccuracyHit(other, &g_entities[ent->r.ownerNum])) { g_entities[ent->r.ownerNum].client->accuracy_hits++; hitClient = qtrue; } BG_EvaluateTrajectoryDelta(&ent->s.pos, level.time, velocity); if(VectorLength(velocity) == 0) { velocity[2] = 1; // stepped on a grenade } G_Damage(other, ent, &g_entities[ent->r.ownerNum], velocity, ent->s.origin, ent->damage, 0, ent->methodOfDeath); } } #ifdef MISSIONPACK if(ent->s.weapon == WP_PROX_LAUNCHER) { if(ent->s.pos.trType != TR_GRAVITY) { return; } // if it's a player, stick it on to them (flag them and remove this entity) if(other->s.eType == ET_PLAYER && other->health > 0) { ProximityMine_Player(ent, other); return; } SnapVectorTowards(trace->endpos, ent->s.pos.trBase); G_SetOrigin(ent, trace->endpos); ent->s.pos.trType = TR_STATIONARY; VectorClear(ent->s.pos.trDelta); G_AddEvent(ent, EV_PROXIMITY_MINE_STICK, trace->surfaceFlags); ent->think = ProximityMine_Activate; ent->nextthink = level.time + 2000; VectorToAngles(trace->plane.normal, ent->s.angles); ent->s.angles[0] += 90; // link the prox mine to the other entity ent->enemy = other; ent->die = ProximityMine_Die; VectorCopy(trace->plane.normal, ent->movedir); VectorSet(ent->r.mins, -4, -4, -4); VectorSet(ent->r.maxs, 4, 4, 4); trap_LinkEntity(ent); return; } #endif if(!strcmp(ent->classname, "hook")) { gentity_t *nent; vec3_t v; nent = G_Spawn(); if(other->takedamage && other->client) { G_AddEvent(nent, EV_PROJECTILE_HIT, DirToByte(trace->plane.normal)); nent->s.otherEntityNum = other->s.number; ent->enemy = other; v[0] = other->r.currentOrigin[0] + (other->r.mins[0] + other->r.maxs[0]) * 0.5; v[1] = other->r.currentOrigin[1] + (other->r.mins[1] + other->r.maxs[1]) * 0.5; v[2] = other->r.currentOrigin[2] + (other->r.mins[2] + other->r.maxs[2]) * 0.5; SnapVectorTowards(v, ent->s.pos.trBase); // save net bandwidth } else { VectorCopy(trace->endpos, v); G_AddEvent(nent, EV_PROJECTILE_MISS, DirToByte(trace->plane.normal)); ent->enemy = NULL; } SnapVectorTowards(v, ent->s.pos.trBase); // save net bandwidth nent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact nent->s.eType = ET_GENERAL; ent->s.eType = ET_GRAPPLE; G_SetOrigin(ent, v); G_SetOrigin(nent, v); ent->think = Weapon_HookThink; ent->nextthink = level.time + FRAMETIME; ent->parent->client->ps.pm_flags |= PMF_GRAPPLE_PULL; VectorCopy(ent->r.currentOrigin, ent->parent->client->ps.grapplePoint); trap_LinkEntity(ent); trap_LinkEntity(nent); 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_PROJECTILE_HIT, DirToByte(trace->plane.normal)); ent->s.otherEntityNum = other->s.number; } else if(trace->surfaceFlags & SURF_METALSTEPS) { G_AddEvent(ent, EV_PROJECTILE_MISS_METAL, DirToByte(trace->plane.normal)); } else { G_AddEvent(ent, EV_PROJECTILE_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) { if(G_RadiusDamage(trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, other, ent->splashMethodOfDeath)) { if(!hitClient) { g_entities[ent->r.ownerNum].client->accuracy_hits++; } } } trap_LinkEntity(ent); }
/* ================ G_MissileImpact ================ */ void G_MissileImpact( gentity_t *ent, trace_t *trace ) { gentity_t *other; qboolean hitPlayer = qfalse; #if defined MISSIONPACK && !defined TURTLEARENA // POWERS vec3_t forward, impactpoint, bouncedir; int eFlags; #endif #ifdef TA_WEAPSYS qboolean damagedOther = qfalse; #endif other = &g_entities[trace->entityNum]; #if defined MISSIONPACK && !defined TURTLEARENA // POWERS if ( other->takedamage ) { #ifdef TA_WEAPSYS if ( !bg_projectileinfo[ent->s.weapon].stickOnImpact ) #else if ( ent->s.weapon != WP_PROX_LAUNCHER ) #endif { if ( other->player && other->player->invulnerabilityTime > level.time ) { // VectorCopy( ent->s.pos.trDelta, forward ); VectorNormalize( forward ); if (G_InvulnerabilityEffect( other, forward, ent->s.pos.trBase, impactpoint, bouncedir )) { VectorCopy( bouncedir, trace->plane.normal ); eFlags = ent->s.eFlags & EF_BOUNCE_HALF; ent->s.eFlags &= ~EF_BOUNCE_HALF; G_BounceMissile( ent, trace ); ent->s.eFlags |= eFlags; } ent->target_ent = other; return; } } } #endif // impact damage if (other->takedamage #ifdef TA_WEAPSYS // stickOnImpact only damages once && !(ent->count & 2) #endif ) { // FIXME: wrong damage direction? if ( ent->damage ) { vec3_t velocity; if( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) { g_entities[ent->r.ownerNum].player->accuracy_hits++; hitPlayer = qtrue; } BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); if ( VectorLength( velocity ) == 0 ) { #ifdef IOQ3ZTM VectorCopy(trace->plane.normal, velocity); #else velocity[2] = 1; // stepped on a grenade #endif } #ifdef TA_WEAPSYS damagedOther = G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, ent->s.origin, ent->damage, 0, ent->methodOfDeath); #else G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, ent->s.origin, ent->damage, 0, ent->methodOfDeath); #endif } } // check for bounce if ( #ifdef TA_WEAPSYS !damagedOther && #else !other->takedamage && #endif ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) { G_BounceMissile( ent, trace ); #ifdef TA_WEAPSYS // Bounce missiles // Die on Nth bounce if (ent->s.modelindex2 > 0) { ent->s.modelindex2--; if (ent->s.modelindex2 == 0) { // Kill missile G_ExplodeMissile( ent ); return; } } G_AddEvent( ent, EV_PROJECTILE_BOUNCE, DirToByte( trace->plane.normal ) ); ent->s.time2 = trace->surfaceFlags; // surface #else G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); #endif return; } #ifdef TA_WEAPSYS if (bg_projectileinfo[ent->s.weapon].stickOnImpact != PSOI_NONE) { vec3_t dir; #ifndef TURTLEARENA // if it's a player, stick it on to them (flag them and remove this entity) if( bg_projectileinfo[ent->s.weapon].explosionType == PE_PROX && other->s.eType == ET_PLAYER && other->health > 0 ) { ProximityMine_Player( ent, other ); return; } #endif if (ent->count & 2) { // Already stuck to wall return; } ent->count |= 2; // Don't stick to players or obelisk if (other->s.eType == ET_PLAYER #ifdef MISSIONPACK || (other->pain == ObeliskPain) #endif ) { goto missileExplode; } // Don't stick to the entity that this missile just killed or other missiles if ((damagedOther && other->health <= 0) || other->s.eType == ET_MISSILE) { // Don't remove projectile if it doesn't explode. if (bg_projectileinfo[ent->s.weapon].explosionType == PE_NONE) { ent->s.pos.trType = TR_GRAVITY; ent->count &= ~2; return; } else { goto missileExplode; } } if (bg_projectileinfo[ent->s.weapon].shootable) VectorMA(trace->endpos, -8, trace->plane.normal, trace->endpos); SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); G_SetOrigin( ent, trace->endpos ); if (bg_projectileinfo[ent->s.weapon].stickOnImpact == PSOI_KEEP_ANGLES) { #if 0 // convert direction of travel into axis if ( VectorNormalize2( ent->s.pos.trDelta, dir ) == 0 ) { dir[2] = 1; } // Set the angles vectoangles( dir, ent->s.angles ); #else VectorCopy(trace->plane.normal, dir); #endif } else { VectorCopy(trace->plane.normal, dir); vectoangles( dir, ent->s.angles ); switch (bg_projectileinfo[ent->s.weapon].stickOnImpact) { case PSOI_ANGLE_270: ent->s.angles[0] += 270; break; case PSOI_ANGLE_180: ent->s.angles[0] += 180; break; case PSOI_ANGLE_90: // Maybe this is good for prox mines, but doesn't look good on my // rocket or shuirkens... ent->s.angles[0] += 90; break; case PSOI_ANGLE_0: break; } } // Save direction VectorCopy(dir, ent->s.angles2); ent->s.pos.trType = TR_STATIONARY; VectorClear( ent->s.pos.trDelta ); G_AddEvent( ent, EV_PROJECTILE_STICK, DirToByte(trace->plane.normal) ); ent->s.time2 = trace->surfaceFlags; // surface if (bg_projectileinfo[ent->s.weapon].explosionType == PE_PROX) { // When a BREAKABLE ET_MOVER is killed it drops the projectiles stuck to it, // so don't setup the prox mine when it impact a surface if it already hit been setup. if (ent->die != ProximityMine_Die) { ent->think = ProximityMine_Activate; ent->nextthink = level.time + 2000; ent->die = ProximityMine_Die; } } else { ent->die = G_Missile_Die; } // link the prox mine to the other entity ent->enemy = other; VectorCopy(dir, ent->movedir); VectorSet(ent->s.mins, -4, -4, -4); VectorSet(ent->s.maxs, 4, 4, 4); trap_LinkEntity(ent); return; } #elif defined MISSIONPACK if( ent->s.weapon == WP_PROX_LAUNCHER ) { if( ent->s.pos.trType != TR_GRAVITY ) { return; } // if it's a player, stick it on to them (flag them and remove this entity) if( other->s.eType == ET_PLAYER && other->health > 0 ) { ProximityMine_Player( ent, other ); return; } SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); G_SetOrigin( ent, trace->endpos ); ent->s.pos.trType = TR_STATIONARY; VectorClear( ent->s.pos.trDelta ); G_AddEvent( ent, EV_PROXIMITY_MINE_STICK, trace->surfaceFlags ); ent->think = ProximityMine_Activate; ent->nextthink = level.time + 2000; vectoangles( trace->plane.normal, ent->s.angles ); ent->s.angles[0] += 90; // link the prox mine to the other entity ent->enemy = other; ent->die = ProximityMine_Die; VectorCopy(trace->plane.normal, ent->movedir); VectorSet(ent->s.mins, -4, -4, -4); VectorSet(ent->s.maxs, 4, 4, 4); trap_LinkEntity(ent); return; } #endif #ifdef TA_WEAPSYS if (bg_projectileinfo[ent->s.weapon].grappling) #else if (!strcmp(ent->classname, "hook")) #endif { gentity_t *nent; vec3_t v; nent = G_Spawn(); if ( other->takedamage && other->player ) { G_AddEvent( nent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); nent->s.otherEntityNum = other->s.number; v[0] = other->r.currentOrigin[0] + (other->s.mins[0] + other->s.maxs[0]) * 0.5; v[1] = other->r.currentOrigin[1] + (other->s.mins[1] + other->s.maxs[1]) * 0.5; v[2] = other->r.currentOrigin[2] + (other->s.mins[2] + other->s.maxs[2]) * 0.5; } else { VectorCopy(trace->endpos, v); G_AddEvent( nent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); } #ifdef IOQ3ZTM // IOQ3BUGFIX: Fix grapple wallmark/death-effect/debris (only tested with TA_WEAPSYS...) nent->s.weapon = ent->s.weapon; #endif #ifdef TA_WEAPSYS if (ent->parent) nent->s.playerNum = ent->parent->s.number; else nent->s.playerNum = ENTITYNUM_NONE; #endif nent->s.weapon = ent->s.weapon; ent->enemy = other; ent->s.groundEntityNum = other->s.number; SnapVectorTowards( v, ent->s.pos.trBase ); // save net bandwidth nent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact nent->s.eType = ET_GENERAL; ent->s.eType = ET_GRAPPLE; G_SetOrigin( ent, v ); G_SetOrigin( nent, v ); ent->think = Weapon_HookThink; ent->nextthink = level.time + FRAMETIME; ent->parent->player->ps.pm_flags |= PMF_GRAPPLE_PULL; VectorCopy( ent->r.currentOrigin, ent->parent->player->ps.grapplePoint); trap_LinkEntity( ent ); trap_LinkEntity( nent ); #ifdef TA_WEAPSYS // Don't grapple to the entity if you just killed it. if (damagedOther && other->health <= 0) { Weapon_HookFree(ent); } #endif return; } #ifdef TA_WEAPSYS missileExplode: #endif // 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->player ) { 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 ) ); } #ifdef TA_WEAPSYS if (ent->parent) ent->s.playerNum = ent->parent->s.number; else ent->s.playerNum = ENTITYNUM_NONE; #endif ent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact #ifndef TA_WEAPSYS // Must be after G_RadiusDamage ent->s.eType = ET_GENERAL; #endif 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 ) { #ifdef TA_WEAPSYS if( G_RadiusDamage( trace->endpos, ent, ent->parent, ent->splashDamage, ent->splashRadius, other, ent->splashMethodOfDeath ) ) #else if( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, other, ent->splashMethodOfDeath ) ) #endif { if( !hitPlayer ) { g_entities[ent->r.ownerNum].player->accuracy_hits++; } } } #ifdef TA_WEAPSYS ent->s.eType = ET_GENERAL; #endif trap_LinkEntity( ent ); }
void Bullet_Fire (gentity_t *ent, float spread, int damage ) { trace_t tr; vec3_t end; #ifdef MISSIONPACK vec3_t impactpoint, bouncedir; #endif float r; float u; gentity_t *tent; gentity_t *traceEnt; int i, passent; damage *= s_quadFactor; r = random() * M_PI * 2.0f; u = sin(r) * crandom() * spread * 16; r = cos(r) * crandom() * spread * 16; VectorMA (muzzle, 8192*16, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); passent = ent->s.number; for (i = 0; i < 10; i++) { trap_Trace (&tr, muzzle, NULL, NULL, end, passent, MASK_SHOT); if ( tr.surfaceFlags & SURF_NOIMPACT ) { return; } traceEnt = &g_entities[ tr.entityNum ]; // snap the endpos to integers, but nudged towards the line SnapVectorTowards( tr.endpos, muzzle ); // send bullet impact if ( traceEnt->takedamage && traceEnt->client ) { tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_FLESH ); tent->s.eventParm = traceEnt->s.number; if( LogAccuracyHit( traceEnt, ent ) ) { ent->client->accuracy_hits++; } } else { tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_WALL ); tent->s.eventParm = DirToByte( tr.plane.normal ); } tent->s.otherEntityNum = ent->s.number; if ( traceEnt->takedamage) { #ifdef MISSIONPACK if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) { if (G_InvulnerabilityEffect( traceEnt, forward, tr.endpos, impactpoint, bouncedir )) { G_BounceProjectile( muzzle, impactpoint, bouncedir, end ); VectorCopy( impactpoint, muzzle ); // the player can hit him/herself with the bounced rail passent = ENTITYNUM_NONE; } else { VectorCopy( tr.endpos, muzzle ); passent = traceEnt->s.number; } continue; } else { #endif G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_MACHINEGUN); #ifdef MISSIONPACK } #endif } break; } }
// self is the "parent", the entity that owns the missile (like a player or shooter_*) qboolean fire_projectile(gentity_t *self, vec3_t start, vec3_t forward, vec3_t right, vec3_t up, int projnum, float quadFactor, int mod, int splashMod, int handSide) { vec3_t mins = { -8, -8, -8 }; vec3_t maxs = { 8, 8, 8 }; gentity_t *bolt; vec3_t dir; vec3_t end; int count; int spread; int damage; int splashDamage; float splashRadius; int range; qboolean hitClient; if (projnum < 0 || projnum >= BG_NumProjectiles()) { return qfalse; } // Check if can fire grappling projectile if (self && self->player && bg_projectileinfo[projnum].grappling) { #ifdef IOQ3ZTM #ifdef TURTLEARENA // HOLD_SHURIKEN if (handSide == HS_CENTER) // Shuriken holdable item { if ((self->player->ps.pm_flags & PMF_USE_ITEM_HELD) || self->player->hook) { return qfalse; } self->player->ps.pm_flags |= PMF_USE_ITEM_HELD; } else #endif { if ((self->player->ps.pm_flags & PMF_FIRE_HELD) || self->player->hook) { return qfalse; } self->player->ps.pm_flags |= PMF_FIRE_HELD; } #else if (self->player->fireHeld || self->player->hook) { return qfalse; } self->player->fireHeld = qtrue; #endif } spread = bg_projectileinfo[projnum].spread; #if 0 // ZTM: WONTFIX: Only for machinegun if (g_gametype.integer == GT_TEAM) { damage = bg_projectileinfo[projnum].damage * 0.7; splashDamage = bg_projectileinfo[projnum].splashDamage * 0.7; } else #endif { damage = bg_projectileinfo[projnum].damage; splashDamage = bg_projectileinfo[projnum].splashDamage; } splashRadius = bg_projectileinfo[projnum].splashRadius; if (quadFactor > 1) { damage *= quadFactor; splashDamage *= quadFactor; } // Use default kill messages // Missile is spawned if (mod == MOD_UNKNOWN) { mod = MOD_PROJECTILE; } if (splashMod == MOD_UNKNOWN) { splashMod = MOD_PROJECTILE_EXPLOSION; } range = 0; if (bg_projectileinfo[projnum].instantDamage) { range = bg_projectileinfo[projnum].speed; } if (!range) { // 8192 is used by bullets, railgun, and nails // Lightning uses 768 range = 8192; } for (count = 0; count < bg_projectileinfo[projnum].numProjectiles; count++ ) { if (spread) // if spread make your own dir. { float r, u; r = random() * M_PI * 2.0f; u = sin(r) * crandom() * spread * 16; r = cos(r) * crandom() * spread * 16; VectorMA(start, (range * 16), forward, end); VectorMA(end, r, right, end); VectorMA(end, u, up, end); VectorSubtract(end, start, dir); } else { VectorMA(start, range, forward, end); VectorCopy(forward, dir); } if (bg_projectileinfo[projnum].flags & PF_USE_GRAVITY) { // extra vertical velocity dir[2] += 0.2f; } VectorNormalize (dir); if (bg_projectileinfo[projnum].instantDamage) { // Based on Q3's Bullet_Fire // (with extra code from Weapon_LightningFire and weapon_railgun_fire) trace_t tr; #if defined MISSIONPACK && !defined TURTLEARENA // POWERS vec3_t impactpoint, bouncedir; #endif gentity_t *tent; gentity_t *traceEnt; int i, passent; int unlinked; gentity_t *unlinkedEntities[10]; int hits; unlinked = hits = 0; // Do a bullet trace instead of spawning a missile. passent = self->s.number; for (i = 0; i < 10; i++) // 10 for lightning { trap_Trace (&tr, start, NULL, NULL, end, passent, MASK_SHOT); #if defined MISSIONPACK && !defined TURTLEARENA // POWERS // if not the first trace (the lightning bounced off an invulnerability sphere) if (i && bg_projectileinfo[projnum].trailType == PT_LIGHTNING) { // add bounced off lightning bolt temp entity // the first lightning bolt is a cgame only visual // tent = G_TempEntity( start, EV_LIGHTNINGBOLT ); tent->s.weapon = projnum; VectorCopy( tr.endpos, end ); SnapVector( end ); VectorCopy( end, tent->s.origin2 ); } #endif #if 0 // RAIL if ( tr.entityNum >= ENTITYNUM_MAX_NORMAL ) { break; } #else // LIGHTNING if ( tr.entityNum == ENTITYNUM_NONE ) { break; } #endif traceEnt = &g_entities[ tr.entityNum ]; // snap the endpos to integers, but nudged towards the line SnapVectorTowards( tr.endpos, start ); if ( !(tr.surfaceFlags & SURF_NOIMPACT) ) { // send bullet impact if ( traceEnt->takedamage && traceEnt->player ) { #if 1 // lightning tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT ); tent->s.otherEntityNum = traceEnt->s.number; tent->s.eventParm = DirToByte( tr.plane.normal ); tent->s.playerNum = self->s.number; #else tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_FLESH ); tent->s.eventParm = traceEnt->s.number; tent->s.otherEntityNum = self->s.number; #endif tent->s.weapon = projnum; } else if (tr.surfaceFlags & SURF_METALSTEPS) { tent = G_TempEntity( tr.endpos, EV_MISSILE_MISS_METAL ); tent->s.playerNum = self->s.number; tent->s.weapon = projnum; tent->s.eventParm = DirToByte( tr.plane.normal ); } else { #if 1 // lightning tent = G_TempEntity( tr.endpos, EV_MISSILE_MISS ); tent->s.playerNum = self->s.number; #else tent = G_TempEntity( tr.endpos, EV_BULLET_HIT_WALL ); tent->s.otherEntityNum = self->s.number; #endif tent->s.weapon = projnum; tent->s.eventParm = DirToByte( tr.plane.normal ); } } if ( traceEnt->takedamage) { #if defined MISSIONPACK && !defined TURTLEARENA // POWERS if ( traceEnt->player && traceEnt->player->invulnerabilityTime > level.time ) { if (G_InvulnerabilityEffect( traceEnt, dir, tr.endpos, impactpoint, bouncedir )) { G_BounceProjectile( start, impactpoint, bouncedir, end ); if (bg_projectileinfo[projnum].trailType == PT_RAIL) { // snap the endpos to integers to save net bandwidth, but nudged towards the line SnapVectorTowards( tr.endpos, start ); // send railgun beam effect tent = G_TempEntity( tr.endpos, EV_RAILTRAIL ); // set player number for custom colors on the railtrail tent->s.playerNum = self->s.playerNum; tent->s.weapon = projnum; tent->s.weaponHands = MAX_HANDS; // Don't attach to player's gun VectorCopy( start, tent->s.origin2 ); // move origin a bit to come closer to the drawn gun muzzle VectorMA( tent->s.origin2, 4, right, tent->s.origin2 ); VectorMA( tent->s.origin2, -1, up, tent->s.origin2 ); tent->s.eventParm = 255; // don't make the explosion at the end } VectorCopy( impactpoint, start ); #if 1 // lightning VectorSubtract( end, impactpoint, dir ); VectorNormalize(dir); #endif // the player can hit him/herself with the bounced rail passent = ENTITYNUM_NONE; } else { VectorCopy( tr.endpos, start ); passent = traceEnt->s.number; } if (bg_projectileinfo[projnum].trailType != PT_RAIL) { continue; } } else #endif { G_Damage( traceEnt, self, self, #if 1 // ZTM: Knockback in direction projectile was moving dir, #else forward, #endif tr.endpos, damage, 0, mod); hitClient = LogAccuracyHit( traceEnt, self ); // Splash damage! if (G_RadiusDamage(tr.endpos, self, self, damage, splashRadius, traceEnt, splashMod)) { hitClient = qtrue; } if( hitClient ) { hits++; } } } // weapon_railgun_fire if (bg_projectileinfo[projnum].maxHits > 1) { if ( tr.contents & CONTENTS_SOLID ) { break; // we hit something solid enough to stop the beam } // unlink this entity, so the next trace will go past it trap_UnlinkEntity( traceEnt ); unlinkedEntities[unlinked] = traceEnt; unlinked++; if (i+1 >= bg_projectileinfo[projnum].maxHits) { break; } } else { break; } } // link back in any entities we unlinked for ( i = 0 ; i < unlinked ; i++ ) { trap_LinkEntity( unlinkedEntities[i] ); } if (self && self->player) { #ifndef TURTLEARENA // AWARDS if (bg_projectileinfo[projnum].maxHits > 1) { // give the shooter a reward sound if they have made two railgun hits in a row if ( hits == 0 ) { // complete miss self->player->accurateCount = 0; } else { // check for "impressive" reward sound self->player->accurateCount += hits; if ( self->player->accurateCount >= 2 ) { self->player->accurateCount -= 2; self->player->ps.persistant[PERS_IMPRESSIVE_COUNT]++; // add the sprite over the player's head #ifdef IOQ3ZTM self->player->ps.eFlags &= ~EF_AWARD_BITS; #else self->player->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); #endif self->player->ps.eFlags |= EF_AWARD_IMPRESSIVE; self->player->rewardTime = level.time + REWARD_SPRITE_TIME; } self->player->accuracy_hits++; } } else #endif if (hits) { self->player->accuracy_hits++; } } // From weapon_railgun_fire if (bg_projectileinfo[projnum].trailType == PT_RAIL) { gentity_t *tent; // the final trace endpos will be the terminal point of the rail trail // snap the endpos to integers to save net bandwidth, but nudged towards the line SnapVectorTowards( tr.endpos, start ); // send railgun beam effect tent = G_TempEntity( tr.endpos, EV_RAILTRAIL ); // set player number for custom colors on the railtrail tent->s.playerNum = self->s.playerNum; // Set projectile number tent->s.weapon = projnum; // ZTM: NOTE: This could be a problem if multiple hands use the same handSide. tent->s.weaponHands = MAX_HANDS; if (self->player) { for (i = 0; i < MAX_HANDS; i++) { if (self->player->pers.playercfg.handSide[i] == handSide) { tent->s.weaponHands = i; break; } } } VectorCopy( start, tent->s.origin2 ); // move origin a bit to come closer to the drawn gun muzzle if (handSide == HS_RIGHT) VectorMA( tent->s.origin2, 4, right, tent->s.origin2 ); else if (handSide == HS_LEFT) VectorMA( tent->s.origin2, -4, right, tent->s.origin2 ); VectorMA( tent->s.origin2, -1, up, tent->s.origin2 ); } continue; } bolt = G_Spawn(); bolt->classname = bg_projectileinfo[projnum].name; if (self && self->player && bg_projectileinfo[projnum].grappling) { if (bg_projectileinfo[projnum].timetolive == -1) bolt->nextthink = -1; else bolt->nextthink = level.time + bg_projectileinfo[projnum].timetolive; bolt->think = Weapon_HookFree; self->player->hook = bolt; } else if (bg_projectileinfo[projnum].homing) { bolt->nextthink = level.time + bg_projectileinfo[projnum].homing; bolt->think = G_HomingMissile; } else { if (bg_projectileinfo[projnum].timetolive == -1) bolt->nextthink = -1; else bolt->nextthink = level.time + bg_projectileinfo[projnum].timetolive; if (bg_projectileinfo[projnum].fallToGround) bolt->think = G_MissileFall; // Just fall out of air. else bolt->think = G_ExplodeMissile; } bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = projnum; if (self) { bolt->r.ownerNum = self->s.number; } else { bolt->r.ownerNum = ENTITYNUM_WORLD; } bolt->parent = self; // grapple bolt->s.otherEntityNum = bolt->r.ownerNum; // use to match beam in client if (!bg_projectileinfo[projnum].damageAttacker) { bolt->flags |= FL_MISSILE_NO_DAMAGE_PARENT; } bolt->damage = damage; bolt->splashDamage = splashDamage; bolt->splashRadius = splashRadius; bolt->methodOfDeath = mod; bolt->splashMethodOfDeath = splashMod; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; VectorCopy( start, bolt->s.pos.trBase ); bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame G_SetMissileVelocity(bolt, dir, projnum); VectorCopy (start, bolt->r.currentOrigin); if (bg_projectileinfo[projnum].bounceType == PB_FULL) bolt->s.eFlags = EF_BOUNCE; else if (bg_projectileinfo[projnum].bounceType == PB_HALF) bolt->s.eFlags = EF_BOUNCE_HALF; if (bg_projectileinfo[projnum].flags & PF_USE_GRAVITY) bolt->s.pos.trType = TR_GRAVITY; // Limit bounces bolt->s.modelindex2 = bg_projectileinfo[projnum].maxBounces; // ZTM: Shootable missiles, taken from XREAL if (bg_projectileinfo[projnum].shootable) { // Make the projectile shootable bolt->s.contents = CONTENTS_SHOOTABLE; VectorCopy(mins, bolt->s.mins); VectorCopy(maxs, bolt->s.maxs); bolt->takedamage = qtrue; bolt->health = bg_projectileinfo[projnum].damage; if (bg_projectileinfo[projnum].fallToGround) bolt->die = G_MissileFall_Die; else bolt->die = G_Missile_Die; } // Save handSide in missile bolt->s.weaponHands = handSide; if (self && self->player) { // Taken from Q3's fire_prox; // ZTM: Used by prox mines so that if that player changes teams the mines // don't "change" teams as well (or something...). bolt->s.team = self->player->sess.sessionTeam; } else { bolt->s.team = TEAM_FREE; } // Needed for stickOnImpact and grappling projectiles vectoangles( forward, bolt->s.angles ); } return qtrue; }
void Weapon_LightningFire( gentity_t *ent ) { trace_t tr; vec3_t end; #ifdef MISSIONPACK vec3_t impactpoint, bouncedir; #endif gentity_t *traceEnt, *tent; int damage, i, passent; damage = 8 * s_quadFactor; passent = ent->s.number; for (i = 0; i < 10; i++) { VectorMA( g_muzzle, LIGHTNING_RANGE, forward, end ); trap_Trace( &tr, g_muzzle, NULL, NULL, end, passent, MASK_SHOT ); #ifdef MISSIONPACK // if not the first trace (the lightning bounced of an invulnerability sphere) if (i) { // add bounced off lightning bolt temp entity // the first lightning bolt is a cgame only visual // tent = G_TempEntity( g_muzzle, EV_LIGHTNINGBOLT ); VectorCopy( tr.endpos, end ); SnapVector( end ); VectorCopy( end, tent->s.origin2 ); } #endif if ( tr.entityNum == ENTITYNUM_NONE ) { return; } traceEnt = &g_entities[ tr.entityNum ]; if ( traceEnt->takedamage) { #ifdef MISSIONPACK if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) { if (G_InvulnerabilityEffect( traceEnt, forward, tr.endpos, impactpoint, bouncedir )) { G_BounceProjectile( g_muzzle, impactpoint, bouncedir, end ); VectorCopy( impactpoint, g_muzzle ); VectorSubtract( end, impactpoint, forward ); VectorNormalize(forward); // the player can hit him/herself with the bounced lightning passent = ENTITYNUM_NONE; } else { VectorCopy( tr.endpos, g_muzzle ); passent = traceEnt->s.number; } continue; } else { G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_LIGHTNING); } #else G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_LIGHTNING); #endif } if ( traceEnt->takedamage && traceEnt->client ) { tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT ); tent->s.otherEntityNum = traceEnt->s.number; tent->s.eventParm = DirToByte( tr.plane.normal ); tent->s.weapon = ent->s.weapon; if( LogAccuracyHit( traceEnt, ent ) ) { ent->client->accuracy_hits++; } } else if ( !( tr.surfaceFlags & SURF_NOIMPACT ) ) { tent = G_TempEntity( tr.endpos, EV_MISSILE_MISS ); tent->s.eventParm = DirToByte( tr.plane.normal ); } break; } }
/* ================ G_ExplodeMissile Explode a missile without an impact ================ */ void G_ExplodeMissile( gentity_t *ent ) { vec3_t dir; vec3_t origin; #ifdef TA_WEAPSYS if (bg_projectileinfo[ent->s.weapon].grappling) { Weapon_HookFree(ent); return; } if (bg_projectileinfo[ent->s.weapon].explosionType == PE_NONE) { G_FreeEntity(ent); return; } #endif BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); SnapVector( origin ); G_SetOrigin( ent, origin ); #ifdef TA_WEAPSYS // Missile impacted a surface if (ent->count & 2) { VectorCopy(ent->s.angles2, dir); } else { #endif // we don't have a valid direction, so just point straight up dir[0] = dir[1] = 0; dir[2] = 1; #ifdef TA_WEAPSYS } #endif #ifndef TA_WEAPSYS // Must be after G_RadiusDamage ent->s.eType = ET_GENERAL; #endif G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( dir ) ); #ifdef TA_WEAPSYS if (ent->parent) ent->s.playerNum = ent->parent->s.number; else ent->s.playerNum = ENTITYNUM_NONE; #endif ent->freeAfterEvent = qtrue; // splash damage if ( ent->splashDamage ) { #ifdef TA_WEAPSYS if( G_RadiusDamage( ent->r.currentOrigin, ent, ent->parent, ent->splashDamage, ent->splashRadius, ent , ent->splashMethodOfDeath ) ) #else if( G_RadiusDamage( ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent , ent->splashMethodOfDeath ) ) #endif { g_entities[ent->r.ownerNum].player->accuracy_hits++; } } #ifdef TA_WEAPSYS ent->s.eType = ET_GENERAL; #endif trap_LinkEntity( ent ); }
/* * W_Fire_Instagun_Strong */ void W_Fire_Instagun( edict_t *self, vec3_t start, vec3_t dir, float damage, int knockback, int stun, int radius, int range, int mod, int timeDelta ) { vec3_t from, end; trace_t tr; edict_t *ignore, *event, *hit; int hit_movetype; int mask; bool missed = true; int dmgflags = 0; if( GS_Instagib() ) { damage = 9999; } VectorMA( start, range, dir, end ); VectorCopy( start, from ); ignore = self; mask = MASK_SHOT; if( GS_RaceGametype() ) { mask = MASK_SOLID; } tr.ent = -1; while( ignore ) { G_Trace4D( &tr, from, NULL, NULL, end, ignore, mask, timeDelta ); VectorCopy( tr.endpos, from ); ignore = NULL; if( tr.ent == -1 ) { break; } // allow trail to go through SOLID_BBOX entities (players, gibs, etc) hit = &game.edicts[tr.ent]; hit_movetype = hit->movetype; // backup the original movetype as the entity may "die" if( !ISBRUSHMODEL( hit->s.modelindex ) ) { ignore = hit; } if( ( hit != self ) && ( hit->takedamage ) ) { G_Damage( hit, self, self, dir, dir, tr.endpos, damage, knockback, stun, dmgflags, mod ); // spawn a impact event on each damaged ent event = G_SpawnEvent( EV_INSTA_EXPLOSION, DirToByte( tr.plane.normal ), tr.endpos ); event->s.ownerNum = ENTNUM( self ); event->s.firemode = FIRE_MODE_STRONG; if( hit->r.client ) { missed = false; } } // some entity was touched if( hit == world || hit_movetype == MOVETYPE_NONE || hit_movetype == MOVETYPE_PUSH ) { if( g_instajump->integer && self && self->r.client ) { // create a temporary inflictor entity edict_t *inflictor; inflictor = G_Spawn(); inflictor->s.solid = SOLID_NOT; inflictor->timeDelta = 0; VectorCopy( tr.endpos, inflictor->s.origin ); inflictor->s.ownerNum = ENTNUM( self ); inflictor->projectileInfo.maxDamage = 0; inflictor->projectileInfo.minDamage = 0; inflictor->projectileInfo.maxKnockback = knockback; inflictor->projectileInfo.minKnockback = 1; inflictor->projectileInfo.stun = 0; inflictor->projectileInfo.radius = radius; G_RadiusDamage( inflictor, self, &tr.plane, NULL, mod ); G_FreeEdict( inflictor ); } break; } } if( missed && self->r.client ) { G_AwardPlayerMissedElectrobolt( self, mod ); } // send the weapon fire effect event = G_SpawnEvent( EV_INSTATRAIL, ENTNUM( self ), start ); VectorScale( dir, 1024, event->s.origin2 ); }
void weapon_railgun_fire(Gentity *ent) { Vec3 end; #ifdef MISSIONPACK Vec3 impactpoint, bouncedir; #endif Trace trace; Gentity *tent; Gentity *traceEnt; int damage; int i; int hits; int unlinked; int passent; Gentity *unlinkedEntities[MAX_RAIL_HITS]; damage = 100 * s_quadFactor; saddv3 (muzzle, 8192, forward, end); /* trace only against the solids, so the railgun will go through people */ unlinked = 0; hits = 0; passent = ent->s.number; do { trap_Trace (&trace, muzzle, NULL, NULL, end, passent, MASK_SHOT); if(trace.entityNum >= ENTITYNUM_MAX_NORMAL) break; traceEnt = &g_entities[ trace.entityNum ]; if(traceEnt->takedamage){ if(LogAccuracyHit(traceEnt, ent)) hits++; G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN); } if(trace.contents & CONTENTS_SOLID) break; /* we hit something solid enough to stop the beam */ /* unlink this entity, so the next trace will go past it */ trap_UnlinkEntity(traceEnt); unlinkedEntities[unlinked] = traceEnt; unlinked++; } while(unlinked < MAX_RAIL_HITS); /* link back in any entities we unlinked */ for(i = 0; i < unlinked; i++) trap_LinkEntity(unlinkedEntities[i]); /* the final trace endpos will be the terminal point of the rail trail */ /* snap the endpos to integers to save net bandwidth, but nudged towards the line */ snapv3Towards(trace.endpos, muzzle); /* send railgun beam effect */ tent = G_TempEntity(trace.endpos, EV_RAILTRAIL); /* set player number for custom colors on the railtrail */ tent->s.clientNum = ent->s.clientNum; copyv3(muzzle, tent->s.origin2); /* move origin a bit to come closer to the drawn gun muzzle */ saddv3(tent->s.origin2, 4, right, tent->s.origin2); saddv3(tent->s.origin2, -1, up, tent->s.origin2); /* no explosion at end if SURF_NOIMPACT, but still make the trail */ if(trace.surfaceFlags & SURF_NOIMPACT) tent->s.eventParm = 255; /* don't make the explosion at the end */ else tent->s.eventParm = DirToByte(trace.plane.normal); tent->s.clientNum = ent->s.clientNum; /* give the shooter a reward sound if they have made two railgun hits in a row */ if(hits == 0) /* complete miss */ ent->client->accurateCount = 0; else{ /* check for "impressive" reward sound */ ent->client->accurateCount += hits; if(ent->client->accurateCount >= 2){ ent->client->accurateCount -= 2; ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++; /* add the sprite over the player's head */ ent->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP); ent->client->ps.eFlags |= EF_AWARD_IMPRESSIVE; ent->client->rewardTime = level.time + REWARD_SPRITE_TIME; } ent->client->accuracy_hits++; } }