void SabBeh_RunSaberBehavior( gentity_t *self, sabmech_t *mechSelf, gentity_t *otherOwner, sabmech_t *mechOther, vec3_t hitLoc, qboolean *didHit, qboolean otherHitSaberBlade ) { qboolean selfMishap = qfalse; qboolean otherMishap = qfalse; //initalize the sab mechanic data. ClearSabMech(mechSelf); ClearSabMech(mechOther); if(!otherOwner) {//not a saber-on-saber hit, no mishap handling. return; } //G_Printf("BG_SaberInNonIdleDamageMove\n"); if(BG_SaberInNonIdleDamageMove(&self->client->ps, self->localAnimIndex) ) {//self is attacking //G_Printf("(y)\n"); if(BG_SaberInNonIdleDamageMove(&otherOwner->client->ps, otherOwner->localAnimIndex)) {//and otherOwner is attacking SabBeh_AttackVsAttack(self, mechSelf, otherOwner, mechOther, &selfMishap, &otherMishap); } else if(OJP_SaberCanBlock(otherOwner, self, qfalse, hitLoc, -1, -1)) {//and otherOwner is blocking or parrying //this is called with dual with both sabers[DUALRAWR] SabBeh_AttackVsBlock(self, mechSelf, otherOwner, mechOther, hitLoc, otherHitSaberBlade, &selfMishap, &otherMishap); *didHit = qfalse; } else {//otherOwner in some other state //no mishaps for this at the moment. } } else if( OJP_SaberCanBlock(self, otherOwner, qfalse, hitLoc, -1, -1) ) {//self is blocking or parrying if(BG_SaberInNonIdleDamageMove(&otherOwner->client->ps, otherOwner->localAnimIndex)) {//and otherOwner is attacking SabBeh_AttackVsBlock(otherOwner, mechOther, self, mechSelf, hitLoc, qtrue, &otherMishap, &selfMishap); } else if(OJP_SaberCanBlock(otherOwner, self, qfalse, hitLoc, -1, -1)) {//and otherOwner is blocking or parrying } else {//otherOwner in some other state //no mishaps for this at the moment. } } else {//whatever other states self can be in. (returns, bounces, or something) //just act like no mishaps can happen } }
//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] }