//===========================================================================// // 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 #include #pragma warning (push) #include #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::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 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 *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); }