Files
DoomRTX/neo/doom3/game/ai/AI_events.cpp
T
2026-05-22 22:26:19 -07:00

3714 lines
82 KiB
C++

/*
===========================================================================
IceTech GPL Source Code
Copyright (C) 2026 Justin Marshall
This file is part of the IceTech GPL Source Code (?IceTech Source Code?).
IceTech Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
IceTech Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with IceTech Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the IceTech Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the IceTech Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing Justin Marshall, justinmarshall20@gmail.com
===========================================================================
*/
#include "precompiled.h"
#pragma hdrstop
#include "../Game_local.h"
/***********************************************************************
AI Events
***********************************************************************/
const idEventDef AI_FindEnemy("findEnemy", "d", 'e');
const idEventDef AI_FindEnemyAI("findEnemyAI", "d", 'e');
const idEventDef AI_FindEnemyInCombatNodes("findEnemyInCombatNodes", NULL, 'e');
const idEventDef AI_ClosestReachableEnemyOfEntity("closestReachableEnemyOfEntity", "E", 'e');
const idEventDef AI_HeardSound("heardSound", "d", 'e');
const idEventDef AI_SetEnemy("setEnemy", "E");
const idEventDef AI_ClearEnemy("clearEnemy");
const idEventDef AI_MuzzleFlash("muzzleFlash", "s");
const idEventDef AI_CreateMissile("createMissile", "s", 'e');
const idEventDef AI_AttackMissile("attackMissile", "s", 'e');
const idEventDef AI_FireMissileAtTarget("fireMissileAtTarget", "ss", 'e');
const idEventDef AI_LaunchMissile("launchMissile", "vv", 'e');
const idEventDef AI_AttackMelee("attackMelee", "s", 'd');
const idEventDef AI_DirectDamage("directDamage", "es");
const idEventDef AI_RadiusDamageFromJoint("radiusDamageFromJoint", "ss");
const idEventDef AI_BeginAttack("attackBegin", "s");
const idEventDef AI_EndAttack("attackEnd");
const idEventDef AI_MeleeAttackToJoint("meleeAttackToJoint", "ss", 'd');
const idEventDef AI_RandomPath("randomPath", NULL, 'e');
const idEventDef AI_CanBecomeSolid("canBecomeSolid", NULL, 'f');
const idEventDef AI_BecomeSolid("becomeSolid");
const idEventDef AI_BecomeRagdoll("becomeRagdoll", NULL, 'd');
const idEventDef AI_StopRagdoll("stopRagdoll");
const idEventDef AI_SetHealth("setHealth", "f");
const idEventDef AI_GetHealth("getHealth", NULL, 'f');
const idEventDef AI_AllowDamage("allowDamage");
const idEventDef AI_IgnoreDamage("ignoreDamage");
const idEventDef AI_GetCurrentYaw("getCurrentYaw", NULL, 'f');
const idEventDef AI_TurnTo("turnTo", "f");
const idEventDef AI_TurnToPos("turnToPos", "v");
const idEventDef AI_TurnToEntity("turnToEntity", "E");
const idEventDef AI_MoveStatus("moveStatus", NULL, 'd');
const idEventDef AI_StopMove("stopMove");
const idEventDef AI_MoveToCover("moveToCover");
const idEventDef AI_MoveToEnemy("moveToEnemy");
const idEventDef AI_MoveToEnemyHeight("moveToEnemyHeight");
const idEventDef AI_MoveOutOfRange("moveOutOfRange", "ef");
const idEventDef AI_MoveToAttackPosition("moveToAttackPosition", "es");
const idEventDef AI_Wander("wander");
const idEventDef AI_MoveToEntity("moveToEntity", "e");
const idEventDef AI_MoveToPosition("moveToPosition", "v");
const idEventDef AI_SlideTo("slideTo", "vf");
const idEventDef AI_FacingIdeal("facingIdeal", NULL, 'd');
const idEventDef AI_FaceEnemy("faceEnemy");
const idEventDef AI_FaceEntity("faceEntity", "E");
const idEventDef AI_GetCombatNode("getCombatNode", NULL, 'e');
const idEventDef AI_EnemyInCombatCone("enemyInCombatCone", "Ed", 'd');
const idEventDef AI_WaitMove("waitMove");
const idEventDef AI_GetJumpVelocity("getJumpVelocity", "vff", 'v');
const idEventDef AI_EntityInAttackCone("entityInAttackCone", "E", 'd');
const idEventDef AI_CanSeeEntity("canSee", "E", 'd');
const idEventDef AI_SetTalkTarget("setTalkTarget", "E");
const idEventDef AI_GetTalkTarget("getTalkTarget", NULL, 'e');
const idEventDef AI_SetTalkState("setTalkState", "d");
const idEventDef AI_EnemyRange("enemyRange", NULL, 'f');
const idEventDef AI_EnemyRange2D("enemyRange2D", NULL, 'f');
const idEventDef AI_GetEnemy("getEnemy", NULL, 'e');
const idEventDef AI_GetEnemyPos("getEnemyPos", NULL, 'v');
const idEventDef AI_GetEnemyEyePos("getEnemyEyePos", NULL, 'v');
const idEventDef AI_PredictEnemyPos("predictEnemyPos", "f", 'v');
const idEventDef AI_CanHitEnemy("canHitEnemy", NULL, 'd');
const idEventDef AI_CanHitEnemyFromAnim("canHitEnemyFromAnim", "s", 'd');
const idEventDef AI_CanHitEnemyFromJoint("canHitEnemyFromJoint", "s", 'd');
const idEventDef AI_EnemyPositionValid("enemyPositionValid", NULL, 'd');
const idEventDef AI_ChargeAttack("chargeAttack", "s");
const idEventDef AI_TestChargeAttack("testChargeAttack", NULL, 'f');
const idEventDef AI_TestMoveToPosition("testMoveToPosition", "v", 'd');
const idEventDef AI_TestAnimMoveTowardEnemy("testAnimMoveTowardEnemy", "s", 'd');
const idEventDef AI_TestAnimMove("testAnimMove", "s", 'd');
const idEventDef AI_TestMeleeAttack("testMeleeAttack", NULL, 'd');
const idEventDef AI_TestAnimAttack("testAnimAttack", "s", 'd');
const idEventDef AI_Shrivel("shrivel", "f");
const idEventDef AI_Burn("burn");
const idEventDef AI_ClearBurn("clearBurn");
const idEventDef AI_PreBurn("preBurn");
const idEventDef AI_SetSmokeVisibility("setSmokeVisibility", "dd");
const idEventDef AI_NumSmokeEmitters("numSmokeEmitters", NULL, 'd');
const idEventDef AI_WaitAction("waitAction", "s");
const idEventDef AI_StopThinking("stopThinking");
const idEventDef AI_GetTurnDelta("getTurnDelta", NULL, 'f');
const idEventDef AI_GetMoveType("getMoveType", NULL, 'd');
const idEventDef AI_SetMoveType("setMoveType", "d");
const idEventDef AI_SaveMove("saveMove");
const idEventDef AI_RestoreMove("restoreMove");
const idEventDef AI_AllowMovement("allowMovement", "f");
const idEventDef AI_JumpFrame("<jumpframe>");
const idEventDef AI_EnableClip("enableClip");
const idEventDef AI_DisableClip("disableClip");
const idEventDef AI_EnableGravity("enableGravity");
const idEventDef AI_DisableGravity("disableGravity");
const idEventDef AI_EnableAFPush("enableAFPush");
const idEventDef AI_DisableAFPush("disableAFPush");
const idEventDef AI_SetFlySpeed("setFlySpeed", "f");
const idEventDef AI_SetFlyOffset("setFlyOffset", "d");
const idEventDef AI_ClearFlyOffset("clearFlyOffset");
const idEventDef AI_GetClosestHiddenTarget("getClosestHiddenTarget", "s", 'e');
const idEventDef AI_GetRandomTarget("getRandomTarget", "s", 'e');
const idEventDef AI_TravelDistanceToPoint("travelDistanceToPoint", "v", 'f');
const idEventDef AI_TravelDistanceToEntity("travelDistanceToEntity", "e", 'f');
const idEventDef AI_TravelDistanceBetweenPoints("travelDistanceBetweenPoints", "vv", 'f');
const idEventDef AI_TravelDistanceBetweenEntities("travelDistanceBetweenEntities", "ee", 'f');
const idEventDef AI_LookAtEntity("lookAt", "Ef");
const idEventDef AI_LookAtEnemy("lookAtEnemy", "f");
const idEventDef AI_SetJointMod("setBoneMod", "d");
const idEventDef AI_ThrowMoveable("throwMoveable");
const idEventDef AI_ThrowAF("throwAF");
const idEventDef AI_RealKill("<kill>");
const idEventDef AI_Kill("kill");
const idEventDef AI_WakeOnFlashlight("wakeOnFlashlight", "d");
const idEventDef AI_LocateEnemy("locateEnemy");
const idEventDef AI_KickObstacles("kickObstacles", "Ef");
const idEventDef AI_GetObstacle("getObstacle", NULL, 'e');
const idEventDef AI_PushPointIntoNAV("pushPointIntoAAS", "v", 'v');
const idEventDef AI_GetTurnRate("getTurnRate", NULL, 'f');
const idEventDef AI_SetTurnRate("setTurnRate", "f");
const idEventDef AI_AnimTurn("animTurn", "f");
const idEventDef AI_AllowHiddenMovement("allowHiddenMovement", "d");
const idEventDef AI_TriggerParticles("triggerParticles", "s");
const idEventDef AI_FindActorsInBounds("findActorsInBounds", "vv", 'e');
const idEventDef AI_CanReachPosition("canReachPosition", "v", 'd');
const idEventDef AI_CanReachEntity("canReachEntity", "E", 'd');
const idEventDef AI_CanReachEnemy("canReachEnemy", NULL, 'd');
const idEventDef AI_GetReachableEntityPosition("getReachableEntityPosition", "e", 'v');
CLASS_DECLARATION(idActor, idAI)
EVENT(EV_Activate, idAI::Event_Activate)
EVENT(EV_Touch, idAI::Event_Touch)
EVENT(AI_FindEnemy, idAI::Event_FindEnemy)
EVENT(AI_FindEnemyAI, idAI::Event_FindEnemyAI)
EVENT(AI_FindEnemyInCombatNodes, idAI::Event_FindEnemyInCombatNodes)
EVENT(AI_ClosestReachableEnemyOfEntity, idAI::Event_ClosestReachableEnemyOfEntity)
EVENT(AI_HeardSound, idAI::Event_HeardSound)
EVENT(AI_SetEnemy, idAI::Event_SetEnemy)
EVENT(AI_ClearEnemy, idAI::Event_ClearEnemy)
EVENT(AI_MuzzleFlash, idAI::Event_MuzzleFlash)
EVENT(AI_CreateMissile, idAI::Event_CreateMissile)
EVENT(AI_AttackMissile, idAI::Event_AttackMissile)
EVENT(AI_FireMissileAtTarget, idAI::Event_FireMissileAtTarget)
EVENT(AI_LaunchMissile, idAI::Event_LaunchMissile)
EVENT(AI_AttackMelee, idAI::Event_AttackMelee)
EVENT(AI_DirectDamage, idAI::Event_DirectDamage)
EVENT(AI_RadiusDamageFromJoint, idAI::Event_RadiusDamageFromJoint)
EVENT(AI_BeginAttack, idAI::Event_BeginAttack)
EVENT(AI_EndAttack, idAI::Event_EndAttack)
EVENT(AI_MeleeAttackToJoint, idAI::Event_MeleeAttackToJoint)
EVENT(AI_RandomPath, idAI::Event_RandomPath)
EVENT(AI_CanBecomeSolid, idAI::Event_CanBecomeSolid)
EVENT(AI_BecomeSolid, idAI::Event_BecomeSolid)
EVENT(EV_BecomeNonSolid, idAI::Event_BecomeNonSolid)
EVENT(AI_BecomeRagdoll, idAI::Event_BecomeRagdoll)
EVENT(AI_StopRagdoll, idAI::Event_StopRagdoll)
EVENT(AI_SetHealth, idAI::Event_SetHealth)
EVENT(AI_GetHealth, idAI::Event_GetHealth)
EVENT(AI_AllowDamage, idAI::Event_AllowDamage)
EVENT(AI_IgnoreDamage, idAI::Event_IgnoreDamage)
EVENT(AI_GetCurrentYaw, idAI::Event_GetCurrentYaw)
EVENT(AI_TurnTo, idAI::Event_TurnTo)
EVENT(AI_TurnToPos, idAI::Event_TurnToPos)
EVENT(AI_TurnToEntity, idAI::Event_TurnToEntity)
EVENT(AI_MoveStatus, idAI::Event_MoveStatus)
EVENT(AI_StopMove, idAI::Event_StopMove)
EVENT(AI_MoveToCover, idAI::Event_MoveToCover)
EVENT(AI_MoveToEnemy, idAI::Event_MoveToEnemy)
EVENT(AI_MoveToEnemyHeight, idAI::Event_MoveToEnemyHeight)
EVENT(AI_MoveOutOfRange, idAI::Event_MoveOutOfRange)
EVENT(AI_MoveToAttackPosition, idAI::Event_MoveToAttackPosition)
EVENT(AI_Wander, idAI::Event_Wander)
EVENT(AI_MoveToEntity, idAI::Event_MoveToEntity)
EVENT(AI_MoveToPosition, idAI::Event_MoveToPosition)
EVENT(AI_SlideTo, idAI::Event_SlideTo)
EVENT(AI_FacingIdeal, idAI::Event_FacingIdeal)
EVENT(AI_FaceEnemy, idAI::Event_FaceEnemy)
EVENT(AI_FaceEntity, idAI::Event_FaceEntity)
EVENT(AI_WaitAction, idAI::Event_WaitAction)
EVENT(AI_GetCombatNode, idAI::Event_GetCombatNode)
EVENT(AI_EnemyInCombatCone, idAI::Event_EnemyInCombatCone)
EVENT(AI_WaitMove, idAI::Event_WaitMove)
EVENT(AI_GetJumpVelocity, idAI::Event_GetJumpVelocity)
EVENT(AI_EntityInAttackCone, idAI::Event_EntityInAttackCone)
EVENT(AI_CanSeeEntity, idAI::Event_CanSeeEntity)
EVENT(AI_SetTalkTarget, idAI::Event_SetTalkTarget)
EVENT(AI_GetTalkTarget, idAI::Event_GetTalkTarget)
EVENT(AI_SetTalkState, idAI::Event_SetTalkState)
EVENT(AI_EnemyRange, idAI::Event_EnemyRange)
EVENT(AI_EnemyRange2D, idAI::Event_EnemyRange2D)
EVENT(AI_GetEnemy, idAI::Event_GetEnemy)
EVENT(AI_GetEnemyPos, idAI::Event_GetEnemyPos)
EVENT(AI_GetEnemyEyePos, idAI::Event_GetEnemyEyePos)
EVENT(AI_PredictEnemyPos, idAI::Event_PredictEnemyPos)
EVENT(AI_CanHitEnemy, idAI::Event_CanHitEnemy)
EVENT(AI_CanHitEnemyFromAnim, idAI::Event_CanHitEnemyFromAnim)
EVENT(AI_CanHitEnemyFromJoint, idAI::Event_CanHitEnemyFromJoint)
EVENT(AI_EnemyPositionValid, idAI::Event_EnemyPositionValid)
EVENT(AI_ChargeAttack, idAI::Event_ChargeAttack)
EVENT(AI_TestChargeAttack, idAI::Event_TestChargeAttack)
EVENT(AI_TestAnimMoveTowardEnemy, idAI::Event_TestAnimMoveTowardEnemy)
EVENT(AI_TestAnimMove, idAI::Event_TestAnimMove)
EVENT(AI_TestMoveToPosition, idAI::Event_TestMoveToPosition)
EVENT(AI_TestMeleeAttack, idAI::Event_TestMeleeAttack)
EVENT(AI_TestAnimAttack, idAI::Event_TestAnimAttack)
EVENT(AI_Shrivel, idAI::Event_Shrivel)
EVENT(AI_Burn, idAI::Event_Burn)
EVENT(AI_PreBurn, idAI::Event_PreBurn)
EVENT(AI_SetSmokeVisibility, idAI::Event_SetSmokeVisibility)
EVENT(AI_NumSmokeEmitters, idAI::Event_NumSmokeEmitters)
EVENT(AI_ClearBurn, idAI::Event_ClearBurn)
EVENT(AI_StopThinking, idAI::Event_StopThinking)
EVENT(AI_GetTurnDelta, idAI::Event_GetTurnDelta)
EVENT(AI_GetMoveType, idAI::Event_GetMoveType)
EVENT(AI_SetMoveType, idAI::Event_SetMoveType)
EVENT(AI_SaveMove, idAI::Event_SaveMove)
EVENT(AI_RestoreMove, idAI::Event_RestoreMove)
EVENT(AI_AllowMovement, idAI::Event_AllowMovement)
EVENT(AI_JumpFrame, idAI::Event_JumpFrame)
EVENT(AI_EnableClip, idAI::Event_EnableClip)
EVENT(AI_DisableClip, idAI::Event_DisableClip)
EVENT(AI_EnableGravity, idAI::Event_EnableGravity)
EVENT(AI_DisableGravity, idAI::Event_DisableGravity)
EVENT(AI_EnableAFPush, idAI::Event_EnableAFPush)
EVENT(AI_DisableAFPush, idAI::Event_DisableAFPush)
EVENT(AI_SetFlySpeed, idAI::Event_SetFlySpeed)
EVENT(AI_SetFlyOffset, idAI::Event_SetFlyOffset)
EVENT(AI_ClearFlyOffset, idAI::Event_ClearFlyOffset)
EVENT(AI_GetClosestHiddenTarget, idAI::Event_GetClosestHiddenTarget)
EVENT(AI_GetRandomTarget, idAI::Event_GetRandomTarget)
EVENT(AI_TravelDistanceToPoint, idAI::Event_TravelDistanceToPoint)
EVENT(AI_TravelDistanceToEntity, idAI::Event_TravelDistanceToEntity)
EVENT(AI_TravelDistanceBetweenPoints, idAI::Event_TravelDistanceBetweenPoints)
EVENT(AI_TravelDistanceBetweenEntities, idAI::Event_TravelDistanceBetweenEntities)
EVENT(AI_LookAtEntity, idAI::Event_LookAtEntity)
EVENT(AI_LookAtEnemy, idAI::Event_LookAtEnemy)
EVENT(AI_SetJointMod, idAI::Event_SetJointMod)
EVENT(AI_ThrowMoveable, idAI::Event_ThrowMoveable)
EVENT(AI_ThrowAF, idAI::Event_ThrowAF)
EVENT(EV_GetAngles, idAI::Event_GetAngles)
EVENT(EV_SetAngles, idAI::Event_SetAngles)
EVENT(AI_RealKill, idAI::Event_RealKill)
EVENT(AI_Kill, idAI::Event_Kill)
EVENT(AI_WakeOnFlashlight, idAI::Event_WakeOnFlashlight)
EVENT(AI_LocateEnemy, idAI::Event_LocateEnemy)
EVENT(AI_KickObstacles, idAI::Event_KickObstacles)
EVENT(AI_GetObstacle, idAI::Event_GetObstacle)
EVENT(AI_PushPointIntoNAV, idAI::Event_PushPointIntoNAV)
EVENT(AI_GetTurnRate, idAI::Event_GetTurnRate)
EVENT(AI_SetTurnRate, idAI::Event_SetTurnRate)
EVENT(AI_AnimTurn, idAI::Event_AnimTurn)
EVENT(AI_AllowHiddenMovement, idAI::Event_AllowHiddenMovement)
EVENT(AI_TriggerParticles, idAI::Event_TriggerParticles)
EVENT(AI_FindActorsInBounds, idAI::Event_FindActorsInBounds)
EVENT(AI_CanReachPosition, idAI::Event_CanReachPosition)
EVENT(AI_CanReachEntity, idAI::Event_CanReachEntity)
EVENT(AI_CanReachEnemy, idAI::Event_CanReachEnemy)
EVENT(AI_GetReachableEntityPosition, idAI::Event_GetReachableEntityPosition)
END_CLASS
/*
=====================
idAI::Touch
=====================
*/
void idAI::Touch(idEntity* other, trace_t* trace) {
if (!enemy.GetEntity() && !other->fl.notarget && (ReactionTo(other) & ATTACK_ON_ACTIVATE)) {
Activate(other);
}
AI_PUSHED = true;
}
/*
=====================
idAI::FindEnemy
=====================
*/
idEntity* idAI::FindEnemy(int useFOV) {
int i;
idEntity* ent;
idActor* actor;
if (gameLocal.InPlayerPVS(this)) {
for (i = 0; i < gameLocal.numClients; i++) {
ent = gameLocal.entities[i];
if (!ent || !ent->IsType(idActor::Type)) {
continue;
}
actor = static_cast<idActor*>(ent);
if ((actor->health <= 0) || !(ReactionTo(actor) & ATTACK_ON_SIGHT)) {
continue;
}
if (CanSee(actor, useFOV != 0)) {
return actor;
}
}
}
return NULL;
}
/*
=====================
idAI::FindEnemyAI
=====================
*/
idEntity* idAI::FindEnemyAI(int useFOV) {
idEntity* ent;
idActor* actor;
idActor* bestEnemy;
float bestDist;
float dist;
idVec3 delta;
pvsHandle_t pvs;
pvs = gameLocal.pvs.SetupCurrentPVS(GetPVSAreas(), GetNumPVSAreas());
bestDist = idMath::INFINITY;
bestEnemy = NULL;
for (ent = gameLocal.activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next()) {
if (ent->fl.hidden || ent->fl.isDormant || !ent->IsType(idActor::Type)) {
continue;
}
actor = static_cast<idActor*>(ent);
if ((actor->health <= 0) || !(ReactionTo(actor) & ATTACK_ON_SIGHT)) {
continue;
}
if (!gameLocal.pvs.InCurrentPVS(pvs, actor->GetPVSAreas(), actor->GetNumPVSAreas())) {
continue;
}
delta = physicsObj.GetOrigin() - actor->GetPhysics()->GetOrigin();
dist = delta.LengthSqr();
if ((dist < bestDist) && CanSee(actor, useFOV != 0)) {
bestDist = dist;
bestEnemy = actor;
}
}
gameLocal.pvs.FreeCurrentPVS(pvs);
return bestEnemy;
}
/*
=====================
idAI::FindEnemyInCombatNodes
=====================
*/
idEntity* idAI::FindEnemyInCombatNodes(void) {
int i, j;
idCombatNode* node;
idEntity* ent;
idEntity* targetEnt;
idActor* actor;
if (!gameLocal.InPlayerPVS(this)) {
// don't locate the player when we're not in his PVS
return NULL;
}
for (i = 0; i < gameLocal.numClients; i++) {
ent = gameLocal.entities[i];
if (!ent || !ent->IsType(idActor::Type)) {
continue;
}
actor = static_cast<idActor*>(ent);
if ((actor->health <= 0) || !(ReactionTo(actor) & ATTACK_ON_SIGHT)) {
continue;
}
for (j = 0; j < targets.Num(); j++) {
targetEnt = targets[j].GetEntity();
if (!targetEnt || !targetEnt->IsType(idCombatNode::Type)) {
continue;
}
node = static_cast<idCombatNode*>(targetEnt);
if (!node->IsDisabled() && node->EntityInView(actor, actor->GetPhysics()->GetOrigin())) {
return actor;
}
}
}
return NULL;
}
/*
=====================
idAI::ClosestReachableEnemyOfEntity
=====================
*/
idEntity* idAI::ClosestReachableEnemyOfEntity(idEntity* team_mate) {
idActor* actor;
idActor* ent;
idActor* bestEnt;
float bestDistSquared;
float distSquared;
idVec3 delta;
int areaNum;
int enemyAreaNum;
aasPath_t path;
if (!team_mate->IsType(idActor::Type)) {
gameLocal.Error("Entity '%s' is not an AI character or player", team_mate->GetName());
}
actor = static_cast<idActor*>(team_mate);
const idVec3& origin = physicsObj.GetOrigin();
areaNum = PointReachableAreaNum(origin);
bestDistSquared = idMath::INFINITY;
bestEnt = NULL;
for (ent = actor->enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next()) {
if (ent->fl.hidden) {
continue;
}
delta = ent->GetPhysics()->GetOrigin() - origin;
distSquared = delta.LengthSqr();
if (distSquared < bestDistSquared) {
const idVec3& enemyPos = ent->GetPhysics()->GetOrigin();
enemyAreaNum = PointReachableAreaNum(enemyPos);
if ((areaNum != 0) && PathToGoal(path, areaNum, origin, enemyAreaNum, enemyPos)) {
bestEnt = ent;
bestDistSquared = distSquared;
}
}
}
return bestEnt;
}
/*
=====================
idAI::HeardSound
=====================
*/
idEntity* idAI::HeardSound(int ignore_team) {
// check if we heard any sounds in the last frame
idActor* actor = gameLocal.GetAlertEntity();
if (actor && (!ignore_team || (ReactionTo(actor) & ATTACK_ON_SIGHT)) && gameLocal.InPlayerPVS(this)) {
idVec3 pos = actor->GetPhysics()->GetOrigin();
idVec3 org = physicsObj.GetOrigin();
float dist = (pos - org).LengthSqr();
if (dist < Square(AI_HEARING_RANGE)) {
return actor;
}
}
return NULL;
}
/*
=====================
idAI::SetEnemy
=====================
*/
void idAI::SetEnemy(idEntity* ent) {
if (!ent) {
ClearEnemy();
}
else if (!ent->IsType(idActor::Type)) {
gameLocal.Error("'%s' is not an idActor (player or ai controlled character)", ent->name.c_str());
}
else {
SetEnemy(static_cast<idActor*>(ent));
}
}
/*
=====================
idAI::MuzzleFlash
=====================
*/
void idAI::MuzzleFlash(const char* jointname) {
idVec3 muzzle;
idMat3 axis;
// Match the idWeapon behavior: the short-lived muzzle light should attach
// to the same joint the script fired from, not blindly to a hardcoded joint.
if (jointname && jointname[0]) {
jointHandle_t joint = animator.GetJointHandle(jointname);
if (joint != INVALID_JOINT) {
flashJointWorld = joint;
}
}
GetMuzzle(jointname, muzzle, axis);
TriggerWeaponEffects(muzzle);
}
/*
=====================
idAI::CreateMissile
=====================
*/
idEntity* idAI::CreateMissile(const char* jointname) {
idVec3 muzzle;
idMat3 axis;
if (!projectileDef) {
gameLocal.Warning("%s (%s) doesn't have a projectile specified", name.c_str(), GetEntityDefName());
return NULL;
}
GetMuzzle(jointname, muzzle, axis);
CreateProjectile(muzzle, viewAxis[0] * physicsObj.GetGravityAxis());
if (projectile.GetEntity()) {
if (!jointname || !jointname[0]) {
projectile.GetEntity()->Bind(this, true);
}
else {
projectile.GetEntity()->BindToJoint(this, jointname, true);
}
}
return projectile.GetEntity();
}
/*
=====================
idAI::AttackMissile
=====================
*/
idEntity* idAI::AttackMissile(const char* jointname) {
idProjectile* proj;
proj = LaunchProjectile(jointname, enemy.GetEntity(), true);
return proj;
}
/*
=====================
idAI::FireMissileAtTarget
=====================
*/
idEntity* idAI::FireMissileAtTarget(const char* jointname, const char* targetname) {
idEntity* aent;
idProjectile* proj;
aent = gameLocal.FindEntity(targetname);
if (!aent) {
gameLocal.Warning("Entity '%s' not found for 'fireMissileAtTarget'", targetname);
}
proj = LaunchProjectile(jointname, aent, false);
return proj;
}
/*
=====================
idAI::LaunchMissile
=====================
*/
idEntity* idAI::LaunchMissile(const idVec3& org, const idAngles& ang) {
idVec3 start;
trace_t tr;
idBounds projBounds;
const idClipModel* projClip;
idMat3 axis;
float distance;
if (!projectileDef) {
gameLocal.Warning("%s (%s) doesn't have a projectile specified", name.c_str(), GetEntityDefName());
return NULL;
}
axis = ang.ToMat3();
if (!projectile.GetEntity()) {
CreateProjectile(org, axis[0]);
}
// make sure the projectile starts inside the monster bounding box
const idBounds& ownerBounds = physicsObj.GetAbsBounds();
projClip = projectile.GetEntity()->GetPhysics()->GetClipModel();
projBounds = projClip->GetBounds().Rotate(projClip->GetAxis());
// check if the owner bounds is bigger than the projectile bounds
if (((ownerBounds[1][0] - ownerBounds[0][0]) > (projBounds[1][0] - projBounds[0][0])) &&
((ownerBounds[1][1] - ownerBounds[0][1]) > (projBounds[1][1] - projBounds[0][1])) &&
((ownerBounds[1][2] - ownerBounds[0][2]) > (projBounds[1][2] - projBounds[0][2]))) {
if ((ownerBounds - projBounds).RayIntersection(org, viewAxis[0], distance)) {
start = org + distance * viewAxis[0];
}
else {
start = ownerBounds.GetCenter();
}
}
else {
// projectile bounds bigger than the owner bounds, so just start it from the center
start = ownerBounds.GetCenter();
}
gameLocal.clip.Translation(tr, start, org, projClip, projClip->GetAxis(), MASK_SHOT_RENDERMODEL, this);
// launch the projectile
idProjectile* launchedProjectile = projectile.GetEntity();
launchedProjectile->Launch(tr.endpos, axis[0], vec3_origin);
projectile = NULL;
TriggerWeaponEffects(tr.endpos);
lastAttackTime = gameLocal.time;
return launchedProjectile;
}
/*
=====================
idAI::DirectDamage
=====================
*/
void idAI::DirectDamage(idEntity* damageTarget, const char* damageDefName) {
DirectDamage(damageDefName, damageTarget);
}
/*
=====================
idAI::RadiusDamageFromJoint
=====================
*/
void idAI::RadiusDamageFromJoint(const char* jointname, const char* damageDefName) {
jointHandle_t joint;
idVec3 org;
idMat3 axis;
if (!jointname || !jointname[0]) {
org = physicsObj.GetOrigin();
}
else {
joint = animator.GetJointHandle(jointname);
if (joint == INVALID_JOINT) {
gameLocal.Error("Unknown joint '%s' on %s", jointname, GetEntityDefName());
}
GetJointWorldTransform(joint, gameLocal.time, org, axis);
}
gameLocal.RadiusDamage(org, this, this, this, this, damageDefName);
}
/*
=====================
idAI::RandomPath
=====================
*/
idEntity* idAI::RandomPath(void) {
idPathCorner* path;
path = idPathCorner::RandomPath(this, NULL);
return path;
}
/*
=====================
idAI::MeleeAttackToJoint
=====================
*/
int idAI::MeleeAttackToJoint(const char* jointname, const char* meleeDefName) {
jointHandle_t joint;
idVec3 start;
idVec3 end;
idMat3 axis;
trace_t trace;
idEntity* hitEnt;
joint = animator.GetJointHandle(jointname);
if (joint == INVALID_JOINT) {
gameLocal.Error("Unknown joint '%s' on %s", jointname, GetEntityDefName());
}
animator.GetJointTransform(joint, gameLocal.time, end, axis);
end = physicsObj.GetOrigin() + (end + modelOffset) * viewAxis * physicsObj.GetGravityAxis();
start = GetEyePosition();
if (ai_debugMove.GetBool()) {
gameRenderWorld->DebugLine(colorYellow, start, end, gameLocal.msec);
}
gameLocal.clip.TranslationEntities(trace, start, end, NULL, mat3_identity, MASK_SHOT_BOUNDINGBOX, this);
if (trace.fraction < 1.0f) {
hitEnt = gameLocal.GetTraceEntity(trace);
if (hitEnt && hitEnt->IsType(idActor::Type)) {
DirectDamage(meleeDefName, hitEnt);
return true;
}
}
return false;
}
/*
=====================
idAI::CanBecomeSolid
=====================
*/
float idAI::CanBecomeSolid(void) {
int i;
int num;
idEntity* hit;
idClipModel* cm;
idClipModel* clipModels[MAX_GENTITIES];
num = gameLocal.clip.ClipModelsTouchingBounds(physicsObj.GetAbsBounds(), MASK_MONSTERSOLID, clipModels, MAX_GENTITIES);
for (i = 0; i < num; i++) {
cm = clipModels[i];
// don't check render entities
if (cm->IsRenderModel()) {
continue;
}
hit = cm->GetEntity();
if ((hit == this) || !hit->fl.takedamage) {
continue;
}
if (physicsObj.ClipContents(cm)) {
return false;
}
}
return true;
}
/*
=====================
idAI::BecomeSolid
=====================
*/
void idAI::BecomeSolid(void) {
physicsObj.EnableClip();
if (spawnArgs.GetBool("big_monster")) {
physicsObj.SetContents(0);
}
else if (use_combat_bbox) {
physicsObj.SetContents(CONTENTS_BODY | CONTENTS_SOLID);
}
else {
physicsObj.SetContents(CONTENTS_BODY);
}
physicsObj.GetClipModel()->Link(gameLocal.clip);
fl.takedamage = !spawnArgs.GetBool("noDamage");
}
/*
=====================
idAI::BecomeNonSolid
=====================
*/
void idAI::BecomeNonSolid(void) {
fl.takedamage = false;
physicsObj.SetContents(0);
physicsObj.GetClipModel()->Unlink();
}
/*
=====================
idAI::BecomeRagdoll
=====================
*/
int idAI::BecomeRagdoll(void) {
bool result;
result = StartRagdoll();
return result;
}
/*
=====================
idAI::StopRagdoll
=====================
*/
void idAI::StopRagdoll(void) {
idActor::StopRagdoll();
// set back the monster physics
SetPhysics(&physicsObj);
}
/*
=====================
idAI::SetHealth
=====================
*/
void idAI::SetHealth(float newHealth) {
health = newHealth;
fl.takedamage = true;
if (health > 0) {
AI_DEAD = false;
}
else {
AI_DEAD = true;
}
}
/*
=====================
idAI::GetHealth
=====================
*/
float idAI::GetHealth(void) {
return health;
}
/*
=====================
idAI::AllowDamage
=====================
*/
void idAI::AllowDamage(void) {
fl.takedamage = true;
}
/*
=====================
idAI::IgnoreDamage
=====================
*/
void idAI::IgnoreDamage(void) {
fl.takedamage = false;
}
/*
=====================
idAI::GetCurrentYaw
=====================
*/
float idAI::GetCurrentYaw(void) {
return current_yaw;
}
/*
=====================
idAI::TurnTo
=====================
*/
void idAI::TurnTo(float angle) {
TurnToward(angle);
}
/*
=====================
idAI::TurnToPos
=====================
*/
void idAI::TurnToPos(const idVec3& pos) {
TurnToward(pos);
}
/*
=====================
idAI::TurnToEntity
=====================
*/
void idAI::TurnToEntity(idEntity* ent) {
if (ent) {
TurnToward(ent->GetPhysics()->GetOrigin());
}
}
/*
=====================
idAI::MoveStatus
=====================
*/
int idAI::MoveStatus(void) {
return move.moveStatus;
}
/*
=====================
idAI::StopMove
=====================
*/
void idAI::StopMove(void) {
StopMove(MOVE_STATUS_DONE);
}
/*
=====================
idAI::MoveToCover
=====================
*/
void idAI::MoveToCover(void) {
idActor* enemyEnt = enemy.GetEntity();
StopMove(MOVE_STATUS_DEST_NOT_FOUND);
if (!enemyEnt || !MoveToCover(enemyEnt, lastVisibleEnemyPos)) {
return;
}
}
/*
=====================
idAI::SlideTo
=====================
*/
void idAI::SlideTo(const idVec3& pos, float time) {
SlideToPosition(pos, time);
}
/*
=====================
idAI::Wander
=====================
*/
void idAI::Wander(void) {
WanderAround();
}
/*
=====================
idAI::GetCombatNode
=====================
*/
idEntity* idAI::GetCombatNode(void) {
int i;
float dist;
idEntity* targetEnt;
idCombatNode* node;
float bestDist;
idCombatNode* bestNode;
idActor* enemyEnt = enemy.GetEntity();
if (!targets.Num()) {
// no combat nodes
return NULL;
}
if (!enemyEnt || !EnemyPositionValid()) {
// don't return a combat node if we don't have an enemy or
// if we can see he's not in the last place we saw him
return NULL;
}
// find the closest attack node that can see our enemy and is closer than our enemy
bestNode = NULL;
const idVec3& myPos = physicsObj.GetOrigin();
bestDist = (myPos - lastVisibleEnemyPos).LengthSqr();
for (i = 0; i < targets.Num(); i++) {
targetEnt = targets[i].GetEntity();
if (!targetEnt || !targetEnt->IsType(idCombatNode::Type)) {
continue;
}
node = static_cast<idCombatNode*>(targetEnt);
if (!node->IsDisabled() && node->EntityInView(enemyEnt, lastVisibleEnemyPos)) {
idVec3 org = node->GetPhysics()->GetOrigin();
dist = (myPos - org).LengthSqr();
if (dist < bestDist) {
bestNode = node;
bestDist = dist;
}
}
}
return bestNode;
}
/*
=====================
idAI::EnemyInCombatCone
=====================
*/
int idAI::EnemyInCombatCone(idEntity* ent, int use_current_enemy_location) {
idCombatNode* node;
bool result;
idActor* enemyEnt = enemy.GetEntity();
if (!targets.Num()) {
// no combat nodes
return false;
}
if (!enemyEnt) {
// have to have an enemy
return false;
}
if (!ent || !ent->IsType(idCombatNode::Type)) {
// not a combat node
return false;
}
node = static_cast<idCombatNode*>(ent);
if (use_current_enemy_location) {
const idVec3& pos = enemyEnt->GetPhysics()->GetOrigin();
result = node->EntityInView(enemyEnt, pos);
}
else {
result = node->EntityInView(enemyEnt, lastVisibleEnemyPos);
}
return result;
}
/*
=====================
idAI::GetJumpVelocity
=====================
*/
idVec3 idAI::GetJumpVelocity(const idVec3& pos, float speed, float max_height) {
idVec3 start;
idVec3 end;
idVec3 dir;
float dist;
bool result;
idEntity* enemyEnt = enemy.GetEntity();
if (!enemyEnt) {
return vec3_zero;
}
if (speed <= 0.0f) {
gameLocal.Error("Invalid speed. speed must be > 0.");
}
start = physicsObj.GetOrigin();
end = pos;
dir = end - start;
dist = dir.Normalize();
if (dist > 16.0f) {
dist -= 16.0f;
end -= dir * 16.0f;
}
result = PredictTrajectory(start, end, speed, physicsObj.GetGravity(), physicsObj.GetClipModel(), MASK_MONSTERSOLID, max_height, this, enemyEnt, ai_debugMove.GetBool() ? 4000 : 0, dir);
if (result) {
return dir * speed;
}
else {
return vec3_zero;
}
}
/*
=====================
idAI::EntityInAttackCone
=====================
*/
int idAI::EntityInAttackCone(idEntity* ent) {
float attack_cone;
idVec3 delta;
float yaw;
float relYaw;
if (!ent) {
return false;
}
delta = ent->GetPhysics()->GetOrigin() - GetEyePosition();
// get our gravity normal
const idVec3& gravityDir = GetPhysics()->GetGravityNormal();
// infinite vertical vision, so project it onto our orientation plane
delta -= gravityDir * (gravityDir * delta);
delta.Normalize();
yaw = delta.ToYaw();
attack_cone = spawnArgs.GetFloat("attack_cone", "70");
relYaw = idMath::AngleNormalize180(ideal_yaw - yaw);
if (idMath::Fabs(relYaw) < (attack_cone * 0.5f)) {
return true;
}
else {
return false;
}
}
/*
=====================
idAI::CanSeeEntity
=====================
*/
int idAI::CanSeeEntity(idEntity* ent) {
if (!ent) {
return false;
}
bool cansee = CanSee(ent, false);
return cansee;
}
/*
=====================
idAI::SetTalkTarget
=====================
*/
void idAI::SetTalkTarget(idEntity* target) {
if (target && !target->IsType(idActor::Type)) {
gameLocal.Error("Cannot set talk target to '%s'. Not a character or player.", target->GetName());
}
talkTarget = static_cast<idActor*>(target);
if (target) {
AI_TALK = true;
}
else {
AI_TALK = false;
}
}
/*
=====================
idAI::GetTalkTarget
=====================
*/
idEntity* idAI::GetTalkTarget(void) {
return talkTarget.GetEntity();
}
/*
=====================
idAI::SetTalkState
=====================
*/
void idAI::SetTalkState(int state) {
if ((state < 0) || (state >= NUM_TALK_STATES)) {
gameLocal.Error("Invalid talk state (%d)", state);
}
talk_state = static_cast<talkState_t>(state);
}
/*
=====================
idAI::EnemyRange
=====================
*/
float idAI::EnemyRange(void) {
float dist;
idActor* enemyEnt = enemy.GetEntity();
if (enemyEnt) {
dist = (enemyEnt->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin()).Length();
}
else {
// Just some really high number
dist = idMath::INFINITY;
}
return dist;
}
/*
=====================
idAI::EnemyRange2D
=====================
*/
float idAI::EnemyRange2D(void) {
float dist;
idActor* enemyEnt = enemy.GetEntity();
if (enemyEnt) {
dist = (enemyEnt->GetPhysics()->GetOrigin().ToVec2() - GetPhysics()->GetOrigin().ToVec2()).Length();
}
else {
// Just some really high number
dist = idMath::INFINITY;
}
return dist;
}
/*
=====================
idAI::GetEnemyPos
=====================
*/
idVec3 idAI::GetEnemyPos(void) {
return lastVisibleEnemyPos;
}
/*
=====================
idAI::GetEnemyEyePos
=====================
*/
idVec3 idAI::GetEnemyEyePos(void) {
return lastVisibleEnemyPos + lastVisibleEnemyEyeOffset;
}
/*
=====================
idAI::PredictEnemyPos
=====================
*/
idVec3 idAI::PredictEnemyPos(float time) {
predictedPath_t path;
idActor* enemyEnt = enemy.GetEntity();
// if no enemy set
if (!enemyEnt) {
return physicsObj.GetOrigin();
}
// predict the enemy movement
idAI::PredictPath(enemyEnt, aas, lastVisibleEnemyPos, enemyEnt->GetPhysics()->GetLinearVelocity(), SEC2MS(time), SEC2MS(time), (move.moveType == MOVETYPE_FLY) ? SE_BLOCKED : (SE_BLOCKED | SE_ENTER_LEDGE_AREA), path);
return path.endPos;
}
/*
=====================
idAI::CanHitEnemy
=====================
*/
int idAI::CanHitEnemy(void) {
trace_t tr;
idEntity* hit;
idActor* enemyEnt = enemy.GetEntity();
if (!AI_ENEMY_VISIBLE || !enemyEnt) {
return false;
}
// don't check twice per frame
if (gameLocal.time == lastHitCheckTime) {
return lastHitCheckResult;
}
lastHitCheckTime = gameLocal.time;
idVec3 toPos = enemyEnt->GetEyePosition();
idVec3 eye = GetEyePosition();
idVec3 dir;
// expand the ray out as far as possible so we can detect anything behind the enemy
dir = toPos - eye;
dir.Normalize();
toPos = eye + dir * MAX_WORLD_SIZE;
gameLocal.clip.TracePoint(tr, eye, toPos, MASK_SHOT_BOUNDINGBOX, this);
hit = gameLocal.GetTraceEntity(tr);
if (tr.fraction >= 1.0f || (hit == enemyEnt)) {
lastHitCheckResult = true;
}
else if ((tr.fraction < 1.0f) && (hit->IsType(idAI::Type)) &&
(static_cast<idAI*>(hit)->team != team)) {
lastHitCheckResult = true;
}
else {
lastHitCheckResult = false;
}
return lastHitCheckResult;
}
/*
=====================
idAI::CanHitEnemyFromAnim
=====================
*/
int idAI::CanHitEnemyFromAnim(const char* animname) {
int anim;
idVec3 dir;
idVec3 local_dir;
idVec3 fromPos;
idMat3 axis;
idVec3 start;
trace_t tr;
float distance;
idActor* enemyEnt = enemy.GetEntity();
if (!AI_ENEMY_VISIBLE || !enemyEnt) {
return false;
}
anim = GetAnim(ANIMCHANNEL_LEGS, animname);
if (!anim) {
return false;
}
// just do a ray test if close enough
if (enemyEnt->GetPhysics()->GetAbsBounds().IntersectsBounds(physicsObj.GetAbsBounds().Expand(16.0f))) {
return CanHitEnemy();
}
// calculate the world transform of the launch position
const idVec3& org = physicsObj.GetOrigin();
dir = lastVisibleEnemyPos - org;
physicsObj.GetGravityAxis().ProjectVector(dir, local_dir);
local_dir.z = 0.0f;
local_dir.ToVec2().Normalize();
axis = local_dir.ToMat3();
fromPos = physicsObj.GetOrigin() + missileLaunchOffset[anim] * axis;
if (projectileClipModel == NULL) {
CreateProjectileClipModel();
}
// check if the owner bounds is bigger than the projectile bounds
const idBounds& ownerBounds = physicsObj.GetAbsBounds();
const idBounds& projBounds = projectileClipModel->GetBounds();
if (((ownerBounds[1][0] - ownerBounds[0][0]) > (projBounds[1][0] - projBounds[0][0])) &&
((ownerBounds[1][1] - ownerBounds[0][1]) > (projBounds[1][1] - projBounds[0][1])) &&
((ownerBounds[1][2] - ownerBounds[0][2]) > (projBounds[1][2] - projBounds[0][2]))) {
if ((ownerBounds - projBounds).RayIntersection(org, viewAxis[0], distance)) {
start = org + distance * viewAxis[0];
}
else {
start = ownerBounds.GetCenter();
}
}
else {
// projectile bounds bigger than the owner bounds, so just start it from the center
start = ownerBounds.GetCenter();
}
gameLocal.clip.Translation(tr, start, fromPos, projectileClipModel, mat3_identity, MASK_SHOT_RENDERMODEL, this);
fromPos = tr.endpos;
if (GetAimDir(fromPos, enemy.GetEntity(), this, dir)) {
return true;
}
else {
return false;
}
}
/*
=====================
idAI::CanHitEnemyFromJoint
=====================
*/
int idAI::CanHitEnemyFromJoint(const char* jointname) {
trace_t tr;
idVec3 muzzle;
idMat3 axis;
idVec3 start;
float distance;
idActor* enemyEnt = enemy.GetEntity();
if (!AI_ENEMY_VISIBLE || !enemyEnt) {
return false;
}
// don't check twice per frame
if (gameLocal.time == lastHitCheckTime) {
return lastHitCheckResult;
}
lastHitCheckTime = gameLocal.time;
const idVec3& org = physicsObj.GetOrigin();
idVec3 toPos = enemyEnt->GetEyePosition();
jointHandle_t joint = animator.GetJointHandle(jointname);
if (joint == INVALID_JOINT) {
gameLocal.Error("Unknown joint '%s' on %s", jointname, GetEntityDefName());
}
animator.GetJointTransform(joint, gameLocal.time, muzzle, axis);
muzzle = org + (muzzle + modelOffset) * viewAxis * physicsObj.GetGravityAxis();
if (projectileClipModel == NULL) {
CreateProjectileClipModel();
}
// check if the owner bounds is bigger than the projectile bounds
const idBounds& ownerBounds = physicsObj.GetAbsBounds();
const idBounds& projBounds = projectileClipModel->GetBounds();
if (((ownerBounds[1][0] - ownerBounds[0][0]) > (projBounds[1][0] - projBounds[0][0])) &&
((ownerBounds[1][1] - ownerBounds[0][1]) > (projBounds[1][1] - projBounds[0][1])) &&
((ownerBounds[1][2] - ownerBounds[0][2]) > (projBounds[1][2] - projBounds[0][2]))) {
if ((ownerBounds - projBounds).RayIntersection(org, viewAxis[0], distance)) {
start = org + distance * viewAxis[0];
}
else {
start = ownerBounds.GetCenter();
}
}
else {
// projectile bounds bigger than the owner bounds, so just start it from the center
start = ownerBounds.GetCenter();
}
gameLocal.clip.Translation(tr, start, muzzle, projectileClipModel, mat3_identity, MASK_SHOT_BOUNDINGBOX, this);
muzzle = tr.endpos;
gameLocal.clip.Translation(tr, muzzle, toPos, projectileClipModel, mat3_identity, MASK_SHOT_BOUNDINGBOX, this);
if (tr.fraction >= 1.0f || (gameLocal.GetTraceEntity(tr) == enemyEnt)) {
lastHitCheckResult = true;
}
else {
lastHitCheckResult = false;
}
return lastHitCheckResult;
}
/*
=====================
idAI::ChargeAttack
=====================
*/
void idAI::ChargeAttack(const char* damageDef) {
idActor* enemyEnt = enemy.GetEntity();
StopMove(MOVE_STATUS_DEST_NOT_FOUND);
if (enemyEnt) {
idVec3 enemyOrg;
if (move.moveType == MOVETYPE_FLY) {
// position destination so that we're in the enemy's view
enemyOrg = enemyEnt->GetEyePosition();
enemyOrg -= enemyEnt->GetPhysics()->GetGravityNormal() * fly_offset;
}
else {
enemyOrg = enemyEnt->GetPhysics()->GetOrigin();
}
BeginAttack(damageDef);
DirectMoveToPosition(enemyOrg);
TurnToward(enemyOrg);
}
}
/*
=====================
idAI::TestChargeAttack
=====================
*/
float idAI::TestChargeAttack(void) {
trace_t trace;
idActor* enemyEnt = enemy.GetEntity();
predictedPath_t path;
idVec3 end;
if (!enemyEnt) {
return 0.0f;
}
if (move.moveType == MOVETYPE_FLY) {
// position destination so that we're in the enemy's view
end = enemyEnt->GetEyePosition();
end -= enemyEnt->GetPhysics()->GetGravityNormal() * fly_offset;
}
else {
end = enemyEnt->GetPhysics()->GetOrigin();
}
idAI::PredictPath(this, aas, physicsObj.GetOrigin(), end - physicsObj.GetOrigin(), 1000, 1000, (move.moveType == MOVETYPE_FLY) ? SE_BLOCKED : (SE_ENTER_OBSTACLE | SE_BLOCKED | SE_ENTER_LEDGE_AREA), path);
if (ai_debugMove.GetBool()) {
gameRenderWorld->DebugLine(colorGreen, physicsObj.GetOrigin(), end, gameLocal.msec);
gameRenderWorld->DebugBounds(path.endEvent == 0 ? colorYellow : colorRed, physicsObj.GetBounds(), end, gameLocal.msec);
}
if ((path.endEvent == 0) || (path.blockingEntity == enemyEnt)) {
idVec3 delta = end - physicsObj.GetOrigin();
float time = delta.LengthFast();
return time;
}
else {
return 0.0f;
}
}
/*
=====================
idAI::TestAnimMoveTowardEnemy
=====================
*/
int idAI::TestAnimMoveTowardEnemy(const char* animname) {
int anim;
predictedPath_t path;
idVec3 moveVec;
float yaw;
idVec3 delta;
idActor* enemyEnt;
enemyEnt = enemy.GetEntity();
if (!enemyEnt) {
return false;
}
anim = GetAnim(ANIMCHANNEL_LEGS, animname);
if (!anim) {
gameLocal.DWarning("missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName());
return false;
}
delta = enemyEnt->GetPhysics()->GetOrigin() - physicsObj.GetOrigin();
yaw = delta.ToYaw();
moveVec = animator.TotalMovementDelta(anim) * idAngles(0.0f, yaw, 0.0f).ToMat3() * physicsObj.GetGravityAxis();
idAI::PredictPath(this, aas, physicsObj.GetOrigin(), moveVec, 1000, 1000, (move.moveType == MOVETYPE_FLY) ? SE_BLOCKED : (SE_ENTER_OBSTACLE | SE_BLOCKED | SE_ENTER_LEDGE_AREA), path);
if (ai_debugMove.GetBool()) {
gameRenderWorld->DebugLine(colorGreen, physicsObj.GetOrigin(), physicsObj.GetOrigin() + moveVec, gameLocal.msec);
gameRenderWorld->DebugBounds(path.endEvent == 0 ? colorYellow : colorRed, physicsObj.GetBounds(), physicsObj.GetOrigin() + moveVec, gameLocal.msec);
}
return path.endEvent == 0;
}
/*
=====================
idAI::TestAnimMove
=====================
*/
int idAI::TestAnimMove(const char* animname) {
int anim;
predictedPath_t path;
idVec3 moveVec;
anim = GetAnim(ANIMCHANNEL_LEGS, animname);
if (!anim) {
gameLocal.DWarning("missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName());
return false;
}
moveVec = animator.TotalMovementDelta(anim) * idAngles(0.0f, ideal_yaw, 0.0f).ToMat3() * physicsObj.GetGravityAxis();
idAI::PredictPath(this, aas, physicsObj.GetOrigin(), moveVec, 1000, 1000, (move.moveType == MOVETYPE_FLY) ? SE_BLOCKED : (SE_ENTER_OBSTACLE | SE_BLOCKED | SE_ENTER_LEDGE_AREA), path);
if (ai_debugMove.GetBool()) {
gameRenderWorld->DebugLine(colorGreen, physicsObj.GetOrigin(), physicsObj.GetOrigin() + moveVec, gameLocal.msec);
gameRenderWorld->DebugBounds(path.endEvent == 0 ? colorYellow : colorRed, physicsObj.GetBounds(), physicsObj.GetOrigin() + moveVec, gameLocal.msec);
}
return path.endEvent == 0;
}
/*
=====================
idAI::TestMoveToPosition
=====================
*/
int idAI::TestMoveToPosition(const idVec3& position) {
predictedPath_t path;
idAI::PredictPath(this, aas, physicsObj.GetOrigin(), position - physicsObj.GetOrigin(), 1000, 1000, (move.moveType == MOVETYPE_FLY) ? SE_BLOCKED : (SE_ENTER_OBSTACLE | SE_BLOCKED | SE_ENTER_LEDGE_AREA), path);
if (ai_debugMove.GetBool()) {
gameRenderWorld->DebugLine(colorGreen, physicsObj.GetOrigin(), position, gameLocal.msec);
gameRenderWorld->DebugBounds(colorYellow, physicsObj.GetBounds(), position, gameLocal.msec);
if (path.endEvent) {
gameRenderWorld->DebugBounds(colorRed, physicsObj.GetBounds(), path.endPos, gameLocal.msec);
}
}
return path.endEvent == 0;
}
/*
=====================
idAI::TestMeleeAttack
=====================
*/
int idAI::TestMeleeAttack(void) {
bool result = TestMelee();
return result;
}
/*
=====================
idAI::TestAnimAttack
=====================
*/
int idAI::TestAnimAttack(const char* animname) {
int anim;
predictedPath_t path;
anim = GetAnim(ANIMCHANNEL_LEGS, animname);
if (!anim) {
gameLocal.DWarning("missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName());
return false;
}
idAI::PredictPath(this, aas, physicsObj.GetOrigin(), animator.TotalMovementDelta(anim), 1000, 1000, (move.moveType == MOVETYPE_FLY) ? SE_BLOCKED : (SE_ENTER_OBSTACLE | SE_BLOCKED | SE_ENTER_LEDGE_AREA), path);
return path.blockingEntity && (path.blockingEntity == enemy.GetEntity());
}
/*
=====================
idAI::PreBurn
=====================
*/
void idAI::PreBurn(void) {
// for now this just turns shadows off
renderEntity.noShadow = true;
}
/*
=====================
idAI::Burn
=====================
*/
void idAI::Burn(void) {
renderEntity.shaderParms[SHADERPARM_TIME_OF_DEATH] = gameLocal.time * 0.001f;
SpawnParticles("smoke_burnParticleSystem");
UpdateVisuals();
}
/*
=====================
idAI::ClearBurn
=====================
*/
void idAI::ClearBurn(void) {
renderEntity.noShadow = spawnArgs.GetBool("noshadows");
renderEntity.shaderParms[SHADERPARM_TIME_OF_DEATH] = 0.0f;
UpdateVisuals();
}
/*
=====================
idAI::SetSmokeVisibility
=====================
*/
void idAI::SetSmokeVisibility(int num, int on) {
int i;
int time;
if (num >= particles.Num()) {
gameLocal.Warning("Particle #%d out of range (%d particles) on entity '%s'", num, particles.Num(), name.c_str());
return;
}
if (on != 0) {
time = gameLocal.time;
BecomeActive(TH_UPDATEPARTICLES);
}
else {
time = 0;
}
if (num >= 0) {
particles[num].time = time;
}
else {
for (i = 0; i < particles.Num(); i++) {
particles[i].time = time;
}
}
UpdateVisuals();
}
/*
=====================
idAI::NumSmokeEmitters
=====================
*/
int idAI::NumSmokeEmitters(void) {
return particles.Num();
}
/*
=====================
idAI::StopThinking
=====================
*/
void idAI::StopThinking(void) {
BecomeInactive(TH_THINK);
}
/*
=====================
idAI::GetTurnDelta
=====================
*/
float idAI::GetTurnDelta(void) {
float amount;
if (turnRate) {
amount = idMath::AngleNormalize180(ideal_yaw - current_yaw);
return amount;
}
else {
return 0.0f;
}
}
/*
=====================
idAI::GetMoveType
=====================
*/
int idAI::GetMoveType(void) {
return move.moveType;
}
/*
=====================
idAI::SetMoveType
=====================
*/
void idAI::SetMoveType(int moveType) {
if ((moveType < 0) || (moveType >= NUM_MOVETYPES)) {
gameLocal.Error("Invalid movetype %d", moveType);
}
move.moveType = static_cast<moveType_t>(moveType);
if (move.moveType == MOVETYPE_FLY) {
travelFlags = TFL_WALK | TFL_AIR | TFL_FLY;
}
else {
travelFlags = TFL_WALK | TFL_AIR;
}
}
/*
=====================
idAI::SaveMove
=====================
*/
void idAI::SaveMove(void) {
savedMove = move;
}
/*
=====================
idAI::RestoreMove
=====================
*/
void idAI::RestoreMove(void) {
idVec3 goalPos;
idVec3 dest;
switch (savedMove.moveCommand) {
case MOVE_NONE:
StopMove(savedMove.moveStatus);
break;
case MOVE_FACE_ENEMY:
FaceEnemy();
break;
case MOVE_FACE_ENTITY:
FaceEntity(savedMove.goalEntity.GetEntity());
break;
case MOVE_TO_ENEMY:
MoveToEnemy();
break;
case MOVE_TO_ENEMYHEIGHT:
MoveToEnemyHeight();
break;
case MOVE_TO_ENTITY:
MoveToEntity(savedMove.goalEntity.GetEntity());
break;
case MOVE_OUT_OF_RANGE:
MoveOutOfRange(savedMove.goalEntity.GetEntity(), savedMove.range);
break;
case MOVE_TO_ATTACK_POSITION:
MoveToAttackPosition(savedMove.goalEntity.GetEntity(), savedMove.anim);
break;
case MOVE_TO_COVER:
MoveToCover(savedMove.goalEntity.GetEntity(), lastVisibleEnemyPos);
break;
case MOVE_TO_POSITION:
MoveToPosition(savedMove.moveDest);
break;
case MOVE_TO_POSITION_DIRECT:
DirectMoveToPosition(savedMove.moveDest);
break;
case MOVE_SLIDE_TO_POSITION:
SlideToPosition(savedMove.moveDest, savedMove.duration);
break;
case MOVE_WANDER:
WanderAround();
break;
}
if (GetMovePos(goalPos)) {
CheckObstacleAvoidance(goalPos, dest);
}
}
/*
=====================
idAI::AllowMovement
=====================
*/
void idAI::AllowMovement(float flag) {
allowMove = (flag != 0.0f);
}
/*
=====================
idAI::JumpFrame
=====================
*/
void idAI::JumpFrame(void) {
AI_JUMP = true;
}
/*
=====================
idAI::EnableClip
=====================
*/
void idAI::EnableClip(void) {
physicsObj.SetClipMask(MASK_MONSTERSOLID);
disableGravity = false;
}
/*
=====================
idAI::DisableClip
=====================
*/
void idAI::DisableClip(void) {
physicsObj.SetClipMask(0);
disableGravity = true;
}
/*
=====================
idAI::EnableGravity
=====================
*/
void idAI::EnableGravity(void) {
disableGravity = false;
}
/*
=====================
idAI::DisableGravity
=====================
*/
void idAI::DisableGravity(void) {
disableGravity = true;
}
/*
=====================
idAI::EnableAFPush
=====================
*/
void idAI::EnableAFPush(void) {
af_push_moveables = true;
}
/*
=====================
idAI::DisableAFPush
=====================
*/
void idAI::DisableAFPush(void) {
af_push_moveables = false;
}
/*
=====================
idAI::SetFlySpeed
=====================
*/
void idAI::SetFlySpeed(float speed) {
if (move.speed == fly_speed) {
move.speed = speed;
}
fly_speed = speed;
}
/*
=====================
idAI::SetFlyOffset
=====================
*/
void idAI::SetFlyOffset(int offset) {
fly_offset = offset;
}
/*
=====================
idAI::ClearFlyOffset
=====================
*/
void idAI::ClearFlyOffset(void) {
spawnArgs.GetInt("fly_offset", "0", fly_offset);
}
/*
=====================
idAI::GetClosestHiddenTarget
=====================
*/
idEntity* idAI::GetClosestHiddenTarget(const char* type) {
int i;
idEntity* ent;
idEntity* bestEnt;
float time;
float bestTime;
const idVec3& org = physicsObj.GetOrigin();
idActor* enemyEnt = enemy.GetEntity();
if (!enemyEnt) {
// no enemy to hide from
return NULL;
}
if (targets.Num() == 1) {
ent = targets[0].GetEntity();
if (ent && idStr::Cmp(ent->GetEntityDefName(), type) == 0) {
if (!EntityCanSeePos(enemyEnt, lastVisibleEnemyPos, ent->GetPhysics()->GetOrigin())) {
return ent;
}
}
return NULL;
}
bestEnt = NULL;
bestTime = idMath::INFINITY;
for (i = 0; i < targets.Num(); i++) {
ent = targets[i].GetEntity();
if (ent && idStr::Cmp(ent->GetEntityDefName(), type) == 0) {
const idVec3& destOrg = ent->GetPhysics()->GetOrigin();
time = TravelDistance(org, destOrg);
if ((time >= 0.0f) && (time < bestTime)) {
if (!EntityCanSeePos(enemyEnt, lastVisibleEnemyPos, destOrg)) {
bestEnt = ent;
bestTime = time;
}
}
}
}
return bestEnt;
}
/*
=====================
idAI::GetRandomTarget
=====================
*/
idEntity* idAI::GetRandomTarget(const char* type) {
int i;
int num;
int which;
idEntity* ent;
idEntity* ents[MAX_GENTITIES];
num = 0;
for (i = 0; i < targets.Num(); i++) {
ent = targets[i].GetEntity();
if (ent && idStr::Cmp(ent->GetEntityDefName(), type) == 0) {
ents[num++] = ent;
if (num >= MAX_GENTITIES) {
break;
}
}
}
if (!num) {
return NULL;
}
which = gameLocal.random.RandomInt(num);
return ents[which];
}
/*
=====================
idAI::TravelDistanceToPoint
=====================
*/
float idAI::TravelDistanceToPoint(const idVec3& pos) {
float time;
time = TravelDistance(physicsObj.GetOrigin(), pos);
return time;
}
/*
=====================
idAI::TravelDistanceToEntity
=====================
*/
float idAI::TravelDistanceToEntity(idEntity* ent) {
float time;
time = TravelDistance(physicsObj.GetOrigin(), ent->GetPhysics()->GetOrigin());
return time;
}
/*
=====================
idAI::TravelDistanceBetweenPoints
=====================
*/
float idAI::TravelDistanceBetweenPoints(const idVec3& source, const idVec3& dest) {
float time;
time = TravelDistance(source, dest);
return time;
}
/*
=====================
idAI::TravelDistanceBetweenEntities
=====================
*/
float idAI::TravelDistanceBetweenEntities(idEntity* source, idEntity* dest) {
float time;
assert(source);
assert(dest);
time = TravelDistance(source->GetPhysics()->GetOrigin(), dest->GetPhysics()->GetOrigin());
return time;
}
/*
=====================
idAI::LookAtEntity
=====================
*/
void idAI::LookAtEntity(idEntity* ent, float duration) {
if (ent == this) {
ent = NULL;
}
if ((ent != focusEntity.GetEntity()) || (focusTime < gameLocal.time)) {
focusEntity = ent;
alignHeadTime = gameLocal.time;
forceAlignHeadTime = gameLocal.time + SEC2MS(1);
blink_time = 0;
}
focusTime = gameLocal.time + SEC2MS(duration);
}
/*
=====================
idAI::LookAtEnemy
=====================
*/
void idAI::LookAtEnemy(float duration) {
idActor* enemyEnt;
enemyEnt = enemy.GetEntity();
if ((enemyEnt != focusEntity.GetEntity()) || (focusTime < gameLocal.time)) {
focusEntity = enemyEnt;
alignHeadTime = gameLocal.time;
forceAlignHeadTime = gameLocal.time + SEC2MS(1);
blink_time = 0;
}
focusTime = gameLocal.time + SEC2MS(duration);
}
/*
=====================
idAI::SetJointMod
=====================
*/
void idAI::SetJointMod(int allow) {
allowJointMod = (allow != 0);
}
/*
=====================
idAI::ThrowMoveable
=====================
*/
void idAI::ThrowMoveable(void) {
idEntity* ent;
idEntity* moveable = NULL;
for (ent = GetNextTeamEntity(); ent != NULL; ent = ent->GetNextTeamEntity()) {
if (ent->GetBindMaster() == this && ent->IsType(idMoveable::Type)) {
moveable = ent;
break;
}
}
if (moveable) {
moveable->Unbind();
moveable->PostEventMS(&EV_SetOwner, 200, NULL);
}
}
/*
=====================
idAI::ThrowAF
=====================
*/
void idAI::ThrowAF(void) {
idEntity* ent;
idEntity* af = NULL;
for (ent = GetNextTeamEntity(); ent != NULL; ent = ent->GetNextTeamEntity()) {
if (ent->GetBindMaster() == this && ent->IsType(idAFEntity_Base::Type)) {
af = ent;
break;
}
}
if (af) {
af->Unbind();
af->PostEventMS(&EV_SetOwner, 200, NULL);
}
}
/*
=====================
idAI::SetAngles
=====================
*/
void idAI::SetAngles(idAngles const& ang) {
current_yaw = ang.yaw;
viewAxis = idAngles(0, current_yaw, 0).ToMat3();
}
/*
=====================
idAI::GetAngles
=====================
*/
idVec3 idAI::GetAngles(void) {
return idVec3(0.0f, current_yaw, 0.0f);
}
/*
=====================
idAI::RealKill
=====================
*/
void idAI::RealKill(void) {
health = 0;
if (af.IsLoaded()) {
// clear impacts
af.Rest();
// physics is turned off by calling af.Rest()
BecomeActive(TH_PHYSICS);
}
Killed(this, this, 0, vec3_zero, INVALID_JOINT);
}
/*
=====================
idAI::Kill
=====================
*/
void idAI::Kill(void) {
PostEventMS(&AI_RealKill, 0);
}
/*
=====================
idAI::WakeOnFlashlight
=====================
*/
void idAI::WakeOnFlashlight(int enable) {
wakeOnFlashlight = (enable != 0);
}
/*
=====================
idAI::LocateEnemy
=====================
*/
void idAI::LocateEnemy(void) {
idActor* enemyEnt;
int areaNum;
enemyEnt = enemy.GetEntity();
if (!enemyEnt) {
return;
}
enemyEnt->GetNAVLocation(aas, lastReachableEnemyPos, areaNum);
SetEnemyPosition();
UpdateEnemyPosition();
}
/*
=====================
idAI::KickObstacles
=====================
*/
void idAI::KickObstacles(idEntity* kickEnt, float force) {
idVec3 dir;
idEntity* obEnt;
if (kickEnt) {
obEnt = kickEnt;
}
else {
obEnt = move.obstacle.GetEntity();
}
if (obEnt) {
dir = obEnt->GetPhysics()->GetOrigin() - physicsObj.GetOrigin();
dir.Normalize();
}
else {
dir = viewAxis[0];
}
KickObstacles(dir, force, obEnt);
}
/*
=====================
idAI::GetObstacle
=====================
*/
idEntity* idAI::GetObstacle(void) {
return move.obstacle.GetEntity();
}
/*
=====================
idAI::PushPointIntoNAV
=====================
*/
idVec3 idAI::PushPointIntoNAV(const idVec3& pos) {
int areaNum;
idVec3 newPos;
areaNum = PointReachableAreaNum(pos);
if (areaNum) {
newPos = pos;
aas->PushPointIntoAreaNum(areaNum, newPos);
return newPos;
}
else {
return pos;
}
}
/*
=====================
idAI::GetTurnRate
=====================
*/
float idAI::GetTurnRate(void) {
return turnRate;
}
/*
=====================
idAI::SetTurnRate
=====================
*/
void idAI::SetTurnRate(float rate) {
turnRate = rate;
}
/*
=====================
idAI::AnimTurn
=====================
*/
void idAI::AnimTurn(float angles) {
turnVel = 0.0f;
anim_turn_angles = angles;
if (angles) {
anim_turn_yaw = current_yaw;
anim_turn_amount = idMath::Fabs(idMath::AngleNormalize180(current_yaw - ideal_yaw));
if (anim_turn_amount > anim_turn_angles) {
anim_turn_amount = anim_turn_angles;
}
}
else {
anim_turn_amount = 0.0f;
animator.CurrentAnim(ANIMCHANNEL_LEGS)->SetSyncedAnimWeight(0, 1.0f);
animator.CurrentAnim(ANIMCHANNEL_LEGS)->SetSyncedAnimWeight(1, 0.0f);
animator.CurrentAnim(ANIMCHANNEL_TORSO)->SetSyncedAnimWeight(0, 1.0f);
animator.CurrentAnim(ANIMCHANNEL_TORSO)->SetSyncedAnimWeight(1, 0.0f);
}
}
/*
=====================
idAI::AllowHiddenMovement
=====================
*/
void idAI::AllowHiddenMovement(int enable) {
allowHiddenMovement = (enable != 0);
}
/*
=====================
idAI::FindActorsInBounds
=====================
*/
idEntity* idAI::FindActorsInBounds(const idVec3& mins, const idVec3& maxs) {
idEntity* ent;
idEntity* entityList[MAX_GENTITIES];
int numListedEntities;
int i;
numListedEntities = gameLocal.clip.EntitiesTouchingBounds(idBounds(mins, maxs), CONTENTS_BODY, entityList, MAX_GENTITIES);
for (i = 0; i < numListedEntities; i++) {
ent = entityList[i];
if (ent != this && !ent->IsHidden() && (ent->health > 0) && ent->IsType(idActor::Type)) {
return ent;
}
}
return NULL;
}
/*
=====================
idAI::CanReachPosition
=====================
*/
int idAI::CanReachPosition(const idVec3& pos) {
aasPath_t path;
int toAreaNum;
int areaNum;
toAreaNum = PointReachableAreaNum(pos);
areaNum = PointReachableAreaNum(physicsObj.GetOrigin());
if (!toAreaNum || !PathToGoal(path, areaNum, physicsObj.GetOrigin(), toAreaNum, pos)) {
return false;
}
else {
return true;
}
}
/*
=====================
idAI::CanReachEntity
=====================
*/
int idAI::CanReachEntity(idEntity* ent) {
aasPath_t path;
int toAreaNum;
int areaNum;
idVec3 pos;
if (!ent) {
return false;
}
if (move.moveType != MOVETYPE_FLY) {
if (!ent->GetFloorPos(64.0f, pos)) {
return false;
}
if (ent->IsType(idActor::Type) && static_cast<idActor*>(ent)->OnLadder()) {
return false;
}
}
else {
pos = ent->GetPhysics()->GetOrigin();
}
toAreaNum = PointReachableAreaNum(pos);
if (!toAreaNum) {
return false;
}
const idVec3& org = physicsObj.GetOrigin();
areaNum = PointReachableAreaNum(org);
if (!toAreaNum || !PathToGoal(path, areaNum, org, toAreaNum, pos)) {
return false;
}
else {
return true;
}
}
/*
=====================
idAI::CanReachEnemy
=====================
*/
int idAI::CanReachEnemy(void) {
aasPath_t path;
int toAreaNum;
int areaNum;
idVec3 pos;
idActor* enemyEnt;
enemyEnt = enemy.GetEntity();
if (!enemyEnt) {
return false;
}
if (move.moveType != MOVETYPE_FLY) {
if (enemyEnt->OnLadder()) {
return false;
}
enemyEnt->GetNAVLocation(aas, pos, toAreaNum);
}
else {
pos = enemyEnt->GetPhysics()->GetOrigin();
toAreaNum = PointReachableAreaNum(pos);
}
if (!toAreaNum) {
return false;
}
const idVec3& org = physicsObj.GetOrigin();
areaNum = PointReachableAreaNum(org);
if (!PathToGoal(path, areaNum, org, toAreaNum, pos)) {
return false;
}
else {
return true;
}
}
/*
=====================
idAI::GetReachableEntityPosition
=====================
*/
idVec3 idAI::GetReachableEntityPosition(idEntity* ent) {
int toAreaNum;
idVec3 pos;
if (move.moveType != MOVETYPE_FLY) {
if (!ent->GetFloorPos(64.0f, pos)) {
// NOTE: not a good way to return 'false'
return vec3_zero;
}
if (ent->IsType(idActor::Type) && static_cast<idActor*>(ent)->OnLadder()) {
// NOTE: not a good way to return 'false'
return vec3_zero;
}
}
else {
pos = ent->GetPhysics()->GetOrigin();
}
if (aas) {
toAreaNum = PointReachableAreaNum(pos);
aas->PushPointIntoAreaNum(toAreaNum, pos);
}
return pos;
}
/*
=====================
idAI::WaitAction
=====================
*/
void idAI::WaitAction(const char* waitForState) {
SetWaitState(waitForState);
}
/*
=====================
idAI::WaitActionDone
=====================
*/
bool idAI::WaitActionDone(void) const {
return (WaitState() == NULL);
}
/*
=====================
idAI::WaitMoveDone
=====================
*/
bool idAI::WaitMoveDone(void) const {
return MoveDone();
}
/*
=====================
idAI::Shrivel
=====================
*/
bool idAI::Shrivel(float shrivel_time, bool firstFrame) {
float t;
if (firstFrame) {
if (shrivel_time <= 0.0f) {
return true;
}
shrivel_rate = 0.001f / shrivel_time;
shrivel_start = gameLocal.time;
}
t = (gameLocal.time - shrivel_start) * shrivel_rate;
if (t > 0.25f) {
renderEntity.noShadow = true;
}
if (t > 1.0f) {
t = 1.0f;
renderEntity.shaderParms[SHADERPARM_MD5_SKINSCALE] = 1.0f - t * 0.5f;
UpdateVisuals();
return true;
}
renderEntity.shaderParms[SHADERPARM_MD5_SKINSCALE] = 1.0f - t * 0.5f;
UpdateVisuals();
return false;
}
/*
=====================
idAI::Event_Activate
=====================
*/
void idAI::Event_Activate(idEntity* activator) {
Activate(activator);
}
/*
=====================
idAI::Event_Touch
=====================
*/
void idAI::Event_Touch(idEntity* other, trace_t* trace) {
Touch(other, trace);
}
/*
=====================
idAI::Event_FindEnemy
=====================
*/
void idAI::Event_FindEnemy(int useFOV) {
idThread::ReturnEntity(FindEnemy(useFOV));
}
/*
=====================
idAI::Event_FindEnemyAI
=====================
*/
void idAI::Event_FindEnemyAI(int useFOV) {
idThread::ReturnEntity(FindEnemyAI(useFOV));
}
/*
=====================
idAI::Event_FindEnemyInCombatNodes
=====================
*/
void idAI::Event_FindEnemyInCombatNodes(void) {
idThread::ReturnEntity(FindEnemyInCombatNodes());
}
/*
=====================
idAI::Event_ClosestReachableEnemyOfEntity
=====================
*/
void idAI::Event_ClosestReachableEnemyOfEntity(idEntity* team_mate) {
idThread::ReturnEntity(ClosestReachableEnemyOfEntity(team_mate));
}
/*
=====================
idAI::Event_HeardSound
=====================
*/
void idAI::Event_HeardSound(int ignore_team) {
idThread::ReturnEntity(HeardSound(ignore_team));
}
/*
=====================
idAI::Event_SetEnemy
=====================
*/
void idAI::Event_SetEnemy(idEntity* ent) {
SetEnemy(ent);
}
/*
=====================
idAI::Event_ClearEnemy
=====================
*/
void idAI::Event_ClearEnemy(void) {
ClearEnemy();
}
/*
=====================
idAI::Event_MuzzleFlash
=====================
*/
void idAI::Event_MuzzleFlash(const char* jointname) {
MuzzleFlash(jointname);
}
/*
=====================
idAI::Event_CreateMissile
=====================
*/
void idAI::Event_CreateMissile(const char* jointname) {
idThread::ReturnEntity(CreateMissile(jointname));
}
/*
=====================
idAI::Event_AttackMissile
=====================
*/
void idAI::Event_AttackMissile(const char* jointname) {
idThread::ReturnEntity(AttackMissile(jointname));
}
/*
=====================
idAI::Event_FireMissileAtTarget
=====================
*/
void idAI::Event_FireMissileAtTarget(const char* jointname, const char* targetname) {
idThread::ReturnEntity(FireMissileAtTarget(jointname, targetname));
}
/*
=====================
idAI::Event_LaunchMissile
=====================
*/
void idAI::Event_LaunchMissile(const idVec3& org, const idAngles& ang) {
idThread::ReturnEntity(LaunchMissile(org, ang));
}
/*
=====================
idAI::Event_AttackMelee
=====================
*/
void idAI::Event_AttackMelee(const char* meleeDefName) {
idThread::ReturnInt(AttackMelee(meleeDefName));
}
/*
=====================
idAI::Event_DirectDamage
=====================
*/
void idAI::Event_DirectDamage(idEntity* damageTarget, const char* damageDefName) {
DirectDamage(damageTarget, damageDefName);
}
/*
=====================
idAI::Event_RadiusDamageFromJoint
=====================
*/
void idAI::Event_RadiusDamageFromJoint(const char* jointname, const char* damageDefName) {
RadiusDamageFromJoint(jointname, damageDefName);
}
/*
=====================
idAI::Event_RandomPath
=====================
*/
void idAI::Event_RandomPath(void) {
idThread::ReturnEntity(RandomPath());
}
/*
=====================
idAI::Event_BeginAttack
=====================
*/
void idAI::Event_BeginAttack(const char* name) {
BeginAttack(name);
}
/*
=====================
idAI::Event_EndAttack
=====================
*/
void idAI::Event_EndAttack(void) {
EndAttack();
}
/*
=====================
idAI::Event_MeleeAttackToJoint
=====================
*/
void idAI::Event_MeleeAttackToJoint(const char* jointname, const char* meleeDefName) {
idThread::ReturnInt(MeleeAttackToJoint(jointname, meleeDefName));
}
/*
=====================
idAI::Event_CanBecomeSolid
=====================
*/
void idAI::Event_CanBecomeSolid(void) {
idThread::ReturnFloat(CanBecomeSolid());
}
/*
=====================
idAI::Event_BecomeSolid
=====================
*/
void idAI::Event_BecomeSolid(void) {
BecomeSolid();
}
/*
=====================
idAI::Event_BecomeNonSolid
=====================
*/
void idAI::Event_BecomeNonSolid(void) {
BecomeNonSolid();
}
/*
=====================
idAI::Event_BecomeRagdoll
=====================
*/
void idAI::Event_BecomeRagdoll(void) {
idThread::ReturnInt(BecomeRagdoll());
}
/*
=====================
idAI::Event_StopRagdoll
=====================
*/
void idAI::Event_StopRagdoll(void) {
StopRagdoll();
}
/*
=====================
idAI::Event_SetHealth
=====================
*/
void idAI::Event_SetHealth(float newHealth) {
SetHealth(newHealth);
}
/*
=====================
idAI::Event_GetHealth
=====================
*/
void idAI::Event_GetHealth(void) {
idThread::ReturnFloat(GetHealth());
}
/*
=====================
idAI::Event_AllowDamage
=====================
*/
void idAI::Event_AllowDamage(void) {
AllowDamage();
}
/*
=====================
idAI::Event_IgnoreDamage
=====================
*/
void idAI::Event_IgnoreDamage(void) {
IgnoreDamage();
}
/*
=====================
idAI::Event_GetCurrentYaw
=====================
*/
void idAI::Event_GetCurrentYaw(void) {
idThread::ReturnFloat(GetCurrentYaw());
}
/*
=====================
idAI::Event_TurnTo
=====================
*/
void idAI::Event_TurnTo(float angle) {
TurnTo(angle);
}
/*
=====================
idAI::Event_TurnToPos
=====================
*/
void idAI::Event_TurnToPos(const idVec3& pos) {
TurnToPos(pos);
}
/*
=====================
idAI::Event_TurnToEntity
=====================
*/
void idAI::Event_TurnToEntity(idEntity* ent) {
TurnToEntity(ent);
}
/*
=====================
idAI::Event_MoveStatus
=====================
*/
void idAI::Event_MoveStatus(void) {
idThread::ReturnInt(MoveStatus());
}
/*
=====================
idAI::Event_StopMove
=====================
*/
void idAI::Event_StopMove(void) {
StopMove();
}
/*
=====================
idAI::Event_MoveToCover
=====================
*/
void idAI::Event_MoveToCover(void) {
MoveToCover();
}
/*
=====================
idAI::Event_MoveToEnemy
=====================
*/
void idAI::Event_MoveToEnemy(void) {
MoveToEnemy();
}
/*
=====================
idAI::Event_MoveToEnemyHeight
=====================
*/
void idAI::Event_MoveToEnemyHeight(void) {
MoveToEnemyHeight();
}
/*
=====================
idAI::Event_MoveOutOfRange
=====================
*/
void idAI::Event_MoveOutOfRange(idEntity* entity, float range) {
MoveOutOfRange(entity, range);
}
/*
=====================
idAI::Event_MoveToAttackPosition
=====================
*/
void idAI::Event_MoveToAttackPosition(idEntity* entity, const char* attack_anim) {
const int anim = GetAnim(ANIMCHANNEL_LEGS, attack_anim);
if (!anim) {
gameLocal.Error("Unknown anim '%s'", attack_anim);
}
MoveToAttackPosition(entity, anim);
}
/*
=====================
idAI::Event_MoveToEntity
=====================
*/
void idAI::Event_MoveToEntity(idEntity* ent) {
MoveToEntity(ent);
}
/*
=====================
idAI::Event_MoveToPosition
=====================
*/
void idAI::Event_MoveToPosition(const idVec3& pos) {
MoveToPosition(pos);
}
/*
=====================
idAI::Event_SlideTo
=====================
*/
void idAI::Event_SlideTo(const idVec3& pos, float time) {
SlideTo(pos, time);
}
/*
=====================
idAI::Event_Wander
=====================
*/
void idAI::Event_Wander(void) {
Wander();
}
/*
=====================
idAI::Event_FacingIdeal
=====================
*/
void idAI::Event_FacingIdeal(void) {
idThread::ReturnInt(FacingIdeal());
}
/*
=====================
idAI::Event_FaceEnemy
=====================
*/
void idAI::Event_FaceEnemy(void) {
FaceEnemy();
}
/*
=====================
idAI::Event_FaceEntity
=====================
*/
void idAI::Event_FaceEntity(idEntity* ent) {
FaceEntity(ent);
}
/*
=====================
idAI::Event_WaitAction
=====================
*/
void idAI::Event_WaitAction(const char* waitForState) {
if (idThread::BeginMultiFrameEvent(this, &AI_WaitAction)) {
WaitAction(waitForState);
}
if (WaitActionDone()) {
idThread::EndMultiFrameEvent(this, &AI_WaitAction);
}
}
/*
=====================
idAI::Event_GetCombatNode
=====================
*/
void idAI::Event_GetCombatNode(void) {
idThread::ReturnEntity(GetCombatNode());
}
/*
=====================
idAI::Event_EnemyInCombatCone
=====================
*/
void idAI::Event_EnemyInCombatCone(idEntity* ent, int use_current_enemy_location) {
idThread::ReturnInt(EnemyInCombatCone(ent, use_current_enemy_location));
}
/*
=====================
idAI::Event_WaitMove
=====================
*/
void idAI::Event_WaitMove(void) {
idThread::BeginMultiFrameEvent(this, &AI_WaitMove);
if (WaitMoveDone()) {
idThread::EndMultiFrameEvent(this, &AI_WaitMove);
}
}
/*
=====================
idAI::Event_GetJumpVelocity
=====================
*/
void idAI::Event_GetJumpVelocity(const idVec3& pos, float speed, float max_height) {
idThread::ReturnVector(GetJumpVelocity(pos, speed, max_height));
}
/*
=====================
idAI::Event_EntityInAttackCone
=====================
*/
void idAI::Event_EntityInAttackCone(idEntity* ent) {
idThread::ReturnInt(EntityInAttackCone(ent));
}
/*
=====================
idAI::Event_CanSeeEntity
=====================
*/
void idAI::Event_CanSeeEntity(idEntity* ent) {
idThread::ReturnInt(CanSeeEntity(ent));
}
/*
=====================
idAI::Event_SetTalkTarget
=====================
*/
void idAI::Event_SetTalkTarget(idEntity* target) {
SetTalkTarget(target);
}
/*
=====================
idAI::Event_GetTalkTarget
=====================
*/
void idAI::Event_GetTalkTarget(void) {
idThread::ReturnEntity(GetTalkTarget());
}
/*
=====================
idAI::Event_SetTalkState
=====================
*/
void idAI::Event_SetTalkState(int state) {
SetTalkState(state);
}
/*
=====================
idAI::Event_EnemyRange
=====================
*/
void idAI::Event_EnemyRange(void) {
idThread::ReturnFloat(EnemyRange());
}
/*
=====================
idAI::Event_EnemyRange2D
=====================
*/
void idAI::Event_EnemyRange2D(void) {
idThread::ReturnFloat(EnemyRange2D());
}
/*
=====================
idAI::Event_GetEnemy
=====================
*/
void idAI::Event_GetEnemy(void) {
idThread::ReturnEntity(GetEnemy());
}
/*
=====================
idAI::Event_GetEnemyPos
=====================
*/
void idAI::Event_GetEnemyPos(void) {
idThread::ReturnVector(GetEnemyPos());
}
/*
=====================
idAI::Event_GetEnemyEyePos
=====================
*/
void idAI::Event_GetEnemyEyePos(void) {
idThread::ReturnVector(GetEnemyEyePos());
}
/*
=====================
idAI::Event_PredictEnemyPos
=====================
*/
void idAI::Event_PredictEnemyPos(float time) {
idThread::ReturnVector(PredictEnemyPos(time));
}
/*
=====================
idAI::Event_CanHitEnemy
=====================
*/
void idAI::Event_CanHitEnemy(void) {
idThread::ReturnInt(CanHitEnemy());
}
/*
=====================
idAI::Event_CanHitEnemyFromAnim
=====================
*/
void idAI::Event_CanHitEnemyFromAnim(const char* animname) {
idThread::ReturnInt(CanHitEnemyFromAnim(animname));
}
/*
=====================
idAI::Event_CanHitEnemyFromJoint
=====================
*/
void idAI::Event_CanHitEnemyFromJoint(const char* jointname) {
idThread::ReturnInt(CanHitEnemyFromJoint(jointname));
}
/*
=====================
idAI::Event_EnemyPositionValid
=====================
*/
void idAI::Event_EnemyPositionValid(void) {
idThread::ReturnInt(EnemyPositionValid());
}
/*
=====================
idAI::Event_ChargeAttack
=====================
*/
void idAI::Event_ChargeAttack(const char* damageDef) {
ChargeAttack(damageDef);
}
/*
=====================
idAI::Event_TestChargeAttack
=====================
*/
void idAI::Event_TestChargeAttack(void) {
idThread::ReturnFloat(TestChargeAttack());
}
/*
=====================
idAI::Event_TestAnimMoveTowardEnemy
=====================
*/
void idAI::Event_TestAnimMoveTowardEnemy(const char* animname) {
idThread::ReturnInt(TestAnimMoveTowardEnemy(animname));
}
/*
=====================
idAI::Event_TestAnimMove
=====================
*/
void idAI::Event_TestAnimMove(const char* animname) {
idThread::ReturnInt(TestAnimMove(animname));
}
/*
=====================
idAI::Event_TestMoveToPosition
=====================
*/
void idAI::Event_TestMoveToPosition(const idVec3& position) {
idThread::ReturnInt(TestMoveToPosition(position));
}
/*
=====================
idAI::Event_TestMeleeAttack
=====================
*/
void idAI::Event_TestMeleeAttack(void) {
idThread::ReturnInt(TestMeleeAttack());
}
/*
=====================
idAI::Event_TestAnimAttack
=====================
*/
void idAI::Event_TestAnimAttack(const char* animname) {
idThread::ReturnInt(TestAnimAttack(animname));
}
/*
=====================
idAI::Event_Shrivel
=====================
*/
void idAI::Event_Shrivel(float shrivel_time) {
const bool firstFrame = idThread::BeginMultiFrameEvent(this, &AI_Shrivel);
if (Shrivel(shrivel_time, firstFrame)) {
idThread::EndMultiFrameEvent(this, &AI_Shrivel);
}
}
/*
=====================
idAI::Event_PreBurn
=====================
*/
void idAI::Event_PreBurn(void) {
PreBurn();
}
/*
=====================
idAI::Event_Burn
=====================
*/
void idAI::Event_Burn(void) {
Burn();
}
/*
=====================
idAI::Event_ClearBurn
=====================
*/
void idAI::Event_ClearBurn(void) {
ClearBurn();
}
/*
=====================
idAI::Event_SetSmokeVisibility
=====================
*/
void idAI::Event_SetSmokeVisibility(int num, int on) {
SetSmokeVisibility(num, on);
}
/*
=====================
idAI::Event_NumSmokeEmitters
=====================
*/
void idAI::Event_NumSmokeEmitters(void) {
idThread::ReturnInt(NumSmokeEmitters());
}
/*
=====================
idAI::Event_StopThinking
=====================
*/
void idAI::Event_StopThinking(void) {
StopThinking();
idThread* thread = idThread::CurrentThread();
if (thread) {
thread->DoneProcessing();
}
}
/*
=====================
idAI::Event_GetTurnDelta
=====================
*/
void idAI::Event_GetTurnDelta(void) {
idThread::ReturnFloat(GetTurnDelta());
}
/*
=====================
idAI::Event_GetMoveType
=====================
*/
void idAI::Event_GetMoveType(void) {
idThread::ReturnInt(GetMoveType());
}
/*
=====================
idAI::Event_SetMoveType
=====================
*/
void idAI::Event_SetMoveType(int moveType) {
SetMoveType(moveType);
}
/*
=====================
idAI::Event_SaveMove
=====================
*/
void idAI::Event_SaveMove(void) {
SaveMove();
}
/*
=====================
idAI::Event_RestoreMove
=====================
*/
void idAI::Event_RestoreMove(void) {
RestoreMove();
}
/*
=====================
idAI::Event_AllowMovement
=====================
*/
void idAI::Event_AllowMovement(float flag) {
AllowMovement(flag);
}
/*
=====================
idAI::Event_JumpFrame
=====================
*/
void idAI::Event_JumpFrame(void) {
JumpFrame();
}
/*
=====================
idAI::Event_EnableClip
=====================
*/
void idAI::Event_EnableClip(void) {
EnableClip();
}
/*
=====================
idAI::Event_DisableClip
=====================
*/
void idAI::Event_DisableClip(void) {
DisableClip();
}
/*
=====================
idAI::Event_EnableGravity
=====================
*/
void idAI::Event_EnableGravity(void) {
EnableGravity();
}
/*
=====================
idAI::Event_DisableGravity
=====================
*/
void idAI::Event_DisableGravity(void) {
DisableGravity();
}
/*
=====================
idAI::Event_EnableAFPush
=====================
*/
void idAI::Event_EnableAFPush(void) {
EnableAFPush();
}
/*
=====================
idAI::Event_DisableAFPush
=====================
*/
void idAI::Event_DisableAFPush(void) {
DisableAFPush();
}
/*
=====================
idAI::Event_SetFlySpeed
=====================
*/
void idAI::Event_SetFlySpeed(float speed) {
SetFlySpeed(speed);
}
/*
=====================
idAI::Event_SetFlyOffset
=====================
*/
void idAI::Event_SetFlyOffset(int offset) {
SetFlyOffset(offset);
}
/*
=====================
idAI::Event_ClearFlyOffset
=====================
*/
void idAI::Event_ClearFlyOffset(void) {
ClearFlyOffset();
}
/*
=====================
idAI::Event_GetClosestHiddenTarget
=====================
*/
void idAI::Event_GetClosestHiddenTarget(const char* type) {
idThread::ReturnEntity(GetClosestHiddenTarget(type));
}
/*
=====================
idAI::Event_GetRandomTarget
=====================
*/
void idAI::Event_GetRandomTarget(const char* type) {
idThread::ReturnEntity(GetRandomTarget(type));
}
/*
=====================
idAI::Event_TravelDistanceToPoint
=====================
*/
void idAI::Event_TravelDistanceToPoint(const idVec3& pos) {
idThread::ReturnFloat(TravelDistanceToPoint(pos));
}
/*
=====================
idAI::Event_TravelDistanceToEntity
=====================
*/
void idAI::Event_TravelDistanceToEntity(idEntity* ent) {
idThread::ReturnFloat(TravelDistanceToEntity(ent));
}
/*
=====================
idAI::Event_TravelDistanceBetweenPoints
=====================
*/
void idAI::Event_TravelDistanceBetweenPoints(const idVec3& source, const idVec3& dest) {
idThread::ReturnFloat(TravelDistanceBetweenPoints(source, dest));
}
/*
=====================
idAI::Event_TravelDistanceBetweenEntities
=====================
*/
void idAI::Event_TravelDistanceBetweenEntities(idEntity* source, idEntity* dest) {
idThread::ReturnFloat(TravelDistanceBetweenEntities(source, dest));
}
/*
=====================
idAI::Event_LookAtEntity
=====================
*/
void idAI::Event_LookAtEntity(idEntity* ent, float duration) {
LookAtEntity(ent, duration);
}
/*
=====================
idAI::Event_LookAtEnemy
=====================
*/
void idAI::Event_LookAtEnemy(float duration) {
LookAtEnemy(duration);
}
/*
=====================
idAI::Event_SetJointMod
=====================
*/
void idAI::Event_SetJointMod(int allow) {
SetJointMod(allow);
}
/*
=====================
idAI::Event_ThrowMoveable
=====================
*/
void idAI::Event_ThrowMoveable(void) {
ThrowMoveable();
}
/*
=====================
idAI::Event_ThrowAF
=====================
*/
void idAI::Event_ThrowAF(void) {
ThrowAF();
}
/*
=====================
idAI::Event_SetAngles
=====================
*/
void idAI::Event_SetAngles(idAngles const& ang) {
SetAngles(ang);
}
/*
=====================
idAI::Event_GetAngles
=====================
*/
void idAI::Event_GetAngles(void) {
idThread::ReturnVector(GetAngles());
}
/*
=====================
idAI::Event_RealKill
=====================
*/
void idAI::Event_RealKill(void) {
RealKill();
}
/*
=====================
idAI::Event_Kill
=====================
*/
void idAI::Event_Kill(void) {
Kill();
}
/*
=====================
idAI::Event_WakeOnFlashlight
=====================
*/
void idAI::Event_WakeOnFlashlight(int enable) {
WakeOnFlashlight(enable);
}
/*
=====================
idAI::Event_LocateEnemy
=====================
*/
void idAI::Event_LocateEnemy(void) {
LocateEnemy();
}
/*
=====================
idAI::Event_KickObstacles
=====================
*/
void idAI::Event_KickObstacles(idEntity* kickEnt, float force) {
KickObstacles(kickEnt, force);
}
/*
=====================
idAI::Event_GetObstacle
=====================
*/
void idAI::Event_GetObstacle(void) {
idThread::ReturnEntity(GetObstacle());
}
/*
=====================
idAI::Event_PushPointIntoNAV
=====================
*/
void idAI::Event_PushPointIntoNAV(const idVec3& pos) {
idThread::ReturnVector(PushPointIntoNAV(pos));
}
/*
=====================
idAI::Event_GetTurnRate
=====================
*/
void idAI::Event_GetTurnRate(void) {
idThread::ReturnFloat(GetTurnRate());
}
/*
=====================
idAI::Event_SetTurnRate
=====================
*/
void idAI::Event_SetTurnRate(float rate) {
SetTurnRate(rate);
}
/*
=====================
idAI::Event_AnimTurn
=====================
*/
void idAI::Event_AnimTurn(float angles) {
AnimTurn(angles);
}
/*
=====================
idAI::Event_AllowHiddenMovement
=====================
*/
void idAI::Event_AllowHiddenMovement(int enable) {
AllowHiddenMovement(enable);
}
/*
=====================
idAI::Event_TriggerParticles
=====================
*/
void idAI::Event_TriggerParticles(const char* jointName) {
TriggerParticles(jointName);
}
/*
=====================
idAI::Event_FindActorsInBounds
=====================
*/
void idAI::Event_FindActorsInBounds(const idVec3& mins, const idVec3& maxs) {
idThread::ReturnEntity(FindActorsInBounds(mins, maxs));
}
/*
=====================
idAI::Event_CanReachPosition
=====================
*/
void idAI::Event_CanReachPosition(const idVec3& pos) {
idThread::ReturnInt(CanReachPosition(pos));
}
/*
=====================
idAI::Event_CanReachEntity
=====================
*/
void idAI::Event_CanReachEntity(idEntity* ent) {
idThread::ReturnInt(CanReachEntity(ent));
}
/*
=====================
idAI::Event_CanReachEnemy
=====================
*/
void idAI::Event_CanReachEnemy(void) {
idThread::ReturnInt(CanReachEnemy());
}
/*
=====================
idAI::Event_GetReachableEntityPosition
=====================
*/
void idAI::Event_GetReachableEntityPosition(idEntity* ent) {
idThread::ReturnVector(GetReachableEntityPosition(ent));
}