/* ================== BotValidChatPosition ================== */ int BotValidChatPosition(bot_state_t *bs) { vec3_t point, start, end, mins, maxs; bsp_trace_t trace; //if the bot is dead all positions are valid if (BotIsDead(bs)) return qtrue; //never start chatting with a powerup if (bs->inventory[INVENTORY_QUAD] || bs->inventory[INVENTORY_ENVIRONMENTSUIT] || bs->inventory[INVENTORY_HASTE] || bs->inventory[INVENTORY_INVISIBILITY] || bs->inventory[INVENTORY_REGEN] || bs->inventory[INVENTORY_FLIGHT]) return qfalse; //must be on the ground //if (bs->cur_ps.groundEntityNum != ENTITYNUM_NONE) return qfalse; //do not chat if in lava or slime VectorCopy(bs->origin, point); point[2] -= 24; if (trap_PointContents(point,bs->entitynum) & (CONTENTS_LAVA|CONTENTS_SLIME)) return qfalse; //do not chat if under water VectorCopy(bs->origin, point); point[2] += 32; if (trap_PointContents(point,bs->entitynum) & MASK_WATER) return qfalse; //must be standing on the world entity VectorCopy(bs->origin, start); VectorCopy(bs->origin, end); start[2] += 1; end[2] -= 10; trap_AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, mins, maxs); BotAI_Trace(&trace, start, mins, maxs, end, bs->playernum, MASK_SOLID); if (trace.entityNum != ENTITYNUM_WORLD) return qfalse; //the bot is in a position where it can chat return qtrue; }
/* ================== BotValidChatPosition ================== */ int BotValidChatPosition( bot_state_t *bs ) { vec3_t point, start, end, mins, maxs; bsp_trace_t trace; //if the bot is dead all positions are valid if ( BotIsDead( bs ) ) { return qtrue; } //must be on the ground //if (bs->cur_ps.groundEntityNum != ENTITYNUM_NONE) return qfalse; //do not chat if in lava or slime VectorCopy( bs->origin, point ); point[2] -= 24; if ( trap_PointContents( point,bs->entitynum ) & ( CONTENTS_LAVA | CONTENTS_SLIME ) ) { return qfalse; } //do not chat if under water VectorCopy( bs->origin, point ); point[2] += 32; if ( trap_PointContents( point,bs->entitynum ) & MASK_WATER ) { return qfalse; } //must be standing on the world entity VectorCopy( bs->origin, start ); VectorCopy( bs->origin, end ); start[2] += 1; end[2] -= 10; trap_AAS_PresenceTypeBoundingBox( PRESENCE_CROUCH, mins, maxs ); BotAI_Trace( &trace, start, mins, maxs, end, bs->client, MASK_SOLID ); if ( trace.ent != ENTITYNUM_WORLD ) { return qfalse; } //the bot is in a position where it can chat return qtrue; }
/* ================ G_RunItem ================ */ void G_RunItem( gentity_t *ent ) { vec3_t origin; trace_t tr; int contents; int mask; // if groundentity has been set to -1, it may have been pushed off an edge if ( ent->s.groundEntityNum == -1 ) { if ( ent->s.pos.trType != TR_GRAVITY ) { ent->s.pos.trType = TR_GRAVITY; ent->s.pos.trTime = level.time; } } if ( ent->s.pos.trType == TR_STATIONARY ) { // check think function G_RunThink( ent ); return; } // get current position BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); // trace a line from the previous position to the current position if ( ent->clipmask ) { mask = ent->clipmask; } else { mask = MASK_PLAYERSOLID & ~CONTENTS_BODY;//MASK_SOLID; } trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, ent->r.ownerNum, mask ); VectorCopy( tr.endpos, ent->r.currentOrigin ); if ( tr.startsolid ) { tr.fraction = 0; } trap_LinkEntity( ent ); // FIXME: avoid this for stationary? // check think function G_RunThink( ent ); if ( tr.fraction == 1 ) { return; } // if it is in a nodrop volume, remove it contents = trap_PointContents( ent->r.currentOrigin, -1 ); if ( contents & CONTENTS_NODROP ) { if (ent->item && ent->item->giType == IT_TEAM) { Team_FreeEntity(ent); } else { G_FreeEntity( ent ); } return; } G_BounceItem( ent, &tr ); }
void SprotzThink(gentity_t *self){ vec3_t newOrigin, oldorg; trace_t trace; int contents; VectorCopy(self->s.pos.trBase, oldorg); // calculate new position BG_EvaluateTrajectory( &self->s.pos, level.time, newOrigin ); // trace a line from previous position to new position trap_Trace( &trace, oldorg, NULL, NULL, newOrigin, -1, CONTENTS_SOLID ); contents = trap_PointContents(self->r.currentOrigin, self->r.ownerNum); if(contents & CONTENTS_WATER){ G_FreeEntity( self); return; } // still in free fall if ( trace.fraction == 1.0 ){ VectorCopy(newOrigin, self->r.currentOrigin); } else { SetWhiskeyPool(self->parent, trace.endpos, trace.plane.normal, self->s.apos.trDelta[0]); G_FreeEntity(self); return; } self->nextthink = level.time+50; }
/* JPW NERVE ================ limbo ================ */ void limbo(gentity_t *ent) { int i; int startclient = ent->client->ps.clientNum; if (ent->r.svFlags & SVF_POW) { return; } if (!(ent->client->ps.pm_flags & PMF_LIMBO)) { // DHM - Nerve :: First save off persistant info we'll need for respawn for (i = 0; i < MAX_PERSISTANT; i++) { ent->client->saved_persistant[i] = ent->client->ps.persistant[i]; } ent->client->ps.pm_flags |= PMF_LIMBO; ent->client->ps.pm_flags |= PMF_FOLLOW; trap_UnlinkEntity(ent); // DHM - Nerve :: reset these values ent->client->ps.viewlocked = 0; ent->client->ps.viewlocked_entNum = 0; ent->r.maxs[2] = 0; ent->r.currentOrigin[2] += 8; trap_PointContents(ent->r.currentOrigin, -1); // drop stuff ent->client->sess.spectatorClient = startclient; } }
//---------------------------------------------------------------- static void turret_fire ( gentity_t *ent, vec3_t start, vec3_t dir ) //---------------------------------------------------------------- { vec3_t org; gentity_t *bolt; if ( (trap_PointContents( start, ent->s.number )&MASK_SHOT) ) { return; } VectorMA( start, -START_DIS, dir, org ); // dumb.... G_PlayEffectID( ent->genericValue13, org, dir ); bolt = G_Spawn(); //use a custom shot effect bolt->s.otherEntityNum2 = ent->genericValue14; //use a custom impact effect bolt->s.emplacedOwner = ent->genericValue15; bolt->classname = "turret_proj"; bolt->nextthink = level.time + 10000; bolt->think = G_FreeEntity; bolt->s.eType = ET_MISSILE; bolt->s.weapon = WP_EMPLACED_GUN; bolt->r.ownerNum = ent->s.number; bolt->damage = ent->damage; bolt->alliedTeam = ent->alliedTeam; bolt->teamnodmg = ent->teamnodmg; //bolt->dflags = DAMAGE_NO_KNOCKBACK;// | DAMAGE_HEAVY_WEAP_CLASS; // Don't push them around, or else we are constantly re-aiming bolt->splashDamage = ent->damage; bolt->splashRadius = 100; bolt->methodOfDeath = MOD_TARGET_LASER; //[BugFix16] bolt->splashMethodOfDeath = MOD_TARGET_LASER; //[/BugFix16] bolt->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; //bolt->trigger_formation = qfalse; // don't draw tail on first frame VectorSet( bolt->r.maxs, 1.5, 1.5, 1.5 ); VectorScale( bolt->r.maxs, -1, bolt->r.mins ); bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time; VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, ent->mass, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin); bolt->parent = ent; }
//---------------------------------------------------------------- static void turret_fire ( gentity_t *ent, vec3_t start, vec3_t dir ) //---------------------------------------------------------------- { vec3_t org; gentity_t *bolt; if ( (trap_PointContents( start, ent->s.number )&MASK_SHOT) ) { return; } VectorMA( start, -START_DIS, dir, org ); // dumb.... G_PlayEffectID( ent->genericValue13, org, dir ); bolt = G_Spawn(); //use a custom shot effect bolt->s.otherEntityNum2 = ent->genericValue14; //use a custom impact effect bolt->s.emplacedOwner = ent->genericValue15; bolt->classname = "turret_proj"; bolt->nextthink = level.time + 10000; bolt->think = G_FreeEntity; bolt->s.eType = ET_MISSILE; bolt->s.weapon = WP_EMPLACED_GUN; bolt->r.ownerNum = ent->s.number; bolt->damage = ent->damage; bolt->alliedTeam = ent->alliedTeam; bolt->teamnodmg = ent->teamnodmg; bolt->splashDamage = ent->damage; bolt->splashRadius = 100; bolt->methodOfDeath = MOD_TARGET_LASER; bolt->splashMethodOfDeath = MOD_TARGET_LASER; bolt->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; VectorSet( bolt->r.maxs, 1.5, 1.5, 1.5 ); VectorScale( bolt->r.maxs, -1, bolt->r.mins ); bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time; VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, ent->mass, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin); bolt->parent = ent; }
void Rune_Phase_Use(gentity_t *ent) { vec3_t oldmuzzle,muzzle,forward,right,up,end; trace_t trace, trace2; AngleVectors (ent->client->ps.viewangles, forward, right, up); CalcMuzzlePoint ( ent, forward, right, up, muzzle ); if (ent->runetime<level.time) { VectorMA (muzzle, 250, forward, end); trap_Trace (&trace, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT ); // we didn't hit anything, so exit if ( trace.fraction == 1) return; SnapVectorTowards( trace.endpos, muzzle ); // prepare for firing through the wall VectorCopy (muzzle, oldmuzzle); VectorCopy (trace.endpos, muzzle); VectorMA (muzzle, 96, forward, muzzle); if ( !( trap_PointContents( muzzle, -1 ) & CONTENTS_SOLID )) { // the point isn't inside a wall, so check to see if we are outside the bounds of the level VectorCopy(muzzle,end); end[2]=-10000; trap_Trace (&trace2, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT ); // if we didn't hit anything after that big a trace, we are outside the level if (trace2.fraction==1) return; TeleportPlayer(ent,muzzle,ent->client->ps.viewangles, qtrue, qtrue); ent->runetime = level.time + RUNE_PHASE_RECHARGE; } } else { //trap_SendServerCommand( ent->s.clientNum, va("print \"Phase is recharging\n\"")); } }
int luautil_pointcontents(lua_State *L) { vec3_t point; int pass = -1; int contents; //luaL_checkudata(L,1,"Vector"); if(lua_type(L,1) != LUA_TUSERDATA) return 0; lua_tovector(L,1,point); if(lua_type(L,2) == LUA_TNUMBER) { pass = lua_tonumber(L,2); } contents = trap_PointContents(point,pass); lua_pushinteger(L,contents); return 1; }
gentity_t *fire_speargun( gentity_t *self, vec3_t start, vec3_t dir ) { gentity_t *bolt; VectorNormalize( dir ); bolt = G_Spawn(); bolt->classname = const_cast<char*>("spear"); bolt->nextthink = level.time + 10000; bolt->think = G_ExplodeMissile; bolt->s.eType = ET_MISSILE; bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN; bolt->s.weapon = WP_SPEARGUN; bolt->r.ownerNum = self->s.number; bolt->parent = self; bolt->damage = 15; // (SA) spear damage here bolt->splashDamage = 0; bolt->methodOfDeath = MOD_SPEARGUN; bolt->clipmask = MASK_MISSILESHOT; bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME; // move a bit on the very first frame VectorCopy( start, bolt->s.pos.trBase ); // (SA) Kind of a cheap hack to make the speargun worthless out of the water // This'll probably change to something better if ( ( trap_PointContents( start, -1 ) & CONTENTS_WATER ) ) { bolt->s.pos.trType = TR_LINEAR; VectorScale( dir, SPEAR_WATERSPEED, bolt->s.pos.trDelta ); } else { bolt->s.pos.trType = TR_GRAVITY_LOW; VectorScale( dir, SPEAR_AIRSPEED, bolt->s.pos.trDelta ); } SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin ); return bolt; }
// Wandering code (based on old ACE movement code) void ACEMV_Wander(gentity_t * self) { vec3_t tmp; // do not move if(self->bs.next_move_time > level.time) return; // Special check for elevators, stand still until the ride comes to a complete stop. /* * FIXME if(self->groundentity != NULL && self->groundentity->use == Use_Plat) if(self->groundentity->moveinfo.state == STATE_UP || self->groundentity->moveinfo.state == STATE_DOWN) // only move when platform not { self->velocity[0] = 0; self->velocity[1] = 0; self->velocity[2] = 0; self->next_move_time = level.time + 500; return; } */ // touched jumppad last Frame? if(self->s.groundEntityNum == ENTITYNUM_NONE) { if(VectorLength(self->client->ps.velocity) > 120) { VectorNormalize2(self->client->ps.velocity, tmp); if(AngleBetweenVectors(self->bs.moveVector, tmp) >= 120) { // we might have been knocked back by someone or something .. if(!self->bs.moveTarget) { VectorCopy(tmp, self->bs.moveVector); ACEMV_ChangeBotAngle(self); } } } //ACEMV_ChangeBotAngle(self); //self->client->ps.velocity[0] = self->bs.moveVector[0] * 360; //self->client->ps.velocity[1] = self->bs.moveVector[1] * 360; //return; } // is there a target to move to if(self->bs.moveTarget) { ACEMV_MoveToGoal(self); } // swimming? VectorCopy(self->client->ps.origin, tmp); tmp[2] += 24; if(trap_PointContents(tmp, self->s.number) & MASK_WATER) { // if drowning and no node, move up if(self->client->airOutTime > 0) { self->client->pers.cmd.upmove = 1; self->bs.viewAngles[PITCH] = -45; } else self->client->pers.cmd.upmove = 15; self->client->pers.cmd.forwardmove = 100; } else { //self->client->airOutTime = 0; // probably shound not be messing with this, but } // lava? tmp[2] -= 48; if(trap_PointContents(tmp, self->s.number) & (CONTENTS_LAVA | CONTENTS_SLIME)) { // safe_bprintf(PRINT_MEDIUM,"lava jump\n"); self->bs.viewAngles[YAW] += random() * 360 - 180; self->client->pers.cmd.forwardmove = 127; self->client->pers.cmd.upmove = 127; return; } // check for special movement if we have a normal move (have to test) if(VectorLength(self->client->ps.velocity) < 37) { //if(random() > 0.1 && ACEMV_SpecialMove(self)) // return; //removed this because when wandering, the last thing you want is bots jumping //over things and going off ledges. It's better for them to just bounce around the map. self->bs.viewAngles[YAW] += random() * 180 - 90; if(ACEMV_CanMove(self, MOVE_FORWARD)) self->client->pers.cmd.forwardmove = 127; else if(ACEMV_CanMove(self, MOVE_BACK)) self->client->pers.cmd.forwardmove = -127; // if there is ground continue otherwise wait for next move if( /*!M_CheckBottom || */ self->s.groundEntityNum != ENTITYNUM_NONE) { if(ACEMV_CanMove(self, MOVE_FORWARD)) self->client->pers.cmd.forwardmove = 127; } return; } if(ACEMV_CheckEyes(self)) return; if(ACEMV_CanMove(self, MOVE_FORWARD)) self->client->pers.cmd.forwardmove = 127; }
// Main movement code. (following node path) void ACEMV_Move(gentity_t * self) { int currentNodeType = -1; int nextNodeType = -1; // get current and next node back from nav code. if(!ACEND_FollowPath(self)) { self->bs.state = STATE_WANDER; self->bs.wander_timeout = level.time + 1000; // center view //self->bs.viewAngles[PITCH] = 0; //-self->client->ps.delta_angles[PITCH]; return; } currentNodeType = nodes[self->bs.currentNode].type; nextNodeType = nodes[self->bs.nextNode].type; // move to a selected goal, if any if(self->bs.moveTarget) { ACEMV_MoveToGoal(self); } // grapple /* if(nextNodeType == NODE_GRAPPLE) { ACEMV_ChangeBotAngle(self); ACEIT_ChangeWeapon(self, FindItem("grapple")); self->client->pers.cmd.buttons = BUTTON_ATTACK; return; } // Reset the grapple if hangin on a graple node if(currentNodeType == NODE_GRAPPLE) { CTFPlayerResetGrapple(self); return; } */ #if 0 // check for platforms if(currentNodeType != NODE_PLATFORM && nextNodeType == NODE_PLATFORM) { // check to see if lift is down? for(i = 0; i < num_items; i++) if(item_table[i].node == self->bs.nextNode) if(item_table[i].ent->moverState != MOVER_POS1) return; // Wait for elevator } #endif if(currentNodeType == NODE_PLATFORM && nextNodeType == NODE_PLATFORM) { // move to the center self->bs.moveVector[2] = 0; // kill z movement if(VectorLength(self->bs.moveVector) > 10) self->client->pers.cmd.forwardmove = 200; // walk to center ACEMV_ChangeBotAngle(self); return; // No move, riding elevator } // jumpto nodes if(nextNodeType == NODE_JUMP || (currentNodeType == NODE_JUMP && nextNodeType != NODE_ITEM && nodes[self->bs.nextNode].origin[2] > self->client->ps.origin[2])) { // set up a jump move if(ACEMV_CanMove(self, MOVE_FORWARD)) self->client->pers.cmd.forwardmove = 127; self->client->pers.cmd.upmove = 127; ACEMV_ChangeBotAngle(self); //VectorCopy(self->bs.moveVector, dist); //VectorScale(dist, 127, self->client->ps.velocity); return; } // ladder nodes /* if(nextNodeType == NODE_LADDER && nodes[self->nextNode].origin[2] > self->s.origin[2]) { // Otherwise move as fast as we can self->client->pers.cmd.forwardmove = 400; self->velocity[2] = 320; ACEMV_ChangeBotAngle(self); return; } // If getting off the ladder if(currentNodeType == NODE_LADDER && nextNodeType != NODE_LADDER && nodes[self->nextNode].origin[2] > self->s.origin[2]) { self->client->pers.cmd.forwardmove = 400; self->client->pers.cmd.upmove = 200; self->velocity[2] = 200; ACEMV_ChangeBotAngle(self); return; } */ // water nodes if(currentNodeType == NODE_WATER) { // we need to be pointed up/down ACEMV_ChangeBotAngle(self); // ff the next node is not in the water, then move up to get out. if(nextNodeType != NODE_WATER && !(trap_PointContents(nodes[self->bs.nextNode].origin, self->s.number) & MASK_WATER)) { // exit water self->client->pers.cmd.upmove = 127; } self->client->pers.cmd.forwardmove = 100; return; } // falling off ledge? if(self->s.groundEntityNum == ENTITYNUM_NONE) { ACEMV_ChangeBotAngle(self); //self->client->ps.velocity[0] = self->bs.moveVector[0] * 360; //self->client->ps.velocity[1] = self->bs.moveVector[1] * 360; return; } // check to see if stuck, and if so try to free us // also handles crouching if(VectorLength(self->client->ps.velocity) < 37) { // keep a random factor just in case.... if(random() > 0.1 && ACEMV_SpecialMove(self)) return; self->bs.viewAngles[YAW] += random() * 180 - 90; if(ACEMV_CanMove(self, MOVE_FORWARD)) self->client->pers.cmd.forwardmove = 127; else if(ACEMV_CanMove(self, MOVE_BACK)) self->client->pers.cmd.forwardmove = -127; return; } // otherwise move as fast as we can if(ACEMV_CanMove(self, MOVE_FORWARD)) self->client->pers.cmd.forwardmove = 127; ACEMV_ChangeBotAngle(self); }
void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) { gentity_t *ent; // TTimo might be used uninitialized int contents = 0; int killer; int i; const char *killerName, *obit; qboolean nogib = qtrue; gitem_t *item = NULL; // JPW NERVE for flag drop vec3_t launchvel,launchspot; // JPW NERVE gentity_t *flag; // JPW NERVE if ( self->client->ps.pm_type == PM_DEAD ) { return; } if ( level.intermissiontime ) { return; } self->client->ps.pm_type = PM_DEAD; G_AddEvent( self, EV_STOPSTREAMINGSOUND, 0 ); 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]++; // JPW NERVE -- if player is holding ticking grenade, drop it if ( g_gametype.integer != GT_SINGLE_PLAYER ) { if ( ( self->client->ps.grenadeTimeLeft ) && ( self->s.weapon != WP_DYNAMITE ) ) { launchvel[0] = crandom(); launchvel[1] = crandom(); launchvel[2] = random(); VectorScale( launchvel, 160, launchvel ); VectorCopy( self->r.currentOrigin, launchspot ); launchspot[2] += 40; fire_grenade( self, launchspot, launchvel, self->s.weapon ); } } // jpw if ( attacker && attacker->client ) { if ( attacker == self || OnSameTeam( self, attacker ) ) { // DHM - Nerve :: Complaint lodging if ( attacker != self && level.warmupTime <= 0 ) { if ( attacker->client->pers.localClient ) { trap_SendServerCommand( self - g_entities, "complaint -4" ); } else { trap_SendServerCommand( self - g_entities, va( "complaint %i", attacker->s.number ) ); self->client->pers.complaintClient = attacker->s.clientNum; self->client->pers.complaintEndTime = level.time + 20500; } } // dhm // JPW NERVE if ( g_gametype.integer >= GT_WOLF ) { // high penalty to offset medic heal AddScore( attacker, WOLF_FRIENDLY_PENALTY ); } else { // jpw AddScore( attacker, -1 ); } } else { // JPW NERVE -- mostly added as conveneience so we can tweak from the #defines all in one place if ( g_gametype.integer >= GT_WOLF ) { AddScore( attacker, WOLF_FRAG_BONUS ); } else { // jpw AddScore( attacker, 1 ); } attacker->client->lastKillTime = level.time; } } else { AddScore( self, -1 ); } // Add team bonuses Team_FragBonuses( self, inflictor, attacker ); // if client is in a nodrop area, don't drop anything // JPW NERVE new drop behavior if ( g_gametype.integer == GT_SINGLE_PLAYER ) { // only drop here in single player; in multiplayer, drop @ limbo contents = trap_PointContents( self->r.currentOrigin, -1 ); if ( !( contents & CONTENTS_NODROP ) ) { TossClientItems( self ); } } // drop flag regardless if ( g_gametype.integer != GT_SINGLE_PLAYER ) { if ( self->client->ps.powerups[PW_REDFLAG] ) { item = BG_FindItem( "Red Flag" ); if ( !item ) { item = BG_FindItem( "Objective" ); } self->client->ps.powerups[PW_REDFLAG] = 0; } if ( self->client->ps.powerups[PW_BLUEFLAG] ) { item = BG_FindItem( "Blue Flag" ); if ( !item ) { item = BG_FindItem( "Objective" ); } self->client->ps.powerups[PW_BLUEFLAG] = 0; } if ( item ) { launchvel[0] = crandom() * 20; launchvel[1] = crandom() * 20; launchvel[2] = 10 + random() * 10; flag = LaunchItem( item,self->r.currentOrigin,launchvel,self->s.number ); flag->s.modelindex2 = self->s.otherEntityNum2; // JPW NERVE FIXME set player->otherentitynum2 with old modelindex2 from flag and restore here flag->message = self->message; // DHM - Nerve :: also restore item name // Clear out player's temp copies self->s.otherEntityNum2 = 0; self->message = NULL; } // send a fancy "MEDIC!" scream. Sissies, ain' they? if ( self->client != NULL ) { if ( self->health > GIB_HEALTH && meansOfDeath != MOD_SUICIDE ) { if ( self->client->sess.sessionTeam == TEAM_RED ) { if ( random() > 0.5 ) { G_AddEvent( self, EV_GENERAL_SOUND, G_SoundIndex( "sound/multiplayer/axis/g-medic2.wav" ) ); } else { G_AddEvent( self, EV_GENERAL_SOUND, G_SoundIndex( "sound/multiplayer/axis/g-medic3.wav" ) ); } } else { if ( random() > 0.5 ) { G_AddEvent( self, EV_GENERAL_SOUND, G_SoundIndex( "sound/multiplayer/allies/a-medic3.wav" ) ); } else { G_AddEvent( self, EV_GENERAL_SOUND, G_SoundIndex( "sound/multiplayer/allies/a-medic2.wav" ) ); } } } } } // jpw 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->r.contents = CONTENTS_CORPSE; self->s.powerups = 0; // JPW NERVE -- only corpse in SP; in MP, need CONTENTS_BODY so medic can operate if ( g_gametype.integer == GT_SINGLE_PLAYER ) { self->s.weapon = WP_NONE; self->s.angles[0] = 0; } else { self->client->limboDropWeapon = self->s.weapon; // store this so it can be dropped in limbo } // jpw self->s.angles[2] = 0; LookAtKiller( self, inflictor, attacker ); VectorCopy( self->s.angles, self->client->ps.viewangles ); self->s.loopSound = 0; trap_UnlinkEntity( self ); self->r.maxs[2] = 0; self->client->ps.maxs[2] = 0; trap_LinkEntity( self ); // don't allow respawn until the death anim is done // g_forcerespawn may force spawning at some later time self->client->respawnTime = level.time + 800; // 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 ) ) { GibEntity( self, killer ); nogib = qfalse; } if ( nogib ) { // normal death // 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; } // JPW NERVE for medic self->client->medicHealAmt = 0; // jpw // DHM - Play death animation, and set pm_time to delay 'fallen' animation self->client->ps.pm_time = BG_AnimScriptEvent( &self->client->ps, ANIM_ET_DEATH, qfalse, qtrue ); G_AddEvent( self, EV_DEATH1 + 1, killer ); // the body can still be gibbed self->die = body_die; } trap_LinkEntity( self ); if ( g_gametype.integer >= GT_WOLF && meansOfDeath == MOD_SUICIDE ) { limbo( self, qtrue ); } }
/* ============= SpawnCorpse A player is respawning, so make an entity that looks just like the existing corpse to leave behind. ============= */ static void SpawnCorpse( gentity_t *ent ) { gentity_t *body; int contents; vec3_t origin, mins; VectorCopy( ent->r.currentOrigin, origin ); trap_UnlinkEntity( ent ); // if client is in a nodrop area, don't leave the body contents = trap_PointContents( origin, -1 ); if ( contents & CONTENTS_NODROP ) { return; } body = G_NewEntity(); VectorCopy( ent->s.apos.trBase, body->s.angles ); body->s.eFlags = EF_DEAD; body->s.eType = entityType_t::ET_CORPSE; body->timestamp = level.time; body->s.event = 0; body->r.contents = CONTENTS_CORPSE; body->clipmask = MASK_DEADSOLID; body->s.clientNum = ent->client->ps.stats[ STAT_CLASS ]; body->nonSegModel = ent->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL; if ( ent->client->pers.team == TEAM_HUMANS ) { body->classname = "humanCorpse"; } else { body->classname = "alienCorpse"; } body->s.misc = MAX_CLIENTS; body->think = BodySink; body->nextthink = level.time + 20000; body->s.legsAnim = ent->s.legsAnim; if ( !body->nonSegModel ) { switch ( body->s.legsAnim & ~ANIM_TOGGLEBIT ) { case BOTH_DEATH1: case BOTH_DEAD1: body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD1; break; case BOTH_DEATH2: case BOTH_DEAD2: body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD2; break; case BOTH_DEATH3: case BOTH_DEAD3: default: body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD3; break; } } else { switch ( body->s.legsAnim & ~ANIM_TOGGLEBIT ) { case NSPA_DEATH1: case NSPA_DEAD1: body->s.legsAnim = NSPA_DEAD1; break; case NSPA_DEATH2: case NSPA_DEAD2: body->s.legsAnim = NSPA_DEAD2; break; case NSPA_DEATH3: case NSPA_DEAD3: default: body->s.legsAnim = NSPA_DEAD3; break; } } //change body dimensions BG_ClassBoundingBox( ent->client->ps.stats[ STAT_CLASS ], mins, nullptr, nullptr, body->r.mins, body->r.maxs ); //drop down to match the *model* origins of ent and body origin[2] += mins[ 2 ] - body->r.mins[ 2 ]; G_SetOrigin( body, origin ); body->s.pos.trType = trType_t::TR_GRAVITY; body->s.pos.trTime = level.time; VectorCopy( ent->client->ps.velocity, body->s.pos.trDelta ); trap_LinkEntity( body ); }
/* ============= CopyToBodyQue A player is respawning, so make an entity that looks just like the existing corpse to leave behind. ============= */ void CopyToBodyQue( gentity_t *ent ) { #ifdef MISSIONPACK gentity_t *e; int i; #endif gentity_t *body; int contents; trap_UnlinkEntity (ent); // if client is in a nodrop area, don't leave the body contents = trap_PointContents( ent->s.origin, -1 ); if ( contents & CONTENTS_NODROP ) { return; } // grab a body que and cycle to the next one body = level.bodyQue[ level.bodyQueIndex ]; level.bodyQueIndex = (level.bodyQueIndex + 1) % BODY_QUEUE_SIZE; trap_UnlinkEntity (body); body->s = ent->s; body->s.eFlags = EF_DEAD; // clear EF_TALK, etc #ifdef MISSIONPACK if ( ent->s.eFlags & EF_KAMIKAZE ) { body->s.eFlags |= EF_KAMIKAZE; // check if there is a kamikaze timer around for this owner for (i = 0; i < MAX_GENTITIES; i++) { e = &g_entities[i]; if (!e->inuse) continue; if (e->activator != ent) continue; if (strcmp(e->classname, "kamikaze timer")) continue; e->activator = body; break; } } #endif body->s.powerups = 0; // clear powerups body->s.loopSound = 0; // clear lava burning body->s.number = body - g_entities; body->timestamp = level.time; body->physicsObject = qtrue; body->physicsBounce = 0; // don't bounce if ( body->s.groundEntityNum == ENTITYNUM_NONE ) { body->s.pos.trType = TR_GRAVITY; body->s.pos.trTime = level.time; VectorCopy( ent->client->ps.velocity, body->s.pos.trDelta ); } else { body->s.pos.trType = TR_STATIONARY; } body->s.event = 0; // change the animation to the last-frame only, so the sequence // doesn't repeat anew for the body switch ( body->s.legsAnim & ~ANIM_TOGGLEBIT ) { case BOTH_DEATH1: case BOTH_DEAD1: body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD1; break; case BOTH_DEATH2: case BOTH_DEAD2: body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD2; break; case BOTH_DEATH3: case BOTH_DEAD3: default: body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD3; break; } body->r.svFlags = ent->r.svFlags; VectorCopy (ent->r.mins, body->r.mins); VectorCopy (ent->r.maxs, body->r.maxs); VectorCopy (ent->r.absmin, body->r.absmin); VectorCopy (ent->r.absmax, body->r.absmax); body->clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP; body->r.contents = CONTENTS_CORPSE; body->r.ownerNum = ent->s.number; body->nextthink = level.time + 5000; body->think = BodySink; body->die = body_die; // don't take more damage if already gibbed if ( ent->health <= GIB_HEALTH ) { body->takedamage = qfalse; } else { body->takedamage = qtrue; } VectorCopy ( body->s.pos.trBase, body->r.currentOrigin ); trap_LinkEntity (body); }
//---------------------------------------------------------------- static void turretG2_fire ( gentity_t *ent, vec3_t start, vec3_t dir ) //---------------------------------------------------------------- { vec3_t org, ang; gentity_t *bolt; if ( (trap_PointContents( start, ent->s.number )&MASK_SHOT) ) { return; } VectorMA( start, -START_DIS, dir, org ); // dumb.... if ( ent->random ) { vectoangles( dir, ang ); ang[PITCH] += flrand( -ent->random, ent->random ); ang[YAW] += flrand( -ent->random, ent->random ); AngleVectors( ang, dir, NULL, NULL ); } vectoangles(dir, ang); if ( (ent->spawnflags&SPF_TURRETG2_TURBO) ) { //muzzle flash G_PlayEffectID( ent->genericValue13, org, ang ); WP_FireTurboLaserMissile( ent, start, dir ); if ( ent->alt_fire ) { TurboLaser_SetBoneAnim( ent, 2, 3 ); } else { TurboLaser_SetBoneAnim( ent, 0, 1 ); } } else { G_PlayEffectID( G_EffectIndex("blaster/muzzle_flash"), org, ang ); bolt = G_Spawn(); bolt->classname = "turret_proj"; bolt->nextthink = level.time + 10000; bolt->think = G_FreeEntity; bolt->s.eType = ET_MISSILE; bolt->s.weapon = WP_BLASTER; bolt->r.ownerNum = ent->s.number; bolt->damage = ent->damage; bolt->alliedTeam = ent->alliedTeam; bolt->teamnodmg = ent->teamnodmg; bolt->dflags = (DAMAGE_NO_KNOCKBACK|DAMAGE_HEAVY_WEAP_CLASS); // Don't push them around, or else we are constantly re-aiming bolt->splashDamage = ent->splashDamage; bolt->splashRadius = ent->splashDamage; bolt->methodOfDeath = MOD_TARGET_LASER;//MOD_ENERGY; bolt->splashMethodOfDeath = MOD_TARGET_LASER;//MOD_ENERGY; bolt->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; //bolt->trigger_formation = qfalse; // don't draw tail on first frame VectorSet( bolt->r.maxs, 1.5, 1.5, 1.5 ); VectorScale( bolt->r.maxs, -1, bolt->r.mins ); bolt->s.pos.trType = TR_LINEAR; bolt->s.pos.trTime = level.time; VectorCopy( start, bolt->s.pos.trBase ); VectorScale( dir, ent->mass, bolt->s.pos.trDelta ); SnapVector( bolt->s.pos.trDelta ); // save net bandwidth VectorCopy( start, bolt->r.currentOrigin); } }
void Weapon_LightningFire( gentity_t *ent ) { trace_t tr; vec3_t end; #ifdef MISSIONPACK vec3_t impactpoint, bouncedir; #endif gentity_t *traceEnt, *tent; int damage, i, passent; damage = 8 * s_quadFactor; passent = ent->s.number; for (i = 0; i < 10; i++) { VectorMA( muzzle, LIGHTNING_RANGE, forward, end ); // eser - lightning discharge if (trap_PointContents (muzzle, -1) & MASK_WATER) { int zaps; gentity_t *tent; zaps = ent->client->ps.ammo[WP_LIGHTNING]; // determines size/power of discharge if (!zaps) return; // prevents any subsequent frames causing second discharge + error zaps++; // pmove does an ammo[gun]--, so we must compensate SnapVectorTowards (muzzle, ent->s.origin); // save bandwidth tent = G_TempEntity (muzzle, EV_LIGHTNING_DISCHARGE); tent->s.eventParm = zaps; // duration / size of explosion graphic ent->client->ps.ammo[WP_LIGHTNING] = 0; // drain ent's lightning count if (G_RadiusDamage (muzzle, ent, damage * zaps, (damage * zaps) + 16, NULL, MOD_LIGHTNING_DISCHARGE, qtrue)) ent->client->accuracy_hits++; return; } // eser - lightning discharge trap_Trace( &tr, muzzle, NULL, NULL, end, passent, MASK_SHOT ); #ifdef MISSIONPACK // if not the first trace (the lightning bounced of an invulnerability sphere) if (i) { // add bounced off lightning bolt temp entity // the first lightning bolt is a cgame only visual // tent = G_TempEntity( muzzle, EV_LIGHTNINGBOLT ); VectorCopy( tr.endpos, end ); SnapVector( end ); VectorCopy( end, tent->s.origin2 ); } #endif if ( tr.entityNum == ENTITYNUM_NONE ) { return; } traceEnt = &g_entities[ tr.entityNum ]; if ( traceEnt->takedamage) { #ifdef MISSIONPACK if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) { if (G_InvulnerabilityEffect( traceEnt, forward, tr.endpos, impactpoint, bouncedir )) { G_BounceProjectile( muzzle, impactpoint, bouncedir, end ); VectorCopy( impactpoint, muzzle ); VectorSubtract( end, impactpoint, forward ); VectorNormalize(forward); // the player can hit him/herself with the bounced lightning passent = ENTITYNUM_NONE; } else { VectorCopy( tr.endpos, muzzle ); passent = traceEnt->s.number; } continue; } #endif if( LogAccuracyHit( traceEnt, ent ) ) { ent->client->accuracy_hits++; } G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_LIGHTNING); } if ( traceEnt->takedamage && traceEnt->client ) { tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT ); tent->s.otherEntityNum = traceEnt->s.number; tent->s.eventParm = DirToByte( tr.plane.normal ); tent->s.weapon = ent->s.weapon; } else if ( !( tr.surfaceFlags & SURF_NOIMPACT ) ) { tent = G_TempEntity( tr.endpos, EV_MISSILE_MISS ); tent->s.eventParm = DirToByte( tr.plane.normal ); } break; } }
/* ================ G_RunItem ================ */ void G_RunItem(gentity_t * ent) { vec3_t origin; trace_t tr; int contents; int mask; // if its groundentity has been set to none, it may have been pushed off an edge if (ent->s.groundEntityNum == ENTITYNUM_NONE) { if (ent->s.pos.trType != TR_GRAVITY) { ent->s.pos.trType = TR_GRAVITY; ent->s.pos.trTime = level.time; } } if (ent->s.pos.trType == TR_STATIONARY) { // check think function G_RunThink(ent); return; } // get current position G_EvaluateTrajectory(&ent->s.pos, level.time, origin); // trace a line from the previous position to the current position if (ent->clipmask) { mask = ent->clipmask; } else { mask = MASK_PLAYERSOLID & ~CONTENTS_BODY; //MASK_SOLID; } trap_Trace(&tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, ent->r.ownerNum, mask); VectorCopy(tr.endpos, ent->r.currentOrigin); if (tr.startsolid) { tr.fraction = 0; } if (ent->flags & FL_DROPPED_ITEM && VectorLength(ent->s.pos.trDelta) != 0 && (ent->item->giType == IT_WEAPON || ent->item->giType == IT_HOLDABLE)) { // calculate spin -- should be identical to cg.autoAngles //cg.autoAnglesFast[1] = ( cg.time & 1023 ) * 360 / 1024.0f; ent->s.angles[1] = (level.time & 1023) * 360 / 1024.0f; } trap_LinkEntity(ent); // FIXME: avoid this for stationary? // check think function G_RunThink(ent); if (tr.fraction == 1) { return; } //Elder: debug //if (ent->item && ent->item->giType == IT_WEAPON) { //G_Printf("item velocity: %s\n", vtos(ent->s.pos.trDelta)); //} // if it is in a nodrop volume, remove it contents = trap_PointContents(ent->r.currentOrigin, -1); if (contents & CONTENTS_NODROP) { if (ent->item && ent->item->giType == IT_TEAM) { Team_FreeEntity(ent); } else if (ent->item && ent->item->giType == IT_WEAPON) { //Elder: force-call the weaponthink function RQ3_DroppedWeaponThink(ent); } else if (ent->item && ent->item->giType == IT_HOLDABLE) { RQ3_DroppedItemThink(ent); } else { G_FreeEntity(ent); } return; } G_BounceItem(ent, &tr); }
/* ---------------------------------------- DeadThink ---------------------------------------- */ static void DeadThink ( void ) { trace_t trace; //HACKHACKHACKHACKHACK //We should really have a seperate G2 bounding box (seperate from the physics bbox) for G2 collisions only //FIXME: don't ever inflate back up? NPC->r.maxs[2] = NPC->client->renderInfo.eyePoint[2] - NPC->r.currentOrigin[2] + 4; if ( NPC->r.maxs[2] < -8 ) { NPC->r.maxs[2] = -8; } if ( VectorCompare( NPC->client->ps.velocity, vec3_origin ) ) {//not flying through the air if ( NPC->r.mins[0] > -32 ) { NPC->r.mins[0] -= 1; trap_Trace (&trace, NPC->r.currentOrigin, NPC->r.mins, NPC->r.maxs, NPC->r.currentOrigin, NPC->s.number, NPC->clipmask ); if ( trace.allsolid ) { NPC->r.mins[0] += 1; } } if ( NPC->r.maxs[0] < 32 ) { NPC->r.maxs[0] += 1; trap_Trace (&trace, NPC->r.currentOrigin, NPC->r.mins, NPC->r.maxs, NPC->r.currentOrigin, NPC->s.number, NPC->clipmask ); if ( trace.allsolid ) { NPC->r.maxs[0] -= 1; } } if ( NPC->r.mins[1] > -32 ) { NPC->r.mins[1] -= 1; trap_Trace (&trace, NPC->r.currentOrigin, NPC->r.mins, NPC->r.maxs, NPC->r.currentOrigin, NPC->s.number, NPC->clipmask ); if ( trace.allsolid ) { NPC->r.mins[1] += 1; } } if ( NPC->r.maxs[1] < 32 ) { NPC->r.maxs[1] += 1; trap_Trace (&trace, NPC->r.currentOrigin, NPC->r.mins, NPC->r.maxs, NPC->r.currentOrigin, NPC->s.number, NPC->clipmask ); if ( trace.allsolid ) { NPC->r.maxs[1] -= 1; } } } //HACKHACKHACKHACKHACK { //death anim done (or were given a specific amount of time to wait before removal), wait the requisite amount of time them remove if ( level.time >= NPCInfo->timeOfDeath + BodyRemovalPadTime( NPC ) ) { if ( NPC->client->ps.eFlags & EF_NODRAW ) { if (!trap_ICARUS_IsRunning(NPC->s.number)) { NPC->think = G_FreeEntity; NPC->nextthink = level.time + level.frameTime; } } else { class_t npc_class; // Start the body effect first, then delay 400ms before ditching the corpse NPC_RemoveBodyEffect(); //FIXME: keep it running through physics somehow? NPC->think = NPC_RemoveBody; NPC->nextthink = level.time + level.frameTime; npc_class = NPC->client->NPC_class; // check for droids if ( npc_class == CLASS_SEEKER || npc_class == CLASS_REMOTE || npc_class == CLASS_PROBE || npc_class == CLASS_MOUSE || npc_class == CLASS_GONK || npc_class == CLASS_R2D2 || npc_class == CLASS_R5D2 || npc_class == CLASS_MARK2 || npc_class == CLASS_SENTRY ) { NPC->client->ps.eFlags |= EF_NODRAW; NPCInfo->timeOfDeath = level.time + level.frameTime * 8; } else NPCInfo->timeOfDeath = level.time + level.frameTime * 4; } return; } } // If the player is on the ground and the resting position contents haven't been set yet...(BounceCount tracks the contents) if ( NPC->bounceCount < 0 && NPC->s.groundEntityNum >= 0 ) { // if client is in a nodrop area, make him/her nodraw int contents = NPC->bounceCount = trap_PointContents( NPC->r.currentOrigin, -1 ); if ( ( contents & CONTENTS_NODROP ) ) { NPC->client->ps.eFlags |= EF_NODRAW; } } CorpsePhysics( NPC ); }
/* ============== AICast_VisibleFromPos ============== */ qboolean AICast_VisibleFromPos( vec3_t srcpos, int srcnum, vec3_t destpos, int destnum, qboolean updateVisPos ) { int i, contents_mask, passent, hitent; trace_t trace; vec3_t start, end, middle, eye; cast_state_t *cs = NULL; int srcviewheight; vec3_t destmins, destmaxs; vec3_t right, vec; qboolean inPVS; if ( g_entities[destnum].flags & FL_NOTARGET ) { return qfalse; } if ( srcnum < aicast_maxclients ) { cs = AICast_GetCastState( srcnum ); } // if ( cs && cs->bs ) { srcviewheight = cs->bs->cur_ps.viewheight; } else if ( g_entities[srcnum].client ) { srcviewheight = g_entities[srcnum].client->ps.viewheight; } else { srcviewheight = 0; } // VectorCopy( g_entities[destnum].r.mins, destmins ); VectorCopy( g_entities[destnum].r.maxs, destmaxs ); // //calculate middle of bounding box VectorAdd( destmins, destmaxs, middle ); VectorScale( middle, 0.5, middle ); VectorAdd( destpos, middle, middle ); // calculate eye position VectorCopy( srcpos, eye ); eye[2] += srcviewheight; // // set the right vector VectorSubtract( middle, eye, vec ); VectorNormalize( vec ); right[0] = vec[1]; right[1] = vec[0]; right[2] = 0; // inPVS = qfalse; // for ( i = 0; i < 5; i++ ) { if ( cs && updateVisPos ) { // if it's a grenade or something, PVS checks don't work very well //if the point is not in potential visible sight if ( i < 3 ) { // don't do PVS check for left/right checks if ( !trap_InPVS( eye, middle ) ) { continue; } else { inPVS = qtrue; } } else if ( !inPVS ) { break; // wasn't in potential view in either of the previous tests } // so don't bother doing left/right } // contents_mask = MASK_AISIGHT; //(MASK_SHOT | CONTENTS_AI_NOSIGHT) & ~(CONTENTS_BODY); // we can see anything that a bullet can pass through passent = srcnum; hitent = destnum; VectorCopy( eye, start ); VectorCopy( middle, end ); //if the entity is in water, lava or slime if ( trap_PointContents( middle, destnum ) & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) { contents_mask |= ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ); } //end if //if eye is in water, lava or slime if ( trap_PointContents( eye, srcnum ) & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) { if ( !( contents_mask & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) ) { passent = destnum; hitent = srcnum; VectorCopy( middle, start ); VectorCopy( eye, end ); } //end if contents_mask ^= ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ); } //end if //trace from start to end trap_Trace( &trace, start, NULL, NULL, end, ENTITYNUM_NONE /*passent*/, contents_mask ); //if water was hit if ( trace.contents & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) { //if the water surface is translucent // if (trace.surface.flags & (SURF_TRANS33|SURF_TRANS66)) { //trace through the water contents_mask &= ~( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ); trap_Trace( &trace, trace.endpos, NULL, NULL, end, passent, contents_mask ); } //end if } //end if //if a full trace or the hitent was hit if ( trace.fraction >= 1 || trace.entityNum == hitent ) { return qtrue; } //check bottom and top of bounding box as well if ( i == 0 ) { middle[2] -= ( destmaxs[2] - destmins[2] ) * 0.5; } else if ( i == 1 ) { middle[2] += destmaxs[2] - destmins[2]; } else if ( i == 2 ) { // right side middle[2] -= ( destmaxs[2] - destmins[2] ) / 2.0; VectorMA( eye, destmaxs[0] - 0.5, right, eye ); } else if ( i == 3 ) { // left side VectorMA( eye, -2.0 * ( destmaxs[0] - 0.5 ), right, eye ); } } //end for return qfalse; }
void DoImpact( gentity_t *self, gentity_t *other, qboolean damageSelf ) { float magnitude, my_mass; vec3_t velocity; int cont; if( self->client ) { VectorCopy( self->client->ps.velocity, velocity ); my_mass = self->mass; } else { VectorCopy( self->s.pos.trDelta, velocity ); if ( self->s.pos.trType == TR_GRAVITY ) { velocity[2] -= 0.25f * g_gravity.value; } if( !self->mass ) { my_mass = 1; } else if ( self->mass <= 10 ) { my_mass = 10; } else { my_mass = self->mass;///10; } } magnitude = VectorLength( velocity ) * my_mass / 10; /* if(pointcontents(self.absmax)==CONTENT_WATER)//FIXME: or other watertypes magnitude/=3; //water absorbs 2/3 velocity if(self.classname=="barrel"&&self.aflag)//rolling barrels are made for impacts! magnitude*=3; if(self.frozen>0&&magnitude<300&&self.flags&FL_ONGROUND&&loser==world&&self.velocity_z<-20&&self.last_onground+0.3<time) magnitude=300; */ if ( !self->client || self->client->ps.lastOnGround+300<level.time || ( self->client->ps.lastOnGround+100 < level.time && other->material >= MAT_GLASS ) ) { vec3_t dir1, dir2; float force = 0, dot; if ( other->material >= MAT_GLASS ) magnitude *= 2; //damage them if ( magnitude >= 100 && other->s.number < ENTITYNUM_WORLD ) { VectorCopy( velocity, dir1 ); VectorNormalize( dir1 ); if( VectorCompare( other->r.currentOrigin, vec3_origin ) ) {//a brush with no origin VectorCopy ( dir1, dir2 ); } else { VectorSubtract( other->r.currentOrigin, self->r.currentOrigin, dir2 ); VectorNormalize( dir2 ); } dot = DotProduct( dir1, dir2 ); if ( dot >= 0.2 ) { force = dot; } else { force = 0; } force *= (magnitude/50); cont = trap_PointContents( other->r.absmax, other->s.number ); if( (cont&CONTENTS_WATER) )//|| (self.classname=="barrel"&&self.aflag))//FIXME: or other watertypes { force /= 3; //water absorbs 2/3 velocity } /* if(self.frozen>0&&force>10) force=10; */ if( ( force >= 1 && other->s.number != 0 ) || force >= 10) { /* dprint("Damage other ("); dprint(loser.classname); dprint("): "); dprint(ftos(force)); dprint("\n"); */ if ( other->r.svFlags & SVF_GLASS_BRUSH ) { other->splashRadius = (float)(self->r.maxs[0] - self->r.mins[0])/4.0f; } if ( other->takedamage ) { G_Damage( other, self, self, velocity, self->r.currentOrigin, force, DAMAGE_NO_ARMOR, MOD_CRUSH);//FIXME: MOD_IMPACT } else { G_ApplyKnockback( other, dir2, force ); } } } if ( damageSelf && self->takedamage ) { //Now damage me //FIXME: more lenient falling damage, especially for when driving a vehicle if ( self->client && self->client->ps.fd.forceJumpZStart ) {//we were force-jumping if ( self->r.currentOrigin[2] >= self->client->ps.fd.forceJumpZStart ) {//we landed at same height or higher than we landed magnitude = 0; } else {//FIXME: take off some of it, at least? magnitude = (self->client->ps.fd.forceJumpZStart-self->r.currentOrigin[2])/3; } } //if(self.classname!="monster_mezzoman"&&self.netname!="spider")//Cats always land on their feet if( ( magnitude >= 100 + self->health && self->s.number != 0 && self->s.weapon != WP_SABER ) || ( magnitude >= 700 ) )//&& self.safe_time < level.time ))//health here is used to simulate structural integrity { if ( (self->s.weapon == WP_SABER || self->s.number == 0) && self->client && self->client->ps.groundEntityNum < ENTITYNUM_NONE && magnitude < 1000 ) {//players and jedi take less impact damage //allow for some lenience on high falls magnitude /= 2; /* if ( self.absorb_time >= time )//crouching on impact absorbs 1/2 the damage { magnitude/=2; } */ } magnitude /= 40; magnitude = magnitude - force/2;//If damage other, subtract half of that damage off of own injury if ( magnitude >= 1 ) { //FIXME: Put in a thingtype impact sound function /* dprint("Damage self ("); dprint(self.classname); dprint("): "); dprint(ftos(magnitude)); dprint("\n"); */ /* if ( self.classname=="player_sheep "&& self.flags&FL_ONGROUND && self.velocity_z > -50 ) return; */ G_Damage( self, NULL, NULL, NULL, self->r.currentOrigin, magnitude/2, DAMAGE_NO_ARMOR, MOD_FALLING );//FIXME: MOD_IMPACT } } } //FIXME: slow my velocity some? // NOTENOTE We don't use lastimpact as of yet // self->lastImpact = level.time; /* if(self.flags&FL_ONGROUND) self.last_onground=time; */ } }
/* ============= CopyToBodyQue A player is respawning, so make an entity that looks just like the existing corpse to leave behind. ============= */ void CopyToBodyQue( gentity_t *ent ) { gentity_t *body; int contents; if (level.intermissiontime) { return; } trap_UnlinkEntity (ent); // if client is in a nodrop area, don't leave the body contents = trap_PointContents( ent->s.origin, -1 ); if ( contents & CONTENTS_NODROP ) { return; } if (ent->client && (ent->client->ps.eFlags & EF_DISINTEGRATION)) { //for now, just don't spawn a body if you got disint'd return; } // grab a body que and cycle to the next one body = level.bodyQue[ level.bodyQueIndex ]; level.bodyQueIndex = (level.bodyQueIndex + 1) % BODY_QUEUE_SIZE; trap_UnlinkEntity (body); body->s = ent->s; //avoid oddly angled corpses floating around body->s.angles[PITCH] = body->s.angles[ROLL] = body->s.apos.trBase[PITCH] = body->s.apos.trBase[ROLL] = 0; body->s.g2radius = 100; body->s.eType = ET_BODY; body->s.eFlags = EF_DEAD; // clear EF_TALK, etc if (ent->client && (ent->client->ps.eFlags & EF_DISINTEGRATION)) { body->s.eFlags |= EF_DISINTEGRATION; } VectorCopy(ent->client->ps.lastHitLoc, body->s.origin2); body->s.powerups = 0; // clear powerups body->s.loopSound = 0; // clear lava burning body->s.number = body - g_entities; body->timestamp = level.time; body->physicsObject = qtrue; body->physicsBounce = 0; // don't bounce if ( body->s.groundEntityNum == ENTITYNUM_NONE ) { body->s.pos.trType = TR_GRAVITY; body->s.pos.trTime = level.time; VectorCopy( ent->client->ps.velocity, body->s.pos.trDelta ); } else { body->s.pos.trType = TR_STATIONARY; } body->s.event = 0; body->s.weapon = ent->s.bolt2; if (body->s.weapon == WP_SABER && ent->client->ps.saberInFlight) { body->s.weapon = WP_BLASTER; //lie to keep from putting a saber on the corpse, because it was thrown at death } G_AddEvent(body, EV_BODY_QUEUE_COPY, ent->s.clientNum); body->r.svFlags = ent->r.svFlags | SVF_BROADCAST; VectorCopy (ent->r.mins, body->r.mins); VectorCopy (ent->r.maxs, body->r.maxs); VectorCopy (ent->r.absmin, body->r.absmin); VectorCopy (ent->r.absmax, body->r.absmax); body->s.torsoAnim = body->s.legsAnim = ent->client->ps.legsAnim & ~ANIM_TOGGLEBIT; body->clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP; body->r.contents = CONTENTS_CORPSE; body->r.ownerNum = ent->s.number; body->nextthink = level.time + BODY_SINK_TIME; body->think = BodySink; body->die = body_die; // don't take more damage if already gibbed if ( ent->health <= GIB_HEALTH ) { body->takedamage = qfalse; } else { body->takedamage = qtrue; } VectorCopy ( body->s.pos.trBase, body->r.currentOrigin ); trap_LinkEntity (body); }
/* ============= SpawnCorpse A player is respawning, so make an entity that looks just like the existing corpse to leave behind. ============= */ static void SpawnCorpse( gentity_t *ent ) { gentity_t *body; int contents; vec3_t origin, dest; trace_t tr; float vDiff; VectorCopy( ent->r.currentOrigin, origin ); trap_UnlinkEntity( ent ); // if client is in a nodrop area, don't leave the body contents = trap_PointContents( origin, -1 ); if( contents & CONTENTS_NODROP ) return; body = G_Spawn( ); VectorCopy( ent->s.apos.trBase, body->s.angles ); body->s.eFlags = EF_DEAD; body->s.eType = ET_CORPSE; body->s.number = body - g_entities; body->timestamp = level.time; body->s.event = 0; body->r.contents = CONTENTS_CORPSE; body->s.clientNum = ent->client->ps.stats[ STAT_CLASS ]; body->nonSegModel = ent->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL; if( ent->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) body->classname = "humanCorpse"; else body->classname = "alienCorpse"; body->s.misc = MAX_CLIENTS; body->think = BodySink; body->nextthink = level.time + 20000; body->s.legsAnim = ent->s.legsAnim; if( !body->nonSegModel ) { switch( body->s.legsAnim & ~ANIM_TOGGLEBIT ) { case BOTH_DEATH1: case BOTH_DEAD1: body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD1; break; case BOTH_DEATH2: case BOTH_DEAD2: body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD2; break; case BOTH_DEATH3: case BOTH_DEAD3: default: body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD3; break; } } else { switch( body->s.legsAnim & ~ANIM_TOGGLEBIT ) { case NSPA_DEATH1: case NSPA_DEAD1: body->s.legsAnim = NSPA_DEAD1; break; case NSPA_DEATH2: case NSPA_DEAD2: body->s.legsAnim = NSPA_DEAD2; break; case NSPA_DEATH3: case NSPA_DEAD3: default: body->s.legsAnim = NSPA_DEAD3; break; } } body->takedamage = qfalse; body->health = ent->health = ent->client->ps.stats[ STAT_HEALTH ]; ent->health = 0; //change body dimensions BG_ClassBoundingBox( ent->client->ps.stats[ STAT_CLASS ], NULL, NULL, NULL, body->r.mins, body->r.maxs ); vDiff = body->r.mins[ 2 ] - ent->r.mins[ 2 ]; //drop down to match the *model* origins of ent and body VectorSet( dest, origin[ 0 ], origin[ 1 ], origin[ 2 ] - vDiff ); trap_Trace( &tr, origin, body->r.mins, body->r.maxs, dest, body->s.number, body->clipmask ); VectorCopy( tr.endpos, origin ); G_SetOrigin( body, origin ); VectorCopy( origin, body->s.origin ); body->s.pos.trType = TR_GRAVITY; body->s.pos.trTime = level.time; VectorCopy( ent->client->ps.velocity, body->s.pos.trDelta ); VectorCopy ( body->s.pos.trBase, body->r.currentOrigin ); trap_LinkEntity( body ); }
/* ================== 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 ); }
/* ================== 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); }
/* ================ G_Physics ================ */ void G_Physics( gentity_t *ent, int msec ) { vec3_t origin; trace_t tr; int contents; // if groundentity has been set to ENTITYNUM_NONE, it may have been pushed off an edge if ( ent->s.groundEntityNum == ENTITYNUM_NONE ) { if ( ent->s.eType == ET_BUILDABLE ) { if ( ent->s.pos.trType != BG_Buildable( ent->s.modelindex )->traj ) { ent->s.pos.trType = BG_Buildable( ent->s.modelindex )->traj; ent->s.pos.trTime = level.time; } } else if ( ent->s.pos.trType != TR_GRAVITY ) { ent->s.pos.trType = TR_GRAVITY; ent->s.pos.trTime = level.time; } } if ( ent->s.pos.trType == TR_STATIONARY ) { // check think function G_RunThink( ent ); //check floor infrequently if ( ent->nextPhysicsTime < level.time ) { VectorCopy( ent->r.currentOrigin, origin ); VectorMA( origin, -2.0f, ent->s.origin2, origin ); trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, ent->s.number, ent->clipmask ); if ( tr.fraction == 1.0f ) { ent->s.groundEntityNum = ENTITYNUM_NONE; } ent->nextPhysicsTime = level.time + PHYSICS_TIME; } return; } // trace a line from the previous position to the current position // get current position BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, ent->s.number, ent->clipmask ); VectorCopy( tr.endpos, ent->r.currentOrigin ); if ( tr.startsolid ) { tr.fraction = 0; } trap_LinkEntity( ent ); // FIXME: avoid this for stationary? // check think function G_RunThink( ent ); if ( tr.fraction == 1.0f ) { return; } // if it is in a nodrop volume, remove it contents = trap_PointContents( ent->r.currentOrigin, -1 ); if ( contents & CONTENTS_NODROP ) { G_FreeEntity( ent ); return; } G_Bounce( ent, &tr ); }
/* ============ AICast_Die ============ */ void AICast_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) { int contents; int killer; cast_state_t *cs; qboolean nogib = qtrue; // print debugging message if ( aicast_debug.integer == 2 && attacker->s.number == 0 ) { G_Printf( "killed %s\n", self->aiName ); } cs = AICast_GetCastState( self->s.number ); if ( attacker ) { killer = attacker->s.number; } else { killer = ENTITYNUM_WORLD; } // record the sighting (FIXME: silent weapons shouldn't do this, but the AI should react in some way) if ( attacker->client ) { AICast_UpdateVisibility( self, attacker, qtrue, qtrue ); } // the zombie should show special effect instead of gibbing if ( self->aiCharacter == AICHAR_ZOMBIE && cs->secondDeadTime ) { if ( cs->secondDeadTime > 1 ) { // we are already totally dead self->health += damage; // don't drop below gib_health if we weren't already below it return; } /* if (!cs->rebirthTime) { self->health = -999; damage = 999; } else if ( self->health >= GIB_HEALTH ) { // while waiting for rebirth, we only "die" if we drop below gib health return; } */ // always gib self->health = -999; damage = 999; } // Zombies are very fragile against highly explosives if ( self->aiCharacter == AICHAR_ZOMBIE && damage > 20 && inflictor != attacker ) { self->health = -999; damage = 999; } // process the event if ( self->client->ps.pm_type == PM_DEAD ) { // already dead if ( self->health < GIB_HEALTH ) { if ( self->aiCharacter == AICHAR_ZOMBIE ) { // RF, changed this so Zombies always gib now GibEntity( self, killer ); nogib = qfalse; /* // Zombie has special exploding cloud effect if (attacker != inflictor || attacker->s.weapon == WP_VENOM) { GibEntity( self, killer ); nogib = qfalse; } else { // Zombie will decompose upon dying self->client->ps.eFlags |= EF_MONSTER_EFFECT2; self->s.effect2Time = level.time+200; self->health = -1; } */ self->takedamage = qfalse; self->r.contents = 0; cs->secondDeadTime = 2; cs->rebirthTime = 0; cs->revivingTime = 0; } else { body_die( self, inflictor, attacker, damage, meansOfDeath ); return; } } } else { // this is our first death, so set everything up if ( level.intermissiontime ) { return; } self->client->ps.pm_type = PM_DEAD; self->enemy = attacker; // drop a weapon? // if client is in a nodrop area, don't drop anything contents = trap_PointContents( self->r.currentOrigin, -1 ); if ( !( contents & CONTENTS_NODROP ) ) { TossClientItems( self ); } // make sure the client doesn't forget about this entity until it's set to "dead" frame // otherwise it might replay it's death animation if it goes out and into client view self->r.svFlags |= SVF_BROADCAST; 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[1] = self->client->ps.viewangles[1]; self->s.angles[2] = 0; VectorCopy( self->s.angles, self->client->ps.viewangles ); self->s.loopSound = 0; self->r.maxs[2] = -8; self->client->ps.maxs[2] = self->r.maxs[2]; // remove powerups memset( self->client->ps.powerups, 0, sizeof( self->client->ps.powerups ) ); //cs->rebirthTime = 0; // never gib in a nodrop if ( self->health <= GIB_HEALTH ) { if ( self->aiCharacter == AICHAR_ZOMBIE ) { // RF, changed this so Zombies always gib now GibEntity( self, killer ); nogib = qfalse; /* // Zombie has special exploding cloud effect if (attacker != inflictor || attacker->s.weapon == WP_VENOM) { GibEntity( self, killer ); nogib = qfalse; self->takedamage = qfalse; self->r.contents = 0; cs->secondDeadTime = 2; } else { self->client->ps.eFlags |= EF_MONSTER_EFFECT2; self->s.effect2Time = level.time+200; self->takedamage = qfalse; self->r.contents = 0; self->health = -1; cs->secondDeadTime = 2; } */ } else if ( !( contents & CONTENTS_NODROP ) ) { body_die( self, inflictor, attacker, damage, meansOfDeath ); //GibEntity( self, killer ); nogib = qfalse; } } // if we are a zombie, and lying down during our first death, then we should just die if ( !( self->aiCharacter == AICHAR_ZOMBIE && cs->secondDeadTime && cs->rebirthTime ) ) { // set enemy weapon BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_WEAPON, 0, qfalse ); if ( attacker->client ) { BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_WEAPON, inflictor->s.weapon, qtrue ); } else { BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_WEAPON, 0, qfalse ); } // set enemy location BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_POSITION, 0, qfalse ); if ( infront( self, inflictor ) ) { BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_POSITION, POSITION_INFRONT, qtrue ); } else { BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_POSITION, POSITION_BEHIND, qtrue ); } // play the animation BG_AnimScriptEvent( &self->client->ps, ANIM_ET_DEATH, qfalse, qtrue ); // set this flag so no other anims override us self->client->ps.eFlags |= EF_DEAD; self->s.eFlags |= EF_DEAD; } } if ( nogib ) { // set for rebirth if ( self->aiCharacter == AICHAR_ZOMBIE ) { if ( !cs->secondDeadTime ) { cs->rebirthTime = level.time + 5000 + rand() % 2000; cs->secondDeadTime = qtrue; cs->revivingTime = 0; } else if ( cs->secondDeadTime > 1 ) { cs->rebirthTime = 0; cs->revivingTime = 0; cs->deathTime = level.time; } } else { // the body can still be gibbed self->die = body_die; } } trap_LinkEntity( self ); // mark the time of death cs->deathTime = level.time; // dying ai's can trigger a target if ( !cs->rebirthTime ) { G_UseTargets( self, self ); // really dead now, so call the script AICast_ScriptEvent( cs, "death", "" ); // call the deathfunc for this cast, so we can play associated sounds, or do any character-specific things if ( !( cs->aiFlags & AIFL_DENYACTION ) && cs->deathfunc ) { cs->deathfunc( self, attacker, damage, meansOfDeath ); //----(SA) added mod } } else { // really dead now, so call the script AICast_ScriptEvent( cs, "fakedeath", "" ); // call the deathfunc for this cast, so we can play associated sounds, or do any character-specific things if ( !( cs->aiFlags & AIFL_DENYACTION ) && cs->deathfunc ) { cs->deathfunc( self, attacker, damage, meansOfDeath ); //----(SA) added mod } } }
/* ================== 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 = 0; int killer; int i; char *killerName, *obit; qboolean nogib = qtrue; gitem_t *item = NULL; // JPW NERVE for flag drop vec3_t launchvel; // JPW NERVE gentity_t *flag; // JPW NERVE if ( self->client->ps.pm_type == PM_DEAD ) { return; } if ( level.intermissiontime ) { return; } //----(SA) commented out as we have no hook // 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]++; if ( attacker && attacker->client ) { if ( attacker == self || OnSameTeam( self, attacker ) ) { AddScore( attacker, -1 ); } else { AddScore( attacker, 1 ); // Ridah, not in single player if ( g_gametype.integer != GT_SINGLE_PLAYER ) { // done. if ( meansOfDeath == MOD_GAUNTLET ) { attacker->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++; attacker->client->ps.persistant[PERS_REWARD] = REWARD_GAUNTLET; attacker->client->ps.persistant[PERS_REWARD_COUNT]++; // add the sprite over the player's head // attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT /*| EF_AWARD_GAUNTLET*/ ); //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_REWARD] = REWARD_GAUNTLET; self->client->ps.persistant[PERS_REWARD_COUNT]++; } // 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 ) { attacker->client->ps.persistant[PERS_REWARD_COUNT]++; attacker->client->ps.persistant[PERS_REWARD] = REWARD_EXCELLENT; 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*/ ); // attacker->client->ps.eFlags |= EF_AWARD_EXCELLENT; attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; } // Ridah } // done. attacker->client->lastKillTime = level.time; } } else { AddScore( self, -1 ); } // Add team bonuses Team_FragBonuses( self, inflictor, attacker ); // if client is in a nodrop area, don't drop anything // JPW NERVE new drop behavior if ( g_gametype.integer == GT_SINGLE_PLAYER ) { // only drop here in single player; in multiplayer, drop @ limbo contents = trap_PointContents( self->r.currentOrigin, -1 ); if ( !( contents & CONTENTS_NODROP ) ) { TossClientItems( self ); } } // drop flag regardless if ( g_gametype.integer != GT_SINGLE_PLAYER ) { if ( self->client->ps.powerups[PW_REDFLAG] ) { item = BG_FindItem( "Red Flag" ); } if ( self->client->ps.powerups[PW_BLUEFLAG] ) { item = BG_FindItem( "Blue Flag" ); } launchvel[0] = crandom() * 20; launchvel[1] = crandom() * 20; launchvel[2] = 10 + random() * 10; if ( item ) { flag = LaunchItem( item,self->r.currentOrigin,launchvel ); flag->s.modelindex2 = self->s.otherEntityNum2; // JPW NERVE FIXME set player->otherentitynum2 with old modelindex2 from flag and restore here } } // jpw 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.powerups = 0; // JPW NERVE -- only corpse in SP; in MP, need CONTENTS_BODY so medic can operate if ( g_gametype.integer == GT_SINGLE_PLAYER ) { self->r.contents = CONTENTS_CORPSE; self->s.weapon = WP_NONE; } else { self->client->limboDropWeapon = self->s.weapon; // store this so it can be dropped in limbo } // jpw 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 ) ); if ( g_gametype.integer == GT_SINGLE_PLAYER ) { trap_SendServerCommand( -1, "mu_play sound/music/l_failed_1.wav 0\n" ); trap_SetConfigstring( CS_MUSIC_QUEUE, "" ); // clear queue so it'll be quiet after hit trap_SendServerCommand( -1, "cp missionfail0" ); } // never gib in a nodrop if ( self->health <= GIB_HEALTH && !( contents & CONTENTS_NODROP ) && g_blood.integer ) { // if(self->client->ps.eFlags & EF_HEADSHOT) // { // GibHead(self, killer); // } // else // gib death // { GibEntity( self, killer ); nogib = qfalse; // } } if ( nogib ) { // 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; } // JPW NERVE for medic self->client->medicHealAmt = 0; // jpw 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 + 1, 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 ); if ( g_gametype.integer == GT_SINGLE_PLAYER ) { AICast_ScriptEvent( AICast_GetCastState( self->s.number ), "death", "" ); } }
/* ================== 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); }
/* LQ3A: Added bBroadcast parameter to allow the calling function to suppress the change. */ void SetTeam( gentity_t *ent, char *s, qboolean bBroadcast) { /* LQ3A */ team_t team, oldTeam; gclient_t *client; int clientNum; spectatorState_t specState; int specClient; int teamLeader; // // see what change is requested // client = ent->client; clientNum = client - level.clients; specClient = 0; specState = SPECTATOR_NOT; if ( !Q_stricmp( s, "scoreboard" ) || !Q_stricmp( s, "score" ) ) { team = TEAM_SPECTATOR; specState = SPECTATOR_SCOREBOARD; } else if ( !Q_stricmp( s, "follow1" ) ) { team = TEAM_SPECTATOR; specState = SPECTATOR_FOLLOW; specClient = -1; } else if ( !Q_stricmp( s, "follow2" ) ) { team = TEAM_SPECTATOR; specState = SPECTATOR_FOLLOW; specClient = -2; } else if ( !Q_stricmp( s, "spectator" ) || !Q_stricmp( s, "s" ) ) { team = TEAM_SPECTATOR; specState = SPECTATOR_FREE; } else if ( g_gametype.integer >= GT_TEAM ) { // if running a team game, assign player to one of the teams specState = SPECTATOR_NOT; if ( !Q_stricmp( s, "red" ) || !Q_stricmp( s, "r" ) ) { team = TEAM_RED; } else if ( !Q_stricmp( s, "blue" ) || !Q_stricmp( s, "b" ) ) { team = TEAM_BLUE; } else { // pick the team with the least number of players team = PickTeam( clientNum ); } if ( g_teamForceBalance.integer ) { int counts[TEAM_NUM_TEAMS]; counts[TEAM_BLUE] = TeamCount( ent->client->ps.clientNum, TEAM_BLUE ); counts[TEAM_RED] = TeamCount( ent->client->ps.clientNum, TEAM_RED ); // We allow a spread of two if ( team == TEAM_RED && counts[TEAM_RED] - counts[TEAM_BLUE] > 1 ) { trap_SendServerCommand( ent->client->ps.clientNum, "cp \"Red team has too many players.\n\"" ); return; // ignore the request } if ( team == TEAM_BLUE && counts[TEAM_BLUE] - counts[TEAM_RED] > 1 ) { trap_SendServerCommand( ent->client->ps.clientNum, "cp \"Blue team has too many players.\n\"" ); return; // ignore the request } // It's ok, the team we are switching to has less or same number of players } } else { // force them to spectators if there aren't any spots free /* LQ3A */ team = ((client->sess.sessionTeam == TEAM_FREE) || ((client->sess.sessionTeam != TEAM_FREE) && LQ3A_GetVacantPlayerSlots())) ? TEAM_FREE : TEAM_SPECTATOR; } // override decision if limiting the players if ( (g_gametype.integer == GT_TOURNAMENT) && level.numNonSpectatorClients >= 2 ) { team = TEAM_SPECTATOR; } else if ( g_maxGameClients.integer > 0 && level.numNonSpectatorClients > g_maxGameClients.integer ) { team = TEAM_SPECTATOR; } // // decide if we will allow the change // oldTeam = client->sess.sessionTeam; /* LQ3A */ if ( team == oldTeam /*&& team != TEAM_SPECTATOR*/ ) { return; } /* LQ3A: Ensure we're allowed to spectate. */ if ((team == TEAM_SPECTATOR) && !LQ3A_CanClientSpectate(ent)) { return; } // // execute the team change // // if the player was dead leave the body if ( client->ps.stats[STAT_HEALTH] <= 0 ) { CopyToBodyQue(ent); } // he starts at 'base' client->pers.teamState.state = TEAM_BEGIN; /* LQ3A*/ if (oldTeam == TEAM_SPECTATOR) { ent->r.svFlags &= ~SVF_NOCLIENT; client->pers.enterTime += (level.time - client->sess.spectatorTime); /* Restore the players score. */ client->ps.persistant[PERS_SCORE] = client->pers.iScore; } else if (ent->client->ps.stats[STAT_HEALTH] > 0) { // Kill him (makes sure he loses flags, etc) ent->flags &= ~FL_GODMODE; /* LQ3A: Kill the player if they have too little health. */ if ((g_spectatorFreePass.integer > 0) && (ent->client->ps.stats[STAT_HEALTH] < g_spectatorFreePass.integer)) { ent->client->ps.stats[STAT_HEALTH] = ent->health = 0; player_die (ent, ent, ent, 100000, MOD_SUICIDE); } else { /* LQ3A: Drop items and return flags when we're allowed to become a spectator without commiting suicide. */ // if client is in a nodrop area, don't drop anything (but return CTF flags!) if (!(trap_PointContents(ent->r.currentOrigin, -1) & CONTENTS_NODROP)) { TossClientItems(ent); } else { if (ent->client->ps.powerups[PW_NEUTRALFLAG]) { Team_ReturnFlag(TEAM_FREE); } else if (ent->client->ps.powerups[PW_REDFLAG]) { Team_ReturnFlag(TEAM_RED); } else if (ent->client->ps.powerups[PW_BLUEFLAG]) { Team_ReturnFlag(TEAM_BLUE); } } #ifdef MISSIONPACK TossClientPersistantPowerups(ent); if(g_gametype.integer == GT_HARVESTER) { TossClientCubes(ent); } #endif } } // they go to the end of the line for tournements if ( team == TEAM_SPECTATOR ) { /* LQ3A */ LQ3A_CompleteClientMoveToSpectatorTeam(ent); } client->sess.sessionTeam = team; client->sess.spectatorState = specState; client->sess.spectatorClient = specClient; client->sess.teamLeader = qfalse; if ( team == TEAM_RED || team == TEAM_BLUE ) { teamLeader = TeamLeader( team ); // if there is no team leader or the team leader is a bot and this client is not a bot if ( teamLeader == -1 || ( !(g_entities[clientNum].r.svFlags & SVF_BOT) && (g_entities[teamLeader].r.svFlags & SVF_BOT) ) ) { SetLeader( team, clientNum ); } } // make sure there is a team leader on the team the player came from if ( oldTeam == TEAM_RED || oldTeam == TEAM_BLUE ) { CheckTeamLeader( oldTeam ); } /* LQ3A: Broadcast the change when instructed to do so. */ if (bBroadcast) { BroadcastTeamChange(client, oldTeam); } // get and distribute relevent paramters ClientUserinfoChanged( clientNum ); /* LQ3A */ if (team != TEAM_SPECTATOR) { /* Spawn the client into the game. */ ClientBegin(clientNum); } else { /* Update the cached scores. */ CalculateRanks(); } }