/*
==============
AICast_SightSoundEvent

  this cast has made a sound which should be heard by others
==============
*/
void AICast_SightSoundEvent( cast_state_t *cs, float range ) {
	int i;
	cast_state_t *ocs;
	gentity_t   *oent, *ent;

	ent = &g_entities[cs->entityNum];
	if ( ent->flags & FL_NOTARGET ) {
		return;
	}
	for ( i = 0, ocs = caststates, oent = g_entities; i < level.maxclients; i++, ocs++, oent++ ) {
		if ( !oent->inuse ) {
			continue;
		}
		if ( oent->aiInactive ) {
			continue;
		}
		if ( !ocs->bs ) {
			continue;
		}
		if ( oent->health <= 0 ) {
			continue;
		}
		if ( Distance( oent->r.currentOrigin, ent->r.currentOrigin ) > range * ocs->attributes[HEARING_SCALE] ) {
			continue;
		}
		// they heard us
		AICast_UpdateVisibility( oent, ent, qfalse, qfalse );
	}
}
char *AIFunc_Heinrich_RaiseDead(cast_state_t *cs) {
	int i;
	gentity_t *ent = &g_entities[cs->entityNum];
	gentity_t *enemy = &g_entities[cs->enemyNum];
	gentity_t *trav, *closest;
	float closestDist, dist;
	cs->aiFlags |= AIFL_SPECIAL_FUNC;

	if (cs->enemyNum < 0) {
		if (!ent->client->ps.torsoTimer) {
			return AIFunc_DefaultStart(cs);
		}

		return NULL;
	}
	// record weapon fire
	cs->weaponFireTimes[cs->weaponNum] = level.time;

	if (!ent->client->ps.torsoTimer) {
		return AIFunc_DefaultStart(cs);
	}

	if (ent->count2 && lastRaise < level.time - HEINRICH_RAISEDEAD_DELAY) {
		lastRaise = level.time;
		// summons the closest warrior
		closest = NULL;
		closestDist = 0;   // shutup the compiler
		for (i = 0, trav = g_entities; i < level.maxclients; i++, trav++) {
			if (!trav->inuse) {
				continue;
			}

			if (!trav->aiInactive) {
				continue;
			}

			if (trav->aiCharacter != AICHAR_WARZOMBIE) {
				continue;
			}

			dist = VectorDistance(trav->s.pos.trBase, enemy->r.currentOrigin);

			if (!closest || dist < closestDist) {
				closest = trav;
				closestDist = dist;
			}
		}

		if (closest) {
			closest->AIScript_AlertEntity(closest);
			// make them aware of the player
			AICast_UpdateVisibility(closest, enemy, qtrue, qtrue);
			// reduce the count
			ent->count2--;
		}
	}

	return NULL;
}
示例#3
0
/*
==============
AIFunc_FlameZombie_Portal
==============
*/
const char *AIFunc_FlameZombie_Portal( cast_state_t *cs ) {
	gentity_t *ent = &g_entities[cs->entityNum];
	//
	if ( cs->thinkFuncChangeTime < level.time - PORTAL_ZOMBIE_SPAWNTIME ) {
		// HACK, make them aware of the player
		AICast_UpdateVisibility( &g_entities[cs->entityNum], AICast_FindEntityForName( "player" ), qfalse, qtrue );
		ent->s.time2 = 0;   // turn spawning effect off
		return AIFunc_DefaultStart( cs );
	}
	//
	return NULL;
}
示例#4
0
/*
============
AICast_Pain
============
*/
void AICast_Pain( gentity_t *targ, gentity_t *attacker, int damage, vec3_t point ) {
	cast_state_t    *cs;

	cs = AICast_GetCastState( targ->s.number );

	// print debugging message
	if ( aicast_debug.integer == 2 && attacker->s.number == 0 ) {
		G_Printf( "hit %s %i\n", targ->aiName, targ->health );
	}

	// if we are below alert mode, then go there immediately
	if ( cs->aiState < AISTATE_ALERT ) {
		AICast_StateChange( cs, AISTATE_ALERT );
	}

	if ( cs->aiFlags & AIFL_NOPAIN ) {
		return;
	}

	// process the event (turn to face the attacking direction? go into hide/retreat state?)
	// need to weigh up the situation, but foremost, an inactive AI cast should always react in some way to being hurt
	cs->lastPain = level.time;

	// record the sighting (FIXME: silent weapons shouldn't do this, but the AI should react in some way)
	if ( attacker->client ) {
		AICast_UpdateVisibility( targ, attacker, qtrue, qtrue );
	}

	// if either of us are neutral, then we are now enemies
	if ( targ->aiTeam == AITEAM_NEUTRAL || attacker->aiTeam == AITEAM_NEUTRAL ) {
		cs->vislist[attacker->s.number].flags |= AIVIS_ENEMY;
	}

	AICast_ScriptEvent( cs, "pain", va( "%d %d", targ->health, targ->health + damage ) );

	if ( cs->aiFlags & AIFL_DENYACTION ) {
		// dont play any sounds
		return;
	}

	//
	// call the painfunc for this cast, so we can play associated sounds, or do any character-specific things
	//
	if ( cs->painfunc ) {
		cs->painfunc( targ, attacker, damage, point );
	}
}
示例#5
0
/*
================
AICast_EvaluatePmove

  Avoidance after the event (leaders instruct AI's to get out the way, AI's instruct other non-moving AI's to get out the way)
================
*/
void AICast_EvaluatePmove( int clientnum, pmove_t *pm ) {
	cast_state_t    *cs, *ocs;
	int i, ent;
	bot_goal_t ogoal;

	//vec3_t pos, dir;
	cs = AICast_GetCastState( clientnum );
	// make sure we are using the right AAS data for this entity (one's that don't get set will default to the player's AAS data)
	trap_AAS_SetCurrentWorld( cs->aasWorldIndex );

	// NOTE: this is only enabled for real clients, so their followers get out of their way
	//if (cs->bs)
	//	return;

	// look through the touchent's to see if we've bumped into something we should avoid, or react to
	for ( i = 0; i < pm->numtouch; i++ )
	{
		// mark the time, so they can deal with the obstruction in their own think functions
		cs->blockedTime = level.time;

		if ( pm->touchents[i] == pm->ps->groundEntityNum ) {
			continue;
		}

		// if they are an AI Cast, inform them of our disposition, and hope that they are reasonable
		// enough to assist us in our desire to move beyond our current position
		if ( pm->touchents[i] < aicast_maxclients ) {
			if ( !AICast_EntityVisible( cs, pm->touchents[i], qtrue ) ) {
				continue;
			}

			// if we are inspecting the body, abort if we touch anything
			if ( cs->bs && cs->bs->enemy >= 0 && g_entities[cs->bs->enemy].health <= 0 ) {
				cs->bs->enemy = -1;
			}

			// anything we touch, should see us
			AICast_UpdateVisibility( &g_entities[pm->touchents[i]], &g_entities[cs->entityNum], qfalse, qtrue );

			ocs = AICast_GetCastState( pm->touchents[i] );
			if (    ( ocs->bs ) &&
					( !( ocs->aiFlags & AIFL_NOAVOID ) ) &&
					( ( ocs->leaderNum == cs->entityNum ) || ( VectorLength( ocs->bs->velocity ) < 5 ) ) &&
					( ocs->obstructingTime < ( level.time + 100 ) ) ) {
				// if they are moving away from us already, let them go
				if ( VectorLength( ocs->bs->cur_ps.velocity ) > 10 ) {
					vec3_t v1, v2;

					VectorSubtract( ocs->bs->origin, g_entities[clientnum].client->ps.velocity, v2 );
					VectorNormalize( v2 );
					VectorNormalize2( ocs->bs->cur_ps.velocity, v1 );

					if ( DotProduct( v1, v2 ) > 0.0 ) {
						continue;
					}
				}
				if ( ocs->leaderNum >= 0 ) {
					VectorCopy( g_entities[ocs->leaderNum].r.currentOrigin, ogoal.origin );
					ogoal.areanum = BotPointAreaNum( ogoal.origin );
					ogoal.entitynum = ocs->leaderNum;
					if ( ocs->bs && AICast_GetAvoid( ocs, &ogoal, ocs->obstructingPos, qfalse, cs->entityNum ) ) { // give them time to move somewhere else
						ocs->obstructingTime = level.time + 1000;
					}
				} else {
					if ( ocs->bs && AICast_GetAvoid( ocs, NULL, ocs->obstructingPos, qfalse, cs->entityNum ) ) { // give them time to move somewhere else
						ocs->obstructingTime = level.time + 1000;
					}
				}
			}
		} else if ( cs->bs ) {
			// if we are blocked by a brush entity, see if we can activate it
			ent = pm->touchents[i];
			if ( g_entities[ent].s.modelindex > 0 && g_entities[ent].s.eType == ET_MOVER ) {
				//find the bsp entity which should be activated in order to remove
				//the blocking entity

				if ( !g_entities[ent].isProp
					 && Q_stricmp( g_entities[ent].classname, "func_static" )
					 && Q_stricmp( g_entities[ent].classname, "func_button" )
					 && Q_stricmp( g_entities[ent].classname, "func_tram" ) ) {
					G_Activate( &g_entities[ent], &g_entities[cs->entityNum] );
				}

			}
		}
	}
}
示例#6
0
/*
============
AICast_Die
============
*/
void AICast_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) {
	int contents;
	int killer;
	cast_state_t    *cs;
	qboolean nogib = qtrue;

	// print debugging message
	if ( aicast_debug.integer == 2 && attacker->s.number == 0 ) {
		G_Printf( "killed %s\n", self->aiName );
	}

	cs = AICast_GetCastState( self->s.number );

	if ( attacker ) {
		killer = attacker->s.number;
	} else {
		killer = ENTITYNUM_WORLD;
	}

	// record the sighting (FIXME: silent weapons shouldn't do this, but the AI should react in some way)
	if ( attacker->client ) {
		AICast_UpdateVisibility( self, attacker, qtrue, qtrue );
	}

	// the zombie should show special effect instead of gibbing
	if ( self->aiCharacter == AICHAR_ZOMBIE && cs->secondDeadTime ) {
		if ( cs->secondDeadTime > 1 ) {
			// we are already totally dead
			self->health += damage; // don't drop below gib_health if we weren't already below it
			return;
		}
/*
		if (!cs->rebirthTime)
		{
			self->health = -999;
			damage = 999;
		} else if ( self->health >= GIB_HEALTH ) {
			// while waiting for rebirth, we only "die" if we drop below gib health
			return;
		}
*/
		// always gib
		self->health = -999;
		damage = 999;
	}

	// Zombies are very fragile against highly explosives
	if ( self->aiCharacter == AICHAR_ZOMBIE && damage > 20 && inflictor != attacker ) {
		self->health = -999;
		damage = 999;
	}

	// process the event
	if ( self->client->ps.pm_type == PM_DEAD ) {
		// already dead
		if ( self->health < GIB_HEALTH ) {
			if ( self->aiCharacter == AICHAR_ZOMBIE ) {
				// RF, changed this so Zombies always gib now
				GibEntity( self, killer );
				nogib = qfalse;
/*
				// Zombie has special exploding cloud effect
				if (attacker != inflictor || attacker->s.weapon == WP_VENOM)
				{
					GibEntity( self, killer );
					nogib = qfalse;
				} else {
					// Zombie will decompose upon dying
					self->client->ps.eFlags |= EF_MONSTER_EFFECT2;
					self->s.effect2Time = level.time+200;
					self->health = -1;
				}
*/
				self->takedamage = qfalse;
				self->r.contents = 0;
				cs->secondDeadTime = 2;
				cs->rebirthTime = 0;
				cs->revivingTime = 0;
			} else {
				body_die( self, inflictor, attacker, damage, meansOfDeath );
				return;
			}
		}

	} else {    // this is our first death, so set everything up

		if ( level.intermissiontime ) {
			return;
		}

		self->client->ps.pm_type = PM_DEAD;

		self->enemy = attacker;

		// drop a weapon?
		// if client is in a nodrop area, don't drop anything
		contents = trap_PointContents( self->r.currentOrigin, -1 );
		if ( !( contents & CONTENTS_NODROP ) ) {
			TossClientItems( self );
		}

		// make sure the client doesn't forget about this entity until it's set to "dead" frame
		// otherwise it might replay it's death animation if it goes out and into client view
		self->r.svFlags |= SVF_BROADCAST;

		self->takedamage = qtrue;   // can still be gibbed

		self->s.weapon = WP_NONE;
		self->s.powerups = 0;
		self->r.contents = CONTENTS_CORPSE;

		self->s.angles[0] = 0;
		self->s.angles[1] = self->client->ps.viewangles[1];
		self->s.angles[2] = 0;

		VectorCopy( self->s.angles, self->client->ps.viewangles );

		self->s.loopSound = 0;

		self->r.maxs[2] = -8;
		self->client->ps.maxs[2] = self->r.maxs[2];

		// remove powerups
		memset( self->client->ps.powerups, 0, sizeof( self->client->ps.powerups ) );

		//cs->rebirthTime = 0;

		// never gib in a nodrop
		if ( self->health <= GIB_HEALTH ) {
			if ( self->aiCharacter == AICHAR_ZOMBIE ) {
				// RF, changed this so Zombies always gib now
				GibEntity( self, killer );
				nogib = qfalse;
/*
				// Zombie has special exploding cloud effect
				if (attacker != inflictor || attacker->s.weapon == WP_VENOM)
				{
					GibEntity( self, killer );
					nogib = qfalse;
					self->takedamage = qfalse;
					self->r.contents = 0;
					cs->secondDeadTime = 2;
				} else {
					self->client->ps.eFlags |= EF_MONSTER_EFFECT2;
					self->s.effect2Time = level.time+200;
					self->takedamage = qfalse;
					self->r.contents = 0;
					self->health = -1;
					cs->secondDeadTime = 2;
				}
*/
			} else if ( !( contents & CONTENTS_NODROP ) ) {
				body_die( self, inflictor, attacker, damage, meansOfDeath );
				//GibEntity( self, killer );
				nogib = qfalse;
			}
		}

		// if we are a zombie, and lying down during our first death, then we should just die
		if ( !( self->aiCharacter == AICHAR_ZOMBIE && cs->secondDeadTime && cs->rebirthTime ) ) {

			// set enemy weapon
			BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_WEAPON, 0, qfalse );
			if ( attacker->client ) {
				BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_WEAPON, inflictor->s.weapon, qtrue );
			} else {
				BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_WEAPON, 0, qfalse );
			}

			// set enemy location
			BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_POSITION, 0, qfalse );
			if ( infront( self, inflictor ) ) {
				BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_POSITION, POSITION_INFRONT, qtrue );
			} else {
				BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_POSITION, POSITION_BEHIND, qtrue );
			}

			// play the animation
			BG_AnimScriptEvent( &self->client->ps, ANIM_ET_DEATH, qfalse, qtrue );

			// set this flag so no other anims override us
			self->client->ps.eFlags |= EF_DEAD;
			self->s.eFlags |= EF_DEAD;

		}
	}

	if ( nogib ) {
		// set for rebirth
		if ( self->aiCharacter == AICHAR_ZOMBIE ) {
			if ( !cs->secondDeadTime ) {
				cs->rebirthTime = level.time + 5000 + rand() % 2000;
				cs->secondDeadTime = qtrue;
				cs->revivingTime = 0;
			} else if ( cs->secondDeadTime > 1 ) {
				cs->rebirthTime = 0;
				cs->revivingTime = 0;
				cs->deathTime = level.time;
			}
		} else {
			// the body can still be gibbed
			self->die = body_die;
		}
	}

	trap_LinkEntity( self );

	// mark the time of death
	cs->deathTime = level.time;

	// dying ai's can trigger a target
	if ( !cs->rebirthTime ) {
		G_UseTargets( self, self );
		// really dead now, so call the script
		AICast_ScriptEvent( cs, "death", "" );
		// call the deathfunc for this cast, so we can play associated sounds, or do any character-specific things
		if ( !( cs->aiFlags & AIFL_DENYACTION ) && cs->deathfunc ) {
			cs->deathfunc( self, attacker, damage, meansOfDeath );   //----(SA)	added mod
		}
	} else {
		// really dead now, so call the script
		AICast_ScriptEvent( cs, "fakedeath", "" );
		// call the deathfunc for this cast, so we can play associated sounds, or do any character-specific things
		if ( !( cs->aiFlags & AIFL_DENYACTION ) && cs->deathfunc ) {
			cs->deathfunc( self, attacker, damage, meansOfDeath );   //----(SA)	added mod
		}
	}
}
示例#7
0
void AICast_SightUpdate( int numchecks ) {
	int count = 0, destcount, srccount;
	int src, dest;
	gentity_t       *srcent, *destent;
	cast_state_t    *cs, *dcs;
	//static int	lastNumUpdated; // TTimo: unused
	cast_visibility_t *vis;
	#define SIGHT_MIN_DELAY 200

	src = 0;
	dest = 0;
	if ( numchecks < 5 ) {
		numchecks = 5;
	}

	if ( trap_Cvar_VariableIntegerValue( "savegame_loading" ) ) {
		return;
	}

	if ( saveGamePending ) {
		return;
	}

	// First, check all REAL clients, so sighting player is only effected by reaction_time, not
	// effected by framerate also
	for (   srccount = 0, src = 0, srcent = &g_entities[0];
			src < aicast_maxclients && srccount < level.numPlayingClients;
			src++, srcent++ )
	{
		if ( !srcent->inuse ) {
			continue;
		}

		srccount++;

		if ( srcent->aiInactive ) {
			continue;
		}
		if ( srcent->health <= 0 ) {
			continue;
		}
		if ( !( srcent->r.svFlags & SVF_CASTAI ) ) { // only source check AI Cast
			continue;
		}

		cs = AICast_GetCastState( src );

		if ( cs->castScriptStatus.scriptNoSightTime >= level.time ) {
			continue;
		}

		// make sure we are using the right AAS data for this entity (one's that don't get set will default to the player's AAS data)
		trap_AAS_SetCurrentWorld( cs->aasWorldIndex );

		for (   destcount = 0, dest = 0, destent = g_entities;
				//dest < aicast_maxclients && destcount < level.numPlayingClients;
				destent == g_entities;  // only check the player
				dest++, destent++ )
		{
			if ( !destent->inuse ) {
				continue;
			}

			destcount++;

			if ( destent->health <= 0 ) {
				continue;
			}
			if ( destent->r.svFlags & SVF_CASTAI ) {      // only dest check REAL clients
				continue;
			}

			if ( src == dest ) {
				continue;
			}

			vis = &cs->vislist[destent->s.number];

			// OPTIMIZATION: if we have seen the player, abort checking each frame
			//if (vis->real_visible_timestamp && cs->aiState > AISTATE_QUERY && AICast_HostileEnemy(cs, destent->s.number))
			//	continue;

			// if we saw them last frame, skip this test, so we only check initial sightings each frame
			if ( vis->lastcheck_timestamp == vis->real_visible_timestamp ) {
				continue;
			}

			// if we recently checked this vis, skip
			if ( vis->lastcheck_timestamp >= level.time - ( 40 + rand() % 40 ) ) {
				continue;
			}

			// check for visibility
			if (    !( destent->flags & FL_NOTARGET )
					&&  ( AICast_CheckVisibility( srcent, destent ) ) ) {
				// record the sighting
				AICast_UpdateVisibility( srcent, destent, qtrue, qtrue );
			} else // if (vis->lastcheck_timestamp == vis->real_update_timestamp)
			{
				AICast_UpdateNonVisibility( srcent, destent, qtrue );
			}
		}
	}

	// Now do the normal timeslice checks
	for (   srccount = 0, src = lastsrc, srcent = &g_entities[lastsrc];
			src < aicast_maxclients; // && srccount < level.numPlayingClients;
			src++, srcent++ )
	{
		if ( !srcent->inuse ) {
			continue;
		}

		srccount++;

		if ( srcent->aiInactive ) {
			continue;
		}
		if ( srcent->health <= 0 ) {
			continue;
		}

		cs = AICast_GetCastState( src );

		if ( cs->castScriptStatus.scriptNoSightTime >= level.time ) {
			continue;
		}

		// make sure we are using the right AAS data for this entity (one's that don't get set will default to the player's AAS data)
		trap_AAS_SetCurrentWorld( cs->aasWorldIndex );

		if ( lastdest < 0 ) {
			lastdest = 0;
		}

		for (   destcount = 0, dest = lastdest, destent = &g_entities[lastdest];
				dest < aicast_maxclients; // && destcount < level.numPlayingClients;
				dest++, destent++ )
		{
			if ( !destent->inuse ) {
				continue;
			}

			destcount++;

			if ( destent->aiInactive ) {
				continue;
			}
			if ( src == dest ) {
				continue;
			}

			dcs = AICast_GetCastState( destent->s.number );

			vis = &cs->vislist[destent->s.number];

			// don't check too often
			if ( vis->lastcheck_timestamp > ( level.time - SIGHT_MIN_DELAY ) ) {
				continue;
			}

			if ( destent->health <= 0 ) {
				// only check dead guys until they are sighted
				if ( vis->lastcheck_health < 0 ) {
					continue;
				}
			}

			if ( vis->lastcheck_timestamp > level.time ) {
				continue;   // let the loadgame settle down

			}
			// if they are friends, only check very infrequently
			if ( AICast_SameTeam( cs, destent->s.number ) && ( vis->lastcheck_timestamp == vis->visible_timestamp )
				 &&  ( destent->health == vis->lastcheck_health + 1 ) ) {
				if ( dcs->aiState < AISTATE_COMBAT ) {
					if ( vis->lastcheck_timestamp > ( level.time - ( 2000 + rand() % 1000 ) ) ) {
						continue;   // dont check too often
					}
				} else {    // check a little more frequently
					if ( vis->lastcheck_timestamp > ( level.time - ( 500 + rand() % 500 ) ) ) {
						continue;   // dont check too often
					}
				}
			}

			// check for visibility
			if (    !( destent->flags & FL_NOTARGET )
					&&  ( AICast_CheckVisibility( srcent, destent ) ) ) {
				// make sure they are still with us
				if ( destent->inuse ) {
					// record the sighting
					AICast_UpdateVisibility( srcent, destent, qtrue, qtrue );
				}
			} else // if (vis->lastcheck_timestamp == vis->real_update_timestamp)
			{
				AICast_UpdateNonVisibility( srcent, destent, qtrue );
			}

			// break if we've processed the maximum visibilities
			if ( ++count > numchecks ) {
				dest++;
				if ( dest >= aicast_maxclients ) {
					src++;
				}
				goto escape;
			}
		}

		lastdest = 0;
	}

escape:

	if ( src >= aicast_maxclients ) {
		src = 0;
	}
	lastsrc = src;
	if ( dest >= aicast_maxclients ) {
		dest = 0;
	}
	lastdest = dest;
}