2004-07-27 23:00:53 +02:00
|
|
|
#include "precompiled.h"
|
|
|
|
|
2005-05-18 07:32:09 +02:00
|
|
|
#include <vector>
|
|
|
|
|
2004-07-27 23:00:53 +02:00
|
|
|
#include <timer.h>
|
2005-03-30 18:14:19 +02:00
|
|
|
#include "Profile.h"
|
2004-07-27 23:00:53 +02:00
|
|
|
|
|
|
|
#include "Simulation.h"
|
2004-08-16 17:19:17 +02:00
|
|
|
#include "TurnManager.h"
|
2004-07-27 23:00:53 +02:00
|
|
|
#include "Game.h"
|
|
|
|
#include "EntityManager.h"
|
2005-05-10 09:13:25 +02:00
|
|
|
#include "Projectile.h"
|
2004-07-27 23:00:53 +02:00
|
|
|
#include "Scheduler.h"
|
2004-08-16 17:19:17 +02:00
|
|
|
#include "Network/NetMessage.h"
|
|
|
|
#include "CLogger.h"
|
2004-09-21 16:40:43 +02:00
|
|
|
#include "CConsole.h"
|
2004-12-12 20:43:55 +01:00
|
|
|
#include "Unit.h"
|
|
|
|
#include "Model.h"
|
2005-10-09 05:43:03 +02:00
|
|
|
#include "LOSManager.h"
|
2005-03-22 03:17:55 +01:00
|
|
|
#include "Loader.h"
|
2005-03-22 22:00:56 +01:00
|
|
|
#include "LoaderThunks.h"
|
2005-10-10 05:35:32 +02:00
|
|
|
#include "GameAttributes.h"
|
2006-01-22 00:27:42 +01:00
|
|
|
#include "renderer/Renderer.h"
|
|
|
|
#include "renderer/WaterManager.h"
|
2004-07-27 23:00:53 +02:00
|
|
|
|
|
|
|
#include "gui/CGUI.h"
|
|
|
|
|
2005-05-18 07:32:09 +02:00
|
|
|
using namespace std;
|
|
|
|
|
2004-09-21 16:40:43 +02:00
|
|
|
extern CConsole *g_Console;
|
|
|
|
|
2004-07-27 23:00:53 +02:00
|
|
|
CSimulation::CSimulation(CGame *pGame):
|
|
|
|
m_pGame(pGame),
|
|
|
|
m_pWorld(pGame->GetWorld()),
|
2004-08-16 17:19:17 +02:00
|
|
|
m_pTurnManager((g_SinglePlayerTurnManager=new CSinglePlayerTurnManager())),
|
2004-07-27 23:00:53 +02:00
|
|
|
m_DeltaTime(0)
|
|
|
|
{}
|
|
|
|
|
2004-08-16 17:19:17 +02:00
|
|
|
CSimulation::~CSimulation()
|
|
|
|
{
|
|
|
|
delete g_SinglePlayerTurnManager;
|
|
|
|
g_SinglePlayerTurnManager=NULL;
|
|
|
|
}
|
|
|
|
|
2005-10-10 05:35:32 +02:00
|
|
|
int CSimulation::Initialize(CGameAttributes* pAttribs)
|
2004-07-31 17:57:18 +02:00
|
|
|
{
|
2004-08-16 17:19:17 +02:00
|
|
|
m_pTurnManager->Initialize(m_pGame->GetNumPlayers());
|
|
|
|
|
2004-11-11 08:09:32 +01:00
|
|
|
g_EntityManager.InitializeAll();
|
2005-10-09 05:43:03 +02:00
|
|
|
|
2005-11-20 01:41:45 +01:00
|
|
|
#ifndef SCED
|
2005-10-10 05:35:32 +02:00
|
|
|
m_pWorld->GetLOSManager()->Initialize(pAttribs->m_LOSSetting);
|
2005-11-20 01:41:45 +01:00
|
|
|
#endif
|
2005-10-09 05:43:03 +02:00
|
|
|
|
2005-05-03 23:36:57 +02:00
|
|
|
return 0;
|
2004-07-31 17:57:18 +02:00
|
|
|
}
|
2004-07-27 23:00:53 +02:00
|
|
|
|
2005-03-22 03:17:55 +01:00
|
|
|
|
|
|
|
void CSimulation::RegisterInit(CGameAttributes *pAttribs)
|
|
|
|
{
|
2005-09-21 18:42:56 +02:00
|
|
|
RegMemFun1(this, &CSimulation::Initialize, pAttribs, L"CSimulation", 50);
|
2005-03-22 03:17:55 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-07-27 23:00:53 +02:00
|
|
|
void CSimulation::Update(double frameTime)
|
|
|
|
{
|
|
|
|
m_DeltaTime += frameTime;
|
2004-08-16 17:19:17 +02:00
|
|
|
|
2005-12-02 22:08:10 +01:00
|
|
|
if( m_DeltaTime >= 0.0 && frameTime )
|
2004-07-27 23:00:53 +02:00
|
|
|
{
|
2005-03-30 18:14:19 +02:00
|
|
|
PROFILE( "simulation turn" );
|
2004-07-27 23:00:53 +02:00
|
|
|
// A new simulation frame is required.
|
|
|
|
MICROLOG( L"calculate simulation" );
|
|
|
|
Simulate();
|
2004-08-16 17:19:17 +02:00
|
|
|
m_DeltaTime -= (m_pTurnManager->GetTurnLength()/1000.0);
|
2004-07-27 23:00:53 +02:00
|
|
|
if( m_DeltaTime >= 0.0 )
|
|
|
|
{
|
|
|
|
// The desired sim frame rate can't be achieved. Settle for process & render
|
|
|
|
// frames as fast as possible.
|
2005-10-10 21:49:02 +02:00
|
|
|
frameTime -= m_DeltaTime; // so the animation stays in sync with the sim
|
2004-07-27 23:00:53 +02:00
|
|
|
m_DeltaTime = 0.0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-03-30 18:14:19 +02:00
|
|
|
PROFILE_START( "simulation interpolation" );
|
2004-08-16 17:19:17 +02:00
|
|
|
Interpolate(frameTime, ((1000.0*m_DeltaTime) / (float)m_pTurnManager->GetTurnLength()) + 1.0);
|
2005-03-30 18:14:19 +02:00
|
|
|
PROFILE_END( "simulation interpolation" );
|
2004-07-27 23:00:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CSimulation::Interpolate(double frameTime, double offset)
|
|
|
|
{
|
|
|
|
const std::vector<CUnit*>& units=m_pWorld->GetUnitManager()->GetUnits();
|
|
|
|
for (uint i=0;i<units.size();++i)
|
2004-07-28 17:07:54 +02:00
|
|
|
units[i]->GetModel()->Update((float)frameTime);
|
2004-07-27 23:00:53 +02:00
|
|
|
|
2005-05-10 09:13:25 +02:00
|
|
|
g_EntityManager.interpolateAll( (float)offset );
|
|
|
|
g_ProjectileManager.InterpolateAll( (float)offset );
|
2006-01-22 00:27:42 +01:00
|
|
|
g_Renderer.GetWaterManager()->m_WaterTexTimer += frameTime;
|
2004-07-27 23:00:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CSimulation::Simulate()
|
|
|
|
{
|
2005-03-30 18:14:19 +02:00
|
|
|
PROFILE_START( "scheduler tick" );
|
2004-08-16 17:19:17 +02:00
|
|
|
g_Scheduler.update(m_pTurnManager->GetTurnLength());
|
2005-03-30 18:14:19 +02:00
|
|
|
PROFILE_END( "scheduler tick" );
|
|
|
|
|
|
|
|
PROFILE_START( "entity updates" );
|
2004-08-16 17:19:17 +02:00
|
|
|
g_EntityManager.updateAll( m_pTurnManager->GetTurnLength() );
|
2005-03-30 18:14:19 +02:00
|
|
|
PROFILE_END( "entity updates" );
|
2004-08-16 17:19:17 +02:00
|
|
|
|
2005-05-10 09:13:25 +02:00
|
|
|
PROFILE_START( "projectile updates" );
|
|
|
|
g_ProjectileManager.UpdateAll( m_pTurnManager->GetTurnLength() );
|
|
|
|
PROFILE_END( "projectile updates" );
|
|
|
|
|
2005-10-09 05:43:03 +02:00
|
|
|
PROFILE_START( "los update" );
|
|
|
|
m_pWorld->GetLOSManager()->Update();
|
|
|
|
PROFILE_END( "los update" );
|
|
|
|
|
2005-03-30 18:14:19 +02:00
|
|
|
PROFILE_START( "turn manager update" );
|
2004-08-16 17:19:17 +02:00
|
|
|
m_pTurnManager->NewTurn();
|
|
|
|
m_pTurnManager->IterateBatch(0, TranslateMessage, this);
|
2005-03-30 18:14:19 +02:00
|
|
|
PROFILE_END( "turn manager update" );
|
2004-08-16 17:19:17 +02:00
|
|
|
}
|
|
|
|
|
2005-05-18 07:32:09 +02:00
|
|
|
// Location randomizer, for group orders...
|
|
|
|
// Having the group turn up at the destination with /some/ sort of cohesion is
|
|
|
|
// good but tasking them all to the exact same point will leave them brawling
|
|
|
|
// for it at the other end (it shouldn't, but the PASAP pathfinder is too
|
|
|
|
// simplistic)
|
|
|
|
|
|
|
|
// Task them all to a point within a radius of the target, radius depends upon
|
|
|
|
// the number of units in the group.
|
|
|
|
|
|
|
|
void RandomizeLocations(CEntityOrder order, const vector <HEntity> &entities, bool clearQueue)
|
2004-08-16 17:19:17 +02:00
|
|
|
{
|
2005-05-18 07:32:09 +02:00
|
|
|
vector<HEntity>::const_iterator it;
|
|
|
|
float radius = 2.0f * sqrt( (float)entities.size() - 1 );
|
|
|
|
|
|
|
|
for (it = entities.begin(); it < entities.end(); it++)
|
|
|
|
{
|
|
|
|
float _x, _y;
|
|
|
|
CEntityOrder randomizedOrder = order;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
_x = (float)( rand() % 20000 ) / 10000.0f - 1.0f;
|
|
|
|
_y = (float)( rand() % 20000 ) / 10000.0f - 1.0f;
|
|
|
|
}
|
|
|
|
while( ( _x * _x ) + ( _y * _y ) > 1.0f );
|
|
|
|
|
|
|
|
randomizedOrder.m_data[0].location.x += _x * radius;
|
|
|
|
randomizedOrder.m_data[0].location.y += _y * radius;
|
|
|
|
|
|
|
|
// Clamp it to within the map, just in case.
|
|
|
|
float mapsize = (float)g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide() * CELL_SIZE;
|
|
|
|
|
|
|
|
if( randomizedOrder.m_data[0].location.x < 0.0f )
|
|
|
|
randomizedOrder.m_data[0].location.x = 0.0f;
|
|
|
|
if( randomizedOrder.m_data[0].location.x >= mapsize )
|
|
|
|
randomizedOrder.m_data[0].location.x = mapsize;
|
|
|
|
if( randomizedOrder.m_data[0].location.y < 0.0f )
|
|
|
|
randomizedOrder.m_data[0].location.y = 0.0f;
|
|
|
|
if( randomizedOrder.m_data[0].location.y >= mapsize )
|
|
|
|
randomizedOrder.m_data[0].location.y = mapsize;
|
|
|
|
|
|
|
|
if( clearQueue )
|
|
|
|
(*it)->clearOrders();
|
|
|
|
|
|
|
|
(*it)->pushOrder( randomizedOrder );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QueueOrder(CEntityOrder order, const vector <HEntity> &entities, bool clearQueue)
|
|
|
|
{
|
|
|
|
vector<HEntity>::const_iterator it;
|
2004-08-16 17:19:17 +02:00
|
|
|
|
2005-05-18 07:32:09 +02:00
|
|
|
for (it = entities.begin(); it < entities.end(); it++)
|
|
|
|
{
|
|
|
|
if( clearQueue )
|
|
|
|
(*it)->clearOrders();
|
|
|
|
|
|
|
|
(*it)->pushOrder( order );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-08-09 17:55:44 +02:00
|
|
|
uint CSimulation::TranslateMessage(CNetMessage* pMsg, uint clientMask, void* UNUSED(userdata))
|
2005-05-18 07:32:09 +02:00
|
|
|
{
|
|
|
|
CEntityOrder order;
|
|
|
|
bool clearQueue = true;
|
|
|
|
|
|
|
|
#define ENTITY_POSITION(_msg, _order) do\
|
|
|
|
{ \
|
|
|
|
_msg *msg=(_msg *)pMsg; \
|
|
|
|
order.m_type=CEntityOrder::_order; \
|
|
|
|
order.m_data[0].location.x=(float)msg->m_TargetX; \
|
|
|
|
order.m_data[0].location.y=(float)msg->m_TargetY; \
|
|
|
|
RandomizeLocations(order, msg->m_Entities, clearQueue); \
|
|
|
|
} while(0)
|
|
|
|
#define ENTITY_ENTITY(_msg, _order) do\
|
|
|
|
{ \
|
|
|
|
_msg *msg=(_msg *)pMsg; \
|
|
|
|
order.m_type=CEntityOrder::_order; \
|
|
|
|
order.m_data[0].entity=msg->m_Target; \
|
|
|
|
QueueOrder(order, msg->m_Entities, clearQueue); \
|
|
|
|
} while(0)
|
2005-12-29 09:42:44 +01:00
|
|
|
#define ENTITY_ENTITY_INT(_msg, _order) do\
|
|
|
|
{ \
|
|
|
|
_msg *msg=(_msg *)pMsg; \
|
|
|
|
order.m_type=CEntityOrder::_order; \
|
|
|
|
order.m_data[0].entity=msg->m_Target; \
|
|
|
|
order.m_data[1].data=msg->m_Action; \
|
|
|
|
QueueOrder(order, msg->m_Entities, clearQueue); \
|
|
|
|
} while(0)
|
2005-05-18 07:32:09 +02:00
|
|
|
|
2004-08-16 17:19:17 +02:00
|
|
|
switch (pMsg->GetType())
|
|
|
|
{
|
2005-05-18 07:32:09 +02:00
|
|
|
case NMT_AddWaypoint:
|
|
|
|
{
|
|
|
|
CAddWaypoint *msg=(CAddWaypoint *)pMsg;
|
|
|
|
order.m_type=CEntityOrder::ORDER_LAST;
|
|
|
|
order.m_data[0].location.x=(float)msg->m_TargetX;
|
|
|
|
order.m_data[0].location.y=(float)msg->m_TargetY;
|
|
|
|
vector<HEntity>::iterator it = msg->m_Entities.begin();
|
|
|
|
for (;it != msg->m_Entities.end(); ++it)
|
|
|
|
{
|
|
|
|
deque<CEntityOrder>::const_iterator ord_it;
|
|
|
|
ord_it=(*it)->m_orderQueue.end() - 1;
|
|
|
|
for (;ord_it >= (*it)->m_orderQueue.begin();--ord_it)
|
|
|
|
{
|
|
|
|
if (ord_it->m_type == CEntityOrder::ORDER_PATH_END_MARKER)
|
|
|
|
{
|
|
|
|
order.m_type = CEntityOrder::ORDER_GOTO;
|
|
|
|
(*it)->pushOrder(order);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (ord_it->m_type == CEntityOrder::ORDER_PATROL)
|
|
|
|
{
|
|
|
|
order.m_type = ord_it->m_type;
|
|
|
|
(*it)->pushOrder(order);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (order.m_type == CEntityOrder::ORDER_LAST)
|
|
|
|
{
|
|
|
|
LOG(ERROR, "simulation", "Got an AddWaypoint message for an entity that isn't moving.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case NMT_Goto:
|
|
|
|
ENTITY_POSITION(CGoto, ORDER_GOTO);
|
|
|
|
break;
|
2006-01-16 11:56:47 +01:00
|
|
|
case NMT_Run:
|
|
|
|
ENTITY_POSITION(CRun, ORDER_RUN);
|
|
|
|
break;
|
2005-05-18 07:32:09 +02:00
|
|
|
case NMT_Patrol:
|
|
|
|
ENTITY_POSITION(CPatrol, ORDER_PATROL);
|
|
|
|
break;
|
2005-12-29 09:42:44 +01:00
|
|
|
case NMT_Generic:
|
|
|
|
ENTITY_ENTITY_INT(CGeneric, ORDER_GENERIC);
|
|
|
|
break;
|
2004-08-16 17:19:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return clientMask;
|
|
|
|
}
|
|
|
|
|
2005-08-09 17:55:44 +02:00
|
|
|
uint CSimulation::GetMessageMask(CNetMessage* UNUSED(pMsg), uint UNUSED(oldMask), void* UNUSED(userdata))
|
2004-08-16 17:19:17 +02:00
|
|
|
{
|
2005-08-09 17:55:44 +02:00
|
|
|
//CSimulation *pSimulation=(CSimulation *)userdata;
|
|
|
|
|
2004-08-16 17:19:17 +02:00
|
|
|
// Pending a complete visibility/minimal-update implementation, we'll
|
|
|
|
// simply select the first 32 connected clients ;-)
|
|
|
|
return 0xffffffff;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CSimulation::QueueLocalCommand(CNetMessage *pMsg)
|
|
|
|
{
|
|
|
|
m_pTurnManager->QueueLocalCommand(pMsg);
|
2004-07-27 23:00:53 +02:00
|
|
|
}
|