mirror of
https://github.com/jmarshall23/DoomRTX.git
synced 2026-08-16 08:10:32 +02:00
3461 lines
69 KiB
C++
3461 lines
69 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
|
|
|
|
***********************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
=====================
|
|
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(&EV_idAI_Kill_real, 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, &EV_idAI_WaitAction)) {
|
|
WaitAction(waitForState);
|
|
}
|
|
|
|
if (WaitActionDone()) {
|
|
idThread::EndMultiFrameEvent(this, &EV_idAI_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, &EV_idAI_WaitMove);
|
|
|
|
if (WaitMoveDone()) {
|
|
idThread::EndMultiFrameEvent(this, &EV_idAI_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, &EV_idAI_Shrivel);
|
|
if (Shrivel(shrivel_time, firstFrame)) {
|
|
idThread::EndMultiFrameEvent(this, &EV_idAI_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));
|
|
}
|
|
|