void G_domethink(gentity_t *ent) { vec3_t origin, mins ,maxs; gentity_t *otherEnt; int radius, numListedEntities; int entityList[MAX_GENTITIES]; int e,i; float dist; vec3_t v; ent->nextthink = level.time + 1000; if(level.time > ent->dieTime) { ent->freeAfterEvent = qtrue; ent->parent->numDomes -= 1; return; } VectorCopy(ent->s.origin, origin); radius = DOME_RANGE; for(i = 0;i < 3;i++) { mins[i] = origin[i] - radius; maxs[i] = origin[i] + radius; } numListedEntities = trap_EntitiesInBox(mins, maxs, entityList, MAX_GENTITIES); for(e = 0;e < numListedEntities;e++) { otherEnt = &g_entities[entityList[e]]; if(!otherEnt) continue; if(!otherEnt->client) continue; if (otherEnt->client->sess.sessionTeam == TEAM_SPECTATOR) continue; if(otherEnt->health <= 0) continue; // find the distance from the edge of the bounding box for(i = 0;i < 3;i++) { if (origin[i] < ent->r.absmin[i]) v[i] = ent->r.absmin[i] - origin[i]; else if (origin[i] > ent->r.absmax[i]) v[i] = origin[i] - ent->r.absmax[i]; else v[i] = 0; } dist = VectorLength(v); if (dist >= radius) continue; if(otherEnt->client->ps.stats[STAT_PTEAM] != PTE_HUMANS) { otherEnt->client->ps.stats[STAT_STATE] |= SS_CREEPSLOWED; continue; } if (otherEnt->health < 100) { otherEnt->health = otherEnt->client->ps.stats[STAT_HEALTH] = otherEnt->health + 3; if ( (otherEnt->client->pers.badges[ 18 ] != 1) && (otherEnt->health >= 75) && otherEnt->client->pers.onehp) { G_WinBadge( otherEnt, 18 ); otherEnt->client->pers.badgeupdate[18] = 1; otherEnt->client->pers.badges[18] = 1; } if(otherEnt->health >=100) { otherEnt->health = otherEnt->client->ps.stats[STAT_HEALTH] = 100; //G_AddEvent(otherEnt, EV_MEDKIT_USED, 0); } } } }
/* ============ G_MoverPush Objects need to be moved back on a failed push, otherwise riders would continue to slide. If qfalse is returned, *obstacle will be the blocking entity ============ */ qboolean G_MoverPush( gentity_t *pusher, vec3_t move, vec3_t amove, gentity_t **obstacle ) { int i, e; gentity_t *check; vec3_t mins, maxs; pushed_t *p; int entityList[MAX_GENTITIES]; int listedEntities; vec3_t totalMins, totalMaxs; *obstacle = NULL; // mins/maxs are the bounds at the destination // totalMins / totalMaxs are the bounds for the entire move if ( pusher->r.currentAngles[0] || pusher->r.currentAngles[1] || pusher->r.currentAngles[2] || amove[0] || amove[1] || amove[2] ) { float radius; radius = RadiusFromBounds( pusher->s.mins, pusher->s.maxs ); for ( i = 0 ; i < 3 ; i++ ) { mins[i] = pusher->r.currentOrigin[i] + move[i] - radius; maxs[i] = pusher->r.currentOrigin[i] + move[i] + radius; totalMins[i] = mins[i] - move[i]; totalMaxs[i] = maxs[i] - move[i]; } } else { for (i=0 ; i<3 ; i++) { mins[i] = pusher->r.absmin[i] + move[i]; maxs[i] = pusher->r.absmax[i] + move[i]; } VectorCopy( pusher->r.absmin, totalMins ); VectorCopy( pusher->r.absmax, totalMaxs ); for (i=0 ; i<3 ; i++) { if ( move[i] > 0 ) { totalMaxs[i] += move[i]; } else { totalMins[i] += move[i]; } } } // unlink the pusher so we don't get it in the entityList trap_UnlinkEntity( pusher ); listedEntities = trap_EntitiesInBox( totalMins, totalMaxs, entityList, MAX_GENTITIES ); // move the pusher to its final position VectorAdd( pusher->r.currentOrigin, move, pusher->r.currentOrigin ); VectorAdd( pusher->r.currentAngles, amove, pusher->r.currentAngles ); trap_LinkEntity( pusher ); // see if any solid entities are inside the final position for ( e = 0 ; e < listedEntities ; e++ ) { check = &g_entities[ entityList[ e ] ]; #ifdef MISSIONPACK if ( check->s.eType == ET_MISSILE ) { // if it is a prox mine if ( !strcmp(check->classname, "prox mine") ) { // if this prox mine is attached to this mover try to move it with the pusher if ( check->enemy == pusher ) { if (!G_TryPushingProxMine( check, pusher, move, amove )) { //explode check->s.loopSound = 0; G_AddEvent( check, EV_PROXIMITY_MINE_TRIGGER, 0 ); G_ExplodeMissile(check); if (check->activator) { G_FreeEntity(check->activator); check->activator = NULL; } //G_Printf("prox mine explodes\n"); } } else { //check if the prox mine is crushed by the mover if (!G_CheckProxMinePosition( check )) { //explode check->s.loopSound = 0; G_AddEvent( check, EV_PROXIMITY_MINE_TRIGGER, 0 ); G_ExplodeMissile(check); if (check->activator) { G_FreeEntity(check->activator); check->activator = NULL; } //G_Printf("prox mine explodes\n"); } } continue; } } #endif // only push items and players if ( check->s.eType != ET_ITEM && check->s.eType != ET_PLAYER && !check->physicsObject ) { continue; } // if the entity is standing on the pusher, it will definitely be moved if ( check->s.groundEntityNum != pusher->s.number ) { // see if the ent needs to be tested if ( check->r.absmin[0] >= maxs[0] || check->r.absmin[1] >= maxs[1] || check->r.absmin[2] >= maxs[2] || check->r.absmax[0] <= mins[0] || check->r.absmax[1] <= mins[1] || check->r.absmax[2] <= mins[2] ) { continue; } // see if the ent's bbox is inside the pusher's final position // this does allow a fast moving object to pass through a thin entity... if (!G_TestEntityPosition (check)) { continue; } } // the entity needs to be pushed if ( G_TryPushingEntity( check, pusher, move, amove ) ) { continue; } // the move was blocked an entity // bobbing entities are instant-kill and never get blocked if ( pusher->s.pos.trType == TR_SINE || pusher->s.apos.trType == TR_SINE ) { G_Damage( check, pusher, pusher, NULL, NULL, 99999, 0, MOD_CRUSH ); continue; } // save off the obstacle so we can call the block function (crush, etc) *obstacle = check; // move back any entities we already moved // go backwards, so if the same entity was pushed // twice, it goes back to the original position for ( p=pushed_p-1 ; p>=pushed ; p-- ) { VectorCopy (p->origin, p->ent->s.pos.trBase); VectorCopy (p->angles, p->ent->s.apos.trBase); if ( p->ent->client ) { p->ent->client->ps.delta_angles[YAW] = p->deltayaw; VectorCopy (p->origin, p->ent->client->ps.origin); } trap_LinkEntity (p->ent); } return qfalse; } return qtrue; }
/* ================ G_SetEntState sets the entstate of an entity. ================ */ void G_SetEntState(gentity_t *ent, entState_t state) { if (ent->entstate == state) { G_DPrintf("G_SetEntState: entity %i [%s] already in desired state [%i]\n", ent->s.number, ent->classname, state); return; } switch (state) { case STATE_DEFAULT: if (ent->entstate == STATE_UNDERCONSTRUCTION) { ent->clipmask = ent->realClipmask; ent->r.contents = ent->realContents; if (!ent->realNonSolidBModel) { ent->s.eFlags &= ~EF_NONSOLID_BMODEL; } } ent->entstate = STATE_DEFAULT; ent->s.powerups = STATE_DEFAULT; if (ent->s.eType == ET_WOLF_OBJECTIVE) { char cs[MAX_STRING_CHARS]; trap_GetConfigstring(ent->count, cs, sizeof(cs)); ent->count2 &= ~256; Info_SetValueForKey(cs, "t", va("%i", ent->count2)); trap_SetConfigstring(ent->count, cs); } if (ent->s.eType != ET_COMMANDMAP_MARKER) { trap_LinkEntity(ent); } // deal with any entities in the solid { int listedEntities, e; int entityList[MAX_GENTITIES]; gentity_t *check, *block; listedEntities = trap_EntitiesInBox(ent->r.absmin, ent->r.absmax, entityList, MAX_GENTITIES); for (e = 0; e < listedEntities; e++) { check = &g_entities[entityList[e]]; // ignore everything but items, players and missiles (grenades too) if (check->s.eType != ET_MISSILE && check->s.eType != ET_ITEM && check->s.eType != ET_PLAYER && !check->physicsObject) { continue; } if ((block = G_TestEntityPosition(check)) == NULL) { continue; } if (block != ent) { // the entity is blocked by another entity - that block this should take care of this itself continue; } if (check->client || check->s.eType == ET_CORPSE) { // gibs anything player like G_Damage(check, ent, ent, NULL, NULL, 9999, DAMAGE_NO_PROTECTION, MOD_CRUSH_CONSTRUCTIONDEATH_NOATTACKER); } else if (check->s.eType == ET_ITEM && check->item->giType == IT_TEAM) { // see if it's a critical entity, one that we can't just simply kill (basically flags) Team_DroppedFlagThink(check); } else { // remove the landmine from both teamlists if (check->s.eType == ET_MISSILE && check->methodOfDeath == MOD_LANDMINE) { mapEntityData_t *mEnt; if ((mEnt = G_FindMapEntityData(&mapEntityData[0], check - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[0], mEnt); } if ((mEnt = G_FindMapEntityData(&mapEntityData[1], check - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[1], mEnt); } } // just get rid of it G_FreeEntity(check); } } } break; case STATE_UNDERCONSTRUCTION: ent->entstate = STATE_UNDERCONSTRUCTION; ent->s.powerups = STATE_UNDERCONSTRUCTION; ent->realClipmask = ent->clipmask; if (ent->s.eType != ET_CONSTRUCTIBLE) // don't make nonsolid as we want to make them partially solid for staged construction { ent->clipmask = 0; } ent->realContents = ent->r.contents; if (ent->s.eType != ET_CONSTRUCTIBLE) { ent->r.contents = 0; } if (ent->s.eFlags & EF_NONSOLID_BMODEL) { ent->realNonSolidBModel = qtrue; } else if (ent->s.eType != ET_CONSTRUCTIBLE) { ent->s.eFlags |= EF_NONSOLID_BMODEL; } if (!Q_stricmp(ent->classname, "misc_mg42")) { // stop using the mg42 mg42_stopusing(ent); } else if (!Q_stricmp(ent->classname, "misc_aagun")) { aagun_stopusing(ent); } if (ent->s.eType == ET_COMMANDMAP_MARKER) { mapEntityData_t *mEnt; if ((mEnt = G_FindMapEntityData(&mapEntityData[0], ent - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[0], mEnt); } if ((mEnt = G_FindMapEntityData(&mapEntityData[1], ent - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[1], mEnt); } } trap_LinkEntity(ent); break; case STATE_INVISIBLE: if (ent->entstate == STATE_UNDERCONSTRUCTION) { ent->clipmask = ent->realClipmask; ent->r.contents = ent->realContents; if (!ent->realNonSolidBModel) { ent->s.eFlags &= ~EF_NONSOLID_BMODEL; } } ent->entstate = STATE_INVISIBLE; ent->s.powerups = STATE_INVISIBLE; if (!Q_stricmp(ent->classname, "misc_mg42")) { mg42_stopusing(ent); } else if (!Q_stricmp(ent->classname, "misc_aagun")) { aagun_stopusing(ent); } else if (ent->s.eType == ET_WOLF_OBJECTIVE) { char cs[MAX_STRING_CHARS]; trap_GetConfigstring(ent->count, cs, sizeof(cs)); ent->count2 |= 256; Info_SetValueForKey(cs, "t", va("%i", ent->count2)); trap_SetConfigstring(ent->count, cs); } if (ent->s.eType == ET_COMMANDMAP_MARKER) { mapEntityData_t *mEnt; if ((mEnt = G_FindMapEntityData(&mapEntityData[0], ent - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[0], mEnt); } if ((mEnt = G_FindMapEntityData(&mapEntityData[1], ent - g_entities)) != NULL) { G_FreeMapEntityData(&mapEntityData[1], mEnt); } } trap_UnlinkEntity(ent); break; } }
/* ============ G_TouchTriggers Find all trigger entities that ent's current position touches. Spectators will only interact with teleporters. ============ */ void G_TouchTriggers( gentity_t *ent ) { int i, num; int touch[MAX_GENTITIES]; gentity_t *hit; trace_t trace; vec3_t mins, maxs; static vec3_t range = { 40, 40, 52 }; if ( !ent->client ) { return; } // dead clients don't activate triggers! if ( ent->client->ps.stats[STAT_HEALTH] <= 0 ) { return; } VectorSubtract( ent->client->ps.origin, range, mins ); VectorAdd( ent->client->ps.origin, range, maxs ); num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES ); // can't use ent->absmin, because that has a one unit pad VectorAdd( ent->client->ps.origin, ent->r.mins, mins ); VectorAdd( ent->client->ps.origin, ent->r.maxs, maxs ); for ( i = 0 ; i < num ; i++ ) { hit = &g_entities[touch[i]]; if ( !hit->touch && !ent->touch ) { continue; } if ( !( hit->r.contents & CONTENTS_TRIGGER ) ) { continue; } // ignore most entities if a spectator if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { if ( hit->s.eType != ET_TELEPORT_TRIGGER ) { continue; } } // use seperate code for determining if an item is picked up // so you don't have to actually contact its bounding box if ( hit->s.eType == ET_ITEM ) { if ( !BG_PlayerTouchesItem( &ent->client->ps, &hit->s, level.time ) ) { continue; } } else { // MrE: always use capsule for player //if ( !trap_EntityContactCapsule( mins, maxs, hit ) ) { if ( !trap_EntityContact( mins, maxs, hit ) ) { continue; } } memset( &trace, 0, sizeof( trace ) ); if ( hit->touch ) { hit->touch( hit, ent, &trace ); } if ( ( ent->r.svFlags & SVF_BOT ) && ( ent->touch ) ) { ent->touch( ent, hit, &trace ); } } }
int Team_TouchOurFlag( gentity_t *ent, gentity_t *other, int team ) { int i, num, j, enemyTeam; gentity_t *player; gclient_t *cl = other->client; int enemy_flag; vec3_t mins, maxs; int touch[MAX_GENTITIES]; gentity_t* enemy; float enemyDist, dist; if (cl->sess.sessionTeam == TEAM_RED) { enemy_flag = PW_BLUEFLAG; } else { enemy_flag = PW_REDFLAG; } if ( ent->flags & FL_DROPPED_ITEM ) { // hey, its not home. return it by teleporting it back //PrintMsg( NULL, "%s" S_COLOR_WHITE " returned the %s flag!\n", // cl->pers.netname, TeamName(team)); PrintCTFMessage(other->s.number, team, CTFMESSAGE_PLAYER_RETURNED_FLAG); AddScore(other, ent->r.currentOrigin, CTF_RECOVERY_BONUS); other->client->pers.teamState.flagrecovery++; other->client->pers.teamState.lastreturnedflag = level.time; //ResetFlag will remove this entity! We must return zero Team_ReturnFlagSound(Team_ResetFlag(team), team); return 0; } // the flag is at home base. if the player has the enemy // flag, he's just won! if (!cl->ps.powerups[enemy_flag]) return 0; // We don't have the flag // fix: captures after timelimit hit could // cause game ending with tied score if ( level.intermissionQueued ) { return 0; } // check for enemy closer to grab the flag VectorSubtract( ent->s.pos.trBase, minFlagRange, mins ); VectorAdd( ent->s.pos.trBase, maxFlagRange, maxs ); num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES ); dist = Distance(ent->s.pos.trBase, other->client->ps.origin); if (other->client->sess.sessionTeam == TEAM_RED){ enemyTeam = TEAM_BLUE; } else { enemyTeam = TEAM_RED; } for ( j=0 ; j<num ; j++ ) { enemy = (g_entities + touch[j]); if (!enemy || !enemy->inuse || !enemy->client){ continue; } //check if its alive if (enemy->health < 1) continue; // dead people can't pickup //ignore specs if (enemy->client->sess.sessionTeam == TEAM_SPECTATOR) continue; //check if this is enemy if ((enemy->client->sess.sessionTeam != TEAM_RED && enemy->client->sess.sessionTeam != TEAM_BLUE) || enemy->client->sess.sessionTeam != enemyTeam){ continue; } //check if enemy is closer to our flag than us enemyDist = Distance(ent->s.pos.trBase,enemy->client->ps.origin); if (enemyDist < dist){ // possible recursion is hidden in this, but // infinite recursion wont happen, because we cant // have a < b and b < a at the same time return Team_TouchEnemyFlag( ent, enemy, team ); } } //PrintMsg( NULL, "%s" S_COLOR_WHITE " captured the %s flag!\n", cl->pers.netname, TeamName(OtherTeam(team))); PrintCTFMessage(other->s.number, team, CTFMESSAGE_PLAYER_CAPTURED_FLAG); cl->ps.powerups[enemy_flag] = 0; teamgame.last_flag_capture = level.time; teamgame.last_capture_team = team; // Increase the team's score AddTeamScore(ent->s.pos.trBase, other->client->sess.sessionTeam, 1); // Team_ForceGesture(other->client->sess.sessionTeam); //rww - don't really want to do this now. Mainly because performing a gesture disables your upper torso animations until it's done and you can't fire other->client->pers.teamState.captures++; other->client->rewardTime = level.time + REWARD_SPRITE_TIME; other->client->ps.persistant[PERS_CAPTURES]++; // other gets another 10 frag bonus AddScore(other, ent->r.currentOrigin, CTF_CAPTURE_BONUS); Team_CaptureFlagSound( ent, team ); // Ok, let's do the player loop, hand out the bonuses for (i = 0; i < sv_maxclients.integer; i++) { player = &g_entities[i]; if (!player->inuse || player == other) continue; if (player->client->sess.sessionTeam != cl->sess.sessionTeam) { player->client->pers.teamState.lasthurtcarrier = -5; } else if (player->client->sess.sessionTeam == cl->sess.sessionTeam) { AddScore(player, ent->r.currentOrigin, CTF_TEAM_BONUS); // award extra points for capture assists if (player->client->pers.teamState.lastreturnedflag + CTF_RETURN_FLAG_ASSIST_TIMEOUT > level.time) { AddScore (player, ent->r.currentOrigin, CTF_RETURN_FLAG_ASSIST_BONUS); other->client->pers.teamState.assists++; player->client->ps.persistant[PERS_ASSIST_COUNT]++; player->client->rewardTime = level.time + REWARD_SPRITE_TIME; } //Raz: Was 'else if' meaning people were missing out on some assist scores if (player->client->pers.teamState.lastfraggedcarrier + CTF_FRAG_CARRIER_ASSIST_TIMEOUT > level.time) { AddScore(player, ent->r.currentOrigin, CTF_FRAG_CARRIER_ASSIST_BONUS); other->client->pers.teamState.assists++; player->client->ps.persistant[PERS_ASSIST_COUNT]++; player->client->rewardTime = level.time + REWARD_SPRITE_TIME; } } } Team_ResetFlags(); CalculateRanks(); return 0; // Do not respawn this automatically }
qboolean G_RadiusDamage( vec3_t origin, gentity_t *attacker, float damage, float radius, gentity_t *ignore, int mod ) { float points, dist; gentity_t *ent; int entityList[ MAX_GENTITIES ]; int numListedEntities; vec3_t mins, maxs; vec3_t v; vec3_t dir; int i, e; qboolean hitClient = qfalse; if ( radius < 1 ) { radius = 1; } for ( i = 0; i < 3; i++ ) { mins[ i ] = origin[ i ] - radius; maxs[ i ] = origin[ i ] + radius; } numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); for ( e = 0; e < numListedEntities; e++ ) { ent = &g_entities[ entityList[ e ] ]; if ( ent == ignore ) { continue; } if ( !ent->takedamage ) { continue; } // find the distance from the edge of the bounding box for ( i = 0; i < 3; i++ ) { if ( origin[ i ] < ent->r.absmin[ i ] ) { v[ i ] = ent->r.absmin[ i ] - origin[ i ]; } else if ( origin[ i ] > ent->r.absmax[ i ] ) { v[ i ] = origin[ i ] - ent->r.absmax[ i ]; } else { v[ i ] = 0; } } dist = VectorLength( v ); if ( dist >= radius ) { continue; } points = damage * ( 1.0 - dist / radius ); if ( G_CanDamage( ent, origin ) ) { VectorSubtract( ent->r.currentOrigin, origin, dir ); // push the center of mass higher than the origin so players // get knocked into the air more dir[ 2 ] += 24; VectorNormalize( dir ); hitClient = qtrue; G_Damage( ent, NULL, attacker, dir, origin, ( int ) points, DAMAGE_RADIUS | DAMAGE_NO_LOCDAMAGE, mod ); } } return hitClient; }
/* =============== RailSphereShockWave =============== */ static void RailSphereShockWave(vec3_t origin, gentity_t * attacker, float damage, float push, float radius) { float dist; gentity_t *ent; int entityList[MAX_GENTITIES]; int numListedEntities; vec3_t mins, maxs; vec3_t v; vec3_t dir; int i, e; if(radius < 1) radius = 1; for(i = 0; i < 3; i++) { mins[i] = origin[i] - radius; maxs[i] = origin[i] + radius; } numListedEntities = trap_EntitiesInBox(mins, maxs, entityList, MAX_GENTITIES); for(e = 0; e < numListedEntities; e++) { ent = &g_entities[entityList[e]]; // dont hit things we have already hit if(ent->kamikazeShockTime > level.time) { continue; } // find the distance from the edge of the bounding box for(i = 0; i < 3; i++) { if(origin[i] < ent->r.absmin[i]) { v[i] = ent->r.absmin[i] - origin[i]; } else if(origin[i] > ent->r.absmax[i]) { v[i] = origin[i] - ent->r.absmax[i]; } else { v[i] = 0; } } dist = VectorLength(v); if(dist >= radius) { continue; } // if( CanDamage (ent, origin) ) { VectorSubtract(ent->r.currentOrigin, origin, dir); dir[2] += 24; G_Damage(ent, NULL, attacker, dir, origin, damage, DAMAGE_RADIUS | DAMAGE_NO_TEAM_PROTECTION, MOD_KAMIKAZE); // dir[2] = 0; VectorNormalize(dir); if(ent->client) { ent->client->ps.velocity[0] = dir[0] * push; ent->client->ps.velocity[1] = dir[1] * push; ent->client->ps.velocity[2] = 100; } ent->kamikazeShockTime = level.time + 3000; // } } }
static void KamikazeShockWave(Vec3 origin, Gentity *attacker, float damage, float push, float radius) { float dist; Gentity *ent; int entityList[MAX_GENTITIES]; int numListedEntities; Vec3 mins, maxs; Vec3 v; Vec3 dir; int i, e; if(radius < 1) radius = 1; for(i = 0; i < 3; i++){ mins[i] = origin[i] - radius; maxs[i] = origin[i] + radius; } numListedEntities = trap_EntitiesInBox(mins, maxs, entityList, MAX_GENTITIES); for(e = 0; e < numListedEntities; e++){ ent = &g_entities[entityList[ e ]]; /* dont hit things we have already hit */ if(ent->kamikazeShockTime > level.time) continue; /* find the distance from the edge of the bounding box */ for(i = 0; i < 3; i++){ if(origin[i] < ent->r.absmin[i]) v[i] = ent->r.absmin[i] - origin[i]; else if(origin[i] > ent->r.absmax[i]) v[i] = origin[i] - ent->r.absmax[i]; else v[i] = 0; } dist = lenv3(v); if(dist >= radius) continue; /* if( CanDamage (ent, origin) ) { */ subv3 (ent->r.currentOrigin, origin, dir); dir[2] += 24; G_Damage(ent, NULL, attacker, dir, origin, damage, DAMAGE_RADIUS|DAMAGE_NO_TEAM_PROTECTION, MOD_KAMIKAZE); dir[2] = 0; normv3(dir); if(ent->client){ ent->client->ps.velocity[0] = dir[0] * push; ent->client->ps.velocity[1] = dir[1] * push; ent->client->ps.velocity[2] = 100; } ent->kamikazeShockTime = level.time + 3000; /* } */ } }
/* ============ G_MoverTouchTriggers Find all trigger entities that ent's current position touches. Spectators will only interact with teleporters. ============ */ void G_MoverTouchPushTriggers( gentity_t *ent, vec3_t oldOrg ) { int i, num; float step, stepSize, dist; int touch[MAX_GENTITIES]; gentity_t *hit; trace_t trace; vec3_t mins, maxs, dir, size, checkSpot; const vec3_t range = { 40, 40, 52 }; // non-moving movers don't hit triggers! if ( !VectorLengthSquared( ent->s.pos.trDelta ) ) { return; } VectorSubtract( ent->r.mins, ent->r.maxs, size ); stepSize = VectorLength( size ); if ( stepSize < 1 ) { stepSize = 1; } VectorSubtract( ent->r.currentOrigin, oldOrg, dir ); dist = VectorNormalize( dir ); for ( step = 0; step <= dist; step += stepSize ) { VectorMA( ent->r.currentOrigin, step, dir, checkSpot ); VectorSubtract( checkSpot, range, mins ); VectorAdd( checkSpot, range, maxs ); num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES ); // can't use ent->r.absmin, because that has a one unit pad VectorAdd( checkSpot, ent->r.mins, mins ); VectorAdd( checkSpot, ent->r.maxs, maxs ); for ( i=0 ; i<num ; i++ ) { hit = &g_entities[touch[i]]; if ( hit->s.eType != ET_PUSH_TRIGGER ) { continue; } if ( hit->touch == NULL ) { continue; } if ( !( hit->r.contents & CONTENTS_TRIGGER ) ) { continue; } if ( !trap_EntityContact( mins, maxs, hit ) ) { continue; } memset( &trace, 0, sizeof(trace) ); if ( hit->touch != NULL ) { hit->touch(hit, ent, &trace); } } } }
/* ================== ClientTimerActions Actions that happen once a second ================== */ void ClientTimerActions( gentity_t *ent, int msec ) { gclient_t *client; usercmd_t *ucmd; int aForward, aRight; ucmd = &ent->client->pers.cmd; aForward = abs( ucmd->forwardmove ); aRight = abs( ucmd->rightmove ); client = ent->client; client->time100 += msec; client->time1000 += msec; client->time10000 += msec; while ( client->time100 >= 100 ) { client->time100 -= 100; //if not trying to run then not trying to sprint if( aForward <= 64 ) client->ps.stats[ STAT_STATE ] &= ~SS_SPEEDBOOST; if( BG_InventoryContainsUpgrade( UP_JETPACK, client->ps.stats ) && BG_UpgradeIsActive( UP_JETPACK, client->ps.stats ) ) client->ps.stats[ STAT_STATE ] &= ~SS_SPEEDBOOST; if( ( client->ps.stats[ STAT_STATE ] & SS_SPEEDBOOST ) && ucmd->upmove >= 0 ) { //subtract stamina if( BG_InventoryContainsUpgrade( UP_LIGHTARMOUR, client->ps.stats ) ) client->ps.stats[ STAT_STAMINA ] -= STAMINA_LARMOUR_TAKE; else client->ps.stats[ STAT_STAMINA ] -= STAMINA_SPRINT_TAKE; if( client->ps.stats[ STAT_STAMINA ] < -MAX_STAMINA ) client->ps.stats[ STAT_STAMINA ] = -MAX_STAMINA; } if( ( aForward <= 64 && aForward > 5 ) || ( aRight <= 64 && aRight > 5 ) ) { //restore stamina client->ps.stats[ STAT_STAMINA ] += STAMINA_WALK_RESTORE; if( client->ps.stats[ STAT_STAMINA ] > MAX_STAMINA ) client->ps.stats[ STAT_STAMINA ] = MAX_STAMINA; } else if( aForward <= 5 && aRight <= 5 ) { //restore stamina faster client->ps.stats[ STAT_STAMINA ] += STAMINA_STOP_RESTORE; if( client->ps.stats[ STAT_STAMINA ] > MAX_STAMINA ) client->ps.stats[ STAT_STAMINA ] = MAX_STAMINA; } //client is charging up for a pounce if( client->ps.weapon == WP_ALEVEL3 || client->ps.weapon == WP_ALEVEL3_UPG ) { int pounceSpeed = 0; if( client->ps.weapon == WP_ALEVEL3 ) pounceSpeed = LEVEL3_POUNCE_SPEED; else if( client->ps.weapon == WP_ALEVEL3_UPG ) pounceSpeed = LEVEL3_POUNCE_UPG_SPEED; if( client->ps.stats[ STAT_MISC ] < pounceSpeed && ucmd->buttons & BUTTON_ATTACK2 ) client->ps.stats[ STAT_MISC ] += ( 100.0f / (float)LEVEL3_POUNCE_CHARGE_TIME ) * pounceSpeed; if( !( ucmd->buttons & BUTTON_ATTACK2 ) ) { if( client->ps.stats[ STAT_MISC ] > 0 ) { client->allowedToPounce = qtrue; client->pouncePayload = client->ps.stats[ STAT_MISC ]; } client->ps.stats[ STAT_MISC ] = 0; } if( client->ps.stats[ STAT_MISC ] > pounceSpeed ) client->ps.stats[ STAT_MISC ] = pounceSpeed; } //client is charging up for a... charge if( client->ps.weapon == WP_ALEVEL4 ) { if( client->ps.stats[ STAT_MISC ] < LEVEL4_CHARGE_TIME && ucmd->buttons & BUTTON_ATTACK2 && !client->charging ) { client->charging = qfalse; //should already be off, just making sure client->ps.stats[ STAT_STATE ] &= ~SS_CHARGING; if( ucmd->forwardmove > 0 ) { //trigger charge sound...is quite annoying //if( client->ps.stats[ STAT_MISC ] <= 0 ) // G_AddEvent( ent, EV_LEV4_CHARGE_PREPARE, 0 ); client->ps.stats[ STAT_MISC ] += (int)( 100 * (float)LEVEL4_CHARGE_CHARGE_RATIO ); if( client->ps.stats[ STAT_MISC ] > LEVEL4_CHARGE_TIME ) client->ps.stats[ STAT_MISC ] = LEVEL4_CHARGE_TIME; } else client->ps.stats[ STAT_MISC ] = 0; } if( !( ucmd->buttons & BUTTON_ATTACK2 ) || client->charging || client->ps.stats[ STAT_MISC ] == LEVEL4_CHARGE_TIME ) { if( client->ps.stats[ STAT_MISC ] > LEVEL4_MIN_CHARGE_TIME ) { client->ps.stats[ STAT_MISC ] -= 100; if( client->charging == qfalse ) G_AddEvent( ent, EV_LEV4_CHARGE_START, 0 ); client->charging = qtrue; client->ps.stats[ STAT_STATE ] |= SS_CHARGING; //if the charger has stopped moving take a chunk of charge away if( VectorLength( client->ps.velocity ) < 64.0f || aRight ) client->ps.stats[ STAT_MISC ] = client->ps.stats[ STAT_MISC ] / 2; //can't charge backwards if( ucmd->forwardmove < 0 ) client->ps.stats[ STAT_MISC ] = 0; } else client->ps.stats[ STAT_MISC ] = 0; if( client->ps.stats[ STAT_MISC ] <= 0 ) { client->ps.stats[ STAT_MISC ] = 0; client->charging = qfalse; client->ps.stats[ STAT_STATE ] &= ~SS_CHARGING; } } } //client is charging up an lcannon if( client->ps.weapon == WP_LUCIFER_CANNON ) { int ammo; BG_UnpackAmmoArray( WP_LUCIFER_CANNON, client->ps.ammo, client->ps.powerups, &ammo, NULL ); if( client->ps.stats[ STAT_MISC ] < LCANNON_TOTAL_CHARGE && ucmd->buttons & BUTTON_ATTACK ) client->ps.stats[ STAT_MISC ] += ( 100.0f / LCANNON_CHARGE_TIME ) * LCANNON_TOTAL_CHARGE; if( client->ps.stats[ STAT_MISC ] > LCANNON_TOTAL_CHARGE ) client->ps.stats[ STAT_MISC ] = LCANNON_TOTAL_CHARGE; if( client->ps.stats[ STAT_MISC ] > ( ammo * LCANNON_TOTAL_CHARGE ) / 10 ) client->ps.stats[ STAT_MISC ] = ammo * LCANNON_TOTAL_CHARGE / 10; } switch( client->ps.weapon ) { case WP_ABUILD: case WP_ABUILD2: case WP_HBUILD: case WP_HBUILD2: //set validity bit on buildable if( ( client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ) > BA_NONE ) { int dist = BG_FindBuildDistForClass( ent->client->ps.stats[ STAT_PCLASS ] ); vec3_t dummy; if( G_itemFits( ent, (buildable_t)(client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT), dist, dummy ) == IBE_NONE ) client->ps.stats[ STAT_BUILDABLE ] |= SB_VALID_TOGGLEBIT; else client->ps.stats[ STAT_BUILDABLE ] &= ~SB_VALID_TOGGLEBIT; } //update build timer if( client->ps.stats[ STAT_MISC ] > 0 ) client->ps.stats[ STAT_MISC ] -= 100; if( client->ps.stats[ STAT_MISC ] < 0 ) client->ps.stats[ STAT_MISC ] = 0; break; default: break; } if( client->ps.stats[ STAT_STATE ] & SS_MEDKIT_ACTIVE ) { int remainingStartupTime = MEDKIT_STARTUP_TIME - ( level.time - client->lastMedKitTime ); if( remainingStartupTime < 0 ) { if( ent->health < ent->client->ps.stats[ STAT_MAX_HEALTH ] && ent->client->medKitHealthToRestore && ent->client->ps.pm_type != PM_DEAD ) { ent->client->medKitHealthToRestore--; ent->health++; } else ent->client->ps.stats[ STAT_STATE ] &= ~SS_MEDKIT_ACTIVE; } else { if( ent->health < ent->client->ps.stats[ STAT_MAX_HEALTH ] && ent->client->medKitHealthToRestore && ent->client->ps.pm_type != PM_DEAD ) { //partial increase if( level.time > client->medKitIncrementTime ) { ent->client->medKitHealthToRestore--; ent->health++; client->medKitIncrementTime = level.time + ( remainingStartupTime / MEDKIT_STARTUP_SPEED ); } } else ent->client->ps.stats[ STAT_STATE ] &= ~SS_MEDKIT_ACTIVE; } } } while( client->time1000 >= 1000 ) { client->time1000 -= 1000; //client is poison clouded if( client->ps.stats[ STAT_STATE ] & SS_POISONCLOUDED ) G_Damage( ent, client->lastPoisonCloudedClient, client->lastPoisonCloudedClient, NULL, NULL, LEVEL1_PCLOUD_DMG, 0, MOD_LEVEL1_PCLOUD ); //client is poisoned if( client->ps.stats[ STAT_STATE ] & SS_POISONED ) { int i; int seconds = ( ( level.time - client->lastPoisonTime ) / 1000 ) + 1; int damage = ALIEN_POISON_DMG, damage2 = 0; for( i = 0; i < seconds; i++ ) { if( i == seconds - 1 ) damage2 = damage; damage *= ALIEN_POISON_DIVIDER; } damage = damage2 - damage; G_Damage( ent, client->lastPoisonClient, client->lastPoisonClient, NULL, NULL, damage, 0, MOD_POISON ); } //replenish alien health if( client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) { int entityList[ MAX_GENTITIES ]; vec3_t range = { LEVEL4_REGEN_RANGE, LEVEL4_REGEN_RANGE, LEVEL4_REGEN_RANGE }; vec3_t mins, maxs; int i, num; gentity_t *boostEntity; float modifier = 1.0f; VectorAdd( client->ps.origin, range, maxs ); VectorSubtract( client->ps.origin, range, mins ); num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); for( i = 0; i < num; i++ ) { boostEntity = &g_entities[ entityList[ i ] ]; if( boostEntity->client && boostEntity->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS && boostEntity->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_LEVEL4 ) { modifier = LEVEL4_REGEN_MOD; break; } else if( boostEntity->s.eType == ET_BUILDABLE && boostEntity->s.modelindex == BA_A_BOOSTER && boostEntity->spawned ) { modifier = BOOSTER_REGEN_MOD; break; } } if( ent->health > 0 && ent->health < client->ps.stats[ STAT_MAX_HEALTH ] && ( ent->lastDamageTime + ALIEN_REGEN_DAMAGE_TIME ) < level.time ) ent->health += BG_FindRegenRateForClass( client->ps.stats[ STAT_PCLASS ] ) * modifier; if( ent->health > client->ps.stats[ STAT_MAX_HEALTH ] ) ent->health = client->ps.stats[ STAT_MAX_HEALTH ]; } } while( client->time10000 >= 10000 ) { client->time10000 -= 10000; if( client->ps.weapon == WP_ALEVEL3_UPG ) { int ammo, maxAmmo; BG_FindAmmoForWeapon( WP_ALEVEL3_UPG, &maxAmmo, NULL ); BG_UnpackAmmoArray( WP_ALEVEL3_UPG, client->ps.ammo, client->ps.powerups, &ammo, NULL ); if( ammo < maxAmmo ) { ammo++; BG_PackAmmoArray( WP_ALEVEL3_UPG, client->ps.ammo, client->ps.powerups, ammo, 0 ); } } } }
/* ============== ClientThink This will be called once for each client frame, which will usually be a couple times for each server frame on fast clients. If "g_synchronousClients 1" is set, this will be called exactly once for each server frame, which makes for smooth demo recording. ============== */ void ClientThink_real( gentity_t *ent ) { gclient_t *client; pmove_t pm; int oldEventSequence; int msec; usercmd_t *ucmd; client = ent->client; // don't think if the client is not yet connected (and thus not yet spawned in) if( client->pers.connected != CON_CONNECTED ) return; // mark the time, so the connection sprite can be removed ucmd = &ent->client->pers.cmd; // sanity check the command time to prevent speedup cheating if( ucmd->serverTime > level.time + 200 ) { ucmd->serverTime = level.time + 200; // G_Printf("serverTime <<<<<\n" ); } if( ucmd->serverTime < level.time - 1000 ) { ucmd->serverTime = level.time - 1000; // G_Printf("serverTime >>>>>\n" ); } msec = ucmd->serverTime - client->ps.commandTime; // following others may result in bad times, but we still want // to check for follow toggles if( msec < 1 && client->sess.spectatorState != SPECTATOR_FOLLOW ) return; if( msec > 200 ) msec = 200; if( pmove_msec.integer < 8 ) trap_Cvar_Set( "pmove_msec", "8" ); else if( pmove_msec.integer > 33 ) trap_Cvar_Set( "pmove_msec", "33" ); if( pmove_fixed.integer || client->pers.pmoveFixed ) { ucmd->serverTime = ( ( ucmd->serverTime + pmove_msec.integer - 1 ) / pmove_msec.integer ) * pmove_msec.integer; //if (ucmd->serverTime - client->ps.commandTime <= 0) // return; } // // check for exiting intermission // if( level.intermissiontime ) { ClientIntermissionThink( client ); return; } // spectators don't do much if( client->sess.sessionTeam == TEAM_SPECTATOR ) { if( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) return; SpectatorThink( ent, ucmd ); return; } G_UpdatePTRConnection( client ); // check for inactivity timer, but never drop the local client of a non-dedicated server if( !ClientInactivityTimer( client ) ) return; if( client->noclip ) client->ps.pm_type = PM_NOCLIP; else if( client->ps.stats[ STAT_HEALTH ] <= 0 ) client->ps.pm_type = PM_DEAD; else if( client->ps.stats[ STAT_STATE ] & SS_INFESTING || client->ps.stats[ STAT_STATE ] & SS_HOVELING ) client->ps.pm_type = PM_FREEZE; else if( client->ps.stats[ STAT_STATE ] & SS_BLOBLOCKED || client->ps.stats[ STAT_STATE ] & SS_GRABBED ) client->ps.pm_type = PM_GRABBED; else if( BG_InventoryContainsUpgrade( UP_JETPACK, client->ps.stats ) && BG_UpgradeIsActive( UP_JETPACK, client->ps.stats ) ) client->ps.pm_type = PM_JETPACK; else client->ps.pm_type = PM_NORMAL; if( client->ps.stats[ STAT_STATE ] & SS_GRABBED && client->grabExpiryTime < level.time ) client->ps.stats[ STAT_STATE ] &= ~SS_GRABBED; if( client->ps.stats[ STAT_STATE ] & SS_BLOBLOCKED && client->lastLockTime + 5000 < level.time ) client->ps.stats[ STAT_STATE ] &= ~SS_BLOBLOCKED; if( client->ps.stats[ STAT_STATE ] & SS_SLOWLOCKED && client->lastLockTime + ABUILDER_BLOB_TIME < level.time ) client->ps.stats[ STAT_STATE ] &= ~SS_SLOWLOCKED; client->ps.stats[ STAT_BOOSTTIME ] = level.time - client->lastBoostedTime; if( client->ps.stats[ STAT_STATE ] & SS_BOOSTED && client->lastBoostedTime + BOOST_TIME < level.time ) client->ps.stats[ STAT_STATE ] &= ~SS_BOOSTED; if( client->ps.stats[ STAT_STATE ] & SS_POISONCLOUDED && client->lastPoisonCloudedTime + LEVEL1_PCLOUD_TIME < level.time ) client->ps.stats[ STAT_STATE ] &= ~SS_POISONCLOUDED; if( client->ps.stats[ STAT_STATE ] & SS_POISONED && client->lastPoisonTime + ALIEN_POISON_TIME < level.time ) client->ps.stats[ STAT_STATE ] &= ~SS_POISONED; client->ps.gravity = g_gravity.value; if( BG_InventoryContainsUpgrade( UP_MEDKIT, client->ps.stats ) && BG_UpgradeIsActive( UP_MEDKIT, client->ps.stats ) ) { //if currently using a medkit or have no need for a medkit now if( client->ps.stats[ STAT_STATE ] & SS_MEDKIT_ACTIVE || ( client->ps.stats[ STAT_HEALTH ] == client->ps.stats[ STAT_MAX_HEALTH ] && !( client->ps.stats[ STAT_STATE ] & SS_POISONED ) ) ) { BG_DeactivateUpgrade( UP_MEDKIT, client->ps.stats ); } else if( client->ps.stats[ STAT_HEALTH ] > 0 ) { //remove anti toxin BG_DeactivateUpgrade( UP_MEDKIT, client->ps.stats ); BG_RemoveUpgradeFromInventory( UP_MEDKIT, client->ps.stats ); client->ps.stats[ STAT_STATE ] &= ~SS_POISONED; client->poisonImmunityTime = level.time + MEDKIT_POISON_IMMUNITY_TIME; client->ps.stats[ STAT_STATE ] |= SS_MEDKIT_ACTIVE; client->lastMedKitTime = level.time; client->medKitHealthToRestore = client->ps.stats[ STAT_MAX_HEALTH ] - client->ps.stats[ STAT_HEALTH ]; client->medKitIncrementTime = level.time + ( MEDKIT_STARTUP_TIME / MEDKIT_STARTUP_SPEED ); G_AddEvent( ent, EV_MEDKIT_USED, 0 ); } } if( BG_InventoryContainsUpgrade( UP_GRENADE, client->ps.stats ) && BG_UpgradeIsActive( UP_GRENADE, client->ps.stats ) ) { int lastWeapon = ent->s.weapon; //remove grenade BG_DeactivateUpgrade( UP_GRENADE, client->ps.stats ); BG_RemoveUpgradeFromInventory( UP_GRENADE, client->ps.stats ); //M-M-M-M-MONSTER HACK ent->s.weapon = WP_GRENADE; FireWeapon( ent ); ent->s.weapon = lastWeapon; } // set speed client->ps.speed = g_speed.value * BG_FindSpeedForClass( client->ps.stats[ STAT_PCLASS ] ); if( client->lastCreepSlowTime + CREEP_TIMEOUT < level.time ) client->ps.stats[ STAT_STATE ] &= ~SS_CREEPSLOWED; //randomly disable the jet pack if damaged if( BG_InventoryContainsUpgrade( UP_JETPACK, client->ps.stats ) && BG_UpgradeIsActive( UP_JETPACK, client->ps.stats ) ) { if( ent->lastDamageTime + JETPACK_DISABLE_TIME > level.time ) { if( random( ) > JETPACK_DISABLE_CHANCE ) client->ps.pm_type = PM_NORMAL; } //switch jetpack off if no reactor if( !level.reactorPresent ) BG_DeactivateUpgrade( UP_JETPACK, client->ps.stats ); } // set up for pmove oldEventSequence = client->ps.eventSequence; memset( &pm, 0, sizeof( pm ) ); if( !( ucmd->buttons & BUTTON_TALK ) ) //&& client->ps.weaponTime <= 0 ) //TA: erk more server load { switch( client->ps.weapon ) { case WP_ALEVEL0: if( client->ps.weaponTime <= 0 ) pm.autoWeaponHit[ client->ps.weapon ] = CheckVenomAttack( ent ); break; case WP_ALEVEL1: case WP_ALEVEL1_UPG: CheckGrabAttack( ent ); break; case WP_ALEVEL3: case WP_ALEVEL3_UPG: if( client->ps.weaponTime <= 0 ) pm.autoWeaponHit[ client->ps.weapon ] = CheckPounceAttack( ent ); break; default: break; } } if( ent->flags & FL_FORCE_GESTURE ) { ent->flags &= ~FL_FORCE_GESTURE; ent->client->pers.cmd.buttons |= BUTTON_GESTURE; } pm.ps = &client->ps; pm.cmd = *ucmd; if( pm.ps->pm_type == PM_DEAD ) pm.tracemask = MASK_PLAYERSOLID; // & ~CONTENTS_BODY; if( pm.ps->stats[ STAT_STATE ] & SS_INFESTING || pm.ps->stats[ STAT_STATE ] & SS_HOVELING ) pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY; else pm.tracemask = MASK_PLAYERSOLID; pm.trace = trap_Trace; pm.pointcontents = trap_PointContents; pm.debugLevel = g_debugMove.integer; pm.noFootsteps = (qboolean)0; pm.pmove_fixed = pmove_fixed.integer | client->pers.pmoveFixed; pm.pmove_msec = pmove_msec.integer; VectorCopy( client->ps.origin, client->oldOrigin ); // moved from after Pmove -- potentially the cause of // future triggering bugs if( !ent->client->noclip ) G_TouchTriggers( ent ); Pmove( &pm ); // save results of pmove if( ent->client->ps.eventSequence != oldEventSequence ) ent->eventTime = level.time; if( g_smoothClients.integer ) BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue ); else BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue ); SendPendingPredictableEvents( &ent->client->ps ); if( !( ent->client->ps.eFlags & EF_FIRING ) ) client->fireHeld = qfalse; // for grapple if( !( ent->client->ps.eFlags & EF_FIRING2 ) ) client->fire2Held = qfalse; // use the snapped origin for linking so it matches client predicted versions VectorCopy( ent->s.pos.trBase, ent->r.currentOrigin ); VectorCopy( pm.mins, ent->r.mins ); VectorCopy( pm.maxs, ent->r.maxs ); ent->waterlevel = pm.waterlevel; ent->watertype = pm.watertype; // execute client events ClientEvents( ent, oldEventSequence ); // link entity now, after any personal teleporters have been used trap_LinkEntity( ent ); // NOTE: now copy the exact origin over otherwise clients can be snapped into solid VectorCopy( ent->client->ps.origin, ent->r.currentOrigin ); VectorCopy( ent->client->ps.origin, ent->s.origin ); // touch other objects ClientImpacts( ent, &pm ); // save results of triggers and client events if( ent->client->ps.eventSequence != oldEventSequence ) ent->eventTime = level.time; // swap and latch button actions client->oldbuttons = client->buttons; client->buttons = ucmd->buttons; client->latched_buttons |= client->buttons & ~client->oldbuttons; if( ( client->buttons & BUTTON_GETFLAG ) && !( client->oldbuttons & BUTTON_GETFLAG ) && client->ps.stats[ STAT_HEALTH ] > 0 ) { trace_t trace; vec3_t view, point; gentity_t *traceEnt; if( client->ps.stats[ STAT_STATE ] & SS_HOVELING ) { gentity_t *hovel = client->hovel; //only let the player out if there is room if( !AHovel_Blocked( hovel, ent, qtrue ) ) { //prevent lerping client->ps.eFlags ^= EF_TELEPORT_BIT; client->ps.eFlags &= ~EF_NODRAW; //client leaves hovel client->ps.stats[ STAT_STATE ] &= ~SS_HOVELING; //hovel is empty G_setBuildableAnim( hovel, BANIM_ATTACK2, qfalse ); hovel->active = qfalse; } else { //exit is blocked G_TriggerMenu( ent->client->ps.clientNum, MN_A_HOVEL_BLOCKED ); } } else { #define USE_OBJECT_RANGE 64 int entityList[ MAX_GENTITIES ]; vec3_t range = { USE_OBJECT_RANGE, USE_OBJECT_RANGE, USE_OBJECT_RANGE }; vec3_t mins, maxs; int i, num; //TA: look for object infront of player AngleVectors( client->ps.viewangles, view, NULL, NULL ); VectorMA( client->ps.origin, USE_OBJECT_RANGE, view, point ); trap_Trace( &trace, client->ps.origin, NULL, NULL, point, ent->s.number, MASK_SHOT ); traceEnt = &g_entities[ trace.entityNum ]; if( traceEnt && traceEnt->biteam == client->ps.stats[ STAT_PTEAM ] && traceEnt->use ) traceEnt->use( traceEnt, ent, ent ); //other and activator are the same in this context else { //no entity in front of player - do a small area search VectorAdd( client->ps.origin, range, maxs ); VectorSubtract( client->ps.origin, range, mins ); num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); for( i = 0; i < num; i++ ) { traceEnt = &g_entities[ entityList[ i ] ]; if( traceEnt && traceEnt->biteam == client->ps.stats[ STAT_PTEAM ] && traceEnt->use ) { traceEnt->use( traceEnt, ent, ent ); //other and activator are the same in this context break; } } if( i == num && client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) { if( BG_UpgradeClassAvailable( &client->ps ) ) { //no nearby objects and alien - show class menu G_TriggerMenu( ent->client->ps.clientNum, MN_A_INFEST ); } else { //flash frags G_AddEvent( ent, EV_ALIEN_EVOLVE_FAILED, 0 ); } } } } } // check for respawning if( client->ps.stats[ STAT_HEALTH ] <= 0 ) { // wait for the attack button to be pressed if( level.time > client->respawnTime ) { // forcerespawn is to prevent users from waiting out powerups if( g_forcerespawn.integer > 0 && ( level.time - client->respawnTime ) > 0 ) { respawn( ent ); return; } // pressing attack or use is the normal respawn method if( ucmd->buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) ) { respawn( ent ); } } return; } if( level.framenum > client->retriggerArmouryMenu && client->retriggerArmouryMenu ) { G_TriggerMenu( client->ps.clientNum, MN_H_ARMOURY ); client->retriggerArmouryMenu = 0; } // Give clients some credit periodically if( ent->client->lastKillTime + FREEKILL_PERIOD < level.time ) { if( g_suddenDeathTime.integer && ( level.time - level.startTime >= g_suddenDeathTime.integer * 60000 ) ) { //gotta love logic like this eh? } else if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) G_AddCreditToClient( ent->client, FREEKILL_ALIEN, qtrue ); else if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) G_AddCreditToClient( ent->client, FREEKILL_HUMAN, qtrue ); ent->client->lastKillTime = level.time; } // perform once-a-second actions ClientTimerActions( ent, msec ); if( ent->suicideTime > 0 && ent->suicideTime < level.time ) { ent->flags &= ~FL_GODMODE; ent->client->ps.stats[ STAT_HEALTH ] = ent->health = 0; player_die( ent, ent, ent, 100000, MOD_SUICIDE ); ent->suicideTime = 0; } }
/* ============ G_TouchTriggers Find all trigger entities that ent's current position touches. Spectators will only interact with teleporters. ============ */ void G_TouchTriggers( gentity_t *ent ) { int i, num; int touch[MAX_GENTITIES]; gentity_t *hit; trace_t trace; vec3_t mins, maxs; vec3_t pmins, pmaxs; static vec3_t range = { 10, 10, 10 }; if( !ent->client ) return; // dead clients don't activate triggers! if( ent->client->ps.stats[ STAT_HEALTH ] <= 0 ) return; BG_FindBBoxForClass( ent->client->ps.stats[ STAT_PCLASS ], pmins, pmaxs, NULL, NULL, NULL ); VectorAdd( ent->client->ps.origin, pmins, mins ); VectorAdd( ent->client->ps.origin, pmaxs, maxs ); VectorSubtract( mins, range, mins ); VectorAdd( maxs, range, maxs ); num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES ); // can't use ent->absmin, because that has a one unit pad VectorAdd( ent->client->ps.origin, ent->r.mins, mins ); VectorAdd( ent->client->ps.origin, ent->r.maxs, maxs ); for( i = 0; i < num; i++ ) { hit = &g_entities[ touch[ i ] ]; if( !hit->touch && !ent->touch ) continue; if( !( hit->r.contents & CONTENTS_TRIGGER ) ) continue; // ignore most entities if a spectator if( ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) || ( ent->client->ps.stats[ STAT_STATE ] & SS_INFESTING ) || ( ent->client->ps.stats[ STAT_STATE ] & SS_HOVELING ) ) { if( hit->s.eType != ET_TELEPORT_TRIGGER && // this is ugly but adding a new ET_? type will // most likely cause network incompatibilities hit->touch != Touch_DoorTrigger ) { //check for manually triggered doors manualTriggerSpectator( hit, ent ); continue; } } if( !trap_EntityContact( mins, maxs, hit ) ) continue; memset( &trace, 0, sizeof( trace ) ); if( hit->touch ) hit->touch( hit, ent, &trace ); if( ( ent->r.svFlags & SVF_BOT ) && ( ent->touch ) ) ent->touch( ent, hit, &trace ); } // if we didn't touch a jump pad this pmove frame if( ent->client->ps.jumppad_frame != ent->client->ps.pmove_framecount ) { ent->client->ps.jumppad_frame = 0; ent->client->ps.jumppad_ent = 0; } }
/* ============ etpro_RadiusDamage mutation of G_RadiusDamage which lets us selectively damage only clients or only non clients ============ */ qboolean etpro_RadiusDamage( vec3_t origin, gentity_t *inflictor, gentity_t *attacker, float damage, float radius, gentity_t *ignore, int mod, RadiusScope scope ) { float points, dist; gentity_t *ent; int entityList[MAX_GENTITIES]; int numListedEntities; vec3_t mins, maxs; vec3_t v; vec3_t dir; int i, e; qboolean hitClient = qfalse; float boxradius; vec3_t dest; trace_t tr; vec3_t midpoint; int flags = DAMAGE_RADIUS; if( mod == MOD_SATCHEL || mod == MOD_LANDMINE ) { flags |= DAMAGE_HALF_KNOCKBACK; } if( radius < 1 ) { radius = 1; } boxradius = 1.41421356 * radius; // radius * sqrt(2) for bounding box enlargement -- // bounding box was checking against radius / sqrt(2) if collision is along box plane for( i = 0 ; i < 3 ; i++ ) { mins[i] = origin[i] - boxradius; maxs[i] = origin[i] + boxradius; } numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); for( e = 0 ; e < numListedEntities ; e++ ) { ent = &g_entities[entityList[ e ]]; if( ent == ignore ) { continue; } if( !ent->takedamage && ( !ent->dmgparent || !ent->dmgparent->takedamage )) { continue; } switch (scope) { default: case RADIUS_SCOPE_ANY: break; case RADIUS_SCOPE_CLIENTS: if (!ent->client && ent->s.eType != ET_CORPSE ) continue; break; case RADIUS_SCOPE_NOCLIENTS: if (ent->client) continue; break; } if( ent->waterlevel == 3 && mod == MOD_POISON_GAS) { continue; } G_AdjustedDamageVec( ent, origin, v ); dist = VectorLength( v ); if ( dist >= radius ) { continue; } points = damage * ( 1.0 - dist / radius ); if( CanDamage( ent, origin ) ) { if( ent->dmgparent ) { ent = ent->dmgparent; } if( AccuracyHit( ent, attacker ) ) { hitClient = qtrue; } VectorSubtract (ent->r.currentOrigin, origin, dir); // push the center of mass higher than the origin so players // get knocked into the air more dir[2] += 24; G_Damage( ent, inflictor, attacker, dir, origin, (int)points, flags, mod ); } else { VectorAdd( ent->r.absmin, ent->r.absmax, midpoint ); VectorScale( midpoint, 0.5, midpoint ); VectorCopy( midpoint, dest ); trap_Trace( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID ); if( tr.fraction < 1.0 ) { VectorSubtract( dest, origin, dest ); dist = VectorLength( dest ); if( dist < radius * 0.2f ) { // closer than 1/4 dist if( ent->dmgparent ) { ent = ent->dmgparent; } if( AccuracyHit( ent, attacker ) ) { hitClient = qtrue; } VectorSubtract (ent->r.currentOrigin, origin, dir); dir[2] += 24; G_Damage( ent, inflictor, attacker, dir, origin, (int)(points*0.1f), flags, mod ); } } } } return hitClient; }
/* ============== BotBlastDamage Determine how much damage a blast from "weapon" detonating at "center" deals, not dealing damage to entity "ignore". The information is stored in the "blast" input argument. ============== */ void BotBlastDamage(bot_state_t *bs, int weapon, vec3_t center, damage_multi_t *blast, gentity_t *ignore) { int i, num_contacted; int contacted[MAX_GENTITIES]; float radius, dist, damage; vec3_t offset, mins, maxs; gentity_t *ent; damage_catagory_t *group; // Reset the damage information memset(blast, 0, sizeof(damage_multi_t)); // Only check for weapons with blast radius radius = weapon_stats[weapon].radius; if (radius <= 0) return; // Compute the bounding box containing all entities that could possibly be // damaged from the blast radius. VectorSet (offset, radius, radius, radius); VectorAdd (center, offset, maxs); VectorSubtract(center, offset, mins); // Get a list of all entities possibly within this bounding box num_contacted = trap_EntitiesInBox(mins, maxs, contacted, MAX_GENTITIES); // Estimate damage dealt to each nearby entity // // NOTE: This duplicates much of the code in G_RadiusDamage() in g_combat.c. for (i = 0; i < num_contacted; i++) { // Do not tracked the ignored entity ent = &g_entities[contacted[i]]; if (ent == ignore) continue; // Check if the entity is on the enemy team // // NOTE: This includes damagable structures on the enemy team, // like the Obelisk in Overload. if (BotEnemyTeam(bs, ent)) group = &blast->enemy; // Also check for players on the same team that the bot can damage. // // NOTE: This function purposely ignores self-damage. It also // ignores damagable team structures, like the Obelisk in Overload // (which players can't damage even when friend fire is turned on). else if (g_friendlyFire.integer && ent->client && BotSameTeam(bs, ent) && bs->ent != ent) group = &blast->team; // Never count damage to neutrally aligned entities, even if they are // damagable (like shot-activated buttons) else continue; // Determine how close the blast shot was to the target's bounding box // (in real world coordinates) EntityWorldBounds(ent, mins, maxs); dist = point_bound_distance(center, mins, maxs); // Compute how much damage the blast would deal to entities at this distance damage = WeaponBlast(weapon, dist); // Update the specific catagory data... group->hits++; group->total += damage; if (group->max < damage) group->max = damage; // ... And the aggregate data blast->all.hits++; blast->all.total += damage; if (blast->all.max < damage) blast->all.max = damage; } }
/* ============ G_TouchTriggers Find all trigger entities that ent's current position touches. Spectators will only interact with teleporters. ============ */ void G_TouchTriggers(gentity_t *ent) { int i, num; int touch[MAX_GENTITIES]; gentity_t *hit; trace_t trace; vec3_t mins, maxs; static vec3_t range = { 40, 40, 52 }; if (!ent->client) { return; } // Arnout: reset the pointer that keeps track of trigger_objective_info tracking ent->client->touchingTOI = NULL; // dead clients don't activate triggers! if (ent->client->ps.stats[STAT_HEALTH] <= 0) { return; } VectorSubtract(ent->client->ps.origin, range, mins); VectorAdd(ent->client->ps.origin, range, maxs); num = trap_EntitiesInBox(mins, maxs, touch, MAX_GENTITIES); // can't use ent->absmin, because that has a one unit pad VectorAdd(ent->client->ps.origin, ent->r.mins, mins); VectorAdd(ent->client->ps.origin, ent->r.maxs, maxs); for (i = 0 ; i < num ; ++i) { hit = &g_entities[touch[i]]; if (!hit->touch && !ent->touch) { continue; } if (!(hit->r.contents & CONTENTS_TRIGGER)) { continue; } // Arnout: invisible entities can't be touched // Gordon: radiant tabs arnout! ;) if (hit->entstate == STATE_INVISIBLE || hit->entstate == STATE_UNDERCONSTRUCTION) { continue; } // ignore most entities if a spectator if (ent->client->sess.sessionTeam == TEAM_SPECTATOR && hit->s.eType != ET_TELEPORT_TRIGGER) { continue; } // use seperate code for determining if an item is picked up // so you don't have to actually contact its bounding box if (hit->s.eType == ET_ITEM) { if (!BG_PlayerTouchesItem(&ent->client->ps, &hit->s, level.time)) { continue; } } else { // MrE: always use capsule for player if (!trap_EntityContactCapsule(mins, maxs, hit)) { continue; } } memset(&trace, 0, sizeof (trace)); if (hit->touch) { hit->touch(hit, ent, &trace); } } }
void explodedretch(gentity_t *ent) { int i; int entityList[ MAX_GENTITIES ]; vec3_t range; vec3_t mins, maxs; int num; gentity_t *enemy; float creepSize = (float)650; int freezedcounter = 0; //gentity_t *gren; if(ent->client->ps.persistant[ PERS_CREDIT ] < 1) //Neeeddd fix <3 { trap_SendServerCommand( ent-g_entities, "print \"^5Overmind: ^7You need 1 evos to explode.\n\"" ); } else { ent->client->ps.persistant[ PERS_CREDIT ] -= 1; G_AddPredictableEvent( ent, EV_ALIEN_EVOLVE, 0 ); G_RadiusDamage( ent->r.currentOrigin, ent, 200, 200, ent, MOD_GRENADE); G_Damage( ent, ent, ent, NULL, NULL, 10000, 0, MOD_UNKNOWN ); //Slow down enemy around. VectorSet( range, creepSize, creepSize, creepSize ); VectorAdd( ent->s.origin, range, maxs ); VectorSubtract( ent->s.origin, range, mins ); //find enemys around me num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); for( i = 0; i < num; i++ ) { enemy = &g_entities[ entityList[ i ] ]; if( enemy->flags & FL_NOTARGET ) continue; if( enemy->client && (enemy->client->ps.stats[ STAT_PTEAM ] != ent->client->ps.stats[ STAT_PTEAM ] ) && G_Visible( ent, enemy ) ) { //enemy->client->ps.stats[ STAT_STATE ] |= SS_CREEPSLOWED; enemy->client->ps.stats[ STAT_STATE ] |= SS_ICEWAVED; enemy->client->lastCreepSlowTime = level.time + 5000; freezedcounter++; //G_SelectiveRadiusDamage( ent->s.pos.trBase, ent, (float)dmg, (float)500, ent, MOD_SLOWBLOB, ent->client->ps.stats[ STAT_PTEAM ] ); } } if(ent->client){ //29 Freezing Field Icewave over 5 enemies in 1 icewave if(ent->client->pers.badges[ 29 ] != 1 && freezedcounter >= 5) { ent->client->pers.badgeupdate[29] = 1; ent->client->pers.badges[29] = 1; G_WinBadge( ent, 29 ); } } } /* if(ent->client->pers.teamSelection != PTE_HUMANS && ent->client->pers.teamSelection != PTE_ALIENS ) { trap_SendServerCommand( ent-g_entities, va( "print \"^1You must be on a team\n\"" ) ); return qfalse; } if(ent->client->pers.classSelection == PCL_NONE) { trap_SendServerCommand( ent-g_entities, va( "print \"^1You must be alive\n\"" ) ); return qfalse; } if(ent->client->pers.energy >= 500) { if(ent->client->pers.teamSelection == PTE_HUMANS) { dmg = 3; } if(ent->client->pers.teamSelection == PTE_ALIENS) { dmg = 1; } ent->client->pers.energy-= 500; G_AddEvent( ent, EV_ALIEN_BUILDABLE_EXPLOSION, 0 ); VectorSet( range, creepSize, creepSize, creepSize ); VectorAdd( ent->s.origin, range, maxs ); VectorSubtract( ent->s.origin, range, mins ); //find enemys around me num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); for( i = 0; i < num; i++ ) { enemy = &g_entities[ entityList[ i ] ]; if( enemy->flags & FL_NOTARGET ) continue; if( enemy->client && (enemy->client->ps.stats[ STAT_PTEAM ] != ent->client->ps.stats[ STAT_PTEAM ] ) && G_Visible( ent, enemy ) ) { //enemy->client->ps.stats[ STAT_STATE ] |= SS_CREEPSLOWED; enemy->client->ps.stats[ STAT_STATE ] |= SS_ICEWAVED; enemy->client->lastCreepSlowTime = level.time + 5000; //G_SelectiveRadiusDamage( ent->s.pos.trBase, ent, (float)dmg, (float)500, ent, MOD_SLOWBLOB, ent->client->ps.stats[ STAT_PTEAM ] ); } } return qtrue; }*/ }
qboolean G_SelectiveRadiusDamage( vec3_t origin, gentity_t *attacker, float damage, float radius, gentity_t *ignore, int mod, int ignoreTeam ) { float points, dist; gentity_t *ent; int entityList[ MAX_GENTITIES ]; int numListedEntities; vec3_t mins, maxs; vec3_t v; int i, e; qboolean hitClient = qfalse; if ( radius < 1 ) { radius = 1; } for ( i = 0; i < 3; i++ ) { mins[ i ] = origin[ i ] - radius; maxs[ i ] = origin[ i ] + radius; } numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); for ( e = 0; e < numListedEntities; e++ ) { ent = &g_entities[ entityList[ e ] ]; if ( ent == ignore ) { continue; } if ( !ent->takedamage ) { continue; } if ( ent->flags & FL_NOTARGET ) { continue; } // find the distance from the edge of the bounding box for ( i = 0; i < 3; i++ ) { if ( origin[ i ] < ent->r.absmin[ i ] ) { v[ i ] = ent->r.absmin[ i ] - origin[ i ]; } else if ( origin[ i ] > ent->r.absmax[ i ] ) { v[ i ] = origin[ i ] - ent->r.absmax[ i ]; } else { v[ i ] = 0; } } dist = VectorLength( v ); if ( dist >= radius ) { continue; } points = damage * ( 1.0 - dist / radius ); if ( G_CanDamage( ent, origin ) && ent->client && ent->client->pers.team != ignoreTeam ) { hitClient = qtrue; // don't do knockback, since an attack that spares one team is most likely // not based on kinetic energy G_Damage( ent, NULL, attacker, NULL, origin, ( int ) points, DAMAGE_RADIUS | DAMAGE_NO_LOCDAMAGE | DAMAGE_NO_KNOCKBACK, mod ); } } return hitClient; }
/* ============ G_TouchTriggers Find all trigger entities that ent's current position touches. Spectators will only interact with teleporters. ============ */ void G_TouchTriggers( gentity_t *ent ) { int num; int touch[MAX_GENTITIES]; vec3_t mins; vec3_t maxs; static vec3_t range = { 20, 20, 40 }; if ( !ent->client ) { return; } // dead clients don't activate triggers! if ( G_IsClientDead ( ent->client ) ) { return; } VectorSubtract( ent->client->ps.origin, range, mins ); VectorAdd( ent->client->ps.origin, range, maxs ); num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES ); // can't use ent->r.absmin, because that has a one unit pad VectorAdd( ent->client->ps.origin, ent->r.mins, mins ); VectorAdd( ent->client->ps.origin, ent->r.maxs, maxs ); // Reset the players can use flag ent->client->ps.pm_flags &= ~(PMF_CAN_USE); ent->s.modelindex = 0; for ( int i=0 ; i<num ; i++ ) { gentity_t *hit = &g_entities[touch[i]]; // pmove would have to have detected siamese twins first if ( hit->client && hit != ent && !hit->client->siameseTwin && (ent->client->ps.pm_flags & PMF_SIAMESETWINS) ) { // See if this client has a twin if ( !G_IsClientSiameseTwin ( ent, hit ) ) { continue; } // About time these twins were separated!! ent->client->siameseTwin = hit; hit->client->siameseTwin = ent; } if ( !( hit->r.contents & CONTENTS_TRIGGER ) ) { continue; } if ( !hit->touch && !ent->touch ) { continue; } // ignore most entities if a spectator if ( G_IsClientSpectating ( ent->client ) ) { if ( hit->s.eType != ET_TELEPORT_TRIGGER && // this is ugly but adding a new ET_? type will // most likely cause network incompatibilities hit->touch != Touch_DoorTrigger) { continue; } } // use seperate code for determining if an item is picked up // so you don't have to actually contact its bounding box if ( hit->s.eType == ET_ITEM ) { if ( !BG_PlayerTouchesItem( &ent->client->ps, &hit->s, level.time ) ) { continue; } } else { if ( !trap_EntityContact( mins, maxs, hit ) ) { continue; } } trace_t trace; memset( &trace, 0, sizeof(trace) ); if ( hit->touch ) { hit->touch (hit, ent, &trace); } if ( ( ent->r.svFlags & SVF_BOT ) && ( ent->touch ) ) { ent->touch( ent, hit, &trace ); } } // Dont bother looking for twins again unless pmove says so ent->client->ps.pm_flags &= (~PMF_SIAMESETWINS); }
/* =============== RailSphereRadiusDamage =============== */ static void RailSphereRadiusDamage(vec3_t origin, gentity_t * attacker, float damage, float radius) { float dist; gentity_t *ent; int entityList[MAX_GENTITIES]; int numListedEntities; vec3_t mins, maxs; vec3_t v; vec3_t dir; int i, e; if(radius < 1) { radius = 1; } for(i = 0; i < 3; i++) { mins[i] = origin[i] - radius; maxs[i] = origin[i] + radius; } numListedEntities = trap_EntitiesInBox(mins, maxs, entityList, MAX_GENTITIES); for(e = 0; e < numListedEntities; e++) { ent = &g_entities[entityList[e]]; if(!ent->takedamage) { continue; } // dont hit things we have already hit if(ent->kamikazeTime > level.time) { continue; } // find the distance from the edge of the bounding box for(i = 0; i < 3; i++) { if(origin[i] < ent->r.absmin[i]) { v[i] = ent->r.absmin[i] - origin[i]; } else if(origin[i] > ent->r.absmax[i]) { v[i] = origin[i] - ent->r.absmax[i]; } else { v[i] = 0; } } dist = VectorLength(v); if(dist >= radius) { continue; } // if( CanDamage (ent, origin) ) { VectorSubtract(ent->r.currentOrigin, origin, dir); // push the center of mass higher than the origin so players // get knocked into the air more dir[2] += 24; G_Damage(ent, NULL, attacker, dir, origin, damage, DAMAGE_RADIUS | DAMAGE_NO_TEAM_PROTECTION, MOD_KAMIKAZE); ent->kamikazeTime = level.time + 3000; // } } }
/* ================ G_ExplodeMissile Explode a missile without an impact ================ */ void G_ExplodeMissile( gentity_t *ent ) { vec3_t dir; vec3_t origin; qboolean small = qfalse; qboolean zombiespit = qfalse; int etype; BG_EvaluateTrajectory( &ent->s.pos, level.time, origin ); SnapVector( origin ); G_SetOrigin( ent, origin ); // we don't have a valid direction, so just point straight up dir[0] = dir[1] = 0; dir[2] = 1; etype = ent->s.eType; ent->s.eType = ET_GENERAL; if ( !Q_stricmp( ent->classname, "props_explosion" ) ) { G_AddEvent( ent, EV_MISSILE_MISS_SMALL, DirToByte( dir ) ); small = qtrue; } // JPW NERVE else if ( !Q_stricmp( ent->classname, "air strike" ) ) { G_AddEvent( ent, EV_MISSILE_MISS_LARGE, DirToByte( dir ) ); small = qfalse; } // jpw else if ( !Q_stricmp( ent->classname, "props_explosion_large" ) ) { G_AddEvent( ent, EV_MISSILE_MISS_LARGE, DirToByte( dir ) ); small = qfalse; } else if ( !Q_stricmp( ent->classname, "zombiespit" ) ) { G_AddEvent( ent, EV_SPIT_MISS, DirToByte( dir ) ); zombiespit = qtrue; } else if ( !Q_stricmp( ent->classname, "flamebarrel" ) ) { ent->freeAfterEvent = qtrue; trap_LinkEntity( ent ); return; } else { G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( dir ) ); } ent->freeAfterEvent = qtrue; // splash damage if ( ent->splashDamage ) { if ( G_RadiusDamage( ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent, ent->splashMethodOfDeath ) ) { //----(SA) if ( g_entities[ent->r.ownerNum].client ) { g_entities[ent->r.ownerNum].client->ps.persistant[PERS_ACCURACY_HITS]++; } } } trap_LinkEntity( ent ); if ( etype == ET_MISSILE ) { // DHM - Nerve :: ... in single player anyway if ( g_gametype.integer == GT_SINGLE_PLAYER ) { if ( ent->s.weapon == WP_VENOM_FULL ) { // no default impact smoke zombiespit = qtrue; } else if ( ent->s.weapon == WP_DYNAMITE || ent->s.weapon == WP_DYNAMITE2 ) { // // shot heard round the world... gentity_t *player; player = AICast_FindEntityForName( "player" ); Concussive_fx( player->r.currentOrigin ); } } // JPW NERVE -- big nasty dynamite scoring section else { if ( g_gametype.integer >= GT_WOLF ) { if ( ent->s.weapon == WP_DYNAMITE ) { // do some scoring // check if dynamite is in trigger_objective_info field vec3_t mins, maxs; //static vec3_t range = { 18, 18, 18 }; // NOTE can use this to massage throw distance outside trigger field // TTimo unused int i,num,touch[MAX_GENTITIES]; gentity_t *hit; // NERVE - SMF - made this the actual bounding box of dynamite instead of range VectorAdd( ent->r.currentOrigin, ent->r.mins, mins ); VectorAdd( ent->r.currentOrigin, ent->r.maxs, maxs ); num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES ); VectorAdd( ent->r.currentOrigin, ent->r.mins, mins ); VectorAdd( ent->r.currentOrigin, ent->r.maxs, maxs ); for ( i = 0 ; i < num ; i++ ) { hit = &g_entities[touch[i]]; if ( !hit->target ) { continue; } if ( !( hit->r.contents & CONTENTS_TRIGGER ) ) { continue; } if ( !strcmp( hit->classname,"trigger_objective_info" ) ) { if ( !( hit->spawnflags & ( AXIS_OBJECTIVE | ALLIED_OBJECTIVE ) ) ) { continue; } if ( ( ( hit->spawnflags & AXIS_OBJECTIVE ) && ( ent->s.teamNum == TEAM_BLUE ) ) || ( ( hit->spawnflags & ALLIED_OBJECTIVE ) && ( ent->s.teamNum == TEAM_RED ) ) ) { G_UseTargets( hit,ent ); hit->think = G_FreeEntity; hit->nextthink = level.time + FRAMETIME; if ( ent->parent->client ) { if ( ent->s.teamNum == ent->parent->client->sess.sessionTeam ) { // make sure player hasn't changed teams -- per atvi req AddScore( ent->parent, hit->accuracy ); // set from map, see g_trigger } } } } } } } // give big weapons the shakey shakey if ( ent->s.weapon == WP_DYNAMITE || ent->s.weapon == WP_PANZERFAUST || ent->s.weapon == WP_GRENADE_LAUNCHER || ent->s.weapon == WP_GRENADE_PINEAPPLE || ent->s.weapon == WP_ROCKET_LAUNCHER || ent->s.weapon == WP_MORTAR || ent->s.weapon == WP_ARTY ) { Ground_Shaker( ent->r.currentOrigin, ent->splashDamage * 4 ); } return; } // jpw } if ( !zombiespit ) { gentity_t *Msmoke; Msmoke = G_Spawn(); VectorCopy( ent->r.currentOrigin, Msmoke->s.origin ); if ( small ) { Msmoke->s.density = 1; } Msmoke->think = M_think; Msmoke->nextthink = level.time + FRAMETIME; if ( ent->parent && !Q_stricmp( ent->parent->classname, "props_flamebarrel" ) ) { Msmoke->health = 10; } else { Msmoke->health = 5; } Concussive_fx( Msmoke->s.origin ); } }
void WolfReviveBbox( gentity_t *self ) { int touch[MAX_GENTITIES]; int num,i, touchnum = 0; gentity_t *hit = NULL; vec3_t mins, maxs; gentity_t *capsulehit = NULL; VectorAdd( self->r.currentOrigin, playerMins, mins ); VectorAdd( self->r.currentOrigin, playerMaxs, maxs ); num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES ); // Arnout, we really should be using capsules, do a quick, more refined test using mover collision if ( num ) { capsulehit = G_TestEntityPosition( self ); } for ( i = 0 ; i < num ; i++ ) { hit = &g_entities[touch[i]]; if ( hit->client ) { // ATVI Wolfenstein Misc #467 // don't look at yourself when counting the hits if ( hit->client->ps.persistant[PERS_HWEAPON_USE] && hit != self ) { touchnum++; // Move corpse directly to the person who revived them if ( self->props_frame_state >= 0 ) { trap_UnlinkEntity( self ); VectorCopy( g_entities[self->props_frame_state].client->ps.origin, self->client->ps.origin ); VectorCopy( self->client->ps.origin, self->r.currentOrigin ); trap_LinkEntity( self ); // Reset value so we don't continue to warp them self->props_frame_state = -1; } } else if ( hit->health > 0 ) { if ( hit->s.number != self->s.number ) { WolfRevivePushEnt( hit, self ); touchnum++; } } } else if ( hit->r.contents & ( CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_PLAYERCLIP ) ) { // Arnout: if hit is a mover, use capsulehit (this will only work if we touch one mover at a time - situations where you hit two are // really rare anyway though. The real fix is to move everything to capsule collision detection though if ( hit->s.eType == ET_MOVER ) { if ( capsulehit && capsulehit != hit ) { continue; // we collided with a mover, but we're not stuck in this one } else { continue; // we didn't collide with any movers } } WolfRevivePushEnt( hit, self ); touchnum++; } } if ( g_dbgRevive.integer ) { G_Printf( "WolfReviveBbox: touchnum: %d\n", touchnum ); } if ( touchnum == 0 ) { if ( g_dbgRevive.integer ) { G_Printf( "WolfReviveBbox: Player is solid now!\n" ); } self->r.contents = CONTENTS_BODY; } }
void G_FlameDamage( gentity_t *self ) { gentity_t *body; int entityList[MAX_GENTITIES]; int i, e, numListedEntities; float radius, boxradius, dist; vec3_t mins, maxs, point, v; trace_t tr; radius = self->speed; boxradius = 1.41421356 * radius; // radius * sqrt(2) for bounding box enlargement for ( i = 0 ; i < 3 ; i++ ) { mins[i] = self->r.currentOrigin[i] - boxradius; maxs[i] = self->r.currentOrigin[i] + boxradius; } numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); for ( e = 0 ; e < numListedEntities ; e++ ) { body = &g_entities[entityList[ e ]]; if ( !body->takedamage ) { continue; } // JPW NERVE don't catch fire if invulnerable or same team in no FF if ( body->client ) { if ( body->client->ps.powerups[PW_INVULNERABLE] >= level.time ) { body->flameQuota = 0; body->s.onFireEnd = level.time - 1; continue; } if ( !( g_friendlyFire.integer ) && OnSameTeam( body,self->parent ) ) { continue; } } // jpw // JPW NERVE don't catch fire if under water or invulnerable if ( body->waterlevel >= 3 ) { body->flameQuota = 0; body->s.onFireEnd = level.time - 1; continue; } // jpw if ( !body->r.bmodel ) { VectorCopy( body->r.currentOrigin, point ); if ( body->client ) { point[2] += body->client->ps.viewheight; } VectorSubtract( point, self->r.currentOrigin, v ); } else { for ( i = 0 ; i < 3 ; i++ ) { if ( self->s.origin[i] < body->r.absmin[i] ) { v[i] = body->r.absmin[i] - self->r.currentOrigin[i]; } else if ( self->r.currentOrigin[i] > body->r.absmax[i] ) { v[i] = self->r.currentOrigin[i] - body->r.absmax[i]; } else { v[i] = 0; } } } dist = VectorLength( v ); // The person who shot the flame only burns when within 1/2 the radius if ( body->s.number == self->r.ownerNum && dist >= ( radius * 0.5 ) ) { continue; } if ( dist >= radius ) { continue; } // Non-clients that take damage get damaged here if ( !body->client ) { if ( body->health > 0 ) { G_Damage( body, self->parent, self->parent, vec3_origin, self->r.currentOrigin, 2, 0, MOD_FLAMETHROWER ); } continue; } // JPW NERVE -- do a trace to see if there's a wall btwn. body & flame centroid -- prevents damage through walls trap_Trace( &tr, self->r.currentOrigin, NULL, NULL, point, body->s.number, MASK_SHOT ); if ( tr.fraction < 1.0 ) { continue; } // jpw // now check the damageQuota to see if we should play a pain animation // first reduce the current damageQuota with time if ( body->flameQuotaTime && body->flameQuota > 0 ) { body->flameQuota -= (int)( ( (float)( level.time - body->flameQuotaTime ) / 1000 ) * 2.5f ); if ( body->flameQuota < 0 ) { body->flameQuota = 0; } } G_BurnMeGood( self, body ); } }
void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, vec3_t dir, vec3_t point, int damage, int dflags, int mod ) { gclient_t *client; int take; int save; int asave; int knockback; int max; #ifdef MISSIONPACK vec3_t bouncedir, impactpoint; #endif if (!targ->takedamage) { return; } // the intermission has allready been qualified for, so don't // allow any extra scoring if ( level.intermissionQueued ) { return; } #ifdef MISSIONPACK if ( targ->client && mod != MOD_JUICED) { if ( targ->client->invulnerabilityTime > level.time) { if ( dir && point ) { G_InvulnerabilityEffect( targ, dir, point, impactpoint, bouncedir ); } return; } } #endif if ( !inflictor ) { inflictor = &g_entities[ENTITYNUM_WORLD]; } if ( !attacker ) { attacker = &g_entities[ENTITYNUM_WORLD]; } // shootable doors / buttons don't actually have any health if ( targ->s.eType == ET_MOVER ) { if ( targ->use && targ->moverState == MOVER_POS1 ) { targ->use( targ, inflictor, attacker ); } return; } #ifdef MISSIONPACK if( g_gametype.integer == GT_OBELISK && CheckObeliskAttack( targ, attacker ) ) { return; } #endif // reduce damage by the attacker's handicap value // unless they are rocket jumping if ( attacker->client && attacker != targ ) { max = attacker->client->ps.stats[STAT_MAX_HEALTH]; #ifdef MISSIONPACK if( bg_itemlist[attacker->client->ps.stats[STAT_PERSISTANT_POWERUP]].giTag == PW_GUARD ) { max /= 2; } #endif damage = damage * max / 100; } client = targ->client; if ( client ) { if ( client->noclip ) { return; } } if ( !dir ) { dflags |= DAMAGE_NO_KNOCKBACK; } else { VectorNormalize(dir); } knockback = damage; if ( knockback > 200 ) { knockback = 200; } if ( targ->flags & FL_NO_KNOCKBACK ) { knockback = 0; } if ( dflags & DAMAGE_NO_KNOCKBACK ) { knockback = 0; } // figure momentum add, even if the damage won't be taken if ( knockback && targ->client ) { vec3_t kvel; float mass; mass = 200; VectorScale (dir, g_knockback.value * (float)knockback / mass, kvel); VectorAdd (targ->client->ps.velocity, kvel, targ->client->ps.velocity); // set the timer so that the other client can't cancel // out the movement immediately if ( !targ->client->ps.pm_time ) { int t; t = knockback * 2; if ( t < 50 ) { t = 50; } if ( t > 200 ) { t = 200; } targ->client->ps.pm_time = t; targ->client->ps.pm_flags |= PMF_TIME_KNOCKBACK; } } // check for completely getting out of the damage if ( !(dflags & DAMAGE_NO_PROTECTION) ) { // if TF_NO_FRIENDLY_FIRE is set, don't do damage to the target // if the attacker was on the same team #ifdef MISSIONPACK if ( mod != MOD_JUICED && targ != attacker && !(dflags & DAMAGE_NO_TEAM_PROTECTION) && OnSameTeam (targ, attacker) ) { #else if ( targ != attacker && OnSameTeam (targ, attacker) ) { #endif if ( !g_friendlyFire.integer ) { return; } } #ifdef MISSIONPACK if (mod == MOD_PROXIMITY_MINE) { if (inflictor && inflictor->parent && OnSameTeam(targ, inflictor->parent)) { return; } if (targ == attacker) { return; } } #endif // check for godmode if ( targ->flags & FL_GODMODE ) { return; } } // battlesuit protects from all radius damage (but takes knockback) // and protects 50% against all damage if ( client && client->ps.powerups[PW_BATTLESUIT] ) { G_AddEvent( targ, EV_POWERUP_BATTLESUIT, 0 ); if ( ( dflags & DAMAGE_RADIUS ) || ( mod == MOD_FALLING ) ) { return; } damage *= 0.5; } // add to the attacker's hit counter (if the target isn't a general entity like a prox mine) if ( attacker->client && targ != attacker && targ->health > 0 && targ->s.eType != ET_MISSILE && targ->s.eType != ET_GENERAL) { if ( OnSameTeam( targ, attacker ) ) { attacker->client->ps.persistant[PERS_HITS]--; } else { attacker->client->ps.persistant[PERS_HITS]++; } attacker->client->ps.persistant[PERS_ATTACKEE_ARMOR] = (targ->health<<8)|(client->ps.stats[STAT_ARMOR]); } // always give half damage if hurting self // calculated after knockback, so rocket jumping works if ( targ == attacker) { damage *= 0.5; } if ( damage < 1 ) { damage = 1; } take = damage; save = 0; // save some from armor asave = CheckArmor (targ, take, dflags); take -= asave; if ( g_debugDamage.integer ) { G_Printf( "%i: client:%i health:%i damage:%i armor:%i\n", level.time, targ->s.number, targ->health, take, asave ); } // add to the damage inflicted on a player this frame // the total will be turned into screen blends and view angle kicks // at the end of the frame if ( client ) { if ( attacker ) { client->ps.persistant[PERS_ATTACKER] = attacker->s.number; } else { client->ps.persistant[PERS_ATTACKER] = ENTITYNUM_WORLD; } client->damage_armor += asave; client->damage_blood += take; client->damage_knockback += knockback; if ( dir ) { VectorCopy ( dir, client->damage_from ); client->damage_fromWorld = qfalse; } else { VectorCopy ( targ->r.currentOrigin, client->damage_from ); client->damage_fromWorld = qtrue; } } // See if it's the player hurting the emeny flag carrier #ifdef MISSIONPACK if( g_gametype.integer == GT_CTF || g_gametype.integer == GT_1FCTF ) { #else if( g_gametype.integer == GT_CTF) { #endif Team_CheckHurtCarrier(targ, attacker); } if (targ->client) { // set the last client who damaged the target targ->client->lasthurt_client = attacker->s.number; targ->client->lasthurt_mod = mod; } // do the damage if (take) { targ->health = targ->health - take; if ( targ->client ) { targ->client->ps.stats[STAT_HEALTH] = targ->health; } if ( targ->health <= 0 ) { if ( client ) targ->flags |= FL_NO_KNOCKBACK; if (targ->health < -999) targ->health = -999; targ->enemy = attacker; targ->die (targ, inflictor, attacker, take, mod); return; } else if ( targ->pain ) { targ->pain (targ, attacker, take); } } } /* ============ CanDamage Returns qtrue if the inflictor can directly damage the target. Used for explosions and melee attacks. ============ */ qboolean CanDamage (gentity_t *targ, vec3_t origin) { vec3_t dest; trace_t tr; vec3_t midpoint; // use the midpoint of the bounds instead of the origin, because // bmodels may have their origin is 0,0,0 VectorAdd (targ->r.absmin, targ->r.absmax, midpoint); VectorScale (midpoint, 0.5, midpoint); VectorCopy (midpoint, dest); trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0 || tr.entityNum == targ->s.number) return qtrue; // this should probably check in the plane of projection, // rather than in world coordinate, and also include Z VectorCopy (midpoint, dest); dest[0] += 15.0; dest[1] += 15.0; trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) return qtrue; VectorCopy (midpoint, dest); dest[0] += 15.0; dest[1] -= 15.0; trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) return qtrue; VectorCopy (midpoint, dest); dest[0] -= 15.0; dest[1] += 15.0; trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) return qtrue; VectorCopy (midpoint, dest); dest[0] -= 15.0; dest[1] -= 15.0; trap_Trace ( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID); if (tr.fraction == 1.0) return qtrue; return qfalse; } /* ============ G_RadiusDamage ============ */ qboolean G_RadiusDamage ( vec3_t origin, gentity_t *attacker, float damage, float radius, gentity_t *ignore, int mod) { float points, dist; gentity_t *ent; int entityList[MAX_GENTITIES]; int numListedEntities; vec3_t mins, maxs; vec3_t v; vec3_t dir; int i, e; qboolean hitClient = qfalse; if ( radius < 1 ) { radius = 1; } for ( i = 0 ; i < 3 ; i++ ) { mins[i] = origin[i] - radius; maxs[i] = origin[i] + radius; } numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); for ( e = 0 ; e < numListedEntities ; e++ ) { ent = &g_entities[entityList[ e ]]; if (ent == ignore) continue; if (!ent->takedamage) continue; // find the distance from the edge of the bounding box for ( i = 0 ; i < 3 ; i++ ) { if ( origin[i] < ent->r.absmin[i] ) { v[i] = ent->r.absmin[i] - origin[i]; } else if ( origin[i] > ent->r.absmax[i] ) { v[i] = origin[i] - ent->r.absmax[i]; } else { v[i] = 0; } } dist = VectorLength( v ); if ( dist >= radius ) { continue; } points = damage * ( 1.0 - dist / radius ); if( CanDamage (ent, origin) ) { if( LogAccuracyHit( ent, attacker ) ) { hitClient = qtrue; } VectorSubtract (ent->r.currentOrigin, origin, dir); // push the center of mass higher than the origin so players // get knocked into the air more dir[2] += 24; G_Damage (ent, NULL, attacker, dir, origin, (int)points, DAMAGE_RADIUS, mod); } } return hitClient; }
void WP_GrenadeBlow(gentity_t*self) { if(Q_stricmp(self->classname,"emp_grenade")==0 ||Q_stricmp(self->classname,"cryoban_grenade")==0 ||Q_stricmp(self->classname,"flash_grenade")==0) { vec3_t dir={0,0,1}; int entitys[1024]; vec3_t mins,maxs,v; int num=0,i=0,dist=0,mpDamage=7; int e=0; gentity_t*ent; for ( i = 0 ; i < 3 ; i++ ) { mins[i] = self->r.currentOrigin[i] - TD_SPLASH_RAD / 2; maxs[i] = self->r.currentOrigin[i] + TD_SPLASH_RAD / 2; } num = trap_EntitiesInBox(mins,maxs,entitys,MAX_GENTITIES); for ( i = 0 ; i < num ; i++ ) { ent = &g_entities[entitys[ i ]]; if (ent == self) continue; if (!ent->takedamage) continue; if(!ent->inuse || !ent->client) continue; // find the distance from the edge of the bounding box for ( e = 0 ; e < 3 ; e++ ) { if ( self->r.currentOrigin[e] < ent->r.absmin[e] ) { v[e] = ent->r.absmin[e] - self->r.currentOrigin[e]; } else if ( self->r.currentOrigin[e] > ent->r.absmax[e] ) { v[e] = self->r.currentOrigin[e] - ent->r.absmax[e]; } else { v[e] = 0; } } dist = VectorLength( v ); if ( dist >= TD_SPLASH_RAD ) { continue; } if(Q_stricmp(self->classname,"cryoban_grenade") == 0) G_AddEvent(ent, EV_CRYOBAN, DirToByte(dir)); if(Q_stricmp(self->classname,"emp_grenade")==0) { int shield = Q_irand(25,50); int ammo = Q_irand(15,30); G_DodgeDrain(ent, self, 15);//15 for nau ent->client->ps.saberAttackChainCount += mpDamage; if(ent->client->ps.stats[STAT_ARMOR]-shield >= 0) ent->client->ps.stats[STAT_ARMOR]-=shield; else ent->client->ps.stats[STAT_ARMOR]=0; if(ent->client->ps.ammo[weaponData[ent->client->ps.weapon].ammoIndex]-ammo >= 0) ent->client->ps.ammo[weaponData[ent->client->ps.weapon].ammoIndex]-=ammo; else ent->client->ps.ammo[weaponData[ent->client->ps.weapon].ammoIndex]=0; if(Q_stricmp(ent->classname,"item_seeker")==0||Q_stricmp(ent->classname,"item_sentry_gun")==0) { G_Damage( ent, ent, ent, NULL, NULL, 999, 0, MOD_UNKNOWN ); } ent->client->ps.electrifyTime = level.time + Q_irand( 300, 800 ); } else if(Q_stricmp(self->classname,"cryoban_grenade")==0) { ent->client->frozenTime = level.time+FROZEN_TIME; ent->client->ps.userInt3 |= (1 << FLAG_FROZEN); ent->client->ps.userInt1 |= LOCK_UP; ent->client->ps.userInt1 |= LOCK_DOWN; ent->client->ps.userInt1 |= LOCK_RIGHT; ent->client->ps.userInt1 |= LOCK_LEFT; ent->client->viewLockTime = level.time+FROZEN_TIME; ent->client->ps.legsTimer = ent->client->ps.torsoTimer=level.time+FROZEN_TIME; G_SetAnim(ent, NULL, SETANIM_BOTH, WeaponReadyAnim[ent->client->ps.weapon], SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 100); } else if(Q_stricmp(self->classname,"flash_grenade")==0) G_AddEvent( ent, EV_FLASHGRENADE, DirToByte( dir ) ); } self->s.eType = ET_GENERAL; if(Q_stricmp(self->classname,"emp_grenade") == 0) G_AddEvent( self, EV_EMPGRENADE, DirToByte( dir ) ); else if(Q_stricmp(self->classname,"cryoban_grenade") == 0) G_AddEvent( self, EV_CRYOBAN_EXPLODE, DirToByte( dir ) ); self->freeAfterEvent = qtrue; } else if(Q_stricmp(self->classname,"smoke_grenade")==0) { vec3_t dir={0,0,1}; G_AddEvent( self, EV_SMOKEGRENADE, DirToByte( dir ) ); self->freeAfterEvent = qtrue; } else { self->think = G_FreeEntity; self->nextthink = level.time; } }
int Team_TouchEnemyFlag( gentity_t *ent, gentity_t *other, int team ) { gclient_t *cl = other->client; vec3_t mins, maxs; int num, j, ourFlag; int touch[MAX_GENTITIES]; gentity_t* enemy; float enemyDist, dist; VectorSubtract( ent->s.pos.trBase, minFlagRange, mins ); VectorAdd( ent->s.pos.trBase, maxFlagRange, maxs ); num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES ); dist = Distance(ent->s.pos.trBase, other->client->ps.origin); if (other->client->sess.sessionTeam == TEAM_RED){ ourFlag = PW_REDFLAG; } else { ourFlag = PW_BLUEFLAG; } for(j = 0; j < num; ++j){ enemy = (g_entities + touch[j]); if (!enemy || !enemy->inuse || !enemy->client){ continue; } //ignore specs if (enemy->client->sess.sessionTeam == TEAM_SPECTATOR) continue; //check if its alive if (enemy->health < 1) continue; // dead people can't pick up items //lets check if he has our flag if (!enemy->client->ps.powerups[ourFlag]) continue; //check if enemy is closer to our flag than us enemyDist = Distance(ent->s.pos.trBase,enemy->client->ps.origin); if (enemyDist < dist){ // possible recursion is hidden in this, but // infinite recursion wont happen, because we cant // have a < b and b < a at the same time return Team_TouchOurFlag( ent, enemy, team ); } } //PrintMsg (NULL, "%s" S_COLOR_WHITE " got the %s flag!\n", // other->client->pers.netname, TeamName(team)); PrintCTFMessage(other->s.number, team, CTFMESSAGE_PLAYER_GOT_FLAG); if (team == TEAM_RED) cl->ps.powerups[PW_REDFLAG] = INT_MAX; // flags never expire else cl->ps.powerups[PW_BLUEFLAG] = INT_MAX; // flags never expire Team_SetFlagStatus( team, FLAG_TAKEN ); AddScore(other, ent->r.currentOrigin, CTF_FLAG_BONUS); cl->pers.teamState.flagsince = level.time; Team_TakeFlagSound( ent, team ); return -1; // Do not respawn this automatically, but do delete it if it was FL_DROPPED }
bool G_SelectiveRadiusDamage( vec3_t origin, gentity_t *attacker, float damage, float radius, gentity_t *ignore, int mod, int ignoreTeam ) { float points, dist; gentity_t *ent; int entityList[ MAX_GENTITIES ]; int numListedEntities; vec3_t mins, maxs; vec3_t v; int i, e; bool hitClient = false; if ( radius < 1 ) { radius = 1; } for ( i = 0; i < 3; i++ ) { mins[ i ] = origin[ i ] - radius; maxs[ i ] = origin[ i ] + radius; } numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); for ( e = 0; e < numListedEntities; e++ ) { ent = &g_entities[ entityList[ e ] ]; if ( ent == ignore ) { continue; } if ( !ent->takedamage ) { continue; } if ( ent->flags & FL_NOTARGET ) { continue; } // find the distance from the edge of the bounding box for ( i = 0; i < 3; i++ ) { if ( origin[ i ] < ent->r.absmin[ i ] ) { v[ i ] = ent->r.absmin[ i ] - origin[ i ]; } else if ( origin[ i ] > ent->r.absmax[ i ] ) { v[ i ] = origin[ i ] - ent->r.absmax[ i ]; } else { v[ i ] = 0; } } dist = VectorLength( v ); if ( dist >= radius ) { continue; } points = damage * ( 1.0 - dist / radius ); if ( G_CanDamage( ent, origin ) && ent->client && ent->client->pers.team != ignoreTeam ) { hitClient = true; G_Damage( ent, nullptr, attacker, nullptr, origin, ( int ) points, DAMAGE_RADIUS | DAMAGE_NO_LOCDAMAGE, mod ); } } return hitClient; }
/* ============ G_TouchTriggers Find all trigger entities that ent's current position touches. Spectators will only interact with teleporters (and door triggers for spectators) ============ */ void G_TouchTriggers(gentity_t * ent) { int i, num; int touch[MAX_GENTITIES]; gentity_t *hit; trace_t trace; vec3_t mins, maxs; static vec3_t range = { 40, 40, 52 }; if (!ent->client) { return; } // dead clients don't activate triggers! if (ent->client->ps.stats[STAT_HEALTH] <= 0) { return; } VectorSubtract(ent->client->ps.origin, range, mins); VectorAdd(ent->client->ps.origin, range, maxs); num = trap_EntitiesInBox(mins, maxs, touch, MAX_GENTITIES); // can't use ent->absmin, because that has a one unit pad VectorAdd(ent->client->ps.origin, ent->r.mins, mins); VectorAdd(ent->client->ps.origin, ent->r.maxs, maxs); for (i = 0; i < num; i++) { //Blaze: Print out some debug info if (&g_entities[touch[i]] == NULL) G_Printf("Ln 0429\n"); hit = &g_entities[touch[i]]; if (!hit->touch && !ent->touch) { continue; } if (!(hit->r.contents & CONTENTS_TRIGGER)) { continue; } // ignore most entities if a spectator if (ent->client->sess.sessionTeam == TEAM_SPECTATOR) { if (hit->s.eType != ET_TELEPORT_TRIGGER) { //&& // this is ugly but adding a new ET_? type will // most likely cause network incompatibilities // NiceAss: changed Touch_DoorTrigger to Touch_DoorTriggerSpectator // hit->touch != Touch_DoorTriggerSpectator) { continue; } } // use seperate code for determining if an item is picked up // so you don't have to actually contact its bounding box if (hit->s.eType == ET_ITEM) { if (!BG_PlayerTouchesItem(&ent->client->ps, &hit->s, level.time)) { continue; } } else { if (!trap_EntityContact(mins, maxs, hit)) { continue; } } memset(&trace, 0, sizeof(trace)); if (hit->touch) { hit->touch(hit, ent, &trace); } if ((ent->r.svFlags & SVF_BOT) && (ent->touch)) { ent->touch(ent, hit, &trace); } } // if we didn't touch a jump pad this pmove frame if (ent->client->ps.jumppad_frame != ent->client->ps.pmove_framecount) { ent->client->ps.jumppad_frame = 0; ent->client->ps.jumppad_ent = 0; } if (ent->client->openDoor == 2) { ent->client->openDoor = qfalse; ent->client->openDoorTime = 0; } }
void Warzone_Flag_Think( gentity_t *ent ) { vec3_t mins; vec3_t maxs; int touch[MAX_GENTITIES]; int num = 0; int i = 0; int radius = ent->s.otherEntityNum2; ent->s.eFlags |= EF_RADAROBJECT; #ifdef __UNUSED__ if (flag_file_loaded) { if (!flag_spawnpoints_done) {// Have we initialized spawnpoints yet? int z = 0; for (z=0;z<number_of_flags;z++) { gentity_t *flagent = flag_list[z].flagentity; PreCalculate_Flag_Spawnpoints( flagent->count, flagent->s.angles, flagent->s.origin ); //G_Printf("Flagnum is %i. flagent->s.otherEntityNum is %i\n", flagent->count, flagent->s.otherEntityNum); flagent->alt_fire = qtrue; } flag_spawnpoints_done = qtrue; } } else if (!ent->alt_fire) {// Have we initialized spawnpoints yet? //if (level.numConnectedClients > 0) { PreCalculate_Flag_Spawnpoints( ent->count, ent->s.angles, ent->s.origin ); //G_Printf("Flagnum is %i. ent->s.otherEntityNum is %i\n", ent->count, ent->s.otherEntityNum); ent->alt_fire = qtrue; flag_spawnpoints_done = qtrue; } } #endif //__UNUSED__ if (ent->nextthink > level.time) return; mins[0] = 0-radius; mins[1] = 0-radius; mins[2] = 0-(radius*0.5); maxs[0] = radius; maxs[1] = radius; maxs[2] = radius*0.5; VectorAdd( mins, ent->s.origin, mins ); VectorAdd( maxs, ent->s.origin, maxs ); // Run the think... Look for captures... num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES ); for ( i=0 ; i<num ; i++ ) { gentity_t *hit = &g_entities[touch[i]]; if (!hit) continue; if (!hit->client) continue; if (!hit->health || hit->health <= 0) continue; if (hit->classname == "NPC_Vehicle") continue; //if (hit->s.eType == ET_NPC) // hit->s.teamowner = hit->client->playerTeam; if (!EntityVisible(ent, hit)) { hit->client->ps.stats[STAT_CAPTURE_ENTITYNUM] = 0; continue; } if (ent->s.teamowner == TEAM_RED) {// Red flag... if ((hit->s.eType == ET_PLAYER || hit->s.eType == ET_NPC) && hit->s.teamowner == TEAM_RED) {// Red player consolidating a red flag for spawns... //G_Printf("Consolidating flag!\n"); ent->s.time2++; hit->client->ps.stats[STAT_CAPTURE_ENTITYNUM] = ent->s.number; if (ent->s.time2 >= 100) {// Finished consolidating.. Set new team to blue and initialize... ent->s.teamowner = TEAM_RED; ent->s.time2 = 100; ent->s.genericenemyindex = TEAM_NONE; } } else if ((hit->s.eType == ET_PLAYER || hit->s.eType == ET_NPC) && hit->s.teamowner == TEAM_BLUE) {// Blue player undoing red flag... //G_Printf("Uncapturing flag!\n"); ent->s.time2--; hit->client->ps.stats[STAT_CAPTURE_ENTITYNUM] = ent->s.number; if (ent->s.time2 <= 0) {// Finished capture.. Set new team to blue... ent->s.teamowner = TEAM_NONE; ent->s.time2 = 0; ent->s.genericenemyindex = TEAM_NONE; } } } else if (ent->s.teamowner == TEAM_BLUE) {// Blue flag... if ((hit->s.eType == ET_PLAYER || hit->s.eType == ET_NPC) && hit->s.teamowner == TEAM_BLUE) {// Blue player consolidating a blue flag for spawns... //G_Printf("Consolidating flag!\n"); ent->s.time2++; hit->client->ps.stats[STAT_CAPTURE_ENTITYNUM] = ent->s.number; if (ent->s.time2 >= 100) {// Finished consolidating.. Set new team to blue and initialize... ent->s.teamowner = TEAM_BLUE; ent->s.time2 = 100; ent->s.genericenemyindex = TEAM_NONE; } } else if ((hit->s.eType == ET_PLAYER || hit->s.eType == ET_NPC) && hit->s.teamowner == TEAM_RED) {// Red player undoing blue flag... //G_Printf("Uncapturing flag!\n"); ent->s.time2--; hit->client->ps.stats[STAT_CAPTURE_ENTITYNUM] = ent->s.number; if (ent->s.time2 <= 0) {// Finished capture.. Set new team to blue... ent->s.teamowner = TEAM_NONE; ent->s.time2 = 0; ent->s.genericenemyindex = TEAM_NONE; } } } else {// Neutral flag... if (!ent->s.genericenemyindex || ent->s.genericenemyindex == TEAM_NONE) {// Start capture... Set capturing team number... ent->s.genericenemyindex = hit->s.teamowner; ent->s.time2 = 0; hit->client->ps.stats[STAT_CAPTURE_ENTITYNUM] = ent->s.number; } else if (ent->s.genericenemyindex == hit->s.teamowner) {// Continuing partial capture... ent->s.time2++; hit->client->ps.stats[STAT_CAPTURE_ENTITYNUM] = ent->s.number; // Set the current capturing team number of a neutral flag.. ent->s.genericenemyindex = hit->s.teamowner; //G_Printf("Capturing flag!\n"); if ((hit->s.eType == ET_PLAYER || hit->s.eType == ET_NPC) && hit->s.teamowner == TEAM_BLUE && ent->s.time2 >= 50) {// Blue player capturing neutral flag... Finished capture.. Set new team to blue... ent->s.teamowner = TEAM_BLUE; ent->s.time2 = 0; ent->s.genericenemyindex = TEAM_NONE; } else if ((hit->s.eType == ET_PLAYER || hit->s.eType == ET_NPC) && hit->s.teamowner == TEAM_RED && ent->s.time2 >= 50) {// Red player capturing neutral flag... Finished capture.. Set new team to red... ent->s.teamowner = TEAM_RED; ent->s.time2 = 0; ent->s.genericenemyindex = TEAM_NONE; } } else if (ent->s.genericenemyindex != hit->s.teamowner) {// Undoing partial capture... ent->s.time2--; hit->client->ps.stats[STAT_CAPTURE_ENTITYNUM] = ent->s.number; //G_Printf("Undoing owned flag!\n"); if (ent->s.time2 <= 0) {// Initialize back to untouched flag... ent->s.teamowner = TEAM_NONE; ent->s.time2 = 0; ent->s.genericenemyindex = TEAM_NONE; } } } } // Set next think... ent->nextthink = level.time + 500; }
/* ============ G_RadiusDamage ============ */ qboolean G_RadiusDamage( vec3_t origin, gentity_t *attacker, float damage, float radius, gentity_t *ignore, int mod ) { float points, dist; gentity_t *ent; int entityList[MAX_GENTITIES]; int numListedEntities; vec3_t mins, maxs; vec3_t v; vec3_t dir; int i, e; qboolean hitClient = qfalse; // JPW NERVE float boxradius; vec3_t dest; trace_t tr; vec3_t midpoint; // jpw if ( radius < 1 ) { radius = 1; } boxradius = 1.41421356 * radius; // radius * sqrt(2) for bounding box enlargement -- // bounding box was checking against radius / sqrt(2) if collision is along box plane for ( i = 0 ; i < 3 ; i++ ) { mins[i] = origin[i] - boxradius; // JPW NERVE maxs[i] = origin[i] + boxradius; // JPW NERVE } numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); for ( e = 0 ; e < numListedEntities ; e++ ) { ent = &g_entities[entityList[ e ]]; if ( ent == ignore ) { continue; } if ( !ent->takedamage ) { continue; } /* JPW NERVE -- we can put this back if we need to, but it kinna sucks for human-sized bboxes // find the distance from the edge of the bounding box for ( i = 0 ; i < 3 ; i++ ) { if ( origin[i] < ent->r.absmin[i] ) { v[i] = ent->r.absmin[i] - origin[i]; } else if ( origin[i] > ent->r.absmax[i] ) { v[i] = origin[i] - ent->r.absmax[i]; } else { v[i] = 0; } } */ // JPW NERVE if ( !ent->r.bmodel ) { VectorSubtract( ent->r.currentOrigin,origin,v ); // JPW NERVE simpler centroid check that doesn't have box alignment weirdness } else { for ( i = 0 ; i < 3 ; i++ ) { if ( origin[i] < ent->r.absmin[i] ) { v[i] = ent->r.absmin[i] - origin[i]; } else if ( origin[i] > ent->r.absmax[i] ) { v[i] = origin[i] - ent->r.absmax[i]; } else { v[i] = 0; } } } // jpw dist = VectorLength( v ); if ( dist >= radius ) { continue; } points = damage * ( 1.0 - dist / radius ); // JPW NERVE -- different radiusdmg behavior for MP -- big explosions should do less damage (over less distance) through failed traces if ( CanDamage( ent, origin ) ) { if ( LogAccuracyHit( ent, attacker ) ) { hitClient = qtrue; } VectorSubtract( ent->r.currentOrigin, origin, dir ); // push the center of mass higher than the origin so players // get knocked into the air more dir[2] += 24; G_Damage( ent, NULL, attacker, dir, origin, (int)points, DAMAGE_RADIUS, mod ); } // JPW NERVE -- MP weapons should do 1/8 damage through walls over 1/8th distance else { if ( g_gametype.integer != GT_SINGLE_PLAYER ) { VectorAdd( ent->r.absmin, ent->r.absmax, midpoint ); VectorScale( midpoint, 0.5, midpoint ); VectorCopy( midpoint, dest ); trap_Trace( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID ); if ( tr.fraction < 1.0 ) { VectorSubtract( dest,origin,dest ); dist = VectorLength( dest ); if ( dist < radius * 0.2f ) { // closer than 1/4 dist if ( LogAccuracyHit( ent, attacker ) ) { hitClient = qtrue; } VectorSubtract( ent->r.currentOrigin, origin, dir ); dir[2] += 24; G_Damage( ent, NULL, attacker, dir, origin, (int)( points * 0.1f ), DAMAGE_RADIUS, mod ); } } } } // jpw } return hitClient; }
bool G_RadiusDamage( vec3_t origin, gentity_t *attacker, float damage, float radius, gentity_t *ignore, int dflags, int mod, team_t testHit ) { float points, dist; gentity_t *ent; int entityList[ MAX_GENTITIES ]; int numListedEntities; vec3_t mins, maxs; vec3_t v; vec3_t dir; int i, e; bool hitSomething = false; if ( radius < 1 ) { radius = 1; } for ( i = 0; i < 3; i++ ) { mins[ i ] = origin[ i ] - radius; maxs[ i ] = origin[ i ] + radius; } numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); for ( e = 0; e < numListedEntities; e++ ) { ent = &g_entities[ entityList[ e ] ]; if ( ent == ignore ) { continue; } // find the distance from the edge of the bounding box for ( i = 0; i < 3; i++ ) { if ( origin[ i ] < ent->r.absmin[ i ] ) { v[ i ] = ent->r.absmin[ i ] - origin[ i ]; } else if ( origin[ i ] > ent->r.absmax[ i ] ) { v[ i ] = origin[ i ] - ent->r.absmax[ i ]; } else { v[ i ] = 0; } } dist = VectorLength( v ); if ( dist >= radius ) { continue; } points = damage * ( 1.0 - dist / radius ); if ( G_CanDamage( ent, origin ) ) { if ( testHit == TEAM_NONE ) { VectorSubtract( ent->r.currentOrigin, origin, dir ); // push the center of mass higher than the origin so players // get knocked into the air more dir[ 2 ] += 24; VectorNormalize( dir ); hitSomething = ent->entity->Damage(points, attacker, Vec3::Load(origin), Vec3::Load(dir), (DAMAGE_NO_LOCDAMAGE | dflags), (meansOfDeath_t)mod); } else if ( G_Team( ent ) == testHit && G_Alive( ent ) ) { return true; } } } return hitSomething; }