static void Player_SetFreeze(lua_State *L, jpluaEntity_t *ent){ bool doFreeze = lua_toboolean( L, 3 ) ? true : false; if (doFreeze){ ent->client->pers.adminData.isFrozen = qtrue; if ( ent->client->hook ){ Weapon_HookFree(ent->client->hook); } VectorClear(&ent->client->ps.velocity); } else ent->client->pers.adminData.isFrozen = qfalse; }
void Weapon_HookThink (gentity_t *ent) { ent->nextthink = level.time + FRAMETIME; if (ent->enemy && ent->enemy->player) { vec3_t v, oldorigin; if ( ent->enemy->player->ps.pm_type == PM_DEAD ) { Weapon_HookFree( ent ); return; } VectorCopy(ent->r.currentOrigin, oldorigin); v[0] = ent->enemy->r.currentOrigin[0] + (ent->enemy->s.mins[0] + ent->enemy->s.maxs[0]) * 0.5; v[1] = ent->enemy->r.currentOrigin[1] + (ent->enemy->s.mins[1] + ent->enemy->s.maxs[1]) * 0.5; v[2] = ent->enemy->r.currentOrigin[2] + (ent->enemy->s.mins[2] + ent->enemy->s.maxs[2]) * 0.5; SnapVectorTowards( v, oldorigin ); // save net bandwidth G_SetOrigin( ent, v ); } VectorCopy( ent->r.currentOrigin, ent->parent->player->ps.grapplePoint); }
/* ================== player_die ================== */ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) { gentity_t *ent; int anim; int contents; int killer; int i; char *killerName, *obit; if ( self->client->ps.pm_type == PM_DEAD ) { return; } if ( level.intermissiontime ) { return; } // check for an almost capture CheckAlmostCapture( self, attacker ); // check for a player that almost brought in cubes CheckAlmostScored( self, attacker ); if (self->client && self->client->hook) { Weapon_HookFree(self->client->hook); } #ifdef MISSIONPACK if ((self->client->ps.eFlags & EF_TICKING) && self->activator) { self->client->ps.eFlags &= ~EF_TICKING; self->activator->think = G_FreeEntity; self->activator->nextthink = level.time; } #endif self->client->ps.pm_type = PM_DEAD; if ( attacker ) { killer = attacker->s.number; if ( attacker->client ) { killerName = attacker->client->pers.netname; } else { killerName = "<non-client>"; } } else { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if ( killer < 0 || killer >= MAX_CLIENTS ) { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if ( meansOfDeath < 0 || meansOfDeath >= ARRAY_LEN( modNames ) ) { obit = "<bad obituary>"; } else { obit = modNames[meansOfDeath]; } G_LogPrintf("Kill: %i %i %i: %s killed %s by %s\n", killer, self->s.number, meansOfDeath, killerName, self->client->pers.netname, obit ); // broadcast the death event to everyone ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY ); ent->s.eventParm = meansOfDeath; ent->s.otherEntityNum = self->s.number; ent->s.otherEntityNum2 = killer; ent->r.svFlags = SVF_BROADCAST; // send to everyone self->enemy = attacker; self->client->ps.persistant[PERS_KILLED]++; if (attacker && attacker->client) { attacker->client->lastkilled_client = self->s.number; if ( attacker == self || OnSameTeam (self, attacker ) ) { AddScore( attacker, self->r.currentOrigin, -1 ); } else { AddScore( attacker, self->r.currentOrigin, 1 ); if( meansOfDeath == MOD_GAUNTLET ) { // play humiliation on player attacker->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++; // add the sprite over the player's head attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); attacker->client->ps.eFlags |= EF_AWARD_GAUNTLET; attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; // also play humiliation on target self->client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_GAUNTLETREWARD; } // check for two kills in a short amount of time // if this is close enough to the last kill, give a reward sound if ( level.time - attacker->client->lastKillTime < CARNAGE_REWARD_TIME ) { // play excellent on player attacker->client->ps.persistant[PERS_EXCELLENT_COUNT]++; // add the sprite over the player's head attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); attacker->client->ps.eFlags |= EF_AWARD_EXCELLENT; attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; } attacker->client->lastKillTime = level.time; } } else { AddScore( self, self->r.currentOrigin, -1 ); } // Add team bonuses Team_FragBonuses(self, inflictor, attacker); // if I committed suicide, the flag does not fall, it returns. if (meansOfDeath == MOD_SUICIDE) { if ( self->client->ps.powerups[PW_NEUTRALFLAG] ) { // only happens in One Flag CTF Team_ReturnFlag( TEAM_FREE ); self->client->ps.powerups[PW_NEUTRALFLAG] = 0; } else if ( self->client->ps.powerups[PW_REDFLAG] ) { // only happens in standard CTF Team_ReturnFlag( TEAM_RED ); self->client->ps.powerups[PW_REDFLAG] = 0; } else if ( self->client->ps.powerups[PW_BLUEFLAG] ) { // only happens in standard CTF Team_ReturnFlag( TEAM_BLUE ); self->client->ps.powerups[PW_BLUEFLAG] = 0; } } // if client is in a nodrop area, don't drop anything (but return CTF flags!) contents = trap_PointContents( self->r.currentOrigin, -1 ); if ( !( contents & CONTENTS_NODROP )) { TossClientItems( self ); } else { if ( self->client->ps.powerups[PW_NEUTRALFLAG] ) { // only happens in One Flag CTF Team_ReturnFlag( TEAM_FREE ); } else if ( self->client->ps.powerups[PW_REDFLAG] ) { // only happens in standard CTF Team_ReturnFlag( TEAM_RED ); } else if ( self->client->ps.powerups[PW_BLUEFLAG] ) { // only happens in standard CTF Team_ReturnFlag( TEAM_BLUE ); } } #ifdef MISSIONPACK TossClientPersistantPowerups( self ); if( g_gametype.integer == GT_HARVESTER ) { TossClientCubes( self ); } #endif Cmd_Score_f( self ); // show scores // send updated scores to any clients that are following this one, // or they would get stale scoreboards for ( i = 0 ; i < level.maxclients ; i++ ) { gclient_t *client; client = &level.clients[i]; if ( client->pers.connected != CON_CONNECTED ) { continue; } if ( client->sess.sessionTeam != TEAM_SPECTATOR ) { continue; } if ( client->sess.spectatorClient == self->s.number ) { Cmd_Score_f( g_entities + i ); } } self->takedamage = qtrue; // can still be gibbed self->s.weapon = WP_NONE; self->s.powerups = 0; self->r.contents = CONTENTS_CORPSE; self->s.angles[0] = 0; self->s.angles[2] = 0; LookAtKiller (self, inflictor, attacker); VectorCopy( self->s.angles, self->client->ps.viewangles ); self->s.loopSound = 0; self->r.maxs[2] = -8; // don't allow respawn until the death anim is done // g_forcerespawn may force spawning at some later time self->client->respawnTime = level.time + 1700; // remove powerups memset( self->client->ps.powerups, 0, sizeof(self->client->ps.powerups) ); // never gib in a nodrop if ( (self->health <= GIB_HEALTH && !(contents & CONTENTS_NODROP) && g_blood.integer) || meansOfDeath == MOD_SUICIDE) { // gib death GibEntity( self, killer ); } else { // normal death static int i; switch ( i ) { case 0: anim = BOTH_DEATH1; break; case 1: anim = BOTH_DEATH2; break; case 2: default: anim = BOTH_DEATH3; break; } // for the no-blood option, we need to prevent the health // from going to gib level if ( self->health <= GIB_HEALTH ) { self->health = GIB_HEALTH+1; } self->client->ps.legsAnim = ( ( self->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; self->client->ps.torsoAnim = ( ( self->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; G_AddEvent( self, EV_DEATH1 + i, killer ); // the body can still be gibbed self->die = body_die; // globally cycle through the different death animations i = ( i + 1 ) % 3; #ifdef MISSIONPACK if (self->s.eFlags & EF_KAMIKAZE) { Kamikaze_DeathTimer( self ); } #endif } trap_LinkEntity (self); }
void G_RunMissile( gentity_t *ent ) { vector3 origin, groundSpot; trace_t tr; int passent; qboolean isKnockedSaber = qfalse; if ( ent->neverFree && ent->s.weapon == WP_SABER && (ent->flags & FL_BOUNCE_HALF) ) { isKnockedSaber = qtrue; ent->s.pos.trType = TR_GRAVITY; } // get current position BG_EvaluateTrajectory( &ent->s.pos, level.time, &origin ); // if this missile bounced off an invulnerability sphere if ( ent->target_ent ) { passent = ent->target_ent->s.number; } else { // ignore interactions with the missile owner if ( (ent->r.svFlags&SVF_OWNERNOTSHARED) && (ent->s.eFlags&EF_JETPACK_ACTIVE) ) {//A vehicle missile that should be solid to its owner //I don't care about hitting my owner passent = ent->s.number; } else { passent = ent->r.ownerNum; } } // trace a line from the previous position to the current position if ( d_projectileGhoul2Collision.integer ) { trap->Trace( &tr, &ent->r.currentOrigin, &ent->r.mins, &ent->r.maxs, &origin, passent, ent->clipmask, qfalse, G2TRFLAG_DOGHOULTRACE | G2TRFLAG_GETSURFINDEX | G2TRFLAG_THICK | G2TRFLAG_HITCORPSES, g_g2TraceLod.integer ); if ( tr.fraction != 1.0f && tr.entityNum < ENTITYNUM_WORLD ) { gentity_t *g2Hit = &g_entities[tr.entityNum]; if ( g2Hit->inuse && g2Hit->client && g2Hit->ghoul2 ) { //since we used G2TRFLAG_GETSURFINDEX, tr.surfaceFlags will actually contain the index of the surface on the ghoul2 model we collided with. g2Hit->client->g2LastSurfaceHit = tr.surfaceFlags; g2Hit->client->g2LastSurfaceTime = level.time; } if ( g2Hit->ghoul2 ) { tr.surfaceFlags = 0; //clear the surface flags after, since we actually care about them in here. } //Raz: Portals! if ( g2Hit->s.eType == ET_SPECIAL && g2Hit->s.userInt1 ) { if ( g2Hit->touch ) { g2Hit->touch( g2Hit, ent, &tr ); } JPLua::Entity_CallFunction( g2Hit, JPLua::JPLUA_ENTITY_TOUCH, (intptr_t)ent, (intptr_t)&tr ); goto passthrough; } } } else { trap->Trace( &tr, &ent->r.currentOrigin, &ent->r.mins, &ent->r.maxs, &origin, passent, ent->clipmask, qfalse, 0, 0 ); } if ( tr.startsolid || tr.allsolid ) { // make sure the tr.entityNum is set to the entity we're stuck in trap->Trace( &tr, &ent->r.currentOrigin, &ent->r.mins, &ent->r.maxs, &ent->r.currentOrigin, passent, ent->clipmask, qfalse, 0, 0 ); tr.fraction = 0; } else { VectorCopy( &tr.endpos, &ent->r.currentOrigin ); } if ( ent->passThroughNum && tr.entityNum == (ent->passThroughNum - 1) ) { VectorCopy( &origin, &ent->r.currentOrigin ); trap->LinkEntity( (sharedEntity_t *)ent ); goto passthrough; } trap->LinkEntity( (sharedEntity_t *)ent ); if ( ent->s.weapon == G2_MODEL_PART && !ent->bounceCount ) { vector3 lowerOrg; trace_t trG; VectorCopy( &ent->r.currentOrigin, &lowerOrg ); lowerOrg.z -= 1; trap->Trace( &trG, &ent->r.currentOrigin, &ent->r.mins, &ent->r.maxs, &lowerOrg, passent, ent->clipmask, qfalse, 0, 0 ); VectorCopy( &trG.endpos, &groundSpot ); if ( !trG.startsolid && !trG.allsolid && trG.entityNum == ENTITYNUM_WORLD ) { ent->s.groundEntityNum = trG.entityNum; } else { ent->s.groundEntityNum = ENTITYNUM_NONE; } } if ( ent->parent && ent->parent->client && ent->parent->client->hook && ent->parent->client->hook == ent && (ent->parent->client->ps.duelInProgress || BG_SaberInSpecial( ent->parent->client->ps.saberMove ) || !(japp_allowHook.integer & (1 << level.gametype)) || ent->parent->client->pers.adminData.isSlept || g_entities[tr.entityNum].client) ) { // not allowed to have hook out Weapon_HookFree( ent ); return; } if ( tr.fraction != 1 ) { // never explode or bounce on sky if ( tr.surfaceFlags & SURF_NOIMPACT ) { // If grapple, reset owner // if ( ent->parent && ent->parent->client && ent->parent->client->hook == ent ) // ent->parent->client->hook = NULL; if ( ent->parent && ent->parent->client && ent->parent->client->hook && ent->parent->client->hook == ent ) { Weapon_HookFree( ent->parent->client->hook ); } if ( (ent->s.weapon == WP_SABER && ent->isSaberEntity) || isKnockedSaber ) { G_RunThink( ent ); return; } else if ( ent->s.weapon != G2_MODEL_PART ) { G_FreeEntity( ent ); return; } } if ( ent->s.weapon > WP_NONE && ent->s.weapon < WP_NUM_WEAPONS && (tr.entityNum < MAX_CLIENTS || g_entities[tr.entityNum].s.eType == ET_NPC) ) { //player or NPC, try making a mark on him //copy current pos to s.origin, and current projected traj to origin2 VectorCopy( &ent->r.currentOrigin, &ent->s.origin ); BG_EvaluateTrajectory( &ent->s.pos, level.time, &ent->s.origin2 ); if ( VectorCompare( &ent->s.origin, &ent->s.origin2 ) ) { ent->s.origin2.z += 2.0f; //whatever, at least it won't mess up. } } G_MissileImpact( ent, &tr ); if ( tr.entityNum == ent->s.otherEntityNum ) { // if the impact event other and the trace ent match then it's ok to do the g2 mark ent->s.trickedEntIndex[0] = 1; } if ( ent->s.eType != ET_MISSILE && ent->s.weapon != G2_MODEL_PART ) { return; // exploded } } passthrough: if ( ent->s.pos.trType == TR_STATIONARY && (ent->s.eFlags & EF_MISSILE_STICK) ) { // stuck missiles should check some special stuff G_RunStuckMissile( ent ); return; } if ( ent->s.weapon == G2_MODEL_PART ) { if ( ent->s.groundEntityNum == ENTITYNUM_WORLD ) { ent->s.pos.trType = TR_LINEAR; VectorClear( &ent->s.pos.trDelta ); ent->s.pos.trTime = level.time; VectorCopy( &groundSpot, &ent->s.pos.trBase ); VectorCopy( &groundSpot, &ent->r.currentOrigin ); if ( ent->s.apos.trType != TR_STATIONARY ) { ent->s.apos.trType = TR_STATIONARY; ent->s.apos.trTime = level.time; ent->s.apos.trBase.roll = 0; ent->s.apos.trBase.pitch = 0; } } } // check think function after bouncing G_RunThink( ent ); }
/* ============ G_MoverPush Objects need to be moved back on a failed push, otherwise riders would continue to slide. If qfalse is returned, *obstacle will be the blocking entity ============ */ qboolean G_MoverPush( gentity_t *pusher, vec3_t move, vec3_t amove, gentity_t **obstacle ) { int i, e; gentity_t *check; vec3_t mins, maxs; pushed_t *p; int entityList[MAX_GENTITIES]; int listedEntities; vec3_t totalMins, totalMaxs; *obstacle = NULL; // mins/maxs are the bounds at the destination // totalMins / totalMaxs are the bounds for the entire move if ( pusher->r.currentAngles[0] || pusher->r.currentAngles[1] || pusher->r.currentAngles[2] || amove[0] || amove[1] || amove[2] ) { float radius; radius = RadiusFromBounds( pusher->s.mins, pusher->s.maxs ); for ( i = 0 ; i < 3 ; i++ ) { mins[i] = pusher->r.currentOrigin[i] + move[i] - radius; maxs[i] = pusher->r.currentOrigin[i] + move[i] + radius; totalMins[i] = mins[i] - move[i]; totalMaxs[i] = maxs[i] - move[i]; } } else { for (i=0 ; i<3 ; i++) { mins[i] = pusher->r.absmin[i] + move[i]; maxs[i] = pusher->r.absmax[i] + move[i]; } VectorCopy( pusher->r.absmin, totalMins ); VectorCopy( pusher->r.absmax, totalMaxs ); for (i=0 ; i<3 ; i++) { if ( move[i] > 0 ) { totalMaxs[i] += move[i]; } else { totalMins[i] += move[i]; } } } // unlink the pusher so we don't get it in the entityList trap_UnlinkEntity( pusher ); listedEntities = trap_EntitiesInBox( totalMins, totalMaxs, entityList, MAX_GENTITIES ); // move the pusher to its final position VectorAdd( pusher->r.currentOrigin, move, pusher->r.currentOrigin ); VectorAdd( pusher->r.currentAngles, amove, pusher->r.currentAngles ); trap_LinkEntity( pusher ); // see if any solid entities are inside the final position for ( e = 0 ; e < listedEntities ; e++ ) { check = &g_entities[ entityList[ e ] ]; if ( check->s.eType == ET_GRAPPLE ) { // if this grappling hook is attached to this mover try to move it with the pusher if ( check->enemy == pusher ) { if (!G_TryPushingProxMine( check, pusher, move, amove )) { // remove hook if ( check->parent && check->parent->player && check->parent->player->hook == check) { Weapon_HookFree(check); } } } continue; } if ( check->s.eType == ET_MISSILE ) { // if it is a prox mine if ( !strcmp(check->classname, "prox mine") ) { // if this prox mine is attached to this mover try to move it with the pusher if ( check->enemy == pusher ) { if (!G_TryPushingProxMine( check, pusher, move, amove )) { //explode check->s.loopSound = 0; G_AddEvent( check, EV_PROXIMITY_MINE_TRIGGER, 0 ); G_ExplodeMissile(check); if (check->activator) { G_FreeEntity(check->activator); check->activator = NULL; } //G_Printf("prox mine explodes\n"); } } else { //check if the prox mine is crushed by the mover if (!G_CheckProxMinePosition( check )) { //explode check->s.loopSound = 0; G_AddEvent( check, EV_PROXIMITY_MINE_TRIGGER, 0 ); G_ExplodeMissile(check); if (check->activator) { G_FreeEntity(check->activator); check->activator = NULL; } //G_Printf("prox mine explodes\n"); } } continue; } } // only push items and players if ( check->s.eType != ET_ITEM && check->s.eType != ET_PLAYER && !check->physicsObject ) { continue; } // if the entity is standing on the pusher, it will definitely be moved if ( check->s.groundEntityNum != pusher->s.number ) { // see if the ent needs to be tested if ( check->r.absmin[0] >= maxs[0] || check->r.absmin[1] >= maxs[1] || check->r.absmin[2] >= maxs[2] || check->r.absmax[0] <= mins[0] || check->r.absmax[1] <= mins[1] || check->r.absmax[2] <= mins[2] ) { continue; } // see if the ent's bbox is inside the pusher's final position // this does allow a fast moving object to pass through a thin entity... if (!G_TestEntityPosition (check)) { continue; } } // the entity needs to be pushed if ( G_TryPushingEntity( check, pusher, move, amove ) ) { continue; } // the move was blocked an entity // bobbing entities are instant-kill and never get blocked if ( pusher->s.pos.trType == TR_SINE || pusher->s.apos.trType == TR_SINE ) { G_Damage( check, pusher, pusher, NULL, NULL, 99999, 0, MOD_CRUSH ); continue; } // save off the obstacle so we can call the block function (crush, etc) *obstacle = check; // move back any entities we already moved // go backwards, so if the same entity was pushed // twice, it goes back to the original position for ( p=pushed_p-1 ; p>=pushed ; p-- ) { VectorCopy (p->origin, p->ent->s.pos.trBase); VectorCopy (p->angles, p->ent->s.apos.trBase); if ( p->ent->player ) { p->ent->player->ps.delta_angles[YAW] = p->deltayaw; VectorCopy (p->origin, p->ent->player->ps.origin); } trap_LinkEntity (p->ent); } return qfalse; } return qtrue; }
void G_MissileImpact( gentity_t *ent, trace_t *trace ) { gentity_t *other; qboolean hitClient = qfalse; qboolean isKnockedSaber = qfalse; other = &g_entities[trace->entityNum]; // check for bounce if ( other->takedamage && (ent->bounceCount > 0 || ent->bounceCount == -5) && ( ent->flags & ( FL_BOUNCE | FL_BOUNCE_HALF ) ) && (g_tweakWeapons.integer & WT_ROCKET_MORTAR && ent->s.weapon == WP_REPEATER && ent->bounceCount == 50 && ent->setTime && ent->setTime > level.time - 300)) { //if its a direct hit and first 500ms of mortar, bounce off player. G_BounceMissile( ent, trace ); G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } else if ( !other->takedamage && (ent->bounceCount > 0 || ent->bounceCount == -5) && ( ent->flags & ( FL_BOUNCE | FL_BOUNCE_HALF ) ) ) { //only on the first bounce vv if (!(g_tweakWeapons.integer & WT_ROCKET_MORTAR && ent->s.weapon == WP_REPEATER && ent->bounceCount == 50 && ent->setTime && ent->setTime < level.time - 1000))//give this mortar a 1 second 'fuse' until its armed { G_BounceMissile( ent, trace ); G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } } else if (ent->neverFree && ent->s.weapon == WP_SABER && (ent->flags & FL_BOUNCE_HALF)) { //this is a knocked-away saber if (ent->bounceCount > 0 || ent->bounceCount == -5) { G_BounceMissile( ent, trace ); G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } isKnockedSaber = qtrue; } // I would glom onto the FL_BOUNCE code section above, but don't feel like risking breaking something else if ( (!other->takedamage && (ent->bounceCount > 0 || ent->bounceCount == -5) && ( ent->flags&(FL_BOUNCE_SHRAPNEL) ) ) || ((trace->surfaceFlags&SURF_FORCEFIELD)&&!ent->splashDamage&&!ent->splashRadius&&(ent->bounceCount > 0 || ent->bounceCount == -5)) ) { G_BounceMissile( ent, trace ); if ( ent->bounceCount < 1 ) { ent->flags &= ~FL_BOUNCE_SHRAPNEL; } //trap->Print("Shrapnel is still there\n"); return; } /* if ( !other->takedamage && ent->s.weapon == WP_THERMAL && !ent->alt_fire ) {//rolling thermal det - FIXME: make this an eFlag like bounce & stick!!! //G_BounceRollMissile( ent, trace ); if ( ent->owner && ent->owner->s.number == 0 ) { G_MissileAddAlerts( ent ); } //trap->linkentity( ent ); return; } */ if ((other->r.contents & CONTENTS_LIGHTSABER) && !isKnockedSaber) { //hit this person's saber, so.. gentity_t *otherOwner = &g_entities[other->r.ownerNum]; if (otherOwner->takedamage && otherOwner->client && otherOwner->client->ps.duelInProgress && otherOwner->client->ps.duelIndex != ent->r.ownerNum) { goto killProj; } } else if (!isKnockedSaber) { if (other->takedamage && other->client && other->client->ps.duelInProgress && other->client->ps.duelIndex != ent->r.ownerNum) { goto killProj; } } if (other->flags & FL_DMG_BY_HEAVY_WEAP_ONLY) { if (ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_ROCKET && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_ROCKET_HOMING && ent->methodOfDeath != MOD_THERMAL && ent->methodOfDeath != MOD_THERMAL_SPLASH && ent->methodOfDeath != MOD_TRIP_MINE_SPLASH && ent->methodOfDeath != MOD_TIMED_MINE_SPLASH && ent->methodOfDeath != MOD_DET_PACK_SPLASH && ent->methodOfDeath != MOD_VEHICLE && ent->methodOfDeath != MOD_CONC && ent->methodOfDeath != MOD_CONC_ALT && ent->methodOfDeath != MOD_SABER && ent->methodOfDeath != MOD_TURBLAST) { vec3_t fwd; if (trace) { VectorCopy(trace->plane.normal, fwd); } else { //oh well AngleVectors(other->r.currentAngles, fwd, NULL, NULL); } G_DeflectMissile(other, ent, fwd); G_MissileBounceEffect(ent, ent->r.currentOrigin, fwd); return; } } if ((other->flags & FL_SHIELDED) && ent->s.weapon != WP_ROCKET_LAUNCHER && ent->s.weapon != WP_THERMAL && ent->s.weapon != WP_TRIP_MINE && ent->s.weapon != WP_DET_PACK && ent->s.weapon != WP_DEMP2 && ent->s.weapon != WP_EMPLACED_GUN && ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_TURBLAST && ent->methodOfDeath != MOD_VEHICLE && ent->methodOfDeath != MOD_CONC && ent->methodOfDeath != MOD_CONC_ALT && !(ent->dflags&DAMAGE_HEAVY_WEAP_CLASS) ) { vec3_t fwd; if (other->client) { AngleVectors(other->client->ps.viewangles, fwd, NULL, NULL); } else { AngleVectors(other->r.currentAngles, fwd, NULL, NULL); } G_DeflectMissile(other, ent, fwd); G_MissileBounceEffect(ent, ent->r.currentOrigin, fwd); return; } if (other->takedamage && other->client && ent->s.weapon != WP_ROCKET_LAUNCHER && ent->s.weapon != WP_THERMAL && ent->s.weapon != WP_TRIP_MINE && ent->s.weapon != WP_DET_PACK && ent->s.weapon != WP_DEMP2 && ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_CONC && ent->methodOfDeath != MOD_CONC_ALT && other->client->ps.saberBlockTime < level.time && !isKnockedSaber && WP_SaberCanBlock(other, ent->r.currentOrigin, 0, 0, qtrue, 0)) //loda fixme, add check for dimensions for blocking here? { //only block one projectile per 200ms (to prevent giant swarms of projectiles being blocked) vec3_t fwd; gentity_t *te; int otherDefLevel = other->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE]; te = G_TempEntity( ent->r.currentOrigin, EV_SABER_BLOCK ); VectorCopy(ent->r.currentOrigin, te->s.origin); VectorCopy(trace->plane.normal, te->s.angles); te->s.eventParm = 0; te->s.weapon = 0;//saberNum te->s.legsAnim = 0;//bladeNum /*if (other->client->ps.velocity[2] > 0 || other->client->pers.cmd.forwardmove || other->client->pers.cmd.rightmove) */ if (other->client->ps.velocity[2] > 0 || other->client->pers.cmd.forwardmove < 0) //now we only do it if jumping or running backward. Should be able to full-on charge. { otherDefLevel -= 1; if (otherDefLevel < 0) { otherDefLevel = 0; } } AngleVectors(other->client->ps.viewangles, fwd, NULL, NULL); if (otherDefLevel == FORCE_LEVEL_1) { //if def is only level 1, instead of deflecting the shot it should just die here } else if (otherDefLevel == FORCE_LEVEL_2) { G_DeflectMissile(other, ent, fwd); } else { G_ReflectMissile(other, ent, fwd); } other->client->ps.saberBlockTime = level.time + (350 - (otherDefLevel*100)); //200; //For jedi AI other->client->ps.saberEventFlags |= SEF_DEFLECTED; if (otherDefLevel == FORCE_LEVEL_3) { other->client->ps.saberBlockTime = 0; //^_^ } if (otherDefLevel == FORCE_LEVEL_1) { goto killProj; } return; } else if ((other->r.contents & CONTENTS_LIGHTSABER) && !isKnockedSaber) { //hit this person's saber, so.. gentity_t *otherOwner = &g_entities[other->r.ownerNum]; if (otherOwner->takedamage && otherOwner->client && ent->s.weapon != WP_ROCKET_LAUNCHER && ent->s.weapon != WP_THERMAL && ent->s.weapon != WP_TRIP_MINE && ent->s.weapon != WP_DET_PACK && ent->s.weapon != WP_DEMP2 && ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_CONC && (g_entities[ent->r.ownerNum].s.bolt1 == other->s.bolt1) &&//loda fixme, this stops missiles deflecting, but they still dont passthrough... ent->methodOfDeath != MOD_CONC_ALT /*&& otherOwner->client->ps.saberBlockTime < level.time*/) { //for now still deflect even if saberBlockTime >= level.time because it hit the actual saber vec3_t fwd; gentity_t *te; int otherDefLevel = otherOwner->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE]; //in this case, deflect it even if we can't actually block it because it hit our saber //WP_SaberCanBlock(otherOwner, ent->r.currentOrigin, 0, 0, qtrue, 0); if (otherOwner->client && otherOwner->client->ps.weaponTime <= 0) { WP_SaberBlockNonRandom(otherOwner, ent->r.currentOrigin, qtrue); } te = G_TempEntity( ent->r.currentOrigin, EV_SABER_BLOCK ); VectorCopy(ent->r.currentOrigin, te->s.origin); VectorCopy(trace->plane.normal, te->s.angles); te->s.eventParm = 0; te->s.weapon = 0;//saberNum te->s.legsAnim = 0;//bladeNum /*if (otherOwner->client->ps.velocity[2] > 0 || otherOwner->client->pers.cmd.forwardmove || otherOwner->client->pers.cmd.rightmove)*/ if (otherOwner->client->ps.velocity[2] > 0 || otherOwner->client->pers.cmd.forwardmove < 0) //now we only do it if jumping or running backward. Should be able to full-on charge. { otherDefLevel -= 1; if (otherDefLevel < 0) { otherDefLevel = 0; } } AngleVectors(otherOwner->client->ps.viewangles, fwd, NULL, NULL); if (otherDefLevel == FORCE_LEVEL_1) { //if def is only level 1, instead of deflecting the shot it should just die here } else if (otherDefLevel == FORCE_LEVEL_2) { G_DeflectMissile(otherOwner, ent, fwd); } else { G_ReflectMissile(otherOwner, ent, fwd); } otherOwner->client->ps.saberBlockTime = level.time + (350 - (otherDefLevel*100));//200; //For jedi AI otherOwner->client->ps.saberEventFlags |= SEF_DEFLECTED; if (otherDefLevel == FORCE_LEVEL_3) { otherOwner->client->ps.saberBlockTime = 0; //^_^ } if (otherDefLevel == FORCE_LEVEL_1) { goto killProj; } return; } } // check for sticking if ( !other->takedamage && ( ent->s.eFlags & EF_MISSILE_STICK ) ) { laserTrapStick( ent, trace->endpos, trace->plane.normal ); G_AddEvent( ent, EV_MISSILE_STICK, 0 ); return; } //JAPRO - Serverside - Flag punting - Start if (g_allowFlagThrow.integer && !other->takedamage && other->s.eType == ET_ITEM) { vec3_t velocity; if (ent->s.weapon == WP_REPEATER && (ent->s.eFlags & EF_ALT_FIRING)) { other->s.pos.trType = TR_GRAVITY; other->s.pos.trTime = level.time; BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); VectorScale( velocity, 0.7f, other->s.pos.trDelta ); VectorCopy( other->r.currentOrigin, other->s.pos.trBase ); } else if (ent->s.weapon == WP_ROCKET_LAUNCHER && (ent->s.eFlags & EF_ALT_FIRING)) { other->s.pos.trType = TR_GRAVITY; other->s.pos.trTime = level.time; BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); VectorScale( velocity, 2.5f, other->s.pos.trDelta ); VectorCopy( other->r.currentOrigin, other->s.pos.trBase ); } else if (ent->s.weapon == WP_ROCKET_LAUNCHER) { other->s.pos.trType = TR_GRAVITY; other->s.pos.trTime = level.time; BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); VectorScale( velocity, 0.9f, other->s.pos.trDelta ); VectorCopy( other->r.currentOrigin, other->s.pos.trBase ); } else if (ent->s.weapon == WP_THERMAL) { other->s.pos.trType = TR_GRAVITY; other->s.pos.trTime = level.time; BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); VectorScale( velocity, 0.9f, other->s.pos.trDelta ); //tweak? VectorCopy( other->r.currentOrigin, other->s.pos.trBase ); } } //JAPRO - Serverside - Flag punting - End // impact damage if (other->takedamage && !isKnockedSaber) { // FIXME: wrong damage direction? if ( ent->damage ) { vec3_t velocity; qboolean didDmg = qfalse; if( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; hitClient = qtrue; } BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); if ( VectorLength( velocity ) == 0 ) { velocity[2] = 1; // stepped on a grenade } //damage falloff option, assumes bullet lifetime is 10,000 (default) if ((g_tweakWeapons.integer & WT_NO_SPREAD) && ((ent->s.weapon == WP_BLASTER && (ent->s.eFlags & EF_ALT_FIRING)) || (ent->s.weapon == WP_REPEATER && !(ent->s.eFlags & EF_ALT_FIRING)) )) { //If the weapon has spread, just reduce damage based on distance for nospread tweak. This should probably be accompanied with the damagenumber setting so you can keep track of your dmg.. float lifetime = (10000 - ent->nextthink + level.time) * 0.001; //float scale = powf(2, -lifetime); float scale = -1.5 * lifetime + 1; scale += 0.1f; //offset it a bit so super close shots dont get affected at all if (scale < 0.2f) scale = 0.2f; else if (scale > 1.0f) scale = 1.0f; ent->damage *= scale; //trap->SendServerCommand(-1, va("chat \"Missile has been alive for %.2f s new dmg is %i scale is %.2f\n\"", lifetime, ent->damage, scale)); } if (ent->s.weapon == WP_BOWCASTER || ent->s.weapon == WP_FLECHETTE || ent->s.weapon == WP_ROCKET_LAUNCHER) { if (ent->s.weapon == WP_FLECHETTE && (ent->s.eFlags & EF_ALT_FIRING)) { /* fix: there are rare situations where flechette did explode by timeout AND by impact in the very same frame, then here ent->think was set to G_FreeEntity, so the folowing think did invalidate this entity, BUT it would be reused later in this function for explosion event. This, then, would set ent->freeAfterEvent to qtrue, so event later, when reusing this entity by using G_InitEntity(), it would have this freeAfterEvent set AND this would in case of dropped item erase it from game immeadiately. THIS for example caused very rare flag dissappearing bug. */ if (ent->think == WP_flechette_alt_blow) ent->think(ent); } else { G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, /*ent->s.origin*/ent->r.currentOrigin, ent->damage, DAMAGE_HALF_ABSORB, ent->methodOfDeath); didDmg = qtrue; } } else { G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, /*ent->s.origin*/ent->r.currentOrigin, ent->damage, 0, ent->methodOfDeath); didDmg = qtrue; } if (didDmg && other && other->client) { //What I'm wondering is why this isn't in the NPC pain funcs. But this is what SP does, so whatever. class_t npc_class = other->client->NPC_class; // If we are a robot and we aren't currently doing the full body electricity... if ( npc_class == CLASS_SEEKER || npc_class == CLASS_PROBE || npc_class == CLASS_MOUSE || npc_class == CLASS_GONK || npc_class == CLASS_R2D2 || npc_class == CLASS_R5D2 || npc_class == CLASS_REMOTE || npc_class == CLASS_MARK1 || npc_class == CLASS_MARK2 || //npc_class == CLASS_PROTOCOL ||//no protocol, looks odd npc_class == CLASS_INTERROGATOR || npc_class == CLASS_ATST || npc_class == CLASS_SENTRY ) { // special droid only behaviors if ( other->client->ps.electrifyTime < level.time + 100 ) { // ... do the effect for a split second for some more feedback other->client->ps.electrifyTime = level.time + 450; } //FIXME: throw some sparks off droids,too } } } if ( ent->s.weapon == WP_DEMP2 ) { //a hit with demp2 decloaks people, disables ships if ( other && other->client && other->client->NPC_class == CLASS_VEHICLE ) { //hit a vehicle if ( other->m_pVehicle //valid vehicle ent && other->m_pVehicle->m_pVehicleInfo//valid stats && (other->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER//always affect speeders ||(other->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER && ent->classname && Q_stricmp("vehicle_proj", ent->classname ) == 0) )//only vehicle ion weapons affect a fighter in this manner && !FighterIsLanded( other->m_pVehicle , &other->client->ps )//not landed && !(other->spawnflags&2) )//and not suspended { //vehicles hit by "ion cannons" lose control if ( other->client->ps.electrifyTime > level.time ) { //add onto it //FIXME: extern the length of the "out of control" time? other->client->ps.electrifyTime += Q_irand(200,500); if ( other->client->ps.electrifyTime > level.time + 4000 ) { //cap it other->client->ps.electrifyTime = level.time + 4000; } } else { //start it //FIXME: extern the length of the "out of control" time? other->client->ps.electrifyTime = level.time + Q_irand(200,500); } } } else if ( other && other->client && other->client->ps.powerups[PW_CLOAKED] ) { Jedi_Decloak( other ); if ( ent->methodOfDeath == MOD_DEMP2_ALT ) { //direct hit with alt disables cloak forever //permanently disable the saboteur's cloak other->client->cloakToggleTime = Q3_INFINITE; } else { //temp disable other->client->cloakToggleTime = level.time + Q_irand( 3000, 10000 ); } } } } #if _GRAPPLE//_GRAPPLE if (!strcmp(ent->classname, "laserTrap") && ent->s.weapon == WP_BRYAR_PISTOL) { //gentity_t *nent; vec3_t v; /* nent = G_Spawn(qtrue); nent->freeAfterEvent = qtrue; nent->s.weapon = WP_BRYAR_PISTOL;//WP_GRAPPLING_HOOK; -- idk what this is nent->s.saberInFlight = qtrue; nent->s.owner = ent->s.owner; */ ent->enemy = NULL; ent->s.otherEntityNum = -1; ent->s.groundEntityNum = -1; if ( other->s.eType == ET_MOVER || (other->client && !( other->s.eFlags & EF_DEAD ) ) ) { if ( other->client ) { //G_AddEvent( nent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); //Event if (!ent->s.hasLookTarget) { G_PlayEffectID( G_EffectIndex("tusken/hit"), trace->endpos, trace->plane.normal ); } ent->s.hasLookTarget = qtrue; ent->enemy = other; other->s.otherEntityNum = ent->parent->s.number; v[0] = other->r.currentOrigin[0];// + (other->r.mins[0] + other->r.maxs[0]) * 0.5; v[1] = other->r.currentOrigin[1];// + (other->r.mins[1] + other->r.maxs[1]) * 0.5; v[2] = other->r.currentOrigin[2] + (other->r.mins[2] + other->r.maxs[2]) * 0.5; SnapVectorTowards( v, ent->s.pos.trBase ); // save net bandwidth ent->s.otherEntityNum = ent->enemy->s.clientNum; other->s.otherEntityNum = ent->parent->s.clientNum; } else { if ( !strcmp(other->classname, "func_rotating") || !strcmp(other->classname, "func_pendulum") ) { Weapon_HookFree(ent); // don't work return; } ent->s.otherEntityNum = other->s.number; ent->s.groundEntityNum = other->s.number; VectorCopy(trace->endpos, v); //G_AddEvent( nent, EV_MISSILE_MISS, 0); //DirToByte( trace->plane.normal ) ); //Event if (!ent->s.hasLookTarget) { G_PlayEffectID( G_EffectIndex("tusken/hitwall"), trace->endpos, trace->plane.normal ); } ent->s.hasLookTarget = qtrue; } } else { VectorCopy(trace->endpos, v); //G_AddEvent( nent, EV_MISSILE_MISS, 0);//DirToByte( trace->plane.normal ) ); //Event if (!ent->s.hasLookTarget) { G_PlayEffectID( G_EffectIndex("tusken/hitwall"), trace->endpos, trace->plane.normal ); } ent->s.hasLookTarget = qtrue; } VectorCopy(trace->plane.normal, ent->s.angles); SnapVectorTowards( v, ent->s.pos.trBase ); // save net bandwidth // change over to a normal entity right at the point of impact //nent->s.eType = ET_GENERAL; ent->s.eType = ET_MISSILE; G_SetOrigin( ent, v ); //G_SetOrigin( nent, v ); ent->think = Weapon_HookThink; ent->nextthink = level.time + FRAMETIME; VectorCopy( ent->r.currentOrigin, ent->parent->client->ps.lastHitLoc); VectorSubtract( ent->r.currentOrigin, ent->parent->client->ps.origin, v ); trap->LinkEntity( (sharedEntity_t *)ent ); //trap->LinkEntity( (sharedEntity_t *)nent ); return; } #endif killProj: // is it cheaper in bandwidth to just remove this ent and create a new // one, rather than changing the missile into the explosion? if ( other->takedamage && other->client && !isKnockedSaber ) { G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); ent->s.otherEntityNum = other->s.number; } else if( trace->surfaceFlags & SURF_METALSTEPS ) { G_AddEvent( ent, EV_MISSILE_MISS_METAL, DirToByte( trace->plane.normal ) ); } else if (ent->s.weapon != G2_MODEL_PART && !isKnockedSaber) { G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); } if (!isKnockedSaber) { ent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact ent->s.eType = ET_GENERAL; } SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); // save net bandwidth G_SetOrigin( ent, trace->endpos ); ent->takedamage = qfalse; // splash damage (doesn't apply to person directly hit) if ( ent->splashDamage ) { if( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, other, ent, ent->splashMethodOfDeath ) ) { if( !hitClient && g_entities[ent->r.ownerNum].client ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; } } } if (ent->s.weapon == G2_MODEL_PART) { ent->freeAfterEvent = qfalse; //it will free itself } trap->LinkEntity( (sharedEntity_t *)ent ); }
/* ================ G_ExplodeMissile Explode a missile without an impact ================ */ void G_ExplodeMissile( gentity_t *ent ) { vec3_t dir; vec3_t origin; #ifdef TA_WEAPSYS if (bg_projectileinfo[ent->s.weapon].grappling) { Weapon_HookFree(ent); return; } if (bg_projectileinfo[ent->s.weapon].explosionType == PE_NONE) { G_FreeEntity(ent); return; } #endif BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); SnapVector( origin ); G_SetOrigin( ent, origin ); #ifdef TA_WEAPSYS // Missile impacted a surface if (ent->count & 2) { VectorCopy(ent->s.angles2, dir); } else { #endif // we don't have a valid direction, so just point straight up dir[0] = dir[1] = 0; dir[2] = 1; #ifdef TA_WEAPSYS } #endif #ifndef TA_WEAPSYS // Must be after G_RadiusDamage ent->s.eType = ET_GENERAL; #endif G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( dir ) ); #ifdef TA_WEAPSYS if (ent->parent) ent->s.playerNum = ent->parent->s.number; else ent->s.playerNum = ENTITYNUM_NONE; #endif ent->freeAfterEvent = qtrue; // splash damage if ( ent->splashDamage ) { #ifdef TA_WEAPSYS if( G_RadiusDamage( ent->r.currentOrigin, ent, ent->parent, ent->splashDamage, ent->splashRadius, ent , ent->splashMethodOfDeath ) ) #else if( G_RadiusDamage( ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent , ent->splashMethodOfDeath ) ) #endif { g_entities[ent->r.ownerNum].player->accuracy_hits++; } } #ifdef TA_WEAPSYS ent->s.eType = ET_GENERAL; #endif trap_LinkEntity( ent ); }
/* ================ G_RunMissile ================ */ void G_RunMissile( gentity_t *ent ) { vec3_t origin; trace_t tr; int passent; // get current position BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); // if this missile bounced off an invulnerability sphere if ( ent->target_ent ) { passent = ent->target_ent->s.number; } #ifdef TA_WEAPSYS // missiles that left the owner bbox will attack anything, even the owner else if (ent->count & 1) { passent = ent->s.number; } #elif defined MISSIONPACK // prox mines that left the owner bbox will attach to anything, even the owner else if (ent->s.weapon == WP_PROX_LAUNCHER && ent->count) { passent = ENTITYNUM_NONE; } #endif else { // ignore interactions with the missile owner passent = ent->r.ownerNum; } // trace a line from the previous position to the current position trap_Trace( &tr, ent->r.currentOrigin, ent->s.mins, ent->s.maxs, origin, passent, ent->clipmask ); if ( tr.startsolid || tr.allsolid ) { // make sure the tr.entityNum is set to the entity we're stuck in trap_Trace( &tr, ent->r.currentOrigin, ent->s.mins, ent->s.maxs, ent->r.currentOrigin, passent, ent->clipmask ); tr.fraction = 0; } else { VectorCopy( tr.endpos, ent->r.currentOrigin ); } trap_LinkEntity( ent ); if ( tr.fraction != 1 ) { // never explode or bounce on sky if ( tr.surfaceFlags & SURF_NOIMPACT ) { // If grapple, reset owner if (ent->parent && ent->parent->player && ent->parent->player->hook == ent) { #ifdef IOQ3ZTM Weapon_HookFree(ent); return; #else ent->parent->player->hook = NULL; #endif } G_FreeEntity( ent ); return; } G_MissileImpact( ent, &tr ); if ( ent->s.eType != ET_MISSILE ) { return; // exploded } } #if defined MISSIONPACK || defined TA_WEAPSYS // if the prox mine wasn't yet outside the player body #ifdef TA_WEAPSYS if (!(ent->count & 1) && !(ent->flags & FL_MISSILE_NO_DAMAGE_PARENT)) #else if (ent->s.weapon == WP_PROX_LAUNCHER && !ent->count) #endif { // check if the prox mine is outside the owner bbox trap_Trace( &tr, ent->r.currentOrigin, ent->s.mins, ent->s.maxs, ent->r.currentOrigin, ENTITYNUM_NONE, ent->clipmask ); if (!tr.startsolid || tr.entityNum != ent->r.ownerNum) { #ifdef TA_WEAPSYS ent->count |= 1; #else ent->count = 1; #endif } } #endif // check think function after bouncing G_RunThink( ent ); }
/* ================ G_MissileImpact ================ */ void G_MissileImpact( gentity_t *ent, trace_t *trace ) { gentity_t *other; qboolean hitPlayer = qfalse; #if defined MISSIONPACK && !defined TURTLEARENA // POWERS vec3_t forward, impactpoint, bouncedir; int eFlags; #endif #ifdef TA_WEAPSYS qboolean damagedOther = qfalse; #endif other = &g_entities[trace->entityNum]; #if defined MISSIONPACK && !defined TURTLEARENA // POWERS if ( other->takedamage ) { #ifdef TA_WEAPSYS if ( !bg_projectileinfo[ent->s.weapon].stickOnImpact ) #else if ( ent->s.weapon != WP_PROX_LAUNCHER ) #endif { if ( other->player && other->player->invulnerabilityTime > level.time ) { // VectorCopy( ent->s.pos.trDelta, forward ); VectorNormalize( forward ); if (G_InvulnerabilityEffect( other, forward, ent->s.pos.trBase, impactpoint, bouncedir )) { VectorCopy( bouncedir, trace->plane.normal ); eFlags = ent->s.eFlags & EF_BOUNCE_HALF; ent->s.eFlags &= ~EF_BOUNCE_HALF; G_BounceMissile( ent, trace ); ent->s.eFlags |= eFlags; } ent->target_ent = other; return; } } } #endif // impact damage if (other->takedamage #ifdef TA_WEAPSYS // stickOnImpact only damages once && !(ent->count & 2) #endif ) { // FIXME: wrong damage direction? if ( ent->damage ) { vec3_t velocity; if( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) { g_entities[ent->r.ownerNum].player->accuracy_hits++; hitPlayer = qtrue; } BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); if ( VectorLength( velocity ) == 0 ) { #ifdef IOQ3ZTM VectorCopy(trace->plane.normal, velocity); #else velocity[2] = 1; // stepped on a grenade #endif } #ifdef TA_WEAPSYS damagedOther = G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, ent->s.origin, ent->damage, 0, ent->methodOfDeath); #else G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, ent->s.origin, ent->damage, 0, ent->methodOfDeath); #endif } } // check for bounce if ( #ifdef TA_WEAPSYS !damagedOther && #else !other->takedamage && #endif ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) { G_BounceMissile( ent, trace ); #ifdef TA_WEAPSYS // Bounce missiles // Die on Nth bounce if (ent->s.modelindex2 > 0) { ent->s.modelindex2--; if (ent->s.modelindex2 == 0) { // Kill missile G_ExplodeMissile( ent ); return; } } G_AddEvent( ent, EV_PROJECTILE_BOUNCE, DirToByte( trace->plane.normal ) ); ent->s.time2 = trace->surfaceFlags; // surface #else G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); #endif return; } #ifdef TA_WEAPSYS if (bg_projectileinfo[ent->s.weapon].stickOnImpact != PSOI_NONE) { vec3_t dir; #ifndef TURTLEARENA // if it's a player, stick it on to them (flag them and remove this entity) if( bg_projectileinfo[ent->s.weapon].explosionType == PE_PROX && other->s.eType == ET_PLAYER && other->health > 0 ) { ProximityMine_Player( ent, other ); return; } #endif if (ent->count & 2) { // Already stuck to wall return; } ent->count |= 2; // Don't stick to players or obelisk if (other->s.eType == ET_PLAYER #ifdef MISSIONPACK || (other->pain == ObeliskPain) #endif ) { goto missileExplode; } // Don't stick to the entity that this missile just killed or other missiles if ((damagedOther && other->health <= 0) || other->s.eType == ET_MISSILE) { // Don't remove projectile if it doesn't explode. if (bg_projectileinfo[ent->s.weapon].explosionType == PE_NONE) { ent->s.pos.trType = TR_GRAVITY; ent->count &= ~2; return; } else { goto missileExplode; } } if (bg_projectileinfo[ent->s.weapon].shootable) VectorMA(trace->endpos, -8, trace->plane.normal, trace->endpos); SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); G_SetOrigin( ent, trace->endpos ); if (bg_projectileinfo[ent->s.weapon].stickOnImpact == PSOI_KEEP_ANGLES) { #if 0 // convert direction of travel into axis if ( VectorNormalize2( ent->s.pos.trDelta, dir ) == 0 ) { dir[2] = 1; } // Set the angles vectoangles( dir, ent->s.angles ); #else VectorCopy(trace->plane.normal, dir); #endif } else { VectorCopy(trace->plane.normal, dir); vectoangles( dir, ent->s.angles ); switch (bg_projectileinfo[ent->s.weapon].stickOnImpact) { case PSOI_ANGLE_270: ent->s.angles[0] += 270; break; case PSOI_ANGLE_180: ent->s.angles[0] += 180; break; case PSOI_ANGLE_90: // Maybe this is good for prox mines, but doesn't look good on my // rocket or shuirkens... ent->s.angles[0] += 90; break; case PSOI_ANGLE_0: break; } } // Save direction VectorCopy(dir, ent->s.angles2); ent->s.pos.trType = TR_STATIONARY; VectorClear( ent->s.pos.trDelta ); G_AddEvent( ent, EV_PROJECTILE_STICK, DirToByte(trace->plane.normal) ); ent->s.time2 = trace->surfaceFlags; // surface if (bg_projectileinfo[ent->s.weapon].explosionType == PE_PROX) { // When a BREAKABLE ET_MOVER is killed it drops the projectiles stuck to it, // so don't setup the prox mine when it impact a surface if it already hit been setup. if (ent->die != ProximityMine_Die) { ent->think = ProximityMine_Activate; ent->nextthink = level.time + 2000; ent->die = ProximityMine_Die; } } else { ent->die = G_Missile_Die; } // link the prox mine to the other entity ent->enemy = other; VectorCopy(dir, ent->movedir); VectorSet(ent->s.mins, -4, -4, -4); VectorSet(ent->s.maxs, 4, 4, 4); trap_LinkEntity(ent); return; } #elif defined MISSIONPACK if( ent->s.weapon == WP_PROX_LAUNCHER ) { if( ent->s.pos.trType != TR_GRAVITY ) { return; } // if it's a player, stick it on to them (flag them and remove this entity) if( other->s.eType == ET_PLAYER && other->health > 0 ) { ProximityMine_Player( ent, other ); return; } SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); G_SetOrigin( ent, trace->endpos ); ent->s.pos.trType = TR_STATIONARY; VectorClear( ent->s.pos.trDelta ); G_AddEvent( ent, EV_PROXIMITY_MINE_STICK, trace->surfaceFlags ); ent->think = ProximityMine_Activate; ent->nextthink = level.time + 2000; vectoangles( trace->plane.normal, ent->s.angles ); ent->s.angles[0] += 90; // link the prox mine to the other entity ent->enemy = other; ent->die = ProximityMine_Die; VectorCopy(trace->plane.normal, ent->movedir); VectorSet(ent->s.mins, -4, -4, -4); VectorSet(ent->s.maxs, 4, 4, 4); trap_LinkEntity(ent); return; } #endif #ifdef TA_WEAPSYS if (bg_projectileinfo[ent->s.weapon].grappling) #else if (!strcmp(ent->classname, "hook")) #endif { gentity_t *nent; vec3_t v; nent = G_Spawn(); if ( other->takedamage && other->player ) { G_AddEvent( nent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); nent->s.otherEntityNum = other->s.number; v[0] = other->r.currentOrigin[0] + (other->s.mins[0] + other->s.maxs[0]) * 0.5; v[1] = other->r.currentOrigin[1] + (other->s.mins[1] + other->s.maxs[1]) * 0.5; v[2] = other->r.currentOrigin[2] + (other->s.mins[2] + other->s.maxs[2]) * 0.5; } else { VectorCopy(trace->endpos, v); G_AddEvent( nent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); } #ifdef IOQ3ZTM // IOQ3BUGFIX: Fix grapple wallmark/death-effect/debris (only tested with TA_WEAPSYS...) nent->s.weapon = ent->s.weapon; #endif #ifdef TA_WEAPSYS if (ent->parent) nent->s.playerNum = ent->parent->s.number; else nent->s.playerNum = ENTITYNUM_NONE; #endif nent->s.weapon = ent->s.weapon; ent->enemy = other; ent->s.groundEntityNum = other->s.number; SnapVectorTowards( v, ent->s.pos.trBase ); // save net bandwidth nent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact nent->s.eType = ET_GENERAL; ent->s.eType = ET_GRAPPLE; G_SetOrigin( ent, v ); G_SetOrigin( nent, v ); ent->think = Weapon_HookThink; ent->nextthink = level.time + FRAMETIME; ent->parent->player->ps.pm_flags |= PMF_GRAPPLE_PULL; VectorCopy( ent->r.currentOrigin, ent->parent->player->ps.grapplePoint); trap_LinkEntity( ent ); trap_LinkEntity( nent ); #ifdef TA_WEAPSYS // Don't grapple to the entity if you just killed it. if (damagedOther && other->health <= 0) { Weapon_HookFree(ent); } #endif return; } #ifdef TA_WEAPSYS missileExplode: #endif // is it cheaper in bandwidth to just remove this ent and create a new // one, rather than changing the missile into the explosion? if ( other->takedamage && other->player ) { G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); ent->s.otherEntityNum = other->s.number; } else if( trace->surfaceFlags & SURF_METALSTEPS ) { G_AddEvent( ent, EV_MISSILE_MISS_METAL, DirToByte( trace->plane.normal ) ); } else { G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); } #ifdef TA_WEAPSYS if (ent->parent) ent->s.playerNum = ent->parent->s.number; else ent->s.playerNum = ENTITYNUM_NONE; #endif ent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact #ifndef TA_WEAPSYS // Must be after G_RadiusDamage ent->s.eType = ET_GENERAL; #endif SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); // save net bandwidth G_SetOrigin( ent, trace->endpos ); // splash damage (doesn't apply to person directly hit) if ( ent->splashDamage ) { #ifdef TA_WEAPSYS if( G_RadiusDamage( trace->endpos, ent, ent->parent, ent->splashDamage, ent->splashRadius, other, ent->splashMethodOfDeath ) ) #else if( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, other, ent->splashMethodOfDeath ) ) #endif { if( !hitPlayer ) { g_entities[ent->r.ownerNum].player->accuracy_hits++; } } } #ifdef TA_WEAPSYS ent->s.eType = ET_GENERAL; #endif trap_LinkEntity( ent ); }
/* ================== player_die ================== */ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) { gentity_t *ent; int anim; int contents; int killer; int i,counter2; char *killerName, *obit; if ( !(self->client) || (self->client->ps.pm_type == PM_DEAD) ) { return; } if ( level.intermissiontime ) { return; } //unlagged - backward reconciliation #2 // make sure the body shows up in the client's current position G_UnTimeShiftClient( self ); //unlagged - backward reconciliation #2 //KK-OAX Here is where we run the streak logic. G_RunStreakLogic( attacker, self ); // check for an almost capture CheckAlmostCapture( self, attacker ); // check for a player that almost brought in cubes CheckAlmostScored( self, attacker ); if (self->client && self->client->hook) { Weapon_HookFree(self->client->hook); } if ((self->client->ps.eFlags & EF_TICKING) && self->activator) { self->client->ps.eFlags &= ~EF_TICKING; self->activator->think = G_FreeEntity; self->activator->nextthink = level.time; } self->client->ps.pm_type = PM_DEAD; if ( attacker ) { killer = attacker->s.number; if ( attacker->client ) { killerName = attacker->client->pers.netname; } else { killerName = "<non-client>"; } } else { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if ( killer < 0 || killer >= MAX_CLIENTS ) { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if ( meansOfDeath < 0 || meansOfDeath >= sizeof( modNames ) / sizeof( modNames[0] ) ) { obit = "<bad obituary>"; } else { obit = modNames[meansOfDeath]; } G_LogPrintf("Kill: %i %i %i: %s killed %s by %s\n", killer, self->s.number, meansOfDeath, killerName, self->client->pers.netname, obit ); // broadcast the death event to everyone ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY ); ent->s.eventParm = meansOfDeath; ent->s.otherEntityNum = self->s.number; ent->s.otherEntityNum2 = killer; //Sago: Hmmm... generic? Can I transmit anything I like? Like if it is a team kill? Let's try ent->s.generic1 = OnSameTeam (self, attacker); if( !((g_gametype.integer==GT_ELIMINATION || g_gametype.integer==GT_CTF_ELIMINATION) && level.time < level.roundStartTime) ) ent->r.svFlags = SVF_BROADCAST; // send to everyone (if not an elimination gametype during active warmup) else ent->r.svFlags = SVF_NOCLIENT; self->enemy = attacker; self->client->ps.persistant[PERS_KILLED]++; if (attacker && attacker->client) { attacker->client->lastkilled_client = self->s.number; if ( attacker == self || OnSameTeam (self, attacker ) ) { if(g_gametype.integer!=GT_LMS && !((g_gametype.integer==GT_ELIMINATION || g_gametype.integer==GT_CTF_ELIMINATION) && level.time < level.roundStartTime)) if( (g_gametype.integer <GT_TEAM && g_ffa_gt!=1 && self->client->ps.persistant[PERS_SCORE]>0) || level.numNonSpectatorClients<3) //Cannot get negative scores by suicide AddScore( attacker, self->r.currentOrigin, -1 ); } else { if(g_gametype.integer!=GT_LMS) AddScore( attacker, self->r.currentOrigin, 1 ); if( meansOfDeath == MOD_GAUNTLET ) { // Attack gets a challenge complete: if(!(attacker->r.svFlags & SVF_BOT) && !(self->r.svFlags & SVF_BOT)) ChallengeMessage(attacker,WEAPON_GAUNTLET_KILLS); // play humiliation on player attacker->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++; G_LogPrintf( "Award: %i %i: %s gained the %s award!\n", attacker->client->ps.clientNum, 0, attacker->client->pers.netname, "GAUNTLET" ); // add the sprite over the player's head attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); attacker->client->ps.eFlags |= EF_AWARD_GAUNTLET; attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; // also play humiliation on target self->client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_GAUNTLETREWARD; } //If neither attacker or taget is bots and not the same if(!(attacker->r.svFlags & SVF_BOT) && !(self->r.svFlags & SVF_BOT) && self!=attacker) { switch(meansOfDeath) { case MOD_GAUNTLET: ChallengeMessage(attacker,WEAPON_GAUNTLET_KILLS); break; case MOD_MACHINEGUN: ChallengeMessage(attacker,WEAPON_MACHINEGUN_KILLS); break; case MOD_SHOTGUN: ChallengeMessage(attacker,WEAPON_SHOTGUN_KILLS); break; case MOD_GRENADE: case MOD_GRENADE_SPLASH: ChallengeMessage(attacker,WEAPON_GRANADE_KILLS); break; case MOD_ROCKET: case MOD_ROCKET_SPLASH: ChallengeMessage(attacker,WEAPON_ROCKET_KILLS); break; case MOD_LIGHTNING: ChallengeMessage(attacker,WEAPON_LIGHTNING_KILLS); break; case MOD_PLASMA: case MOD_PLASMA_SPLASH: ChallengeMessage(attacker,WEAPON_PLASMA_KILLS); break; case MOD_RAILGUN: if(g_instantgib.integer) ChallengeMessage(attacker,WEAPON_INSTANT_RAIL_KILLS); else ChallengeMessage(attacker,WEAPON_RAIL_KILLS); break; case MOD_BFG: case MOD_BFG_SPLASH: ChallengeMessage(attacker,WEAPON_BFG_KILLS); break; case MOD_NAIL: ChallengeMessage(attacker,WEAPON_NAILGUN_KILLS); break; case MOD_CHAINGUN: ChallengeMessage(attacker,WEAPON_CHAINGUN_KILLS); break; case MOD_PROXIMITY_MINE: ChallengeMessage(attacker,WEAPON_MINE_KILLS); break; case MOD_GRAPPLE: ChallengeMessage(attacker,WEAPON_GRAPPLE_KILLS); break; case MOD_LAVA: case MOD_SLIME: case MOD_TRIGGER_HURT: case MOD_FALLING: ChallengeMessage(attacker,WEAPON_PUSH_KILLS); break; case MOD_CRUSH: ChallengeMessage(attacker,WEAPON_CRUSH_KILLS); break; case MOD_TELEFRAG: ChallengeMessage(attacker,WEAPON_TELEFRAG_KILLS); break; }; ChallengeMessage(attacker,GENERAL_TOTALKILLS); ChallengeMessage(self,GENERAL_TOTALDEATHS); //Lets count number of powerups: i = 0; counter2 = 0; if(attacker->client->ps.powerups[PW_QUAD]) { ChallengeMessage(attacker,POWERUP_QUAD_KILL); counter2++; } if(self->client->ps.powerups[PW_QUAD]) { ChallengeMessage(attacker,POWERUP_COUNTER_QUAD); i++; } if(attacker->client->ps.powerups[PW_HASTE]) { ChallengeMessage(attacker,POWERUP_SPEED_KILL); counter2++; } if(self->client->ps.powerups[PW_HASTE]) { ChallengeMessage(attacker,POWERUP_COUNTER_SPEED); i++; } if(attacker->client->ps.powerups[PW_INVIS]) { ChallengeMessage(attacker,POWERUP_INVIS_KILL); counter2++; } if(self->client->ps.powerups[PW_INVIS]) { ChallengeMessage(attacker,POWERUP_COUNTER_INVIS); i++; } if(attacker->client->ps.powerups[PW_FLIGHT]) { ChallengeMessage(attacker,POWERUP_FLIGHT_KILL); counter2++; } if(self->client->ps.powerups[PW_FLIGHT]) { ChallengeMessage(attacker,POWERUP_COUNTER_FLIGHT); i++; } if(self->client->ps.powerups[PW_BATTLESUIT]) { ChallengeMessage(attacker,POWERUP_COUNTER_ENVIR); i++; } if(self->client->ps.powerups[PW_REGEN]) { ChallengeMessage(attacker,POWERUP_COUNTER_REGEN); i++; } if(i>1) //The target had more than one powerup ChallengeMessage(attacker,POWERUP_COUNTER_MULTI); if(counter2>1) //The attacker has more than one powerup ChallengeMessage(attacker,POWERUP_MULTI_KILL); } // check for two kills in a short amount of time // if this is close enough to the last kill, give a reward sound if ( level.time - attacker->client->lastKillTime < CARNAGE_REWARD_TIME ) { // KK-OAX // Check if Multikills are enabled if( g_altExcellent.integer ) { attacker->client->pers.multiKillCount++; G_checkForMultiKill( attacker ); } // play excellent on player attacker->client->ps.persistant[PERS_EXCELLENT_COUNT]++; G_LogPrintf( "Award: %i %i: %s gained the %s award!\n", attacker->client->ps.clientNum, 1, attacker->client->pers.netname, "EXCELLENT" ); if(!level.hadBots) //There has not been any bots ChallengeMessage(attacker,AWARD_EXCELLENT); // add the sprite over the player's head attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); attacker->client->ps.eFlags |= EF_AWARD_EXCELLENT; attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; } else { //KK-OAX Clear multikill count //Must be 1 so the correct number of kills are displayed to the clients. attacker->client->pers.multiKillCount = 1; } attacker->client->lastKillTime = level.time; } } else { if(g_gametype.integer!=GT_LMS && !((g_gametype.integer==GT_ELIMINATION || g_gametype.integer==GT_CTF_ELIMINATION) && level.time < level.roundStartTime)) if(self->client->ps.persistant[PERS_SCORE]>0 || level.numNonSpectatorClients<3) //Cannot get negative scores by suicide AddScore( self, self->r.currentOrigin, -1 ); } // Add team bonuses Team_FragBonuses(self, inflictor, attacker); // if I committed suicide, the flag does not fall, it returns. if (meansOfDeath == MOD_SUICIDE) { if ( self->client->ps.powerups[PW_NEUTRALFLAG] ) { // only happens in One Flag CTF Team_ReturnFlag( TEAM_FREE ); self->client->ps.powerups[PW_NEUTRALFLAG] = 0; } else if ( self->client->ps.powerups[PW_REDFLAG] ) { // only happens in standard CTF Team_ReturnFlag( TEAM_RED ); self->client->ps.powerups[PW_REDFLAG] = 0; } else if ( self->client->ps.powerups[PW_BLUEFLAG] ) { // only happens in standard CTF Team_ReturnFlag( TEAM_BLUE ); self->client->ps.powerups[PW_BLUEFLAG] = 0; } } TossClientPersistantPowerups( self ); if( g_gametype.integer == GT_HARVESTER ) { TossClientCubes( self ); } // if client is in a nodrop area, don't drop anything (but return CTF flags!) TossClientItems( self ); //#endif Cmd_Score_f( self ); // show scores // send updated scores to any clients that are following this one, // or they would get stale scoreboards for ( i = 0 ; i < level.maxclients ; i++ ) { gclient_t *client; client = &level.clients[i]; if ( client->pers.connected != CON_CONNECTED ) { continue; } if ( client->sess.sessionTeam != TEAM_SPECTATOR ) { continue; } if ( client->sess.spectatorClient == self->s.number ) { Cmd_Score_f( g_entities + i ); } } self->takedamage = qtrue; // can still be gibbed self->s.weapon = WP_NONE; self->s.powerups = 0; self->r.contents = CONTENTS_CORPSE; self->s.angles[0] = 0; self->s.angles[2] = 0; LookAtKiller (self, inflictor, attacker); VectorCopy( self->s.angles, self->client->ps.viewangles ); self->s.loopSound = 0; self->r.maxs[2] = -8; // don't allow respawn until the death anim is done // g_forcerespawn may force spawning at some later time self->client->respawnTime = level.time + 1700 +i; if(g_respawntime.integer>0) { for(i=0; self->client->respawnTime > i*g_respawntime.integer*1000;i++); self->client->respawnTime = i*g_respawntime.integer*1000; } //For testing: //G_Printf("Respawntime: %i\n",self->client->respawnTime); //However during warm up, we should respawn quicker! if(g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION || g_gametype.integer == GT_LMS) if(level.time<=level.roundStartTime && level.time>level.roundStartTime-1000*g_elimination_activewarmup.integer) self->client->respawnTime = level.time + rand()%800; RespawnTimeMessage(self,self->client->respawnTime); // remove powerups memset( self->client->ps.powerups, 0, sizeof(self->client->ps.powerups) ); // never gib in a nodrop contents = trap_PointContents( self->r.currentOrigin, -1 ); if ( (self->health <= GIB_HEALTH && !(contents & CONTENTS_NODROP) && g_blood.integer) || meansOfDeath == MOD_SUICIDE) { // gib death GibEntity( self, killer ); } else { // normal death static int i; switch ( i ) { case 0: anim = BOTH_DEATH1; break; case 1: anim = BOTH_DEATH2; break; case 2: default: anim = BOTH_DEATH3; break; } // for the no-blood option, we need to prevent the health // from going to gib level if ( self->health <= GIB_HEALTH ) { self->health = GIB_HEALTH+1; } self->client->ps.legsAnim = ( ( self->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; self->client->ps.torsoAnim = ( ( self->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; G_AddEvent( self, EV_DEATH1 + i, killer ); // the body can still be gibbed self->die = body_die; // globally cycle through the different death animations i = ( i + 1 ) % 3; if (self->s.eFlags & EF_KAMIKAZE) { Kamikaze_DeathTimer( self ); } } trap_LinkEntity (self); }
/* ================== player_die ================== */ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) { gentity_t *ent; int anim; int contents; int killer; int i; char *killerName, *obit; if ( self->client->ps.pm_type == PM_DEAD ) { return; } if ( level.intermissiontime ) { return; } //if we're in SP mode and player killed a bot, award score for the kill if ( IsBot( self ) ) { if ( self->parent && self->parent->health && attacker->client ) { AddScore( attacker, self->r.currentOrigin, self->parent->health ); self->s.time = level.time; } } if (self->client && self->client->hook) { Weapon_HookFree(self->client->hook); } self->client->ps.pm_type = PM_DEAD; if ( attacker ) { killer = attacker->s.number; if ( attacker->client ) { killerName = attacker->client->pers.netname; } else { killerName = "<non-client>"; } } else { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if ( killer < 0 || killer >= MAX_CLIENTS ) { killer = ENTITYNUM_WORLD; killerName = "<world>"; } if ( meansOfDeath < 0 || meansOfDeath >= sizeof( modNames ) / sizeof( modNames[0] ) ) { obit = "<bad obituary>"; } else { obit = modNames[ meansOfDeath ]; } G_LogPrintf("Kill: %i %i %i: %s killed %s by %s\n", killer, self->s.number, meansOfDeath, killerName, self->client->pers.netname, obit ); // broadcast the death event to everyone /* ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY ); ent->s.eventParm = meansOfDeath; ent->s.otherEntityNum = self->s.number; ent->s.otherEntityNum2 = killer; ent->r.svFlags = SVF_BROADCAST; // send to everyone */ self->enemy = attacker; self->client->ps.persistant[PERS_KILLED]++; // Add team bonuses //Team_FragBonuses(self, inflictor, attacker); // if client is in a nodrop area, don't drop anything (but return CTF flags!) contents = trap_PointContents( self->r.currentOrigin, -1 ); TossClientItems( self ); Cmd_Score_f( self ); // show scores // send updated scores to any clients that are following this one, // or they would get stale scoreboards for ( i = 0 ; i < level.maxclients ; i++ ) { gclient_t *client; client = &level.clients[i]; if ( client->pers.connected != CON_CONNECTED ) { continue; } if ( client->sess.sessionTeam != TEAM_SPECTATOR ) { continue; } if ( client->sess.spectatorClient == self->s.number ) { Cmd_Score_f( g_entities + i ); } } self->takedamage = qtrue; // can still be gibbed self->s.weapon = WP_NONE; self->s.powerups = 0; self->r.contents = CONTENTS_CORPSE; self->s.angles[0] = 0; self->s.angles[2] = 0; LookAtKiller (self, inflictor, attacker); VectorCopy( self->s.angles, self->client->ps.viewangles ); self->s.loopSound = 0; self->r.maxs[2] = -8; // don't allow respawn until the death anim is done // g_forcerespawn may force spawning at some later time if ( !IsBot(self) ) self->client->respawnTime = level.time + 1700; else self->client->respawnTime = level.time + 5000; //keep bot bodies around slightly longer // remove powerups memset( self->client->ps.powerups, 0, sizeof(self->client->ps.powerups) ); // never gib in a nodrop if ( (self->health <= GIB_HEALTH && !(contents & CONTENTS_NODROP) && g_blood.integer) || meansOfDeath == MOD_SUICIDE) { // gib death GibEntity( self, killer ); } else { // normal death static int i; switch ( i ) { case 0: anim = BOTH_DEATH1; break; case 1: anim = BOTH_DEATH2; break; case 2: default: anim = BOTH_DEATH3; break; } // for the no-blood option, we need to prevent the health // from going to gib level if ( self->health <= GIB_HEALTH ) { self->health = GIB_HEALTH+1; } self->client->ps.legsAnim = ( ( self->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; self->client->ps.torsoAnim = ( ( self->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; G_AddEvent( self, EV_DEATH1 + i, killer ); // the body can still be gibbed self->die = body_die; // globally cycle through the different death animations i = ( i + 1 ) % 3; } trap_LinkEntity (self); // Fire trigger_death and trigger_frag target entities and the deathtarget for the related target_botspawn G_UseTriggerFragAndDeathEntities ( self, attacker ); if ( self->parent ) G_UseDeathTargets( self->parent, self ); if ( !IsBot( self ) ) G_FadeOut( 1.0 ); }