void misc_model_use (gentity_t *self, gentity_t *other, gentity_t *activator) { if ( self->health <= 0 && self->max_health > 0) {//used while broken fired target3 G_UseTargets2( self, activator, self->target3 ); return; } G_ActivateBehavior( self, BSET_USE ); //Don't explode if they've requested it to not if ( self->spawnflags & 64 ) {//Usemodels toggling if ( self->spawnflags & 32 ) { if( self->s.modelindex == self->sound1to2 ) { self->s.modelindex = self->sound2to1; } else { self->s.modelindex = self->sound1to2; } } return; } misc_model_breakable_die( self, other, activator, self->health, MOD_UNKNOWN ); }
/* ============================== G_UseTargets "activator" should be set to the entity that initiated the firing. Search for (string)targetname in all entities that match (string)self.target and call their .use function ============================== */ void G_UseTargets (gentity_t *ent, gentity_t *activator) { // // fire targets // G_UseTargets2 (ent, activator, ent->target); }
//---------------------------------------------------------- void fx_runner_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { if ( self->spawnflags & 2 ) // ONESHOT { // call the effect with the desired position and orientation, as a safety thing, // make sure we aren't thinking at all. fx_runner_think( self ); self->nextthink = -1; if ( self->target2 ) { // let our target know that we have spawned an effect G_UseTargets2( self, self, self->target2 ); } if ( VALIDSTRING( self->soundSet ) == true ) { G_AddEvent( self, EV_BMODEL_SOUND, CAS_GetBModelSound( self->soundSet, BMS_START )); } } else { // ensure we are working with the right think function self->e_ThinkFunc = thinkF_fx_runner_think; // toggle our state if ( self->nextthink == -1 ) { // NOTE: we fire the effect immediately on use, the fx_runner_think func will set // up the nextthink time. fx_runner_think( self ); if ( VALIDSTRING( self->soundSet ) == true ) { G_AddEvent( self, EV_BMODEL_SOUND, CAS_GetBModelSound( self->soundSet, BMS_START )); self->s.loopSound = CAS_GetBModelSound( self->soundSet, BMS_MID ); if ( self->s.loopSound < 0 ) { self->s.loopSound = 0; } } } else { // turn off for now self->nextthink = -1; if ( VALIDSTRING( self->soundSet ) == true ) { G_AddEvent( self, EV_BMODEL_SOUND, CAS_GetBModelSound( self->soundSet, BMS_END )); self->s.loopSound = 0; } } } }
void trigger_cleared_fire (gentity_t *self) { G_UseTargets2( self, self->activator, self->target2 ); self->e_ThinkFunc = thinkF_NULL; // should start the wait timer now, because the trigger's just been cleared, so we must "wait" from this point if ( self->wait > 0 ) { self->nextthink = level.time + ( self->wait + self->random * crandom() ) * 1000; } }
void func_usable_pain(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, const vec3_t point, int damage, int mod,int hitLoc) { if ( self->paintarget ) { G_UseTargets2 (self, self->activator, self->paintarget); } else { GEntity_UseFunc( self, attacker, attacker ); } }
void misc_model_breakable_pain ( gentity_t *self, gentity_t *inflictor, gentity_t *other, vec3_t point, int damage, int mod,int hitLoc ) { if ( self->health > 0 ) { // still alive, react to the pain if ( self->paintarget ) { G_UseTargets2 (self, self->activator, self->paintarget); } // Don't do script if dead G_ActivateBehavior( self, BSET_PAIN ); } }
//pain function for model_breakables void misc_model_breakable_pain (gentity_t *self, gentity_t *attacker, int damage) { if ( self->health > 0 ) { // still alive, react to the pain if ( self->paintarget ) { G_UseTargets2 (self, self->activator, self->paintarget); } // Don't do script if dead G_ActivateBehavior( self, BSET_PAIN ); } }
void target_counter_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { if ( self->count == 0 ) { return; } //gi.Printf("target_counter %s used by %s, entnum %d\n", self->targetname, activator->targetname, activator->s.number ); self->count--; if ( activator ) { Quake3Game()->DebugPrint( IGameInterface::WL_VERBOSE, "target_counter %s used by %s (%d/%d)\n", self->targetname, activator->targetname, (self->max_health-self->count), self->max_health ); } if ( self->count ) { if ( self->target2 ) { //gi.Printf("target_counter %s firing target2 from %s, entnum %d\n", self->targetname, activator->targetname, activator->s.number ); G_UseTargets2( self, activator, self->target2 ); } return; } G_ActivateBehavior( self,BSET_USE ); if ( self->spawnflags & 128 ) { self->svFlags |= SVF_INACTIVE; } self->activator = activator; G_UseTargets( self, activator ); if ( self->count == 0 ) { if ( self->bounceCount == 0 ) { return; } self->count = self->max_health; if ( self->bounceCount > 0 ) {//-1 means bounce back forever self->bounceCount--; } } }
void target_counter_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { if ( self->count == 0 ) { return; } //gi.Printf("target_counter %s used by %s, entnum %d\n", self->targetname, activator->targetname, activator->s.number ); self->count--; if ( activator ) { G_DebugPrint( WL_VERBOSE, "target_counter %s used by %s (%d/%d)\n", self->targetname, activator->targetname, (self->genericValue1-self->count), self->genericValue1 ); } if ( self->count ) { if ( self->target2 ) { //gi.Printf("target_counter %s firing target2 from %s, entnum %d\n", self->targetname, activator->targetname, activator->s.number ); G_UseTargets2( self, activator, self->target2 ); } return; } G_ActivateBehavior( self,BSET_USE ); if ( self->spawnflags & 128 ) { self->flags |= FL_INACTIVE; } self->activator = activator; G_UseTargets( self, activator ); if ( self->count == 0 ) { if ( self->bounceCount == 0 ) { return; } self->count = self->genericValue1; if ( self->bounceCount > 0 ) {//-1 means bounce back forever self->bounceCount--; } } }
void funcBBrushPain(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, vec3_t point, int damage, int mod,int hitLoc) { if ( self->painDebounceTime > level.time ) { return; } if ( self->paintarget ) { G_UseTargets2 (self, self->activator, self->paintarget); } G_ActivateBehavior( self, BSET_PAIN ); if ( self->material == MAT_DRK_STONE || self->material == MAT_LT_STONE || self->material == MAT_GREY_STONE ) { vec3_t org, dir; float scale; VectorSubtract( self->absmax, self->absmin, org );// size // This formula really has no logical basis other than the fact that it seemed to be the closest to yielding the results that I wanted. // Volume is length * width * height...then break that volume down based on how many chunks we have scale = VectorLength( org ) / 100.0f; VectorMA( self->absmin, 0.5, org, org ); VectorAdd( self->absmin,self->absmax, org ); VectorScale( org, 0.5f, org ); if ( attacker != NULL && attacker->client ) { VectorSubtract( attacker->currentOrigin, org, dir ); VectorNormalize( dir ); } else { VectorSet( dir, 0, 0, 1 ); } CG_Chunks( self->s.number, org, dir, self->mins, self->maxs, 300, Q_irand( 1, 3 ), self->material, 0, scale ); } if ( self->wait == -1 ) { self->e_PainFunc = painF_NULL; return; } self->painDebounceTime = level.time + self->wait; }
//---------------------------------------------------------- void emplaced_gun_pain( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, const vec3_t point, int damage, int mod,int hitLoc ) { if ( self->health <= 0 ) { // play pain effect? } else { if ( self->paintarget ) { G_UseTargets2( self, self->activator, self->paintarget ); } // Don't do script if dead G_ActivateBehavior( self, BSET_PAIN ); } }
void Svcmd_Use_f( void ) { char *cmd1 = gi.argv(1); if ( !cmd1 || !cmd1[0] ) { //FIXME: warning message gi.Printf( "'use' takes targetname of ent or 'list' (lists all usable ents)\n" ); return; } else if ( !Q_stricmp("list", cmd1) ) { gentity_t *ent; gi.Printf("Listing all usable entities:\n"); for ( int i = 1; i < ENTITYNUM_WORLD; i++ ) { ent = &g_entities[i]; if ( ent ) { if ( ent->targetname && ent->targetname[0] ) { if ( ent->e_UseFunc != useF_NULL ) { if ( ent->NPC ) { gi.Printf( "%s (NPC)\n", ent->targetname ); } else { gi.Printf( "%s\n", ent->targetname ); } } } } } gi.Printf("End of list.\n"); } else { G_UseTargets2( &g_entities[0], &g_entities[0], cmd1 ); } }
void misc_model_use (gentity_t *self, gentity_t *other, gentity_t *activator) { if ( self->target4 ) {//throw me at my target! misc_model_throw_at_target4( self, activator ); return; } if ( self->health <= 0 && self->max_health > 0) {//used while broken fired target3 G_UseTargets2( self, activator, self->target3 ); return; } // Become solid again. if ( !self->count ) { self->count = 1; self->activator = activator; self->svFlags &= ~SVF_NOCLIENT; self->s.eFlags &= ~EF_NODRAW; } G_ActivateBehavior( self, BSET_USE ); //Don't explode if they've requested it to not if ( self->spawnflags & 64 ) {//Usemodels toggling if ( self->spawnflags & 32 ) { if( self->s.modelindex == self->sound1to2 ) { self->s.modelindex = self->sound2to1; } else { self->s.modelindex = self->sound1to2; } } return; } self->e_DieFunc = dieF_misc_model_breakable_die; misc_model_breakable_die( self, other, activator, self->health, MOD_UNKNOWN ); }
void TurretG2Pain( gentity_t *self, gentity_t *attacker, int damage ) { if ( self->paintarget && self->paintarget[0] ) { if ( self->genericValue8 < level.time ) { G_UseTargets2( self, self, self->paintarget ); self->genericValue8 = level.time + self->genericValue4; } } if ( attacker->client && attacker->client->ps.weapon == WP_DEMP2 ) { self->attackDebounceTime = level.time + 2000 + random() * 500; self->painDebounceTime = self->attackDebounceTime; } if ( !self->enemy ) {//react to being hit G_SetEnemy( self, attacker ); } //self->s.health = self->health; //mmm..yes..bad. }
void NPC_BSRemove (void) { NPC_UpdateAngles ( qtrue, qtrue ); if( !gi.inPVS( NPC->currentOrigin, g_entities[0].currentOrigin ) )//FIXME: use cg.vieworg? { G_UseTargets2( NPC, NPC, NPC->target3 ); NPC->s.eFlags |= EF_NODRAW; NPC->s.eFlags &= ~EF_NPC; NPC->svFlags &= ~SVF_NPC; NPC->s.eType = ET_INVISIBLE; NPC->contents = 0; NPC->health = 0; NPC->targetname = NULL; //Disappear in half a second NPC->e_ThinkFunc = thinkF_G_FreeEntity; NPC->nextthink = level.time + FRAMETIME; }//FIXME: else allow for out of FOV??? }
//---------------------------------------------------------- void fx_runner_think( gentity_t *ent ) { vec3_t temp; EvaluateTrajectory( &ent->s.pos, level.time, ent->currentOrigin ); EvaluateTrajectory( &ent->s.apos, level.time, ent->currentAngles ); // call the effect with the desired position and orientation G_AddEvent( ent, EV_PLAY_EFFECT, ent->fxID ); // Assume angles, we'll do a cross product on the other end to finish up AngleVectors( ent->currentAngles, ent->pos3, NULL, NULL ); MakeNormalVectors( ent->pos3, ent->pos4, temp ); // there IS a reason this is done...it's so that it doesn't break every effect in the game... ent->nextthink = level.time + ent->delay + random() * ent->random; if ( ent->spawnflags & 4 ) // damage { G_RadiusDamage( ent->currentOrigin, ent, ent->splashDamage, ent->splashRadius, ent, MOD_UNKNOWN ); } if ( ent->target2 ) { // let our target know that we have spawned an effect G_UseTargets2( ent, ent, ent->target2 ); } if ( !(ent->spawnflags & 2 ) && !ent->s.loopSound ) // NOT ONESHOT...this is an assy thing to do { if ( VALIDSTRING( ent->soundSet ) == true ) { ent->s.loopSound = CAS_GetBModelSound( ent->soundSet, BMS_MID ); if ( ent->s.loopSound < 0 ) { ent->s.loopSound = 0; } } } }
void func_wait_return_solid( gentity_t *self ) { //once a frame, see if it's clear. self->clipmask = CONTENTS_BODY;//|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP; if ( !(self->spawnflags&16) || G_TestEntityPosition( self ) == NULL ) { gi.SetBrushModel( self, self->model ); VectorCopy( self->currentOrigin, self->pos1 ); InitMover( self ); /* VectorCopy( self->s.origin, self->s.pos.trBase ); VectorCopy( self->s.origin, self->currentOrigin ); */ //if we moved, we want the *current* origin, not our start origin! VectorCopy( self->currentOrigin, self->s.pos.trBase ); gi.linkentity( self ); self->svFlags &= ~SVF_NOCLIENT; self->s.eFlags &= ~EF_NODRAW; self->e_UseFunc = useF_func_usable_use; self->clipmask = 0; if ( self->target2 && self->target2[0] ) { G_UseTargets2( self, self->activator, self->target2 ); } if ( self->s.eFlags & EF_ANIM_ONCE ) {//Start our anim self->s.frame = 0; } //NOTE: be sure to reset the brushmodel before doing this or else CONTENTS_OPAQUE may not be on when you call this if ( !(self->spawnflags&1) ) {//START_OFF doesn't effect area portals gi.AdjustAreaPortalState( self, qfalse ); } } else { self->clipmask = 0; self->e_ThinkFunc = thinkF_func_wait_return_solid; self->nextthink = level.time + FRAMETIME; } }
int G_FindLocalInterestPoint( gentity_t *self ) { int i, bestPoint = ENTITYNUM_NONE; float dist, bestDist = Q3_INFINITE; vec3_t diffVec, eyes; CalcEntitySpot( self, SPOT_HEAD_LEAN, eyes ); for ( i = 0; i < level.numInterestPoints; i++ ) { //Don't ignore portals? If through a portal, need to look at portal! if ( gi.inPVS( level.interestPoints[i].origin, eyes ) ) { VectorSubtract( level.interestPoints[i].origin, eyes, diffVec ); if ( (fabs(diffVec[0]) + fabs(diffVec[1])) / 2 < 48 && fabs(diffVec[2]) > (fabs(diffVec[0]) + fabs(diffVec[1])) / 2 ) {//Too close to look so far up or down continue; } dist = VectorLengthSquared( diffVec ); //Some priority to more interesting points //dist -= ((int)level.interestPoints[i].lookMode * 5) * ((int)level.interestPoints[i].lookMode * 5); if ( dist < MAX_INTEREST_DIST && dist < bestDist ) { if ( G_ClearLineOfSight( eyes, level.interestPoints[i].origin, self->s.number, MASK_OPAQUE ) ) { bestDist = dist; bestPoint = i; } } } } if ( bestPoint != ENTITYNUM_NONE && level.interestPoints[bestPoint].target ) { G_UseTargets2( self, self, level.interestPoints[bestPoint].target ); } return bestPoint; }
// the trigger was just activated // ent->activator should be set to the activator so it can be held through a delay // so wait for the delay time before firing void multi_trigger_run( gentity_t *ent ) { ent->think = 0; G_ActivateBehavior( ent, BSET_USE ); if ( ent->soundSet && ent->soundSet[0] ) { trap->SetConfigstring( CS_GLOBAL_AMBIENT_SET, ent->soundSet ); } if (ent->genericValue4) { //we want to activate target3 for team1 or target4 for team2 if (ent->genericValue4 == SIEGETEAM_TEAM1 && ent->target3 && ent->target3[0]) { G_UseTargets2(ent, ent->activator, ent->target3); } else if (ent->genericValue4 == SIEGETEAM_TEAM2 && ent->target4 && ent->target4[0]) { G_UseTargets2(ent, ent->activator, ent->target4); } ent->genericValue4 = 0; } G_UseTargets (ent, ent->activator); if ( ent->noise_index ) { G_Sound( ent->activator, CHAN_AUTO, ent->noise_index ); } if ( ent->target2 && ent->target2[0] && ent->wait >= 0 ) { ent->think = trigger_cleared_fire; ent->nextthink = level.time + ent->speed; } else if ( ent->wait > 0 ) { if ( ent->painDebounceTime != level.time ) {//first ent to touch it this frame //ent->e_ThinkFunc = thinkF_multi_wait; ent->nextthink = level.time + ( ent->wait + ent->random * crandom() ) * 1000; ent->painDebounceTime = level.time; } } else if ( ent->wait < 0 ) { // we can't just remove (self) here, because this is a touch function // called while looping through area links... ent->r.contents &= ~CONTENTS_TRIGGER;//so the EntityContact trace doesn't have to be done against me ent->think = 0; ent->use = 0; //Don't remove, Icarus may barf? //ent->nextthink = level.time + FRAMETIME; //ent->think = G_FreeEntity; } if( ent->activator && ent->activator->client ) { // mark the trigger as being touched by the player ent->aimDebounceTime = level.time; } }
//----------------------------------------------------- static qboolean turretG2_find_enemies( gentity_t *self ) //----------------------------------------------------- { qboolean found = qfalse; int i, count; float bestDist = self->radius * self->radius; float enemyDist; vec3_t enemyDir, org, org2; qboolean foundClient = qfalse; gentity_t *entity_list[MAX_GENTITIES], *target, *bestTarget = NULL; if ( self->aimDebounceTime > level.time ) // time since we've been shut off { // We were active and alert, i.e. had an enemy in the last 3 secs if ( self->painDebounceTime < level.time ) { if ( !(self->spawnflags&SPF_TURRETG2_TURBO) ) { G_Sound(self, CHAN_BODY, G_SoundIndex( "sound/chars/turret/ping.wav" )); } self->painDebounceTime = level.time + 1000; } } VectorCopy( self->r.currentOrigin, org2 ); if ( self->spawnflags & 2 ) { org2[2] += 20; } else { org2[2] -= 20; } count = G_RadiusList( org2, self->radius, self, qtrue, entity_list ); for ( i = 0; i < count; i++ ) { trace_t tr; target = entity_list[i]; if ( !target->client ) { // only attack clients if ( !(target->flags&FL_BBRUSH)//not a breakable brush || !target->takedamage//is a bbrush, but invincible || (target->NPC_targetname&&self->targetname&&Q_stricmp(target->NPC_targetname,self->targetname)!=0) )//not in invicible bbrush, but can only be broken by an NPC that is not me { continue; } //else: we will shoot at bbrushes! } if ( target == self || !target->takedamage || target->health <= 0 || ( target->flags & FL_NOTARGET )) { continue; } if ( target->client && target->client->sess.sessionTeam == TEAM_SPECTATOR ) { continue; } if ( self->alliedTeam ) { if ( target->client ) { if ( target->client->sess.sessionTeam == self->alliedTeam ) { // A bot/client/NPC we don't want to shoot continue; } } else if ( target->teamnodmg == self->alliedTeam ) { // An ent we don't want to shoot continue; } } if ( !trap_InPVS( org2, target->r.currentOrigin )) { continue; } if ( target->client ) { VectorCopy( target->client->renderInfo.eyePoint, org ); } else { VectorCopy( target->r.currentOrigin, org ); } if ( self->spawnflags & 2 ) { org[2] -= 15; } else { org[2] += 5; } trap_Trace( &tr, org2, NULL, NULL, org, self->s.number, MASK_SHOT ); if ( !tr.allsolid && !tr.startsolid && ( tr.fraction == 1.0 || tr.entityNum == target->s.number )) { // Only acquire if have a clear shot, Is it in range and closer than our best? VectorSubtract( target->r.currentOrigin, self->r.currentOrigin, enemyDir ); enemyDist = VectorLengthSquared( enemyDir ); if ( enemyDist < bestDist || (target->client && !foundClient))// all things equal, keep current { if ( self->attackDebounceTime < level.time ) { // We haven't fired or acquired an enemy in the last 2 seconds-start-up sound if ( !(self->spawnflags&SPF_TURRETG2_TURBO) ) { G_Sound( self, CHAN_BODY, G_SoundIndex( "sound/chars/turret/startup.wav" )); } // Wind up turrets for a bit self->attackDebounceTime = level.time + 1400; } bestTarget = target; bestDist = enemyDist; found = qtrue; if ( target->client ) { //prefer clients over non-clients foundClient = qtrue; } } } } if ( found ) { /* if ( !self->enemy ) {//just aquired one AddSoundEvent( bestTarget, self->r.currentOrigin, 256, AEL_DISCOVERED ); AddSightEvent( bestTarget, self->r.currentOrigin, 512, AEL_DISCOVERED, 20 ); } */ G_SetEnemy( self, bestTarget ); if ( VALIDSTRING( self->target2 )) { G_UseTargets2( self, self, self->target2 ); } } return found; }
void trigger_entdist_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { vec3_t diff; gentity_t *found = NULL; gentity_t *owner = NULL; qboolean useflag; const char *token, *holdString; if ( self->svFlags & SVF_INACTIVE ) // Don't use INACTIVE return; G_ActivateBehavior(self,BSET_USE); if(self->ownername && self->ownername[0]) { owner = G_Find(NULL, FOFS(targetname), self->ownername); } if(owner == NULL) { owner = self; } self->activator = activator; useflag = qfalse; self->svFlags |= SVF_INACTIVE; // Make it inactive after one use if (self->spawnflags & ENTDIST_PLAYER) // Look for player??? { found = &g_entities[0]; if (found) { VectorSubtract(owner->currentOrigin, found->currentOrigin, diff); if(VectorLength(diff) < self->count) { useflag = qtrue; } } } if ((self->spawnflags & ENTDIST_NPC) && (!useflag)) { holdString = self->NPC_target; while (holdString) { token = COM_Parse( &holdString); if ( !token ) // Nothing left to look at { break; } found = G_Find(found, FOFS(targetname), token); // Look for the specified NPC if (found) //Found??? { VectorSubtract(owner->currentOrigin, found->currentOrigin, diff); if(VectorLength(diff) < self->count) // Within distance { useflag = qtrue; break; } } } } if (useflag) { G_UseTargets2 (self, self->activator, self->target); } else if (self->target2) { // This is the negative target G_UseTargets2 (self, self->activator, self->target2); } }
/* =============== NPC_Pain =============== */ void NPC_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, const vec3_t point, int damage, int mod, int hitLoc ) { team_t otherTeam = TEAM_FREE; int voiceEvent = -1; if ( self->NPC == NULL ) return; if ( other == NULL ) return; //or just remove ->pain in player_die? if ( self->client->ps.pm_type == PM_DEAD ) return; if ( other == self ) return; other = G_CheckControlledTurretEnemy(self, other, qfalse); if (!other) { return; } //MCG: Ignore damage from your own team for now if ( other->client ) { otherTeam = other->client->playerTeam; // if ( otherTeam == TEAM_DISGUISE ) // { // otherTeam = TEAM_PLAYER; // } } if ( self->client->playerTeam && other->client && otherTeam == self->client->playerTeam && (!player->client->ps.viewEntity || other->s.number != player->client->ps.viewEntity)) {//hit by a teammate if ( other != self->enemy && self != other->enemy ) {//we weren't already enemies if ( self->enemy || other->enemy || (other->s.number&&other->s.number!=player->client->ps.viewEntity) /*|| (!other->s.number&&Q_irand( 0, 3 ))*/ ) {//if one of us actually has an enemy already, it's okay, just an accident OR wasn't hit by player or someone controlled by player OR player hit ally and didn't get 25% chance of getting mad (FIXME:accumulate anger+base on diff?) //FIXME: player should have to do a certain amount of damage to ally or hit them several times to make them mad //Still run pain and flee scripts if ( self->client && self->NPC ) {//Run any pain instructions if ( self->health <= (self->max_health/3) && G_ActivateBehavior(self, BSET_FLEE) ) { } else// if( VALIDSTRING( self->behaviorSet[BSET_PAIN] ) ) { G_ActivateBehavior(self, BSET_PAIN); } } if ( damage != -1 ) {//-1 == don't play pain anim //Set our proper pain animation if ( Q_irand( 0, 1 ) ) { NPC_ChoosePainAnimation( self, other, point, damage, mod, hitLoc, EV_FFWARN ); } else { NPC_ChoosePainAnimation( self, other, point, damage, mod, hitLoc ); } } return; } else if ( self->NPC && !other->s.number )//should be assumed, but... {//dammit, stop that! if ( self->NPC->charmedTime > level.time ) {//mindtricked return; } else if ( self->NPC->ffireCount < 3+((2-g_spskill->integer)*2) ) {//not mad enough yet //Com_Printf( "chck: %d < %d\n", self->NPC->ffireCount, 3+((2-g_spskill->integer)*2) ); if ( damage != -1 ) {//-1 == don't play pain anim //Set our proper pain animation if ( Q_irand( 0, 1 ) ) { NPC_ChoosePainAnimation( self, other, point, damage, mod, hitLoc, EV_FFWARN ); } else { NPC_ChoosePainAnimation( self, other, point, damage, mod, hitLoc ); } } return; } else if ( G_ActivateBehavior( self, BSET_FFIRE ) ) {//we have a specific script to run, so do that instead return; } else {//okay, we're going to turn on our ally, we need to set and lock our enemy and put ourselves in a bstate that lets us attack him (and clear any flags that would stop us) self->NPC->blockedSpeechDebounceTime = 0; voiceEvent = EV_FFTURN; self->NPC->behaviorState = self->NPC->tempBehavior = self->NPC->defaultBehavior = BS_DEFAULT; other->flags &= ~FL_NOTARGET; self->svFlags &= ~(SVF_IGNORE_ENEMIES|SVF_ICARUS_FREEZE|SVF_NO_COMBAT_SOUNDS); G_SetEnemy( self, other ); self->svFlags |= SVF_LOCKEDENEMY; self->NPC->scriptFlags &= ~(SCF_DONT_FIRE|SCF_CROUCHED|SCF_WALKING|SCF_NO_COMBAT_TALK|SCF_FORCED_MARCH); self->NPC->scriptFlags |= (SCF_CHASE_ENEMIES|SCF_NO_MIND_TRICK); //NOTE: we also stop ICARUS altogether stop_icarus = qtrue; if ( !killPlayerTimer ) { killPlayerTimer = level.time + 10000; } } } } } SaveNPCGlobals(); SetNPCGlobals( self ); //Do extra bits if ( NPCInfo->ignorePain == qfalse ) { NPCInfo->confusionTime = 0;//clear any charm or confusion, regardless if ( NPC->ghoul2.size() && NPC->headBolt != -1 ) { G_StopEffect("force/confusion", NPC->playerModel, NPC->headBolt, NPC->s.number ); } if ( damage != -1 ) {//-1 == don't play pain anim //Set our proper pain animation NPC_ChoosePainAnimation( self, other, point, damage, mod, hitLoc, voiceEvent ); } //Check to take a new enemy if ( NPC->enemy != other && NPC != other ) {//not already mad at them //if it's an eweb or emplaced gun, get mad at the owner, not the gun NPC_CheckAttacker( other, mod ); } } //Attempt to run any pain instructions if ( self->client && self->NPC ) { //FIXME: This needs better heuristics perhaps if(self->health <= (self->max_health/3) && G_ActivateBehavior(self, BSET_FLEE) ) { } else //if( VALIDSTRING( self->behaviorSet[BSET_PAIN] ) ) { G_ActivateBehavior(self, BSET_PAIN); } } //Attempt to fire any paintargets we might have if( self->paintarget && self->paintarget[0] ) { G_UseTargets2(self, other, self->paintarget); } if (self->client && self->client->NPC_class==CLASS_BOBAFETT) { Boba_Pain( self, inflictor, damage, mod); } RestoreNPCGlobals(); }
//----------------------------------------------------- static qboolean turret_find_enemies( gentity_t *self ) //----------------------------------------------------- { qboolean found = qfalse; int i, count; float bestDist = self->radius * self->radius; float enemyDist; vec3_t enemyDir, org, org2; gentity_t *entity_list[MAX_GENTITIES], *target, *bestTarget = NULL; trace_t tr; gentity_t *top = &g_entities[self->r.ownerNum]; if ( !top ) { return qfalse; } if ( self->aimDebounceTime > level.time ) // time since we've been shut off { // We were active and alert, i.e. had an enemy in the last 3 secs if ( self->timestamp < level.time ) { //G_Sound(self, CHAN_BODY, G_SoundIndex( "sound/chars/turret/ping.wav" )); self->timestamp = level.time + 1000; } } VectorCopy( top->r.currentOrigin, org2 ); count = G_RadiusList( org2, self->radius, self, qtrue, entity_list ); for ( i = 0; i < count; i++ ) { target = entity_list[i]; if ( !target->client ) { // only attack clients continue; } if ( target == self || !target->takedamage || target->health <= 0 || ( target->flags & FL_NOTARGET )) { continue; } if ( target->client->sess.sessionTeam == TEAM_SPECTATOR ) { continue; } if ( self->alliedTeam ) { if ( target->client ) { if ( target->client->sess.sessionTeam == self->alliedTeam ) { // A bot/client/NPC we don't want to shoot continue; } } else if ( target->teamnodmg == self->alliedTeam ) { // An ent we don't want to shoot continue; } } if ( !trap_InPVS( org2, target->r.currentOrigin )) { continue; } VectorCopy( target->r.currentOrigin, org ); org[2] += target->r.maxs[2]*0.5f; trap_Trace( &tr, org2, NULL, NULL, org, self->s.number, MASK_SHOT ); if ( !tr.allsolid && !tr.startsolid && ( tr.fraction == 1.0 || tr.entityNum == target->s.number )) { // Only acquire if have a clear shot, Is it in range and closer than our best? VectorSubtract( target->r.currentOrigin, top->r.currentOrigin, enemyDir ); enemyDist = VectorLengthSquared( enemyDir ); if ( enemyDist < bestDist // all things equal, keep current || (!Q_stricmp( "atst_vehicle", target->NPC_type ) && bestTarget && Q_stricmp( "atst_vehicle", bestTarget->NPC_type ) ) )//target AT-STs over non-AT-STs... FIXME: must be a better, easier way to tell this, no? { if ( self->attackDebounceTime < level.time ) { // We haven't fired or acquired an enemy in the last 2 seconds-start-up sound //G_Sound( self, CHAN_BODY, G_SoundIndex( "sound/chars/turret/startup.wav" )); // Wind up turrets for a bit self->attackDebounceTime = level.time + 1400; } bestTarget = target; bestDist = enemyDist; found = qtrue; } } } if ( found ) { G_SetEnemy( self, bestTarget ); if ( VALIDSTRING( self->target2 )) { G_UseTargets2( self, self, self->target2 ); } } return found; }
/* =============== NPC_Pain =============== */ void NPC_Pain( gentity_t *self, gentity_t *other, int damage ) { team_t otherTeam = TEAM_FREE; if ( self->NPC == NULL ) return; if ( other == NULL ) return; //or just remove ->pain in player_die? if ( self->client->ps.pm_type == PM_DEAD ) return; if ( other == self ) return; //MCG: Ignore damage from your own team for now if ( other->client ) { otherTeam = other->client->playerTeam; if ( otherTeam == TEAM_DISGUISE ) { otherTeam = TEAM_STARFLEET; } } if ( other != self->enemy && self->client->playerTeam && other->client && otherTeam == self->client->playerTeam ) {//Still run pain and flee scripts if ( self->client && self->NPC ) {//Run any pain instructions if ( self->health <= (self->max_health/3) && ( VALIDSTRING( self->behaviorSet[BSET_FLEE] ) ) ) { G_ActivateBehavior(self, BSET_FLEE); } else if( VALIDSTRING( self->behaviorSet[BSET_PAIN] ) ) { G_ActivateBehavior(self, BSET_PAIN); } } return; } //Hirogen boss with shield if ( ( self->client->playerTeam == TEAM_HIROGEN ) ) { if ( ( Q_stricmp( self->NPC_type, "hirogenalpha" ) == 0 ) && ( self->s.powerups & ( 1 << PW_HIROGEN_SHIELD ) ) ) return; } SaveNPCGlobals(); SetNPCGlobals( self ); //Do extra bits if ( NPCInfo->ignorePain == qfalse ) { //Check to take a new enemy NPC_CheckAttacker( other ); if ( damage != -1 ) {//don't play pain anim //Set our proper pain animation NPC_ChoosePainAnimation( self, damage ); } } //Attempt to run any pain instructions if(self->client && self->NPC) { //FIXME: This needs better heuristics perhaps if(self->health <= (self->max_health/3) && ( VALIDSTRING( self->behaviorSet[BSET_FLEE] ) ) ) { G_ActivateBehavior(self, BSET_FLEE); } else if( VALIDSTRING( self->behaviorSet[BSET_PAIN] ) ) { G_ActivateBehavior(self, BSET_PAIN); } } //Attempt to fire any paintargets we might have if( self->paintarget && self->paintarget[0] ) { G_UseTargets2(self, other, self->paintarget); } RestoreNPCGlobals(); }