/* =============== lasGunFire =============== */ void lasGunFire( gentity_t *ent ) { trace_t tr; vec3_t end; gentity_t *tent; gentity_t *traceEnt; VectorMA( muzzle, 8192 * 16, forward, end ); G_UnlaggedOn( ent, muzzle, 8192 * 16 ); trap_Trace( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT ); G_UnlaggedOff(); 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 impact if ( traceEnt->takedamage && ( traceEnt->s.eType == ET_BUILDABLE || traceEnt->s.eType == ET_PLAYER ) ) { BloodSpurt( ent, traceEnt, &tr ); } else { tent = G_TempEntity( tr.endpos, EV_MISSILE_MISS ); tent->s.eventParm = DirToByte( tr.plane.normal ); tent->s.weapon = ent->s.weapon; tent->s.generic1 = ent->s.generic1; //weaponMode } if ( traceEnt->takedamage ) { G_Damage( traceEnt, ent, ent, forward, tr.endpos, LASGUN_DAMAGE, 0, MOD_LASGUN ); } }
void massDriverFire( gentity_t *ent ) { trace_t tr; vec3_t end; gentity_t *tent; gentity_t *traceEnt; VectorMA( muzzle, 8192 * 16, 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 ]; // snap the endpos to integers, but nudged towards the line SnapVectorTowards( tr.endpos, muzzle ); // send 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 } else { tent = G_TempEntity( tr.endpos, EV_MISSILE_MISS ); tent->s.eventParm = DirToByte( tr.plane.normal ); tent->s.weapon = ent->s.weapon; tent->s.generic1 = ent->s.generic1; //weaponMode } if( traceEnt->takedamage ) { G_Damage( traceEnt, ent, ent, forward, tr.endpos, MDRIVER_DMG, 0, MOD_MDRIVER ); } }
void Weapon_HookThink (gentity_t *ent) { ent->nextthink = level.time + FRAMETIME; if (ent->enemy && ent->enemy->player) { vec3_t v, oldorigin; if ( ent->enemy->player->ps.pm_type == PM_DEAD ) { Weapon_HookFree( ent ); return; } VectorCopy(ent->r.currentOrigin, oldorigin); v[0] = ent->enemy->r.currentOrigin[0] + (ent->enemy->s.mins[0] + ent->enemy->s.maxs[0]) * 0.5; v[1] = ent->enemy->r.currentOrigin[1] + (ent->enemy->s.mins[1] + ent->enemy->s.maxs[1]) * 0.5; v[2] = ent->enemy->r.currentOrigin[2] + (ent->enemy->s.mins[2] + ent->enemy->s.maxs[2]) * 0.5; SnapVectorTowards( v, oldorigin ); // save net bandwidth G_SetOrigin( ent, v ); } VectorCopy( ent->r.currentOrigin, ent->parent->player->ps.grapplePoint); }
/* ================ 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 ]; if( !other->takedamage && !strcmp( ent->classname, "bounceball" ) && ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) { G_BounceMissile( ent, trace ); G_AddEvent( ent, EV_MISSILE_MISS, 0 ); return; } // 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_PTEAM ] == PTE_HUMANS ) { other->client->ps.stats[ STAT_STATE ] |= SS_BLOBLOCKED; other->client->lastLockTime = level.time; AngleVectors( other->client->ps.viewangles, dir, NULL, NULL ); other->client->ps.stats[ STAT_VIEWLOCK ] = DirToByte( dir ); } } else if( !strcmp( ent->classname, "slowblob" ) ) { if( other->client && other->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) { other->client->ps.stats[ STAT_STATE ] |= SS_SLOWLOCKED; other->client->lastSlowTime = level.time; AngleVectors( other->client->ps.viewangles, dir, NULL, NULL ); other->client->ps.stats[ STAT_VIEWLOCK ] = DirToByte( dir ); } } else if( !strcmp( ent->classname, "hive" ) ) { if( other->s.eType == ET_BUILDABLE && other->s.modelindex == BA_A_HIVE ) { if( !ent->parent ) G_Printf( S_COLOR_YELLOW "WARNING: hive entity has no parent in G_MissileImpact\n" ); else ent->parent->active = qfalse; G_FreeEntity( ent ); return; } else { //prevent collision with the client when returning ent->r.ownerNum = other->s.number; ent->think = AHive_ReturnToHive; ent->nextthink = level.time + FRAMETIME; //only damage humans if( other->client && other->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) returnAfterDamage = qtrue; else return; } } // impact damage if( other->takedamage ) { // FIXME: wrong damage direction? if( ent->damage ) { vec3_t velocity; BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); if( VectorLength( velocity ) == 0 ) velocity[ 2 ] = 1; // stepped on a grenade G_Damage( other, ent, attacker, velocity, ent->s.origin, ent->damage, 0, ent->methodOfDeath ); } } if( returnAfterDamage ) return; // is it cheaper in bandwidth to just remove this ent and create a new // one, rather than changing the missile into the explosion? if( other->takedamage && other->client ) { G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); ent->s.otherEntityNum = other->s.number; } else if( trace->surfaceFlags & SURF_METALSTEPS ) G_AddEvent( ent, EV_MISSILE_MISS_METAL, DirToByte( trace->plane.normal ) ); else G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); ent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact ent->s.eType = ET_GENERAL; SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); // save net bandwidth G_SetOrigin( ent, trace->endpos ); // splash damage (doesn't apply to person directly hit) if( ent->splashDamage ) G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, other, ent->splashMethodOfDeath ); trap_LinkEntity( ent ); }
/* ================ G_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; } #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; 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->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_MISSILE_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_MISSILE_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_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 ) { 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 ); }
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->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; 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); 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, g_randomConeReflection.integer & CONE_REFLECT_SDEF); } other->client->ps.saberBlockTime = level.time + (350 - (otherDefLevel*100)); //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 ) { //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 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; te->s.weapon = 0;//saberNum te->s.legsAnim = 0;//bladeNum 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, g_randomConeReflection.integer & CONE_REFLECT_SDEF); } otherOwner->client->ps.saberBlockTime = level.time + (350 - (otherDefLevel*100)); //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] ) && !ent->isReflected) { 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)) { /* fix: there are rare situations where flechette did explode by timeout AND by impact in the very same frame, then here ent->think was set to G_FreeEntity, so the folowing think did invalidate this entity, BUT it would be reused later in this function for explosion event. This, then, would set ent->freeAfterEvent to qtrue, so event later, when reusing this entity by using G_InitEntity(), it would have this freeAfterEvent set AND this would in case of dropped item erase it from game immeadiately. THIS for example caused very rare flag dissappearing bug. */ if (ent->think == WP_flechette_alt_blow) 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: if (!ent->inuse){ G_LogPrintf("ERROR: entity %i non-used, checkpoint 5\n",ent-g_entities); } // 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 && !ent->isReflected) { 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 ); }
void Bullet_Fire (gentity_t *ent, float spread, int damage ) { trace_t tr; vec3_t end; #if 1 vec3_t impactpoint, bouncedir; #endif float r, f, d; float u; gentity_t *tent; gentity_t *traceEnt; int i, passent; vec3_t forward, right, up; AngleVectors (ent->client->ps.viewangles, forward, right, up); CalcMuzzlePoint ( ent, forward, right, up, muzzle ); ent->client->accuracy_shots++; f = ((float)(ent->client->accuracy_shots % SPIRAL_SIZE)) / SPIRAL_SIZE; // 0 >= f >= 1 d = (f+(1/SPIRAL_SIZE)) * 2.0 * M_PI; damage *= s_quadFactor; //Com_Printf("f: %f, d: %f\n, a: %i", f, d, ent->client->accuracy_shots); u = sin(d) * (f * spread); r = cos(d) * (f * spread); //Com_Printf("end1 : [%f, %f, %f]\n", end[0], end[1], end[2]); VectorMA (muzzle, 8192, forward, end); //Com_Printf("end2 : [%f, %f, %f]\n", end[0], end[1], end[2]); //Com_Printf("right : [%f, %f, %f]\n", right[0], right[1], right[2]); VectorMA (end, r, right, end); //Com_Printf("end3 : [%f, %f, %f]\n", end[0], end[1], end[2]); //Com_Printf("up : [%f, %f, %f]\n", up[0], up[1], up[2]); VectorMA (end, u, up, end); //Com_Printf("end4 : [%f, %f, %f]\n", end[0], end[1], end[2]); 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) { #if 1 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); #if 1 } #endif } break; } }
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; } }
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; } } }
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( muzzle, LIGHTNING_RANGE, forward, end ); // eser - lightning discharge if (trap_PointContents (muzzle, -1) & MASK_WATER) { int zaps; gentity_t *tent; zaps = ent->client->ps.ammo[WP_LIGHTNING]; // determines size/power of discharge if (!zaps) return; // prevents any subsequent frames causing second discharge + error zaps++; // pmove does an ammo[gun]--, so we must compensate SnapVectorTowards (muzzle, ent->s.origin); // save bandwidth tent = G_TempEntity (muzzle, EV_LIGHTNING_DISCHARGE); tent->s.eventParm = zaps; // duration / size of explosion graphic ent->client->ps.ammo[WP_LIGHTNING] = 0; // drain ent's lightning count if (G_RadiusDamage (muzzle, ent, damage * zaps, (damage * zaps) + 16, NULL, MOD_LIGHTNING_DISCHARGE, qtrue)) ent->client->accuracy_hits++; return; } // eser - lightning discharge trap_Trace( &tr, 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( 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( muzzle, impactpoint, bouncedir, end ); VectorCopy( impactpoint, muzzle ); VectorSubtract( end, impactpoint, forward ); VectorNormalize(forward); // the player can hit him/herself with the bounced lightning passent = ENTITYNUM_NONE; } else { VectorCopy( tr.endpos, muzzle ); passent = traceEnt->s.number; } continue; } #endif if( LogAccuracyHit( traceEnt, ent ) ) { ent->client->accuracy_hits++; } G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_LIGHTNING); } if ( traceEnt->takedamage && traceEnt->client ) { tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT ); tent->s.otherEntityNum = traceEnt->s.number; tent->s.eventParm = DirToByte( tr.plane.normal ); tent->s.weapon = ent->s.weapon; } else if ( !( tr.surfaceFlags & SURF_NOIMPACT ) ) { tent = G_TempEntity( tr.endpos, EV_MISSILE_MISS ); tent->s.eventParm = DirToByte( tr.plane.normal ); } break; } }
void weapon_omega_fire ( gentity_t *ent, int weaponTime ) { vec3_t end, delta; trace_t trace; gentity_t *tent; gentity_t *traceEnt; int damage; int i; int unlinked; int passent; gentity_t *unlinkedEntities[MAX_OMEGA_HITS]; weaponTime <<= 4; damage = weLi[WP_OMEGA].damage * weaponTime / weLi[WP_OMEGA].spread_crouch; VectorMA( muzzle, 8192, forward, end); VectorSet( delta, 0, 0, 0 ); // trace only against the solids, so the railgun will go through people unlinked = 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->s.eType != ET_PLAYER && !( traceEnt->s.eType == ET_BREAKABLE && traceEnt->s.generic1 ) ) { // do the damage if ( traceEnt->takedamage ) G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_OMEGA); // we hit something solid enough to stop the beam break; } // do the damage if ( traceEnt->takedamage ) { G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_OMEGA); // compensate for lag effects if ( !unlinked && traceEnt->client ) { VectorSubtract( traceEnt->client->saved.currentOrigin, traceEnt->r.currentOrigin, delta ); VectorScale( delta, 1.0 / ( trace.fraction + 0.001 ), delta ); } } // create effects on the compensated trail VectorMA( trace.endpos, trace.fraction, delta, trace.endpos ); // create an impact effect SnapVectorTowards( trace.endpos, muzzle ); tent = G_TempEntity( trace.endpos, ( traceEnt->s.eType == ET_PLAYER ) ? EV_MISSILE_HIT : EV_MISSILE_MISS ); // set generic1 to charge time for correct trail color tent->s.generic1 = weaponTime >> 4; VectorCopy( trace.endpos, tent->s.origin2 ); tent->s.eventParm = DirToByte( trace.plane.normal ); tent->s.weapon = WP_OMEGA; // unlink this entity, so the next trace will go past it trap_UnlinkEntity( traceEnt ); unlinkedEntities[unlinked] = traceEnt; unlinked++; } while ( unlinked < MAX_OMEGA_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 // create effects on the compensated trail VectorMA( trace.endpos, trace.fraction, delta, trace.endpos ); // snap the endpos to integers to save net bandwidth, but nudged towards the line SnapVectorTowards( trace.endpos, muzzle ); // send railgun beam effect tent = G_TempEntity( trace.endpos, EV_OMEGATRAIL ); // set generic1 to charge time for correct trail color tent->s.generic1 = weaponTime >> 4; VectorCopy( muzzle, tent->s.origin2 ); // move origin a bit to come closer to the drawn gun muzzle VectorMA( tent->s.origin2, 4, right, tent->s.origin2 ); VectorMA( tent->s.origin2, -1, up, tent->s.origin2 ); // no explosion at end if SURF_NOIMPACT, but still make the trail if ( trace.surfaceFlags & SURF_NOIMPACT ) { tent->s.eventParm = 255; // don't make the explosion at the end } else { tent->s.eventParm = DirToByte( trace.plane.normal ); } tent->s.clientNum = ent->s.clientNum; }
void Bullet_Fire (gentity_t *ent, float spread, int damage, int mod ) { 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; //unlagged - attack prediction #2 if ( g_unlagged.integer ) { // we have to use something now that the client knows in advance int seed = ent->client->attackTime % 256; // this has to match what's on the client r = Q_random(&seed) * M_PI * 2.0f; u = sin(r) * Q_crandom(&seed) * spread * 16; r = cos(r) * Q_crandom(&seed) * spread * 16; } else { r = random() * M_PI * 2.0f; u = sin(r) * crandom() * spread * 16; r = cos(r) * crandom() * spread * 16; } //-unlagged - attack prediction #2 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++) { //unlagged - backward reconciliation #2 // backward-reconcile the other clients if ( g_unlagged.integer ) G_DoTimeShiftFor( ent ); //-unlagged - backward reconciliation #2 trap_Trace (&tr, muzzle, NULL, NULL, end, passent, MASK_SHOT); //unlagged - backward reconciliation #2 // put them back if ( g_unlagged.integer ) G_UndoTimeShiftFor( ent ); //-unlagged - backward reconciliation #2 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; //unlagged - attack prediction #2 // we need the client number to determine whether or not to // suppress this event if ( g_unlagged.integer ) tent->s.clientNum = ent->s.clientNum; //-unlagged - attack prediction #2 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 ); //unlagged - attack prediction #2 // we need the client number to determine whether or not to // suppress this event if ( g_unlagged.integer ) tent->s.clientNum = ent->s.clientNum; //-unlagged - attack prediction #2 } 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, DAMAGE_COMBAT, mod); #ifdef MISSIONPACK } #endif } break; } }
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_tweakWeapons.integer & WT_ROCKET_MORTAR && ent->s.weapon == WP_REPEATER && ent->bounceCount == 50 && ent->setTime && ent->setTime > level.time - 300)) { //if its a direct hit and first 500ms of mortar, bounce off player. G_BounceMissile( ent, trace ); G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } else if ( !other->takedamage && (ent->bounceCount > 0 || ent->bounceCount == -5) && ( ent->flags & ( FL_BOUNCE | FL_BOUNCE_HALF ) ) ) { //only on the first bounce vv if (!(g_tweakWeapons.integer & WT_ROCKET_MORTAR && ent->s.weapon == WP_REPEATER && ent->bounceCount == 50 && ent->setTime && ent->setTime < level.time - 1000))//give this mortar a 1 second 'fuse' until its armed { 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; } //trap->Print("Shrapnel is still there\n"); 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 ); } //trap->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)) //loda fixme, add check for dimensions for blocking here? { //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); 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 && (g_entities[ent->r.ownerNum].s.bolt1 == other->s.bolt1) &&//loda fixme, this stops missiles deflecting, but they still dont passthrough... 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; 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); 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; } //JAPRO - Serverside - Flag punting - Start if (g_allowFlagThrow.integer && !other->takedamage && other->s.eType == ET_ITEM) { vec3_t velocity; if (ent->s.weapon == WP_REPEATER && (ent->s.eFlags & EF_ALT_FIRING)) { other->s.pos.trType = TR_GRAVITY; other->s.pos.trTime = level.time; BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); VectorScale( velocity, 0.7f, other->s.pos.trDelta ); VectorCopy( other->r.currentOrigin, other->s.pos.trBase ); } else if (ent->s.weapon == WP_ROCKET_LAUNCHER && (ent->s.eFlags & EF_ALT_FIRING)) { other->s.pos.trType = TR_GRAVITY; other->s.pos.trTime = level.time; BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); VectorScale( velocity, 2.5f, other->s.pos.trDelta ); VectorCopy( other->r.currentOrigin, other->s.pos.trBase ); } else if (ent->s.weapon == WP_ROCKET_LAUNCHER) { other->s.pos.trType = TR_GRAVITY; other->s.pos.trTime = level.time; BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); VectorScale( velocity, 0.9f, other->s.pos.trDelta ); VectorCopy( other->r.currentOrigin, other->s.pos.trBase ); } else if (ent->s.weapon == WP_THERMAL) { other->s.pos.trType = TR_GRAVITY; other->s.pos.trTime = level.time; BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); VectorScale( velocity, 0.9f, other->s.pos.trDelta ); //tweak? VectorCopy( other->r.currentOrigin, other->s.pos.trBase ); } } //JAPRO - Serverside - Flag punting - End // 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 } //damage falloff option, assumes bullet lifetime is 10,000 (default) if ((g_tweakWeapons.integer & WT_NO_SPREAD) && ((ent->s.weapon == WP_BLASTER && (ent->s.eFlags & EF_ALT_FIRING)) || (ent->s.weapon == WP_REPEATER && !(ent->s.eFlags & EF_ALT_FIRING)) )) { //If the weapon has spread, just reduce damage based on distance for nospread tweak. This should probably be accompanied with the damagenumber setting so you can keep track of your dmg.. float lifetime = (10000 - ent->nextthink + level.time) * 0.001; //float scale = powf(2, -lifetime); float scale = -1.5 * lifetime + 1; scale += 0.1f; //offset it a bit so super close shots dont get affected at all if (scale < 0.2f) scale = 0.2f; else if (scale > 1.0f) scale = 1.0f; ent->damage *= scale; //trap->SendServerCommand(-1, va("chat \"Missile has been alive for %.2f s new dmg is %i scale is %.2f\n\"", lifetime, ent->damage, scale)); } 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)) { /* fix: there are rare situations where flechette did explode by timeout AND by impact in the very same frame, then here ent->think was set to G_FreeEntity, so the folowing think did invalidate this entity, BUT it would be reused later in this function for explosion event. This, then, would set ent->freeAfterEvent to qtrue, so event later, when reusing this entity by using G_InitEntity(), it would have this freeAfterEvent set AND this would in case of dropped item erase it from game immeadiately. THIS for example caused very rare flag dissappearing bug. */ if (ent->think == WP_flechette_alt_blow) 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 ); } } } } #if _GRAPPLE//_GRAPPLE if (!strcmp(ent->classname, "laserTrap") && ent->s.weapon == WP_BRYAR_PISTOL) { //gentity_t *nent; vec3_t v; /* nent = G_Spawn(qtrue); nent->freeAfterEvent = qtrue; nent->s.weapon = WP_BRYAR_PISTOL;//WP_GRAPPLING_HOOK; -- idk what this is nent->s.saberInFlight = qtrue; nent->s.owner = ent->s.owner; */ ent->enemy = NULL; ent->s.otherEntityNum = -1; ent->s.groundEntityNum = -1; if ( other->s.eType == ET_MOVER || (other->client && !( other->s.eFlags & EF_DEAD ) ) ) { if ( other->client ) { //G_AddEvent( nent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); //Event if (!ent->s.hasLookTarget) { G_PlayEffectID( G_EffectIndex("tusken/hit"), trace->endpos, trace->plane.normal ); } ent->s.hasLookTarget = qtrue; ent->enemy = other; other->s.otherEntityNum = ent->parent->s.number; 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 ent->s.otherEntityNum = ent->enemy->s.clientNum; other->s.otherEntityNum = ent->parent->s.clientNum; } else { if ( !strcmp(other->classname, "func_rotating") || !strcmp(other->classname, "func_pendulum") ) { Weapon_HookFree(ent); // don't work return; } ent->s.otherEntityNum = other->s.number; ent->s.groundEntityNum = other->s.number; VectorCopy(trace->endpos, v); //G_AddEvent( nent, EV_MISSILE_MISS, 0); //DirToByte( trace->plane.normal ) ); //Event if (!ent->s.hasLookTarget) { G_PlayEffectID( G_EffectIndex("tusken/hitwall"), trace->endpos, trace->plane.normal ); } ent->s.hasLookTarget = qtrue; } } else { VectorCopy(trace->endpos, v); //G_AddEvent( nent, EV_MISSILE_MISS, 0);//DirToByte( trace->plane.normal ) ); //Event if (!ent->s.hasLookTarget) { G_PlayEffectID( G_EffectIndex("tusken/hitwall"), trace->endpos, trace->plane.normal ); } ent->s.hasLookTarget = qtrue; } VectorCopy(trace->plane.normal, ent->s.angles); SnapVectorTowards( v, ent->s.pos.trBase ); // save net bandwidth // change over to a normal entity right at the point of impact //nent->s.eType = ET_GENERAL; ent->s.eType = ET_MISSILE; G_SetOrigin( ent, v ); //G_SetOrigin( nent, v ); ent->think = Weapon_HookThink; ent->nextthink = level.time + FRAMETIME; VectorCopy( ent->r.currentOrigin, ent->parent->client->ps.lastHitLoc); VectorSubtract( ent->r.currentOrigin, ent->parent->client->ps.origin, v ); trap->LinkEntity( (sharedEntity_t *)ent ); //trap->LinkEntity( (sharedEntity_t *)nent ); return; } #endif 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( (sharedEntity_t *)ent ); }
/* ================ 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 ); }
void G_MissileImpact( gentity_t *ent, trace_t *trace ) { #else void G_MissileImpact( gentity_t *ent, trace_t *trace, int shaderNum ) { #endif gentity_t *other; qboolean hitClient = qfalse; #ifndef SMOKINGUNS #ifdef MISSIONPACK vec3_t forward, impactpoint, bouncedir; int eFlags; #endif #else qboolean hitKnife = qfalse; vec3_t bottledirs[ALC_COUNT]; #endif other = &g_entities[trace->entityNum]; #ifndef SMOKINGUNS // 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; } #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) { #else if(other->takedamage) hitKnife = qtrue; // check for bounce if ( ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) { G_BounceMissile( ent, trace ); return; } if (other->takedamage && ent->s.weapon != WP_DYNAMITE) { #endif // 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 } #ifndef SMOKINGUNS G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, ent->s.origin, ent->damage, 0, ent->methodOfDeath); #else // you can't make dynamite exploding by using a knife if(!(ent->s.weapon == WP_KNIFE && other->s.weapon == WP_DYNAMITE && other->s.eType == ET_ITEM)){ // prepare breakable, if not already initialized if(!(other->flags & FL_BREAKABLE_INIT)) G_BreakablePrepare(other, shaderNum); G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, ent->s.origin, ent->damage, 0, ent->methodOfDeath); } #endif } } #ifndef SMOKINGUNS 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->r.mins, -4, -4, -4); VectorSet(ent->r.maxs, 4, 4, 4); trap_LinkEntity(ent); return; } if (!strcmp(ent->classname, "hook")) { gentity_t *nent; vec3_t v; nent = G_Spawn(); if ( other->takedamage && other->client ) { G_AddEvent( nent, EV_MISSILE_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_MISSILE_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; } #endif // is it cheaper in bandwidth to just remove this ent and create a new // one, rather than changing the missile into the explosion? #ifndef SMOKINGUNS if ( other->takedamage && other->client ) { #else // alcoohol impact if( !Q_stricmp(ent->classname, "alcohol")){ // no event //G_AddEvent( ent, EV_MISSILE_ALCOHOL, DirToByte( trace->plane.normal)); } else if( !Q_stricmp(ent->classname, "molotov")){ // we have to launch the whiskey drops int i; // set the directions for(i = 0; i < ALC_COUNT; i++){ int temp; VectorSet(bottledirs[i], (rand()%10)-5, (rand()%10)-5, (rand()%10)-3); // direction has to be exactly the same (client and server) temp = DirToByte(bottledirs[i]); ByteToDir(temp, bottledirs[i]); } // dirs BG_DirsToEntityState(&ent->s, bottledirs); // burning if(ent->s.apos.trDelta[0]) G_AddEvent( ent, EV_MISSILE_FIRE, DirToByte( trace->plane.normal)); // not burning else G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal)); } else if ( other->takedamage && other->client ) { #endif G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); ent->s.otherEntityNum = other->s.number; #ifndef SMOKINGUNS } else if( trace->surfaceFlags & SURF_METALSTEPS ) { #else } else if( trace->surfaceFlags & SURF_METAL ) { #endif G_AddEvent( ent, EV_MISSILE_MISS_METAL, DirToByte( trace->plane.normal ) ); } else { G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); } #ifndef SMOKINGUNS ent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact ent->s.eType = ET_GENERAL; #else if(Q_stricmp(ent->classname, "knife")){ ent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact ent->s.eType = ET_GENERAL; } else { vec3_t dir; gitem_t *item; item = BG_FindItemForWeapon(WP_KNIFE); ent->s.modelindex = item-bg_itemlist; ent->s.modelindex2 = 1; ent->item = item; ent->s.eType = ET_ITEM; ent->s.pos.trType = TR_GRAVITY; ent->physicsBounce = 0.01f; ent->r.contents = CONTENTS_TRIGGER; ent->touch = Touch_Item; ent->nextthink = level.time + 100; ent->think = G_KnifeThink; ent->wait = level.time + 30000; ent->flags |= FL_THROWN_ITEM; vectoangles(ent->s.pos.trDelta, dir); VectorCopy(dir, ent->s.apos.trBase); VectorCopy(dir, ent->r.currentAngles); } //modified by Spoon END #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 ) { if( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, other, ent->splashMethodOfDeath ) ) { if( !hitClient ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; } } } // spawn alcohol missiles #ifdef SMOKINGUNS if(!Q_stricmp(ent->classname, "molotov")){ BottleBreak( ent, trace->endpos, trace->plane.normal, bottledirs); } #endif trap_LinkEntity( ent ); } /* ================ G_RunMissile ================ */ void G_RunMissile( gentity_t *ent ) { vec3_t origin; trace_t tr; int passent; #ifdef SMOKINGUNS int shaderNum; gentity_t *traceEnt; #endif // get current position BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); // if this missile bounced off an invulnerability sphere if ( ent->target_ent ) { passent = ent->target_ent->s.number; } #ifndef SMOKINGUNS // prox mines that left the owner bbox will attach to anything, even the owner else if (ent->s.weapon == WP_PROX_LAUNCHER && ent->count) { passent = ENTITYNUM_NONE; } #endif else { // ignore interactions with the missile owner passent = ent->r.ownerNum; } // trace a line from the previous position to the current position #ifndef SMOKINGUNS trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask ); #else shaderNum = trap_Trace_New2( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask ); traceEnt = &g_entities[tr.entityNum]; #endif if ( tr.startsolid || tr.allsolid ) { // make sure the tr.entityNum is set to the entity we're stuck in #ifndef SMOKINGUNS trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent, ent->clipmask ); #else trap_Trace_New( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent, ent->clipmask ); #endif tr.fraction = 0; } else { VectorCopy( tr.endpos, ent->r.currentOrigin ); } trap_LinkEntity( ent ); if ( tr.fraction != 1 ) { #ifdef SMOKINGUNS VectorCopy(origin, ent->s.origin2); #endif // never explode or bounce on sky if ( tr.surfaceFlags & SURF_NOIMPACT ) { // If grapple, reset owner if (ent->parent && ent->parent->client && ent->parent->client->hook == ent) { ent->parent->client->hook = NULL; } // if its a dynamite or molotov let it move 10 seconds before deleting it #ifdef SMOKINGUNS if(ent->s.weapon == WP_DYNAMITE || ent->s.weapon == WP_MOLOTOV || ent->s.weapon == WP_KNIFE){ if(ent->mappart >= level.time && ent->mappart){ goto think; } else if(ent->mappart){ ent->mappart = 0; } else { ent->mappart = level.time + 5000; goto think; } } #endif G_FreeEntity( ent ); return; } #ifndef SMOKINGUNS G_MissileImpact( ent, &tr ); if ( ent->s.eType != ET_MISSILE ) { #else G_MissileImpact( ent, &tr, shaderNum ); if ( ent->s.eType != ET_MISSILE && ent->s.eType != ET_ITEM) { #endif return; // exploded } } #ifndef SMOKINGUNS // if the prox mine wasn't yet outside the player body if (ent->s.weapon == WP_PROX_LAUNCHER && !ent->count) { // check if the prox mine is outside the owner bbox trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, ENTITYNUM_NONE, ent->clipmask ); if (!tr.startsolid || tr.entityNum != ent->r.ownerNum) { ent->count = 1; } } #endif think: // check think function after bouncing G_RunThink( ent ); } //============================================================================= #ifndef SMOKINGUNS /* ================= fire_plasma ================= */ gentity_t *fire_plasma (gentity_t *self, vec3_t start, vec3_t dir) { gentity_t *bolt; VectorNormalize (dir); bolt = G_Spawn(); bolt->classname = "plasma"; bolt->nextthink = level.time + 10000; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_PLASMAGUN; bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = 20; bolt->splashDamage = 15; bolt->splashRadius = 20; bolt->methodOfDeath = MOD_PLASMA; bolt->splashMethodOfDeath = MOD_PLASMA_SPLASH; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, 2000, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy (start, bolt->r.currentOrigin); return bolt; } //============================================================================= /* ================= fire_grenade ================= */ gentity_t *fire_grenade (gentity_t *self, vec3_t start, vec3_t dir) { gentity_t *bolt; VectorNormalize (dir); bolt = G_Spawn(); bolt->classname = "grenade"; bolt->nextthink = level.time + 2500; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_GRENADE_LAUNCHER; bolt->s.eFlags = EF_BOUNCE_HALF; bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = 100; bolt->splashDamage = 100; bolt->splashRadius = 150; bolt->methodOfDeath = MOD_GRENADE; bolt->splashMethodOfDeath = MOD_GRENADE_SPLASH; bolt->clipmask = MASK_SHOT; bolt->target_ent = NULL; bolt->s.pos.trType = TR_GRAVITY; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, 700, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy (start, bolt->r.currentOrigin); return bolt; }
/* ================ 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 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 ) { vector3 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) ) { vector3 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) vector3 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.z > 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 vector3 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; 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.z > 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 ) { vector3 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.z = 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 ); JPLua::Entity_CallFunction( ent, JPLua::JPLUA_ENTITY_THINK ); } 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; } //Raz: air shots if ( (other->client && other->client->ps.groundEntityNum == ENTITYNUM_NONE) && (ent->methodOfDeath == MOD_CONC || ent->methodOfDeath == MOD_REPEATER_ALT || ent->methodOfDeath == MOD_ROCKET || ent->methodOfDeath == MOD_ROCKET_HOMING || ent->methodOfDeath == MOD_THERMAL) ) { g_entities[ent->r.ownerNum].client->ps.persistant[PERS_IMPRESSIVE_COUNT]++; } 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: if ( !strcmp( ent->classname, "hook" ) ) { // gentity_t *nent = G_Spawn(); vector3 v; int i; if ( other->takedamage && other->client ) { // G_AddEvent( nent, EV_DISRUPTOR_HIT, DirToByte( trace->plane.normal ) ); // nent->s.otherEntityNum = other->s.number; ent->enemy = other; for ( i = 0; i < 3; i++ ) v.raw[i] = other->r.currentOrigin.raw[i] + (other->r.mins.raw[i] + other->r.maxs.raw[i]) * 0.5f; SnapVectorTowards( &v, &ent->s.pos.trBase ); // Save net bandwidth } else { VectorCopy( &trace->endpos, &v ); // G_AddEvent( nent, EV_DISRUPTOR_HIT, DirToByte( trace->plane.normal ) ); ent->enemy = NULL; } SnapVectorTowards( &v, &ent->s.pos.trBase ); // Save net bandwidth // nent->freeAfterEvent = true; //Change over to a normal entity right at the point of impact // nent->s.eType = ET_GENERAL; // ent->s.eType = ET_GENERAL; 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; // ent->parent->client->ps.eFlags |= EF_GRAPPLE_SWING; //ent->genericValue10 = 1; ent->parent->client->fireHeld = qfalse; VectorCopy( &ent->r.currentOrigin, &ent->parent->client->ps.lastHitLoc ); trap->LinkEntity( (sharedEntity_t *)ent ); // trap->LinkEntity( (sharedEntity_t *)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 && !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( (sharedEntity_t *)ent ); }
// 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_railgun_fire (gentity_t *ent) { vec3_t end; trace_t trace; gentity_t *tent; gentity_t *traceEnt; int damage; int i; int hits; int unlinked; int passent; gentity_t *unlinkedEntities[MAX_RAIL_HITS]; if (0) { tent = fire_bfg (ent, muzzle, forward); tent->damage *= s_quadFactor; tent->splashDamage *= s_quadFactor; return; } damage = 80 * s_quadFactor; VectorMA (muzzle, 8192, forward, end); // trace only against the solids, so the railgun will go through people unlinked = 0; hits = 0; passent = ent->s.number; do { trap_Trace (&trace, muzzle, NULL, NULL, end, passent, MASK_SHOT ); if ( trace.entityNum >= ENTITYNUM_MAX_NORMAL ) { break; } traceEnt = &g_entities[ trace.entityNum ]; if ( traceEnt->takedamage ) { if( LogAccuracyHit( traceEnt, ent ) ) { hits++; } G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN); } if ( trace.contents & CONTENTS_SOLID ) { break; // we hit something solid enough to stop the beam } // unlink this entity, so the next trace will go past it trap_UnlinkEntity( traceEnt ); unlinkedEntities[unlinked] = traceEnt; unlinked++; } while ( unlinked < MAX_RAIL_HITS ); // link back in any entities we unlinked for ( i = 0 ; i < unlinked ; i++ ) { trap_LinkEntity( unlinkedEntities[i] ); } // the final trace endpos will be the terminal point of the rail trail // snap the endpos to integers to save net bandwidth, but nudged towards the line SnapVectorTowards( trace.endpos, muzzle ); // send railgun beam effect tent = G_TempEntity( trace.endpos, EV_RAILTRAIL ); // set player number for custom colors on the railtrail tent->s.clientNum = ent->s.clientNum; VectorCopy( muzzle, tent->s.origin2 ); // move origin a bit to come closer to the drawn gun muzzle VectorMA( tent->s.origin2, 4, right, tent->s.origin2 ); VectorMA( tent->s.origin2, -1, up, tent->s.origin2 ); // no explosion at end if SURF_NOIMPACT, but still make the trail if ( trace.surfaceFlags & SURF_NOIMPACT ) { tent->s.eventParm = 255; // don't make the explosion at the end } else { tent->s.eventParm = DirToByte( trace.plane.normal ); } tent->s.clientNum = ent->s.clientNum; // give the shooter a reward sound if they have made two railgun hits in a row if ( hits == 0 ) { // complete miss ent->client->accurateCount = 0; } else { // check for "impressive" reward sound ent->client->accurateCount += hits; if ( ent->client->accurateCount >= 2 ) { ent->client->accurateCount -= 2; ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++; // add the sprite over the player's head ent->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); ent->client->ps.eFlags |= EF_AWARD_IMPRESSIVE; ent->client->rewardTime = level.time + REWARD_SPRITE_TIME; } ent->client->accuracy_hits++; } }
//G_MissileImpact now returns qfalse if and only if the player physically dodged the damage. //this allows G_RunMissile to properly handle he qboolean G_MissileImpact( gentity_t *ent, trace_t *trace ) { //void G_MissileImpact( gentity_t *ent, trace_t *trace ) { //[/DodgeSys] gentity_t *other; qboolean isKnockedSaber = qfalse; //[DodgeSys] int missileDmg; //[/DodgeSys] other = &g_entities[trace->entityNum]; // check for bounce //[WeaponSys] //allow thermals to bounce off players and such. if ( (!other->takedamage || ent->s.weapon == WP_THERMAL) && //if ( !other->takedamage && //[/WeaponSys] (ent->bounceCount > 0 || ent->bounceCount == -5) && ( ent->flags & ( FL_BOUNCE | FL_BOUNCE_HALF ) ) ) { G_BounceMissile( ent, trace ); G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); //[DodgeSys] return qtrue; //return; //[/DodgeSys] } 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 ); //[DodgeSys] return qtrue; //return; //[/DodgeSys] } 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; } //[DodgeSys] return qtrue; //return; //[/DodgeSys] } 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 && //[Asteroids] ent->methodOfDeath != MOD_TURBLAST && ent->methodOfDeath != MOD_TARGET_LASER) //[/Asteroids] { 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); //[DodgeSys] return qtrue; //return; //[/DodgeSys] } } //ROP VEHICLE_IMP START if((other->s.NPC_class == CLASS_VEHICLE && other->m_pVehicle && !other->m_pVehicle->m_pVehicleInfo->AllWeaponsDoDamageToShields && other->client->ps.stats[STAT_ARMOR] > 0) || other->flags & FL_SHIELDED) { if (ent->s.weapon != WP_ROCKET_LAUNCHER && ent->s.weapon != WP_THERMAL && ent->s.weapon != WP_GRENADE && ent->s.weapon != WP_DET_PACK && ent->s.weapon != WP_DEMP2 && ent->s.weapon != WP_EMPLACED_GUN && ent->s.weapon != WP_TURRET && 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); //[DodgeSys] return qtrue; //return; //[/DodgeSys] } } //ROP VEHICLE_IMP END //[BoltBlockSys] if (OJP_SaberCanBlock(other, ent, qfalse, trace->endpos, -1, -1)) //[/BoltBlockSys] { //only block one projectile per 200ms (to prevent giant swarms of projectiles being blocked) //[BoltBlockSys] //racc - missile hit the actual player and it's a type of missile that you can deflect/ref with the saber. //racc - play projectile block animation other->client->ps.weaponTime = 0; WP_SaberBlockNonRandom(other, ent->r.currentOrigin, qtrue); OJP_HandleBoltBlock(ent, other, trace); //[DodgeSys] return qtrue; //return; //[/DodgeSys] //[/BoltBlockSys] } 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_TUSKEN_RIFLE && ent->s.weapon != WP_ROCKET_LAUNCHER && ent->s.weapon != WP_THERMAL && ent->s.weapon != WP_GRENADE && ent->s.weapon != WP_DET_PACK && //[BoltBlockSys] //ent->s.weapon != WP_DEMP2 && //[BoltBlockSys] 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 //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 && !BG_SaberInAttack(otherOwner->client->ps.saberMove)) || (otherOwner->client && (pm->cmd.buttons & BUTTON_FORCEPOWER || pm->cmd.buttons & BUTTON_FORCEGRIP || pm->cmd.buttons & BUTTON_FORCE_LIGHTNING) )) { //racc - play projectile block animation even in . otherOwner->client->ps.weaponTime = 0; WP_SaberBlockNonRandom(otherOwner, ent->r.currentOrigin, qtrue); } //[BoltBlockSys] OJP_HandleBoltBlock(ent, otherOwner, trace); //[/BoltBlockSys] //[DodgeSys] return qtrue; //return; //[/DodgeSys] } } // check for sticking //[SaberThrowSys] if ( !other->takedamage && ( ent->s.eFlags & EF_MISSILE_STICK ) && ent->s.weapon != WP_SABER) //if ( !other->takedamage && ( ent->s.eFlags & EF_MISSILE_STICK ) ) //[/SaberThrowSys] { laserTrapStick( ent, trace->endpos, trace->plane.normal ); G_AddEvent( ent, EV_MISSILE_STICK, 0 ); //[DodgeSys] return qtrue; //return; //[/DodgeSys] } // impact damage if (other->takedamage && !isKnockedSaber) { //[DodgeSys] //make players be able to dodge projectiles. missileDmg = ent->damage; if(G_DoDodge(other, &g_entities[ent->r.ownerNum], trace->endpos, -1, &missileDmg, ent->methodOfDeath)) { //player dodged the damage, have missile continue moving. if(ent->s.weapon == WP_ROCKET_LAUNCHER) ent->genericValue1 = 0; return qfalse; } //[/DodgeSys] // FIXME: wrong damage direction? //[DodgeSys] if ( missileDmg ) { //if ( ent->damage ) { //[/DodgeSys] vec3_t velocity; qboolean didDmg = qfalse; 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, //[DodgeSys] ent->r.currentOrigin, missileDmg, //[/DodgeSys] DAMAGE_HALF_ABSORB, ent->methodOfDeath); didDmg = qtrue; } } else { gentity_t *owner = &g_entities[ent->r.ownerNum]; float distance = VectorDistance(owner->r.currentOrigin,other->r.currentOrigin); if(distance <= 100.0f) { G_Damage (other, ent, owner, velocity, //[DodgeSys] ent->r.currentOrigin, missileDmg * 2, //[/DodgeSys] 0, ent->methodOfDeath); } else if (distance <= 300.0f) { G_Damage (other, ent, owner, velocity, //[DodgeSys] ent->r.currentOrigin, missileDmg * 1.5, //[/DodgeSys] 0, ent->methodOfDeath); } else { G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, //[DodgeSys] ent->r.currentOrigin, missileDmg, //[/DodgeSys] 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 ) { G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, other, ent, ent->splashMethodOfDeath ); } if (ent->s.weapon == G2_MODEL_PART) { ent->freeAfterEvent = qfalse; //it will free itself } trap_LinkEntity( ent ); //[DodgeSys] return qtrue; //[/DodgeSys] }