/*
================
AIFunc_WarriorZombieMeleeStart
================
*/
char *AIFunc_WarriorZombieMeleeStart( cast_state_t *cs ) {
	gentity_t *ent;

	ent = &g_entities[cs->entityNum];
	ent->s.effect1Time = level.time;
	cs->ideal_viewangles[YAW] = cs->viewangles[YAW];
	cs->weaponFireTimes[cs->weaponNum] = level.time;
	cs->animHitCount = 0;

	// face them
	AICast_AimAtEnemy( cs );

	// audible sound
	AIChar_AttackSound( cs );

	// play an anim
	BG_UpdateConditionValue( cs->entityNum, ANIM_COND_WEAPON, cs->weaponNum, qtrue );
	BG_AnimScriptEvent( &ent->client->ps, ANIM_ET_FIREWEAPON, qfalse, qtrue );

	// stop charging
	BG_UpdateConditionValue( cs->entityNum, ANIM_COND_CHARGING, 0, qfalse );
	ent->flags &= ~FL_WARZOMBIECHARGE;

	cs->aifunc = AIFunc_WarriorZombieMelee;
	return "AIFunc_WarriorZombieMelee";
}
Ejemplo n.º 2
0
/*
==============
AICast_QueryThink
==============
*/
void AICast_QueryThink( cast_state_t *cs ) {
	gentity_t *ent;
	qboolean visible;
	cast_state_t *ocs;
	vec3_t vec;

	ent = &g_entities[cs->entityNum];
	ocs = AICast_GetCastState( cs->bs->enemy );

	// never crouch while in this state (by choice anyway)
	cs->bs->attackcrouch_time = 0;

	// look at where we last (thought we) saw them
	VectorSubtract( cs->vislist[cs->bs->enemy].visible_pos, cs->bs->origin, vec );
	VectorNormalize( vec );
	vectoangles( vec, cs->bs->ideal_viewangles );

	// are they visible now?
	visible = AICast_VisibleFromPos( cs->bs->origin, cs->entityNum, g_entities[cs->bs->enemy].r.currentOrigin, cs->bs->enemy, qfalse );

	// make sure we dont process the sighting of this enemy by going into query mode again, without them being visible again after we leave here
	cs->vislist[cs->bs->enemy].flags &= ~AIVIS_PROCESS_SIGHTING;

	// look towards where we last saw them
	AICast_AimAtEnemy( cs );

	// if visible and alert time has expired, go POSTAL
	if ( ( cs->queryAlertSightTime < 0 ) || ( ( cs->queryAlertSightTime < level.time ) && visible ) ) {
		if ( !cs->queryAlertSightTime ) {
			// set the "short reaction" condition
			BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SHORT_REACTION, qtrue, qfalse );
		}
		AICast_StateChange( cs, AISTATE_COMBAT );
		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SHORT_REACTION, qfalse, qfalse );
		AIFunc_BattleStart( cs );
		return;
	}

	// if they've fired since the start of the query mode, go POSTAL
	if ( ocs->lastWeaponFired > cs->queryStartTime ) {
		// set the "short reaction" condition
		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SHORT_REACTION, qtrue, qfalse );
		AICast_StateChange( cs, AISTATE_COMBAT );
		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SHORT_REACTION, qfalse, qfalse );
		AIFunc_BattleStart( cs );
		return;
	}

	// if not visible, then kill the Lock On timer
	if ( ( cs->queryAlertSightTime > 0 ) && !visible ) {
		cs->queryAlertSightTime = 0;
	}

	// if the query has expired, go back to relaxed
	if ( !ent->client->ps.legsTimer ) {
		AICast_StateChange( cs, AISTATE_RELAXED );
	}
}
/*
================
AIFunc_WarriorZombieDefenseStart
================
*/
char *AIFunc_WarriorZombieDefenseStart( cast_state_t *cs ) {
	gentity_t *ent, *enemy;
	vec3_t enemyDir, vec;
	float dist;
	static int lastWarriorDefense;

	if ( lastWarriorDefense <= level.time && lastWarriorDefense > level.time - 3000 ) {
		return NULL;    // dont all go into defense at once
	}
	lastWarriorDefense = level.time;

	ent = &g_entities[cs->entityNum];
	enemy = &g_entities[cs->enemyNum];

	// if our enemy isn't using a dangerous weapon
	if ( enemy->client->ps.weapon < WP_LUGER || enemy->client->ps.weapon > WP_CLASS_SPECIAL ) {
		return NULL;
	}

	// if we are doing a goto
	if ( cs->followEntity >= 0 ) {
		return NULL;
	}

	// if our enemy isn't looking right at us, abort
	VectorSubtract( ent->client->ps.origin, enemy->client->ps.origin, vec );
	dist = VectorNormalize( vec );
	if ( dist > 512 ) {
		dist = 512;
	}
	if ( dist < 128 ) {
		return NULL;
	}
	AngleVectors( enemy->client->ps.viewangles, enemyDir, NULL, NULL );
	if ( DotProduct( vec, enemyDir ) < ( 0.98 - 0.2 * ( dist / 512 ) ) ) {
		return NULL;
	}

	cs->weaponFireTimes[cs->weaponNum] = level.time;

	// face them
	AICast_AimAtEnemy( cs );

	// anim
	BG_UpdateConditionValue( cs->entityNum, ANIM_COND_WEAPON, cs->weaponNum, qtrue );
	BG_AnimScriptEvent( &ent->client->ps, ANIM_ET_FIREWEAPON, qfalse, qtrue );
	ent->client->ps.torsoTimer = 3000;
	ent->client->ps.legsTimer = 3000;

	ent->flags |= FL_DEFENSE_GUARD;

	// when they come out of defense mode, go into charge mode
	BG_UpdateConditionValue( cs->entityNum, ANIM_COND_CHARGING, 1, qfalse );
	ent->flags |= FL_WARZOMBIECHARGE;

	cs->aifunc = AIFunc_WarriorZombieDefense;
	return "AIFunc_WarriorZombieDefense";
}
Ejemplo n.º 4
0
char *AIFunc_Helga_SpiritAttack_Start(cast_state_t *cs) {
	gentity_t *ent;
	ent = &g_entities[cs->entityNum];
	ent->s.otherEntityNum2 = cs->enemyNum;
	ent->s.effect1Time = level.time;
	cs->aiFlags |= AIFL_SPECIAL_FUNC;
	// dont turn
	cs->ideal_viewangles[YAW] = cs->viewangles[YAW];
	// play an anim
	BG_UpdateConditionValue(cs->entityNum, ANIM_COND_WEAPON, WP_MONSTER_ATTACK2, qtrue);
	BG_AnimScriptEvent(&ent->client->ps, ANIM_ET_FIREWEAPON, qfalse, qtrue);
	cs->aifunc = AIFunc_Helga_SpiritAttack;
	return "AIFunc_Helga_SpiritAttack";
}
char *AIFunc_LoperAttack1Start( cast_state_t *cs ) {
	gentity_t *ent;
	//
	ent = &g_entities[cs->entityNum];
	// face them
	AICast_AimAtEnemy( cs );
	// start the animation
	//ent->client->ps.legsAnim =
	//	( ( ent->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | loperMeleeAnims[rand()%NUM_LOPER_MELEE_ANIMS];
	//ent->client->ps.legsTimer = LOPER_MELEE_DURATION;
	BG_UpdateConditionValue( cs->entityNum, ANIM_COND_WEAPON, cs->bs->weaponnum, qtrue );
	BG_AnimScriptEvent( &ent->client->ps, ANIM_ET_FIREWEAPON, qfalse, qtrue );
	// play the sound
	// TODO
	//
	cs->aifunc = AIFunc_LoperAttack1;
	return "AIFunc_LoperAttack1";
}
/*
================
AIFunc_ZombieMeleeStart
================
*/
char *AIFunc_ZombieMeleeStart( cast_state_t *cs ) {
	gentity_t *ent;

	ent = &g_entities[cs->entityNum];
	cs->weaponFireTimes[cs->weaponNum] = level.time;
	cs->animHitCount = 0;

	// face them
	AICast_AimAtEnemy( cs );

	// audible sound
	AIChar_AttackSound( cs );

	// play an anim
	BG_UpdateConditionValue( cs->entityNum, ANIM_COND_WEAPON, cs->weaponNum, qtrue );
	BG_AnimScriptEvent( &ent->client->ps, ANIM_ET_FIREWEAPON, qfalse, qtrue );

	cs->aifunc = AIFunc_ZombieMelee;
	return "AIFunc_ZombieMelee";
}
Ejemplo n.º 7
0
/*
=======================================================================================================================================
AIFunc_Helga_MeleeStart
=======================================================================================================================================
*/
char *AIFunc_Helga_MeleeStart(cast_state_t *cs) {
	gentity_t *ent;

	ent = &g_entities[cs->entityNum];
	ent->s.effect1Time = level.time;
	cs->ideal_viewangles[YAW] = cs->viewangles[YAW];
	cs->weaponFireTimes[cs->weaponNum] = level.time;
	cs->animHitCount = 0;
	cs->aiFlags |= AIFL_SPECIAL_FUNC;
	// face them
	AICast_AimAtEnemy(cs);
	// play an anim
	BG_UpdateConditionValue(cs->entityNum, ANIM_COND_WEAPON, cs->weaponNum, qtrue);
	BG_AnimScriptEvent(&ent->client->ps, ANIM_ET_FIREWEAPON, qfalse, qtrue);
	// play a sound
	G_AddEvent(ent, EV_GENERAL_SOUND, G_SoundIndex(aiDefaults[ent->aiCharacter].soundScripts[ATTACKSOUNDSCRIPT]));

	cs->aifunc = AIFunc_Helga_Melee;
	cs->aifunc(cs);  // think once now, to prevent a delay
	return "AIFunc_Helga_Melee";
}
Ejemplo n.º 8
0
/*
============
AICast_Think

  entry point for all cast AI
============
*/
void AICast_Think( int client, float thinktime ) {
	gentity_t       *ent;
	cast_state_t    *cs;
	int i;
	int animIndex;
	animation_t     *anim;

//	if (saveGamePending || (strlen( g_missionStats.string ) > 2 )) {
//		return;
//	}

	//
	// get the cast ready for processing
	//
	cs = AICast_GetCastState( client );
	ent = &g_entities[client];
	//
	// 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 );
	//
	// make sure we have a valid navigation system
	//
	if ( !trap_AAS_Initialized() ) {
		return;
	}
	//
	trap_EA_ResetInput( client, NULL );
	cs->aiFlags &= ~AIFL_VIEWLOCKED;
	//cs->bs->weaponnum = ent->client->ps.weapon;
	//
	// turn off flags that are set each frame if needed
	ent->client->ps.eFlags &= ~( EF_NOSWINGANGLES | EF_MONSTER_EFFECT | EF_MONSTER_EFFECT2 | EF_MONSTER_EFFECT3 );
	// conditional flags
	if ( ent->aiCharacter == AICHAR_ZOMBIE ) {
		if ( COM_BitCheck( ent->client->ps.weapons, WP_MONSTER_ATTACK1 ) ) {
			cs->aiFlags |= AIFL_NO_FLAME_DAMAGE;
			SET_FLAMING_ZOMBIE( ent->s, 1 );
		} else {
			SET_FLAMING_ZOMBIE( ent->s, 0 );
		}
	}
	//
	// if we're dead, do special stuff only
	if ( ent->health <= 0 || cs->revivingTime || cs->rebirthTime ) {
		//
		if ( cs->revivingTime && cs->revivingTime < level.time ) {
			// start us thinking again
			ent->client->ps.pm_type = PM_NORMAL;
			cs->revivingTime = 0;
		}
		//
		if ( cs->rebirthTime && cs->rebirthTime < level.time ) {
			vec3_t mins, maxs;
			int touch[10], numTouch;
			float oldmaxZ;

			oldmaxZ = ent->r.maxs[2];

			// make sure the area is clear
			AIChar_SetBBox( ent, cs );

			VectorAdd( ent->r.currentOrigin, ent->r.mins, mins );
			VectorAdd( ent->r.currentOrigin, ent->r.maxs, maxs );
			trap_UnlinkEntity( ent );

			numTouch = trap_EntitiesInBox( mins, maxs, touch, 10 );

			if ( numTouch ) {
				for ( i = 0; i < numTouch; i++ ) {
					//if (!g_entities[touch[i]].client || g_entities[touch[i]].r.contents == CONTENTS_BODY)
					if ( g_entities[touch[i]].r.contents & MASK_PLAYERSOLID ) {
						break;
					}
				}
				if ( i == numTouch ) {
					numTouch = 0;
				}
			}

			if ( numTouch == 0 ) {    // ok to spawn

				// give them health when they start reviving, so we won't gib after
				// just a couple shots while reviving
				ent->health =
					ent->client->ps.stats[STAT_HEALTH] =
						ent->client->ps.stats[STAT_MAX_HEALTH] =
							( ( cs->attributes[STARTING_HEALTH] - 50 ) > 30 ? ( cs->attributes[STARTING_HEALTH] - 50 ) : 30 );

				ent->r.contents = CONTENTS_BODY;
				ent->clipmask = MASK_PLAYERSOLID;
				ent->takedamage = qtrue;
				ent->waterlevel = 0;
				ent->watertype = 0;
				ent->flags = 0;
				ent->die = AICast_Die;
				ent->client->ps.eFlags &= ~EF_DEAD;
				ent->s.eFlags &= ~EF_DEAD;

				cs->rebirthTime = 0;
				cs->deathTime = 0;

				// play the revive animation
				cs->revivingTime = level.time + BG_AnimScriptEvent( &ent->client->ps, ANIM_ET_REVIVE, qfalse, qtrue );;
			} else {
				// can't spawn yet, so set bbox back, and wait
				ent->r.maxs[2] = oldmaxZ;
				ent->client->ps.maxs[2] = ent->r.maxs[2];
			}
			trap_LinkEntity( ent );
		}
		// ZOMBIE should set effect flag if really dead
		if ( cs->aiCharacter == AICHAR_ZOMBIE && !ent->r.contents ) {
			ent->client->ps.eFlags |= EF_MONSTER_EFFECT2;
		}
		//
		if ( ent->health > GIB_HEALTH && cs->deathTime && cs->deathTime < ( level.time - 3000 ) ) {
/*
			// been dead for long enough, set our animation to the end frame
			switch ( ent->s.legsAnim & ~ANIM_TOGGLEBIT ) {
			case BOTH_DEATH1:
			case BOTH_DEAD1:
				anim = BOTH_DEAD1;
				break;
			case BOTH_DEATH2:
			case BOTH_DEAD2:
				anim = BOTH_DEAD2;
				break;
			case BOTH_DEATH3:
			case BOTH_DEAD3:
				anim = BOTH_DEAD3;
				break;
			default:
				G_Error( "%s has unknown death animation\n", ent->classname);
			}
			ent->client->ps.torsoAnim = ( ( ent->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
			ent->client->ps.legsAnim = ( ( ent->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
*/
			cs->deathTime = 0;
			ent->r.svFlags &= ~SVF_BROADCAST;
		}
		//
		// no more thinking required
		return;
	}
	//
	// set some anim conditions
	if ( cs->secondDeadTime ) {
		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SECONDLIFE, qtrue, qfalse );
	} else {
		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SECONDLIFE, qfalse, qfalse );
	}
	// set health value
	if ( ent->health <= 0.25 * cs->attributes[STARTING_HEALTH] ) {
		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_HEALTH_LEVEL, 3, qfalse );
	} else if ( ent->health <= 0.5 * cs->attributes[STARTING_HEALTH] ) {
		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_HEALTH_LEVEL, 2, qfalse );
	} else {
		BG_UpdateConditionValue( cs->entityNum, ANIM_COND_HEALTH_LEVEL, 1, qfalse );
	}
	//
	cs->speedScale = 1.0;           // reset each frame, set if required
	cs->actionFlags = 0;            // FIXME: move this to a Cast AI movement init function!
	//retrieve the current client state
	BotAI_GetClientState( client, &( cs->bs->cur_ps ) );
	//
	// setup movement speeds for the given state
	// walking
	animIndex = BG_GetAnimScriptAnimation( cs->entityNum, ent->client->ps.aiState, ANIM_MT_WALK );
	if ( animIndex >= 0 ) {
		anim = BG_GetAnimationForIndex( cs->entityNum, animIndex );
		cs->attributes[WALKING_SPEED] = anim->moveSpeed;
	}
	// crouching
	animIndex = BG_GetAnimScriptAnimation( cs->entityNum, ent->client->ps.aiState, ANIM_MT_WALKCR );
	if ( animIndex >= 0 ) {
		anim = BG_GetAnimationForIndex( cs->entityNum, animIndex );
		cs->attributes[CROUCHING_SPEED] = anim->moveSpeed;
	}
	// running
	animIndex = BG_GetAnimScriptAnimation( cs->entityNum, ent->client->ps.aiState, ANIM_MT_RUN );
	if ( animIndex >= 0 ) {
		anim = BG_GetAnimationForIndex( cs->entityNum, animIndex );
		cs->attributes[RUNNING_SPEED] = anim->moveSpeed;
	}
	// update crouch speed scale
	ent->client->ps.crouchSpeedScale = cs->attributes[CROUCHING_SPEED] / cs->attributes[RUNNING_SPEED];
	//
	// only enable headlook if we want to this frame
	ent->client->ps.eFlags &= ~EF_HEADLOOK;
	if ( cs->bs->enemy >= 0 ) {
		ent->client->ps.eFlags &= ~EF_STAND_IDLE2;  // never use alt idle if fighting
	}
	//
	// check for dead leader
	if ( cs->leaderNum >= 0 && g_entities[cs->leaderNum].health <= 0 ) {
		cs->leaderNum = -1;
	}
	//
#if 0
	// HACK for village2, if they are stuck, find a good position (there is a friendly guy placed inside a table)
	{
		trace_t tr;
		vec3_t org;
		trap_Trace( &tr, cs->bs->cur_ps.origin, cs->bs->cur_ps.mins, cs->bs->cur_ps.maxs, cs->bs->cur_ps.origin, cs->entityNum, CONTENTS_SOLID );
		while ( tr.startsolid ) {
			VectorCopy( cs->bs->cur_ps.origin, org );
			org[0] += 96 * crandom();
			org[1] += 96 * crandom();
			org[2] += 16 * crandom();
			trap_Trace( &tr, org, cs->bs->cur_ps.mins, cs->bs->cur_ps.maxs, org, cs->entityNum, CONTENTS_SOLID );
			G_SetOrigin( &g_entities[cs->entityNum], org );
			VectorCopy( org, g_entities[cs->entityNum].client->ps.origin );
		}
	}
#endif
	//add the delta angles to the cast's current view angles
	for ( i = 0; i < 3; i++ ) {
		cs->bs->viewangles[i] = AngleMod( cs->bs->viewangles[i] + SHORT2ANGLE( cs->bs->cur_ps.delta_angles[i] ) );
	}
	//
	//increase the local time of the cast
	cs->bs->ltime += thinktime;
	//
	cs->bs->thinktime = thinktime;
	//origin of the cast
	VectorCopy( cs->bs->cur_ps.origin, cs->bs->origin );
	//eye coordinates of the cast
	VectorCopy( cs->bs->cur_ps.origin, cs->bs->eye );
	cs->bs->eye[2] += cs->bs->cur_ps.viewheight;
	//get the area the cast is in
	cs->bs->areanum = BotPointAreaNum( cs->bs->origin );
	// clear flags each frame
	cs->bs->flags = 0;
	//
	// check enemy health
	if ( cs->bs->enemy >= 0 && g_entities[cs->bs->enemy].health <= 0 ) {
		cs->bs->enemy = -1;
	}
	//
	// if the previous movetype was temporary, set it back
	if ( cs->movestateType == MSTYPE_TEMPORARY ) {
		cs->movestate = MS_DEFAULT;
		cs->movestateType = MSTYPE_NONE;
	}
	// crouching?
	if (    ( cs->bs->attackcrouch_time > trap_AAS_Time() ) &&
			( ( cs->lastAttackCrouch > level.time - 500 ) || ( cs->thinkFuncChangeTime < level.time - 1000 ) ) ) {
		// if we are not moving, and we are firing, always stand, unless we are allowed to crouch + fire
		if ( VectorLength( cs->bs->cur_ps.velocity ) || ( cs->lastWeaponFired < level.time - 2000 ) || ( cs->aiFlags & AIFL_ATTACK_CROUCH ) ) {
			cs->lastAttackCrouch = level.time;
			trap_EA_Crouch( cs->bs->client );
		}
	}
	//
	//if (cs->bs->enemy >= 0) {
	//update the attack inventory values
	AICast_UpdateBattleInventory( cs, cs->bs->enemy );
	//}
	//
	// if we don't have ammo for the current weapon, get rid of it
	if ( !( COM_BitCheck( cs->bs->cur_ps.weapons, cs->bs->weaponnum ) ) || !AICast_GotEnoughAmmoForWeapon( cs, cs->bs->weaponnum ) ) {
		// select a weapon
		AICast_ChooseWeapon( cs, qfalse );
		// if still no ammo, select a blank weapon
		//if (!AICast_GotEnoughAmmoForWeapon( cs, cs->bs->weaponnum )) {
		//	cs->bs->weaponnum = WP_NONE;
		//}
	}
	//
	// in query mode, we do special handling (pause scripting, check for transition to alert/combat, etc)
	if ( cs->aiState == AISTATE_QUERY ) {
		AICast_QueryThink( cs );
	} else if ( cs->pauseTime < level.time )     {
		// do the thinking
		AICast_ProcessAIFunctions( cs, thinktime );
		//
		// make sure the correct weapon is selected
		trap_EA_SelectWeapon( cs->bs->client, cs->bs->weaponnum );
		//
		// process current script if it exists
		cs->castScriptStatusCurrent = cs->castScriptStatus;
		AICast_ScriptRun( cs, qfalse );
	}
	//
	// set special movestate if necessary
	if ( cs->movestateType != MSTYPE_NONE ) {
		switch ( cs->movestate ) {
		case MS_WALK:
			cs->actionFlags |= CASTACTION_WALK;
			break;
		case MS_CROUCH:
			trap_EA_Crouch( cs->entityNum );
			break;
		default:
			break; // TTimo gcc: MS_DEFAULT MS_RUN not handled in switch
		}
	}
	//
	//subtract the delta angles
	for ( i = 0; i < 3; i++ ) {
		cs->bs->viewangles[i] = AngleMod( cs->bs->viewangles[i] - SHORT2ANGLE( cs->bs->cur_ps.delta_angles[i] ) );
	}
}
Ejemplo n.º 9
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;
//	vec3_t		oldOrigin;
	int oldEventSequence;
	int msec;
	usercmd_t   *ucmd;
	int monsterslick = 0;
// JPW NERVE
	int i;
	vec3_t muzzlebounce;
	gitem_t *item;
	gentity_t *ent2;
	vec3_t velocity, org, offset;
	vec3_t angles,mins,maxs;
	int weapon;
	trace_t tr;
// jpw

	// Rafael wolfkick
	//int			validkick;
	//static int	wolfkicktimer = 0;

	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;
	}

	if ( client->cameraPortal ) {
		G_SetOrigin( client->cameraPortal, client->ps.origin );
		trap_LinkEntity( client->cameraPortal );
		VectorCopy( client->cameraOrigin, client->cameraPortal->s.origin2 );
	}

	// mark the time, so the connection sprite can be removed
	ucmd = &ent->client->pers.cmd;

	ent->client->ps.identifyClient = ucmd->identClient;     // NERVE - SMF

// JPW NERVE -- update counter for capture & hold display
	if ( g_gametype.integer == GT_WOLF_CPH ) {
		client->ps.stats[STAT_CAPTUREHOLD_RED] = level.capturetimes[TEAM_RED];
		client->ps.stats[STAT_CAPTUREHOLD_BLUE] = level.capturetimes[TEAM_BLUE];
	}
// jpw

	// 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;
		/*
		// Ridah, fixes savegame timing issue
		if (msec < -100) {
			client->ps.commandTime = ucmd->serverTime - 100;
			msec = 100;
		} else {
			return;
		}
		*/
		// done.
	}
	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
	// DHM - Nerve :: In limbo use SpectatorThink
	if ( client->sess.sessionTeam == TEAM_SPECTATOR || client->ps.pm_flags & PMF_LIMBO ) {
		if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) {
			return;
		}
		SpectatorThink( ent, ucmd );
		return;
	}

	// JPW NERVE do some time-based muzzle flip -- this never gets touched in single player (see g_weapon.c)
	// #define RIFLE_SHAKE_TIME 150 // JPW NERVE this one goes with the commented out old damped "realistic" behavior below
	#define RIFLE_SHAKE_TIME 300 // per Id request, longer recoil time
	if ( client->sniperRifleFiredTime ) {
		if ( level.time - client->sniperRifleFiredTime > RIFLE_SHAKE_TIME ) {
			client->sniperRifleFiredTime = 0;
		} else {
			VectorCopy( client->ps.viewangles,muzzlebounce );

			// JPW per Id request, longer recoil time
			muzzlebounce[PITCH] -= 2 * cos( 2.5 * ( level.time - client->sniperRifleFiredTime ) / RIFLE_SHAKE_TIME );
			muzzlebounce[YAW] += 0.5*client->sniperRifleMuzzleYaw*cos( 1.0 - ( level.time - client->sniperRifleFiredTime ) * 3 / RIFLE_SHAKE_TIME );
			muzzlebounce[PITCH] -= 0.25 * random() * ( 1.0f - ( level.time - client->sniperRifleFiredTime ) / RIFLE_SHAKE_TIME );
			muzzlebounce[YAW] += 0.5 * crandom() * ( 1.0f - ( level.time - client->sniperRifleFiredTime ) / RIFLE_SHAKE_TIME );
			SetClientViewAngle( ent,muzzlebounce );
		}
	}
	if ( client->ps.stats[STAT_PLAYER_CLASS] == PC_MEDIC ) {
		if ( level.time > client->ps.powerups[PW_REGEN] + 5000 ) {
			client->ps.powerups[PW_REGEN] = level.time;
		}
	}
	// also update weapon recharge time

	// JPW drop button drops secondary weapon so new one can be picked up
	// TTimo explicit braces to avoid ambiguous 'else'
	if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
		if ( ucmd->wbuttons & WBUTTON_DROP ) {
			if ( !client->dropWeaponTime ) {
				client->dropWeaponTime = 1; // just latch it for now
				if ( ( client->ps.stats[STAT_PLAYER_CLASS] == PC_SOLDIER ) || ( client->ps.stats[STAT_PLAYER_CLASS] == PC_LT ) ) {
					for ( i = 0; i < MAX_WEAPS_IN_BANK_MP; i++ ) {
						weapon = weapBanksMultiPlayer[3][i];
						if ( COM_BitCheck( client->ps.weapons,weapon ) ) {

							item = BG_FindItemForWeapon( weapon );
							VectorCopy( client->ps.viewangles, angles );

							// clamp pitch
							if ( angles[PITCH] < -30 ) {
								angles[PITCH] = -30;
							} else if ( angles[PITCH] > 30 ) {
								angles[PITCH] = 30;
							}

							AngleVectors( angles, velocity, NULL, NULL );
							VectorScale( velocity, 64, offset );
							offset[2] += client->ps.viewheight / 2;
							VectorScale( velocity, 75, velocity );
							velocity[2] += 50 + random() * 35;

							VectorAdd( client->ps.origin,offset,org );

							VectorSet( mins, -ITEM_RADIUS, -ITEM_RADIUS, 0 );
							VectorSet( maxs, ITEM_RADIUS, ITEM_RADIUS, 2 * ITEM_RADIUS );

							trap_Trace( &tr, client->ps.origin, mins, maxs, org, ent->s.number, MASK_SOLID );
							VectorCopy( tr.endpos, org );

							ent2 = LaunchItem( item, org, velocity, client->ps.clientNum );
							COM_BitClear( client->ps.weapons,weapon );

							if ( weapon == WP_MAUSER ) {
								COM_BitClear( client->ps.weapons,WP_SNIPERRIFLE );
							}

							// Clear out empty weapon, change to next best weapon
							G_AddEvent( ent, EV_NOAMMO, 0 );

							i = MAX_WEAPS_IN_BANK_MP;
							// show_bug.cgi?id=568
							if ( client->ps.weapon == weapon ) {
								client->ps.weapon = 0;
							}
							ent2->count = client->ps.ammoclip[BG_FindClipForWeapon( weapon )];
							ent2->item->quantity = client->ps.ammoclip[BG_FindClipForWeapon( weapon )];
							client->ps.ammoclip[BG_FindClipForWeapon( weapon )] = 0;
						}
					}
				}
			}
		} else {
			client->dropWeaponTime = 0;
		}
	}
// jpw

	// check for inactivity timer, but never drop the local client of a non-dedicated server
	if ( !ClientInactivityTimer( client ) ) {
		return;
	}

	if ( reloading || client->cameraPortal ) {
		ucmd->buttons = 0;
		ucmd->forwardmove = 0;
		ucmd->rightmove = 0;
		ucmd->upmove = 0;
		ucmd->wbuttons = 0;
		ucmd->wolfkick = 0;
		if ( client->cameraPortal ) {
			client->ps.pm_type = PM_FREEZE;
		}
	} else if ( client->noclip ) {
		client->ps.pm_type = PM_NOCLIP;
	} else if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
		client->ps.pm_type = PM_DEAD;
	} else {
		client->ps.pm_type = PM_NORMAL;
	}

	// set parachute anim condition flag
	BG_UpdateConditionValue( ent->s.number, ANIM_COND_PARACHUTE, ( ent->flags & FL_PARACHUTE ) != 0, qfalse );

	// all playing clients are assumed to be in combat mode
	if ( !client->ps.aiChar ) {
		client->ps.aiState = AISTATE_COMBAT;
	}

	client->ps.gravity = g_gravity.value;

	// set speed
	client->ps.speed = g_speed.value;

	if ( client->ps.powerups[PW_HASTE] ) {
		client->ps.speed *= 1.3;
	}

	// set up for pmove
	oldEventSequence = client->ps.eventSequence;

	client->currentAimSpreadScale = (float)client->ps.aimSpreadScale / 255.0;

	memset( &pm, 0, sizeof( pm ) );

	pm.ps = &client->ps;
	pm.pmext = &client->pmext;
	pm.cmd = *ucmd;
	pm.oldcmd = client->pers.oldcmd;
	if ( pm.ps->pm_type == PM_DEAD ) {
		pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
		// DHM-Nerve added:: EF_DEAD is checked for in Pmove functions, but wasn't being set
		//              until after Pmove
		pm.ps->eFlags |= EF_DEAD;
		// dhm-Nerve end
	} else {
		pm.tracemask = MASK_PLAYERSOLID;
	}
	// MrE: always use capsule for AI and player
	//pm.trace = trap_TraceCapsule;//trap_Trace;
	//DHM - Nerve :: We've gone back to using normal bbox traces
	pm.trace = trap_Trace;
	pm.pointcontents = trap_PointContents;
	pm.debugLevel = g_debugMove.integer;
	pm.noFootsteps = ( g_dmflags.integer & DF_NO_FOOTSTEPS ) > 0;

	pm.pmove_fixed = pmove_fixed.integer | client->pers.pmoveFixed;
	pm.pmove_msec = pmove_msec.integer;

	pm.noWeapClips = ( g_dmflags.integer & DF_NO_WEAPRELOAD ) > 0;
	if ( ent->aiCharacter && AICast_NoReload( ent->s.number ) ) {
		pm.noWeapClips = qtrue; // ensure AI characters don't use clips if they're not supposed to.

	}
	// Ridah
//	if (ent->r.svFlags & SVF_NOFOOTSTEPS)
//		pm.noFootsteps = qtrue;

	VectorCopy( client->ps.origin, client->oldOrigin );

	// NERVE - SMF
	pm.gametype = g_gametype.integer;
	pm.ltChargeTime = g_LTChargeTime.integer;
	pm.soldierChargeTime = g_soldierChargeTime.integer;
	pm.engineerChargeTime = g_engineerChargeTime.integer;
	pm.medicChargeTime = g_medicChargeTime.integer;
	// -NERVE - SMF

	monsterslick = Pmove( &pm );

	if ( monsterslick && !( ent->flags & FL_NO_MONSTERSLICK ) ) {
		//vec3_t	dir;
		//vec3_t	kvel;
		//vec3_t	forward;
		// TTimo gcc: might be used unitialized in this function
		float angle = 0.0f;
		qboolean bogus = qfalse;

		// NE
		if ( ( monsterslick & SURF_MONSLICK_N ) && ( monsterslick & SURF_MONSLICK_E ) ) {
			angle = 45;
		}
		// NW
		else if ( ( monsterslick & SURF_MONSLICK_N ) && ( monsterslick & SURF_MONSLICK_W ) ) {
			angle = 135;
		}
		// N
		else if ( monsterslick & SURF_MONSLICK_N ) {
			angle = 90;
		}
		// SE
		else if ( ( monsterslick & SURF_MONSLICK_S ) && ( monsterslick & SURF_MONSLICK_E ) ) {
			angle = 315;
		}
		// SW
		else if ( ( monsterslick & SURF_MONSLICK_S ) && ( monsterslick & SURF_MONSLICK_W ) ) {
			angle = 225;
		}
		// S
		else if ( monsterslick & SURF_MONSLICK_S ) {
			angle = 270;
		}
		// E
		else if ( monsterslick & SURF_MONSLICK_E ) {
			angle = 0;
		}
		// W
		else if ( monsterslick & SURF_MONSLICK_W ) {
			angle = 180;
		} else
		{
			bogus = qtrue;
		}
	}

	// server cursor hints
	if ( ent->lastHintCheckTime < level.time ) {
		G_CheckForCursorHints( ent );

		ent->lastHintCheckTime = level.time + FRAMETIME;
	}

	// DHM - Nerve :: Set animMovetype to 1 if ducking
	if ( ent->client->ps.pm_flags & PMF_DUCKED ) {
		ent->s.animMovetype = 1;
	} else {
		ent->s.animMovetype = 0;
	}

	// save results of pmove
	if ( ent->client->ps.eventSequence != oldEventSequence ) {
		ent->eventTime = level.time;
		ent->r.eventTime = level.time;
	}

	// Ridah, fixes jittery zombie movement
	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 );
	}

	if ( !( ent->client->ps.eFlags & EF_FIRING ) ) {
		client->fireHeld = qfalse;      // for grapple
	}

//
//	// use the precise origin for linking
//	VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
//
//	// 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 );
	if ( !ent->client->noclip ) {
		G_TouchTriggers( ent );
	}

	// NOTE: now copy the exact origin over otherwise clients can be snapped into solid
	VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );

	// store the client's current position for antilag traces
	G_StoreClientPosition( ent );

	// 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;
//	client->latched_buttons |= client->buttons & ~client->oldbuttons;	// FIXME:? (SA) MP method (causes problems for us.  activate 'sticks')

	//----(SA)	added
	client->oldwbuttons = client->wbuttons;
	client->wbuttons = ucmd->wbuttons;
	client->latched_wbuttons = client->wbuttons & ~client->oldwbuttons;
//	client->latched_wbuttons |= client->wbuttons & ~client->oldwbuttons;	// FIXME:? (SA) MP method

	// Rafael - Activate
	// Ridah, made it a latched event (occurs on keydown only)
	if ( client->latched_buttons & BUTTON_ACTIVATE ) {
		Cmd_Activate_f( ent );
	}

	if ( ent->flags & FL_NOFATIGUE ) {
		ent->client->ps.sprintTime = 20000;
	}


	// check for respawning
	if ( client->ps.stats[STAT_HEALTH] <= 0 ) {

		// DHM - Nerve
		if ( g_gametype.integer >= GT_WOLF ) {
			WolfFindMedic( ent );
		}
		// dhm - end

		// wait for the attack button to be pressed
		if ( level.time > client->respawnTime ) {
			// forcerespawn is to prevent users from waiting out powerups
			if ( ( g_gametype.integer != GT_SINGLE_PLAYER ) &&
				 ( g_forcerespawn.integer > 0 ) &&
				 ( ( level.time - client->respawnTime ) > g_forcerespawn.integer * 1000 )  &&
				 ( !( ent->client->ps.pm_flags & PMF_LIMBO ) ) ) { // JPW NERVE
				// JPW NERVE
				if ( g_gametype.integer >= GT_WOLF ) {
					limbo( ent, qtrue );
				} else {
					respawn( ent );
				}
				// jpw
				return;
			}

			// DHM - Nerve :: Single player game respawns immediately as before,
			//				  but in multiplayer, require button press before respawn
			if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
				respawn( ent );
			}
			// NERVE - SMF - we want to only respawn on jump button now
			else if ( ( ucmd->upmove > 0 ) &&
					  ( !( ent->client->ps.pm_flags & PMF_LIMBO ) ) ) { // JPW NERVE
				// JPW NERVE
				if ( g_gametype.integer >= GT_WOLF ) {
					limbo( ent, qtrue );
				} else {
					respawn( ent );
				}
				// jpw
			}
			// dhm - Nerve :: end
			// NERVE - SMF - we want to immediately go to limbo mode if gibbed
			else if ( client->ps.stats[STAT_HEALTH] <= GIB_HEALTH && !( ent->client->ps.pm_flags & PMF_LIMBO ) ) {
				if ( g_gametype.integer >= GT_WOLF ) {
					limbo( ent, qfalse );
				} else {
					respawn( ent );
				}
			}
			// -NERVE - SMF
		}
		return;
	}

	// perform once-a-second actions
	ClientTimerActions( ent, msec );
}
Ejemplo n.º 10
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
		}
	}
}
Ejemplo n.º 11
0
/*
==============
AICast_UpdateVisibility
==============
*/
void AICast_UpdateVisibility( gentity_t *srcent, gentity_t *destent, qboolean shareVis, qboolean directview ) {
	cast_visibility_t   *vis, *ovis, *svis, oldvis;
	cast_state_t        *cs, *ocs;
	qboolean shareRange;
	int cnt, i;

	if ( destent->flags & FL_NOTARGET ) {
		return;
	}

	cs = AICast_GetCastState( srcent->s.number );
	ocs = AICast_GetCastState( destent->s.number );

	if ( cs->castScriptStatus.scriptNoSightTime >= level.time ) {
		return;     // absolutely no sight (or hear) information allowed

	}
	shareRange = ( VectorDistance( srcent->client->ps.origin, destent->client->ps.origin ) < AIVIS_SHARE_RANGE );

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

	vis->chase_marker_count = 0;

	if ( aicast_debug.integer == 1 ) {
		if ( !vis->visible_timestamp || vis->visible_timestamp < level.time - 5000 ) {
			if ( directview ) {
				G_Printf( "SIGHT (direct): %s sees %s\n", srcent->aiName, destent->aiName );
			} else {
				G_Printf( "SIGHT (non-direct/audible): %s sees %s\n", srcent->aiName, destent->aiName );
			}
		}
	}

	// trigger the sight event
	AICast_Sight( srcent, destent, vis->visible_timestamp );

	// update the values
	vis->lastcheck_timestamp = level.time;
	vis->visible_timestamp = level.time;
	VectorCopy( destent->client->ps.origin, vis->visible_pos );
	VectorCopy( destent->client->ps.velocity, vis->visible_vel );
	vis->lastcheck_health = destent->health - 1;

	// we may need to process this visibility at some point, even after they become not visible again
	vis->flags |= AIVIS_PROCESS_SIGHTING;

	if ( directview ) {
		vis->real_visible_timestamp = level.time;
		VectorCopy( destent->client->ps.origin, vis->real_visible_pos );
		vis->real_update_timestamp = level.time;
	}

	// if we are on fire, then run away from anything we see
	if ( cs->attributes[AGGRESSION] < 1.0 && srcent->s.onFireEnd > level.time && ( !destent->s.number || cs->dangerEntityValidTime < level.time + 2000 ) && !( cs->aiFlags & AIFL_NO_FLAME_DAMAGE ) ) {
		cs->dangerEntity = destent->s.number;
		VectorCopy( destent->r.currentOrigin, cs->dangerEntityPos );
		cs->dangerEntityValidTime = level.time + 5000;
		cs->dangerDist = 99999;
		cs->dangerEntityTimestamp = level.time;
	}

	// Look for reasons to make this character an enemy of ours

	// if they are an enemy and inside the detection radius, go hostile
	if ( !( vis->flags & AIVIS_ENEMY ) && !AICast_SameTeam( cs, destent->s.number ) ) {
		float idr;

		idr = cs->attributes[INNER_DETECTION_RADIUS];
		if ( cs->aiFlags & AIFL_ZOOMING ) {
			idr *= 10;
		}
		if ( !( vis->flags & AIVIS_ENEMY ) && VectorDistance( vis->visible_pos, g_entities[cs->entityNum].r.currentOrigin ) < idr ) {
			// RF, moved them over to AICast_ScanForEnemies()
			//AICast_ScriptEvent( cs, "enemysight", destent->aiName );
			vis->flags |= AIVIS_ENEMY;
		}
		// if we are in (or above) ALERT mode, then we now know this is an enemy
		else if ( cs->aiState >= AISTATE_ALERT ) {
			// RF, moved them over to AICast_ScanForEnemies()
			//AICast_ScriptEvent( cs, "enemysight", destent->aiName );
			vis->flags |= AIVIS_ENEMY;
		}
	}

	// if they are friendly, then we should help them out if they are in trouble
	if ( AICast_SameTeam( cs, destent->s.number ) && ( srcent->aiTeam == AITEAM_ALLIES || srcent->aiTeam == AITEAM_NAZI ) ) {
		// if they are dead, we should check them out
		if ( destent->health <= 0 ) {
			// if we haven't already checked them out
			if ( !( vis->flags & AIVIS_INSPECTED ) ) {
				vis->flags |= AIVIS_INSPECT;
			}
			// if they are mad, we should help, or at least act concerned
		} else if ( cs->aiState < AISTATE_COMBAT && ocs->aiState >= AISTATE_COMBAT && ocs->bs && ( ocs->enemyNum >= 0 ) ) {
			// if we haven't already checked them out
			if ( !( vis->flags & AIVIS_INSPECTED ) ) {
				vis->flags |= AIVIS_INSPECT;
			}
			// if they are alert, we should also go alert
		} else if ( cs->aiState < AISTATE_ALERT && ocs->aiState == AISTATE_ALERT && ocs->bs ) {
			AICast_StateChange( cs, AISTATE_ALERT );
		}
	}

	// if this is a friendly, then check them for hostile's that we currently haven't upgraded so

	if ( ( destent->health > 0 ) &&
		 ( srcent->aiTeam == destent->aiTeam ) && // only share with exact same team, and non-neutrals
		 ( srcent->aiTeam != AITEAM_NEUTRAL ) ) {
		ocs = AICast_GetCastState( destent->s.number );
		cnt = 0;
		//
		for ( i = 0; i < aicast_maxclients && cnt < level.numPlayingClients; i++ ) {
			if ( !g_entities[i].inuse ) {
				continue;
			}
			//
			cnt++;
			//
			if ( i == srcent->s.number ) {
				continue;
			}
			if ( i == destent->s.number ) {
				continue;
			}
			//
			ovis = &ocs->vislist[i];
			svis = &cs->vislist[i];
			//
			// if we are close to the friendly, then we should share their visibility info
			if ( destent->health > 0 && shareRange ) {
				oldvis = *svis;
				// if they have seen this character more recently than us, share
				if ( ( ovis->visible_timestamp > svis->visible_timestamp ) ||
					 ( ( ovis->visible_timestamp > level.time - 5000 ) && ( ovis->flags & AIVIS_ENEMY ) && !( svis->flags & AIVIS_ENEMY ) ) ) {
					// trigger an EVENT

					// trigger the sight event
					AICast_Sight( srcent, destent, ovis->visible_timestamp );

					// we may need to process this visibility at some point, even after they become not visible again
					svis->flags |= AIVIS_PROCESS_SIGHTING;

					// if we are sharing information about an enemy, then trigger a scripted event
					if ( !svis->real_visible_timestamp && ovis->real_visible_timestamp && ( ovis->flags & AIVIS_ENEMY ) ) {
						// setup conditions
						BG_UpdateConditionValue( ocs->entityNum, ANIM_COND_ENEMY_TEAM, g_entities[i].aiTeam, qfalse );
						// call the event
						BG_AnimScriptEvent( &g_entities[ocs->entityNum].client->ps, ANIM_ET_INFORM_FRIENDLY_OF_ENEMY, qfalse, qfalse );
					}
					// copy the whole structure
					*svis = *ovis;
					// minus the flags
					svis->flags = oldvis.flags;
					// keep our sight time if it's sooner
					if ( oldvis.visible_timestamp > ovis->visible_timestamp ) {
						svis->visible_timestamp = oldvis.visible_timestamp;
					}
					// check to see if we just made this character an enemy of ours
					if ( /*(cs->aiState == AISTATE_COMBAT) &&*/ ( ovis->flags & AIVIS_ENEMY ) && !( oldvis.flags & AIVIS_ENEMY ) ) {
						svis->flags |= AIVIS_ENEMY;
						if ( !( cs->vislist[i].flags & AIVIS_SIGHT_SCRIPT_CALLED ) ) {
							AICast_ScriptEvent( cs, "enemysight", g_entities[i].aiName );
							cs->vislist[i].flags |= AIVIS_SIGHT_SCRIPT_CALLED;
							if ( !( cs->aiFlags & AIFL_DENYACTION ) ) {
								G_AddEvent( srcent, EV_GENERAL_SOUND, G_SoundIndex( aiDefaults[cs->aiCharacter].soundScripts[SIGHTSOUNDSCRIPT] ) );
							}
						}
					}
				}
			} else {
				// if either of us haven't seen this character yet, then ignore it
				if ( !svis->visible_timestamp || !ovis->visible_timestamp ) {
					continue;
				}
			}
			//
			// if they have marked this character as hostile, then we should also
			if ( ( cs->aiState == AISTATE_COMBAT ) && AICast_HostileEnemy( ocs, i ) && !AICast_HostileEnemy( cs, i ) ) {
				if ( !( cs->vislist[i].flags & AIVIS_SIGHT_SCRIPT_CALLED ) ) {
					AICast_ScriptEvent( cs, "enemysight", g_entities[i].aiName );
					cs->vislist[i].flags |= AIVIS_SIGHT_SCRIPT_CALLED;
					if ( !( cs->aiFlags & AIFL_DENYACTION ) ) {
						G_AddEvent( srcent, EV_GENERAL_SOUND, G_SoundIndex( aiDefaults[cs->aiCharacter].soundScripts[SIGHTSOUNDSCRIPT] ) );
					}
				}
				svis->flags |= AIVIS_ENEMY;
			}
		}
	}
}