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 ); }
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 ); }
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 G_MissileImpact( gentity_t *ent, trace_t *trace ) { gentity_t *other; qboolean hitClient = qfalse; other = &g_entities[trace->entityNum]; // check for bounce if ( !other->takedamage && (ent->bounceCount > 0 || ent->bounceCount == -5) && ( ent->flags & ( FL_BOUNCE | FL_BOUNCE_HALF ) ) ) { G_BounceMissile( ent, trace ); // G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } if ( other->s.eType == ET_MISSILE && ent->s.weapon == WP_QUANTIZER && other->s.weapon == WP_MORTAR ) {//Quantizer can deflect mortar shots :3 G_DeflectMissile( ent, other, &ent->s.pos.trDelta ); other->r.ownerNum = ent->r.ownerNum; G_FreeEntity( ent ); return; } // impact damage if (other->takedamage) { // FIXME: wrong damage direction? if ( ent->damage ) { vector3 velocity; if( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; hitClient = qtrue; } BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, &velocity ); if ( VectorLength( &velocity ) == 0 ) { velocity.z = 1; // stepped on a grenade } //Raz: The inflictor is the missile, for direct hits from quantizer/RLauncher/etc G_Damage( other, ent, &g_entities[ent->r.ownerNum], ent, NULL/*velocity*/, &ent->s.origin, ent->damage, 0, ent->methodOfDeath ); } } // is it cheaper in bandwidth to just remove this ent and create a new // one, rather than changing the missile into the explosion? if ( other->takedamage && other->client ) { G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( &trace->plane.normal ) ); ent->s.otherEntityNum = other->s.number; } else if( trace->surfaceFlags & SURF_METALSTEPS ) { G_AddEvent( ent, EV_MISSILE_MISS_METAL, DirToByte( &trace->plane.normal ) ); } else { G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( &trace->plane.normal ) ); } ent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact ent->s.eType = ET_GENERAL; VectorSnapTowards( &trace->endpos, &ent->s.pos.trBase ); // save net bandwidth G_SetOrigin( ent, &trace->endpos ); // splash damage (doesn't apply to person directly hit) #if 0 if ( ent->splashDamage ) { if( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, other, ent->splashMethodOfDeath ) ) { if( !hitClient ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; } } } #else //QtZ if ( (ent->splashDamage || ent->splashRadius) && G_RadiusDamage( &trace->endpos, ent->parent, (float)ent->splashDamage, (float)ent->splashRadius, other, ent, ent->splashMethodOfDeath ) && !hitClient && g_entities[ent->r.ownerNum].client ) g_entities[ent->r.ownerNum].client->accuracy_hits++; #endif trap->SV_LinkEntity( (sharedEntity_t *)ent ); }
//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] }
//G_Printf("%i: Bounced Bolt @ Level %i\n", player->s.number, otherDefLevel); void OJP_HandleBoltBlock(gentity_t *bolt, gentity_t *player, trace_t *trace) { //handles all the behavior needed to saber block a blaster bolt. //I created this function to unite the duplicated code in G_MissileImpact vec3_t fwd; gentity_t *saberBoltEffect; int otherDefLevel=ReflectionLevel(player); int randMax = 0; gentity_t *prevOwner = &g_entities[bolt->r.ownerNum]; //previous owner of the bolt. Used for awarding experience to attacker. qboolean manualDeflect = ((player->client->pers.cmd.buttons & BUTTON_ATTACK) ? qtrue : qfalse); //create the bolt saber block effect saberBoltEffect = G_TempEntity( bolt->r.currentOrigin, EV_SABER_BLOCK ); VectorCopy(bolt->r.currentOrigin, saberBoltEffect->s.origin); VectorCopy(trace->plane.normal, saberBoltEffect->s.angles); saberBoltEffect->s.eventParm = 0; saberBoltEffect->s.weapon = 0;//saberNum saberBoltEffect->s.legsAnim = 0;//bladeNum if(manualDeflect) { if(otherDefLevel == FORCE_LEVEL_3) { //manual reflection, bounce to the crosshair, roughly int rand = Q_irand(1,100); rand = ( rand <= 50 ? player->client->shotsBlocked+1 : 9001 ); } else if(otherDefLevel == FORCE_LEVEL_2) { //natural reflection, bounce back to the attacker. int rand = Q_irand(1,100); if(rand <= 25) { randMax=player->client->shotsBlocked+1; } else randMax=9001; } else { //just deflect the attack int rand = Q_irand(1,100); if(rand <= 15) { randMax=player->client->shotsBlocked+1; } else randMax=9001; } } else { //determine reflection level. if(otherDefLevel == FORCE_LEVEL_3) { //manual reflection, bounce to the crosshair, roughly randMax=2; } else if(otherDefLevel == FORCE_LEVEL_2) { //natural reflection, bounce back to the attacker. randMax=5; } else { //just deflect the attack randMax=7; } } //G_Printf("%i: Bounced Bolt @ Level %i\n", player->s.number, otherDefLevel); AngleVectors(player->client->ps.viewangles, fwd, NULL, NULL); player->client->shotsBlocked++; if(player->client->shotsBlocked >= randMax && player->client->ps.fd.saberAnimLevel != SS_MEDIUM && player->client->ps.fd.saberAnimLevel != SS_TAVION && player->client->ps.fd.saberAnimLevel != SS_FAST) { gentity_t *owner = &g_entities[player->r.ownerNum]; float distance = VectorDistance(owner->r.currentOrigin, prevOwner->r.currentOrigin); player->client->shotsBlocked=0; if(distance < 80.0f) { //attempted upclose exception G_DeflectMissile(player, bolt, fwd); } else { vec3_t bounce_dir, angs; float speed; int i=0; //gentity_t *owner = ent; //int isowner = 0; if(!manualDeflect) { for(i=0; i<3; i++) { fwd[i]=1.5f; } } //add some slop factor to the manual reflections. if(player->client->pers.cmd.forwardmove >= 0) { float slopFactor = (MISHAP_MAXINACCURACY-6) * (FORCE_LEVEL_3 - player->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE])/FORCE_LEVEL_3; //[MoreRandom] slopFactor += Q_irand(1,5); vectoangles( fwd, angs ); //angs[PITCH] += flrand(-slopFactor, slopFactor); //angs[YAW] += flrand(-slopFactor, slopFactor); angs[PITCH] += flrand(1, slopFactor); angs[YAW] += flrand(1, slopFactor); AngleVectors( angs, fwd, NULL, NULL ); } else { vectoangles( fwd, angs ); angs[PITCH]+=flrand(1,3); angs[YAW]+=flrand(1,3); AngleVectors( angs, fwd, NULL, NULL ); } //G_Printf("%i: %i: Level 3 Reflect\n", level.time, player->s.number); //save the original speed speed = VectorNormalize( bolt->s.pos.trDelta ); VectorCopy(fwd, bounce_dir); VectorScale( bounce_dir, speed, bolt->s.pos.trDelta ); bolt->s.pos.trTime = level.time; // move a bit on the very first frame VectorCopy( bolt->r.currentOrigin, bolt->s.pos.trBase ); if ( bolt->s.weapon != WP_SABER && bolt->s.weapon != G2_MODEL_PART ) { //you are mine, now! bolt->r.ownerNum = player->s.number; } if ( bolt->s.weapon == WP_ROCKET_LAUNCHER ) { //stop homing bolt->think = 0; bolt->nextthink = 0; } } } else G_DeflectMissile(player, bolt, fwd); /* if (otherDefLevel <= FORCE_LEVEL_1) {//only randomly deflect away the bolt G_DeflectMissile(player, bolt, fwd); } else if (otherDefLevel == FORCE_LEVEL_2) {//bounce the bolt back to sender //G_Printf("%i: %i: Level 2 Reflect\n", level.time, player->s.number); G_DeflectMissile(player, bolt, fwd); } else if(otherDefLevel == FORCE_LEVEL_3 && distance < 80.0f &&(BG_SaberInAttack( player->client->ps.saberMove ) || PM_SaberInStart( player->client->ps.saberMove))) {//manual reflection, bounce to the crosshair, roughly G_DeflectMissile(player, bolt, fwd);; } else if(player->client->ps.fd.saberAnimLevel == SS_FAST || player->client->ps.fd.saberAnimLevel == SS_MEDIUM || player->client->ps.fd.saberAnimLevel == SS_TAVION) { G_DeflectMissile(player, bolt, fwd); } else {//FORCE_LEVEL_3, reflect the bolt to whereever the player is aiming. gentity_t *owner = &g_entities[player->r.ownerNum]; float distance = VectorDistance(owner->r.currentOrigin, prevOwner->r.currentOrigin); if(distance < 80.0f) {//attempted upclose exception G_DeflectMissile(player, bolt, fwd); } else { vec3_t bounce_dir, angs; float speed; //gentity_t *owner = ent; //int isowner = 0; //add some slop factor to the manual reflections. if(player->client->pers.cmd.forwardmove >= 0) { float slopFactor = (MISHAP_MAXINACCURACY-6) * (FORCE_LEVEL_3 - player->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE])/FORCE_LEVEL_3; //[MoreRandom] float distance = VectorDistance(player->r.currentOrigin,prevOwner->r.currentOrigin); slopFactor += Q_irand(1,5); vectoangles( fwd, angs ); //angs[PITCH] += flrand(-slopFactor, slopFactor); //angs[YAW] += flrand(-slopFactor, slopFactor); angs[PITCH] += flrand(1, slopFactor); angs[YAW] += flrand(1, slopFactor); AngleVectors( angs, fwd, NULL, NULL ); } else { vectoangles( fwd, angs ); angs[PITCH]+=flrand(1,3); angs[YAW]+=flrand(1,3); AngleVectors( angs, fwd, NULL, NULL ); } //G_Printf("%i: %i: Level 3 Reflect\n", level.time, player->s.number); //save the original speed speed = VectorNormalize( bolt->s.pos.trDelta ); VectorCopy(fwd, bounce_dir); VectorScale( bounce_dir, speed, bolt->s.pos.trDelta ); bolt->s.pos.trTime = level.time; // move a bit on the very first frame VectorCopy( bolt->r.currentOrigin, bolt->s.pos.trBase ); if ( bolt->s.weapon != WP_SABER && bolt->s.weapon != G2_MODEL_PART ) {//you are mine, now! bolt->r.ownerNum = player->s.number; } if ( bolt->s.weapon == WP_ROCKET_LAUNCHER ) {//stop homing bolt->think = 0; bolt->nextthink = 0; } } } */ //For jedi AI player->client->ps.saberEventFlags |= SEF_DEFLECTED; //deduce DP cost //[ExpSys] bolt->activator = prevOwner; if(otherDefLevel == FORCE_LEVEL_3 && player->client->pers.cmd.forwardmove < 0) { int amount = OJP_SaberBlockCost(player, bolt, trace->endpos); amount/=100*40; G_DodgeDrain(player, prevOwner, amount); } else G_DodgeDrain(player, prevOwner, OJP_SaberBlockCost(player, bolt, trace->endpos)); //[ExpSys] //[SaberLockSys] player->client->ps.saberLockFrame = 0; //break out of saberlocks. //[/SaberLockSys] //debounce time between blocks. player->client->ps.saberBlockTime = level.time + (600 - (player->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE]*200)); }