/* =========================================================================== 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 . 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(""); 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(""); 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(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(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(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(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(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(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(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(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(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(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(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); 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(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(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)); }