Files
firestorm/Gameleap/code/mw4/Code/MW4/noncomai.cpp
T
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
Complete disaster-recovery snapshot: engine/game source, game data assets,
VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive.
Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false,
no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
2026-06-24 21:28:16 -05:00

819 lines
19 KiB
C++

//===========================================================================//
// File: MechAI.cpp //
//---------------------------------------------------------------------------//
// Date Who Modification //
// -------- --- ---------------------------------------------------------- //
// 07/13/99 AHF Created file //
//---------------------------------------------------------------------------//
// Copyright (C) 1999, Microsoft //
// All Rights reserved worldwide //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
//===========================================================================//
#include "MW4Headers.hpp"
#include "mw4.hpp"
#include "noncomai.hpp"
#include "Adept\Interface.hpp"
#include "Adept\Application.hpp"
#include "adept\map.hpp"
#include "mwobject.hpp"
#include "rail_move.hpp"
#include "noncom.hpp"
#include "mwplayer.hpp"
#include "mwmap.hpp"
#include <adept\collisionvolume.hpp>
#include <Adept\DamageObject.hpp>
#pragma warning (push)
#include <algorithm>
#pragma warning (pop)
using namespace MechWarrior4;
using namespace MW4AI;
//#############################################################################
//############################### AI ##################################
//#############################################################################
NonComAI::ClassData* NonComAI::DefaultData = NULL;
__int64 tNonComTime;
const Stuff::Scalar RUN_SPEED = 10.0f; // meters per second to use run frame
const Stuff::Scalar WALK_SPEED = 2.5f; // meters per second to use run frame
const Stuff::Scalar ANIMAL_SPEED = 10.0f;
const Stuff::Scalar max_heading_change_time = 1.5f;
const Stuff::Scalar min_near_player_duration = 4.0f;
const Stuff::Scalar min_flock_update_time = 1.0f;
const Receiver::MessageEntry
NonComAI::MessageEntries[]=
{
MESSAGE_ENTRY(NonComAI, DebugText),
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void NonComAI::InitializeClass()
{
Verify(!DefaultData);
DefaultData =
new ClassData(
NonComAIClassID,
"MechWarrior4::NonComAI",
AI::DefaultData,
ELEMENTS(MessageEntries), MessageEntries,
(Entity::Factory)Make,
(Entity::CreateMessage::Factory)CreateMessage::ConstructCreateMessage,
ExecutionStateEngine::DefaultData,
(Entity::GameModel::Factory)GameModel::ConstructGameModel,
NULL,
(Entity::GameModel::ReadAndVerifier)GameModel::ReadAndVerify,
(Entity::GameModel::ModelWrite)GameModel::WriteToText,
(Entity::GameModel::ModelSave)GameModel::SaveGameModel
);
Register_Object(DefaultData);
#if !defined(NO_TIMERS)
AddStatistic( "NonComAI ", "%", gos_timedata, (void*)&tNonComTime, 0 );
#endif
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void NonComAI::TerminateClass()
{
Unregister_Object(DefaultData);
delete DefaultData;
DefaultData = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NonComAI* NonComAI::Make(CreateMessage *message,ReplicatorID *base_id)
{
Check_Object(message);
AutoHeap local_heap (g_AIHeap);
NonComAI *new_entity = new NonComAI(DefaultData, message, base_id, NULL);
Check_Object(new_entity);
new_entity->SyncMatrices(true);
return new_entity;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NonComAI::NonComAI(ClassData *class_data,CreateMessage *message,ReplicatorID *base_id,ElementRenderer::Element *element)
: AI(class_data, message, base_id, element)
{
Check_Pointer(this);
Check_Object(message);
m_FlockList.clear ();
scriptName[0] = 0;
m_MultX = 0;
m_MultZ = 0;
m_SpeedX = 0;
m_SpeedZ = 0;
m_LastObject = -1;
m_LastType = -1;
m_FidgetDestX = 0;
m_FidgetDestZ = 0;
m_LastHeadingChangeTime = 0;
m_LastTimeNearPlayer = 0;
m_MoveType = WALK;
m_FlockAimDelta = Point3D(0,0,0);
m_NextFlockUpdateTime = 0;
// Since AI turns off ai if we are not the server ( READ: A GOOD THING )
// we need to hack non-com's back on!
if (message->entityID != NameTable::NullObjectID && !Network::GetInstance()->AmIServer())
{
Check_Object(NameTable::GetInstance());
vehicle = NameTable::GetInstance()->FindData(message->entityID);
Check_Object (vehicle);
Verify (vehicle->IsDerivedFrom (MWObject::DefaultData));
ConnectEntity ((MWObject *) vehicle);
}
m_NonCom = Cast_Object (NonCom *,vehicle);
Check_Object (m_NonCom);
executionState->RequestState(ExecutionStateEngine::AlwaysExecuteState);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
NonComAI::~NonComAI()
{
Check_Object (this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void NonComAI::Die (void)
{
Check_Object (this);
inherited::Die ();
}
void NonComAI::PostCollisionExecute(Time till)
{
POSTCOLLISION_LOGIC("NonCom AI");
if (memory[TYPE_INDEX] == FLOCK_TYPE)
{
if (Target())
{
Check_Object (Target());
Check_Object (m_NonCom);
if (Target()->IsDestroyed())
m_NonCom->ReactToDestruction(InternalDamageObject::DestructionDamageMode, 0);
}
}
BaseClass::PostCollisionExecute (till);
}
void NonComAI::PreCollisionExecute(Time till)
{
Check_Object(this);
PRECOLLISION_LOGIC("NonCom AI");
//
//---------------------------------
// Verify that this always executes
//---------------------------------
//
Check_Object(executionState);
Verify (getEntity ());
gosREPORT (getEntity ()->IsDerivedFrom (NonCom::DefaultData),"You have a non com ai attached to a unit that is not a non com");
Verify(executionState->GetState() != ExecutionStateEngine::NeverExecuteState);
if (!(!m_FirstFrame && m_SecondFrame))
{
if (IsAnimal() == true)
{
m_NonCom->SetAsAnimal();
}
AI::PreCollisionExecute (till);
return;
}
// obj->TurnOff ();
__int64 TempTime=GetCycles();
if (memory[TYPE_INDEX] == FIDGET_TYPE)
{
DoFidget (till);
}
else if (memory[TYPE_INDEX] == TRACE_TYPE)
{
DoTrace (till);
}
else if (memory[TYPE_INDEX] == FLOCK_TYPE)
{
DoFlock (till);
}
#if defined(LAB_ONLY)
else
{
PAUSE (("unknown noncomai type in %s",instanceName));
}
#endif
UpdatePos (till);
Check_Object (m_NonCom);
/*
if ((m_SpeedX > 5.0f) || (m_SpeedZ > 5.0f) || (m_SpeedX < -5.0f) || (m_SpeedZ < -5.0f))
m_NonCom->Running (true);
else
m_NonCom->Running (false);
*/
// m_NonCom->Speed (m_FakeSpeedX > m_FakeSpeedZ ? m_FakeSpeedX+(m_FakeSpeedZ/2.0f) : m_FakeSpeedZ + (m_FakeSpeedX/2.0f));
m_NonCom->TextureID ((int) memory[TEXTURE_ID]);
tNonComTime+=GetCycles()-TempTime;
AI::PreCollisionExecute(till);
}
void NonComAI::CreateFakeSpeed (void)
{
Scalar len = (Scalar) sqrt ((double) (m_SpeedX*m_SpeedX) + (m_SpeedZ*m_SpeedZ));
if (len == 0)
{
m_FakeSpeedX = 0;
m_FakeSpeedZ = 0;
m_NonCom->Speed (0);
return;
}
Scalar speed = 0;
switch (m_MoveType)
{
case WALK:
{
speed = WALK_SPEED + (((Stuff::Scalar)(objectID & 0x07)) / 20);
break;
}
case RUN:
{
speed = RUN_SPEED + (((Stuff::Scalar)(objectID & 0x07)) / 8);
break;
}
case ANIMAL:
{
speed = ANIMAL_SPEED + (((Stuff::Scalar)(objectID & 0x07)) / 8);
break;
}
}
m_FakeSpeedX = (m_SpeedX / len) * speed;
m_FakeSpeedZ = (m_SpeedZ / len) * speed;
m_NonCom->Speed(speed);
}
void NonComAI::UpdatePos (Time till)
{
LinearMatrix4D mat;
Point3D trans (getEntity ()->GetLocalToWorld ());
YawPitchRoll rot(getEntity ()->GetLocalToWorld ());
mat.BuildRotation (rot);
Max_Clamp (m_SpeedX,memory[SPEED_INDEX]);
Min_Clamp (m_SpeedX,-memory[SPEED_INDEX]);
Max_Clamp (m_SpeedZ,memory[SPEED_INDEX]);
Min_Clamp (m_SpeedZ,-memory[SPEED_INDEX]);
CreateFakeSpeed ();
trans.x += m_FakeSpeedX * GetTimeSlice (till);
trans.z += m_FakeSpeedZ * GetTimeSlice (till);
BYTE tempmaterial;
// Scalar head;
// if (!Small_Enough(m_FakeSpeedZ))
// head = Arctan(m_FakeSpeedX, m_FakeSpeedZ);
// else
// head = 0;
// YawPitchRoll roll (head,0,0);
Adept::Entity* entity_hit = 0;
trans.y = GetMapY (trans.x,trans.z,getEntity(),tempmaterial,trans.y,&entity_hit);
if (memory[HEIGHT_INDEX] != 0)
{
trans.y += memory[HEIGHT_INDEX];
}
else
{
if (entity_hit != Map::GetInstance())
{
trans.y -= 1000.0f;
}
}
// mat.BuildRotation (roll);
mat.BuildTranslation (trans);
getEntity ()->SetNewLocalToParent (mat);
getEntity ()->SyncMatrices(true);
}
Scalar RandomSign()
{
if (Stuff::Random::GetFraction() < 0.5f)
{
return (1);
}
return (-1);
}
void NonComAI::DoFidget (Time till)
{
Verify (getEntity ());
if (m_LastType != FIDGET_TYPE)
{
m_MultX = 0;
m_MultZ = 0;
m_CenterPoint = getEntity ()->GetLocalToWorld ();
m_SpeedX = 0;
m_SpeedZ = 0;
m_LastType = FIDGET_TYPE;
}
if (m_LastHeadingChangeTime + max_heading_change_time > gos_GetElapsedTime())
{
return;
}
Point3D loc(getEntity()->GetLocalToWorld ());
bool avoid_player(false);
bool recalc_dest(false);
const Scalar radius = memory[RADIUS_INDEX];
MWPlayer *player = 0;
if (MWPlayer::GetInstance() != 0)
{
Check_Object(MWPlayer::GetInstance());
player = Cast_Object (MWPlayer *, MWPlayer::GetInstance());
if (m_LastTimeNearPlayer + min_near_player_duration < gos_GetElapsedTime())
{
Stuff::Scalar playerdist = GetLengthSquared((Point3D)player->GetLocalToWorld(),loc);
if (playerdist < (radius * radius * 2))
{
avoid_player = true;
recalc_dest = true;
m_LastTimeNearPlayer = (Stuff::Scalar)gos_GetElapsedTime();
}
}
else
{
avoid_player = true;
}
}
if ((m_FidgetDestX == 0) &&
(m_FidgetDestZ == 0))
{
recalc_dest = true;
}
else
{
Point3D dest(m_FidgetDestX,loc.y,m_FidgetDestZ);
Scalar distance = GetLengthSquared(loc,dest);
if (distance < 10 * 10)
{
recalc_dest = true;
}
}
Scalar old_destX = m_FidgetDestX;
Scalar old_destZ = m_FidgetDestZ;
if (recalc_dest == true)
{
if ((avoid_player == true) &&
(memory[HEIGHT_INDEX] <= 0))
{
Point3D playerloc(player->GetLocalToWorld());
if (playerloc.x < loc.x)
{
m_FidgetDestX = loc.x + radius;
}
else
{
m_FidgetDestX = loc.x - radius;
}
if (playerloc.z < loc.z)
{
m_FidgetDestZ = loc.z + radius;
}
else
{
m_FidgetDestZ = loc.z - radius;
}
m_FidgetDestX += (RandomSign() * Stuff::Random::GetFraction() * 20);
m_FidgetDestZ += (RandomSign() * Stuff::Random::GetFraction() * 20);
}
else
{
Scalar angle = Stuff::Two_Pi * Stuff::Random::GetFraction();
Stuff::Scalar x_delta = radius * Stuff::Cos(angle);
Stuff::Scalar z_delta = radius * Stuff::Sin(angle);
Stuff::Point3D point1(m_CenterPoint);
point1.x += x_delta;
point1.z += z_delta;
Stuff::Point3D point2(m_CenterPoint);
point2.x -= x_delta;
point2.z -= z_delta;
if (GetLengthSquared(loc,point1) < GetLengthSquared(loc,point2))
{
m_FidgetDestX = point2.x;
m_FidgetDestZ = point2.z;
}
else
{
m_FidgetDestX = point1.x;
m_FidgetDestZ = point1.z;
}
}
if ((m_FidgetDestX == old_destX) &&
(m_FidgetDestZ == old_destZ))
{
return;
}
if (IsAnimal() == true)
{
m_MoveType = ANIMAL;
}
else
{
if (avoid_player == true)
{
m_MoveType = RUN;
}
else
{
m_MoveType = WALK;
}
}
}
const Scalar speed = memory[SPEED_INDEX];
if (m_FidgetDestX < loc.x)
{
m_SpeedX = -speed;
}
else
{
m_SpeedX = speed;
}
if (m_FidgetDestZ < loc.z)
{
m_SpeedZ = -speed;
}
else
{
m_SpeedZ = speed;
}
m_LastHeadingChangeTime = (Scalar)gos_GetElapsedTime();
}
void SetGenericTraceBox(ExtentBox& extent_box, const Point3D& point, Scalar distance)
{
extent_box.minX = point.x - distance;
extent_box.maxX = point.x + distance;
extent_box.minY = point.y - distance;
extent_box.maxY = point.y + distance;
extent_box.minZ = point.z - distance;
extent_box.maxZ = point.z + distance;
}
void NonComAI::DoTrace (Time till)
{
Verify (getEntity ());
if (Target() == 0)
{
return;
}
if (m_LastType != TRACE_TYPE)
{
m_MultX = 0;
m_MultZ = 0;
m_CenterPoint = getEntity ()->GetLocalToWorld ();
m_SpeedX = 0;
m_SpeedZ = 0;
m_LastType = TRACE_TYPE;
OBB *obb;
CollisionVolume* col;
col = Target ()->GetHierarchicalVolume ();
if (!col)
col = Target ()->GetSolidVolume ();
if (col == 0)
{
SetGenericTraceBox(m_TraceBox,(Point3D)(Target()->GetLocalToWorld()),20);
}
else
{
OBB world_bounds;
world_bounds.Multiply(col->m_localSpaceBounds, Target()->GetLocalToWorld());
obb = &(world_bounds);
m_TraceBox = ExtentBox (*obb);
}
}
bool clockwise;
int area = 0;
Point3D loc (getEntity()->GetLocalToWorld ());
clockwise = memory[CLOCKWISE_INDEX] <=0;
int cx,cz;
cx = (int) (((m_TraceBox.maxX - m_TraceBox.minX) / 2.0f) + m_TraceBox.minX);
cz = (int) (((m_TraceBox.maxZ - m_TraceBox.minZ) / 2.0f) + m_TraceBox.minZ);
if (loc.x < m_TraceBox.minX)
area += 0x02;
else if (loc.x > m_TraceBox.maxX)
area += 0x04;
if (loc.z < m_TraceBox.minZ)
area += 0x01;
else if (loc.z > m_TraceBox.maxZ)
area += 0x08;
if (area == 0)
{
if (loc.x < cx)
area += 0x02;
else if (loc.x >= cx)
area += 0x04;
if (loc.z < cz)
area += 0x01;
else if (loc.z >= cz)
area += 0x08;
}
Verify (area != 0);
m_MultX = 0;
m_MultZ = 0;
if (clockwise)
{
if (area & 0x01)
{
if (!(area & 0x04))
{
if (loc.z < m_TraceBox.minZ - Trace_Distance - Trace_Fudge)
m_MultZ += 0.25f;
else if (loc.z > m_TraceBox.minZ - Trace_Distance + Trace_Fudge)
m_MultZ += -0.25f;
}
m_MultX += 1.0f;
}
if (area & 0x08)
{
if (!(area & 0x02))
{
if (loc.z > m_TraceBox.maxZ + Trace_Distance + Trace_Fudge)
m_MultZ += -0.25f;
else if (loc.z < m_TraceBox.maxZ + Trace_Distance - Trace_Fudge)
m_MultZ += 0.25f;
}
m_MultX += -1.0f;
}
if (area & 0x02)
{
if (!(area & 0x01))
{
if (loc.x < m_TraceBox.minX - Trace_Distance - Trace_Fudge)
m_MultX += 0.25f;
else if (loc.x > m_TraceBox.minX - Trace_Distance + Trace_Fudge)
m_MultX += -0.25f;
}
m_MultZ += -1.0f;
}
if (area & 0x04)
{
if (!(area & 0x08))
{
if (loc.x > m_TraceBox.maxX + Trace_Distance + Trace_Fudge)
m_MultX += -0.25f;
else if (loc.x < m_TraceBox.maxX + Trace_Distance - Trace_Fudge)
m_MultX += 0.25f;
}
m_MultZ += 1.0f;
}
}
else
{
if (area & 0x01)
{
if (!(area & 0x02))
{
if (loc.z < m_TraceBox.minZ - Trace_Distance - Trace_Fudge)
m_MultZ += 0.25f;
else if (loc.z > m_TraceBox.minZ - Trace_Distance + Trace_Fudge)
m_MultZ += -0.25f;
}
m_MultX += -1.0f;
}
if (area & 0x08)
{
if (!(area & 0x04))
{
if (loc.z > m_TraceBox.maxZ + Trace_Distance + Trace_Fudge)
m_MultZ += -0.25f;
else if (loc.z < m_TraceBox.maxZ + Trace_Distance - Trace_Fudge)
m_MultZ += 0.25f;
}
m_MultX += 1.0f;
}
if (area & 0x02)
{
if (!(area & 0x08))
{
if (loc.x < m_TraceBox.minX - Trace_Distance - Trace_Fudge)
m_MultX += 0.25f;
else if (loc.x > m_TraceBox.minX - Trace_Distance + Trace_Fudge)
m_MultX += -0.25f;
}
m_MultZ += 1.0f;
}
if (area & 0x04)
{
if (!(area & 0x01))
{
if (loc.x > m_TraceBox.maxX + Trace_Distance + Trace_Fudge)
m_MultX += -0.25f;
else if (loc.x < m_TraceBox.maxX + Trace_Distance - Trace_Fudge)
m_MultX += 0.25f;
}
m_MultZ += -1.0f;
}
}
if (IsAnimal() == true)
{
m_MoveType = ANIMAL;
}
else
{
m_MoveType = WALK;
}
m_SpeedX = m_MultX * memory[SPEED_INDEX];
m_SpeedZ = m_MultZ * memory[SPEED_INDEX];
}
void NonComAI::DoFlock (Time till)
{
if (Target() == 0)
{
return;
}
if ((m_NextFlockUpdateTime != 0) &&
((Scalar)gos_GetElapsedTime() < m_NextFlockUpdateTime))
{
return;
}
m_FlockAimDelta.x += (0.5f - Stuff::Random::GetFraction()) * (Scalar)till;
m_FlockAimDelta.z += (0.5f - Stuff::Random::GetFraction()) * (Scalar)till;
const Scalar radius = memory[RADIUS_INDEX];
Max_Clamp(m_FlockAimDelta.x,radius);
Min_Clamp(m_FlockAimDelta.x,-radius);
Max_Clamp(m_FlockAimDelta.z,radius);
Min_Clamp(m_FlockAimDelta.z,-radius);
if (IsAnimal() == true)
{
m_MoveType = ANIMAL;
}
else
{
m_MoveType = WALK;
}
Point3D loc(getEntity()->GetLocalToWorld ());
Point3D head_point(Target()->GetLocalToWorld());
head_point += m_FlockAimDelta;
const Scalar speed = memory[SPEED_INDEX] * 0.9f;
if (head_point.x < loc.x)
{
m_SpeedX = -speed;
}
else
{
m_SpeedX = speed;
}
if (head_point.z < loc.z)
{
m_SpeedZ = -speed;
}
else
{
m_SpeedZ = speed;
}
m_NextFlockUpdateTime = (Scalar)gos_GetElapsedTime() + min_flock_update_time + Stuff::Random::GetFraction();
}
void NonComAI::AddFlockUnit (Entity *newunit)
{
stlport::vector<Entity *>::iterator iter;
Check_Object (newunit);
for (iter = m_FlockList.begin ();iter != m_FlockList.end ();iter++)
{
if ((*iter) == newunit)
return;
}
m_FlockList.push_back (newunit);
MWMap *map;
Check_Object (Map::GetInstance());
map = Cast_Object (MWMap *,Map::GetInstance());
Check_Object (map);
AI *ai;
ChainIteratorOf<AI*> ais2(&map->m_executingAIs);
while ((ai = ais2.ReadAndNext()) != NULL)
{
Check_Object(ai);
if (ai->IsDerivedFrom (NonComAI::DefaultData))
{
NonComAI *noncom;
noncom = Cast_Object (NonComAI *,ai);
if (noncom == this)
continue;
if ((noncom->memory[TYPE_INDEX] == FLOCK_TYPE) &&
(noncom->Target() == Target()))
{
{
noncom->AddFlockUnit (m_NonCom);
}
}
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void NonComAI::DebugTextMessageHandler(Adept::ReceiverDataMessageOf<int> *message)
{
Check_Object(this);
Check_Object(message);
Verify(message->messageID == DebugTextMessageID);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void NonComAI::TestInstance() const
{
Verify(IsDerivedFrom(DefaultData));
}
void NonComAI::Info (void (*printCallback)(char* s))
{
Check_Object (this);
inherited::Info (printCallback);
}
bool NonComAI::IsAnimal()
{
return (memory[BITMAP_LAYOUT_TYPE] == 1);
}