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.
819 lines
19 KiB
C++
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);
|
|
}
|