#Unit formations and rank textures
-Added functionality for "casting" and creating net messages (without JS) -Rank textures-specified in XML -Formations-currently don't attack correctly (i.e. travel like mobs) and don't switch to their movement formations when tasked to go somewhere (the pathfinder doesn't give any heads up about destination reached, so there's no way to change back to the original). Also, base switching is untested so no garuntees for next and prior formation things. This was SVN commit r3710.
This commit is contained in:
parent
87780e328a
commit
3408b078b7
@ -53,8 +53,8 @@ bool CTerrain::Initialize(u32 size,const u16* data)
|
||||
// store terrain size
|
||||
m_MapSize=(size*PATCH_SIZE)+1;
|
||||
m_MapSizePatches=size;
|
||||
//WaterManager *WaterMgr = g_Renderer.GetWaterManager();
|
||||
//WaterMgr->InitWave();
|
||||
WaterManager *WaterMgr = g_Renderer.GetWaterManager();
|
||||
// WaterMgr->InitWave();
|
||||
// allocate data for new terrain
|
||||
m_Heightmap=new u16[m_MapSize*m_MapSize];
|
||||
m_Patches=new CPatch[m_MapSizePatches*m_MapSizePatches];
|
||||
|
@ -48,9 +48,11 @@
|
||||
#include "maths/MathUtil.h"
|
||||
|
||||
#include "simulation/BaseEntityCollection.h"
|
||||
#include "simulation/BaseFormationCollection.h"
|
||||
#include "simulation/Entity.h"
|
||||
#include "simulation/EntityHandles.h"
|
||||
#include "simulation/EntityManager.h"
|
||||
#include "simulation/FormationManager.h"
|
||||
#include "simulation/PathfindEngine.h"
|
||||
#include "simulation/Scheduler.h"
|
||||
#include "simulation/Projectile.h"
|
||||
@ -354,6 +356,8 @@ void Render()
|
||||
g_Selection.renderHealthBars();
|
||||
g_Mouseover.renderStaminaBars();
|
||||
g_Selection.renderStaminaBars();
|
||||
g_Selection.renderRanks();
|
||||
g_Mouseover.renderRanks();
|
||||
glPopMatrix();
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glPopMatrix();
|
||||
@ -495,6 +499,9 @@ static void InitScripting()
|
||||
CScriptEvent::ScriptingInit();
|
||||
CJSProgressTimer::ScriptingInit();
|
||||
CProjectile::ScriptingInit();
|
||||
|
||||
g_ScriptingHost.DefineConstant( "FORMATION_ENTER", CFormationEvent::FORMATION_ENTER );
|
||||
g_ScriptingHost.DefineConstant( "FORMATION_LEAVE", CFormationEvent::FORMATION_LEAVE );
|
||||
|
||||
g_ScriptingHost.DefineConstant( "NOTIFY_NONE", CEntityListener::NOTIFY_NONE );
|
||||
g_ScriptingHost.DefineConstant( "NOTIFY_GOTO", CEntityListener::NOTIFY_GOTO );
|
||||
@ -506,7 +513,7 @@ static void InitScripting()
|
||||
g_ScriptingHost.DefineConstant( "NOTIFY_ESCORT", CEntityListener::NOTIFY_ESCORT );
|
||||
g_ScriptingHost.DefineConstant( "NOTIFY_HEAL", CEntityListener::NOTIFY_HEAL );
|
||||
g_ScriptingHost.DefineConstant( "NOTIFY_GATHER", CEntityListener::NOTIFY_GATHER );
|
||||
|
||||
|
||||
g_ScriptingHost.DefineConstant( "ORDER_NONE", -1 );
|
||||
g_ScriptingHost.DefineConstant( "ORDER_GOTO", CEntityOrder::ORDER_GOTO );
|
||||
g_ScriptingHost.DefineConstant( "ORDER_RUN", CEntityOrder::ORDER_RUN );
|
||||
@ -742,7 +749,9 @@ void Shutdown()
|
||||
TIMER_BEGIN("shutdown game scripting stuff");
|
||||
delete &g_GameAttributes;
|
||||
delete &g_JSGameEvents;
|
||||
|
||||
|
||||
delete &g_FormationManager;
|
||||
delete &g_EntityFormationCollection;
|
||||
delete &g_EntityTemplateCollection;
|
||||
TIMER_END("shutdown game scripting stuff");
|
||||
|
||||
@ -968,6 +977,10 @@ void Init(int argc, char* argv[], uint flags)
|
||||
TIMER("Init_entitiessection");
|
||||
// This needs to be done after the renderer has loaded all its actors...
|
||||
new CBaseEntityCollection;
|
||||
new CBaseFormationCollection;
|
||||
g_EntityFormationCollection.loadTemplates();
|
||||
new CFormationManager;
|
||||
|
||||
// CEntityManager is managed by CWorld
|
||||
//new CEntityManager;
|
||||
new CSelectedEntities;
|
||||
@ -988,6 +1001,8 @@ void Init(int argc, char* argv[], uint flags)
|
||||
// Register a few Game/Network JS globals
|
||||
g_ScriptingHost.SetGlobal("g_GameAttributes", OBJECT_TO_JSVAL(g_GameAttributes.GetScript()));
|
||||
|
||||
|
||||
|
||||
// Check for heap corruption after every allocation. Very, very slowly.
|
||||
// (And it highlights the allocation just after the one you care about,
|
||||
// so you need to run it again and tell it to break on the one before.)
|
||||
|
@ -13,11 +13,14 @@
|
||||
#include "timer.h"
|
||||
#include "Game.h"
|
||||
#include "ps/Globals.h"
|
||||
#include "ps/VFSUtil.h"
|
||||
#include "Network/NetMessage.h"
|
||||
#include "BoundingObjects.h"
|
||||
#include "Unit.h"
|
||||
#include "Model.h"
|
||||
#include "simulation/BaseEntityCollection.h"
|
||||
#include "simulation/EntityFormation.h"
|
||||
#include "simulation/FormationManager.h"
|
||||
#include "scripting/GameEvents.h"
|
||||
#include "UnitManager.h"
|
||||
#include "MathUtil.h"
|
||||
@ -92,6 +95,7 @@ void CSelectedEntities::renderHealthBars()
|
||||
for( it = m_groups[m_group_highlight].begin(); it < m_groups[m_group_highlight].end(); it++ )
|
||||
(*it)->renderHealthBar();
|
||||
|
||||
|
||||
glDisable( GL_BLEND );
|
||||
}
|
||||
}
|
||||
@ -113,6 +117,25 @@ void CSelectedEntities::renderStaminaBars()
|
||||
glDisable( GL_BLEND );
|
||||
}
|
||||
}
|
||||
void CSelectedEntities::renderRanks()
|
||||
{
|
||||
std::vector<HEntity>::iterator it;
|
||||
for( it = m_selected.begin(); it < m_selected.end(); it++ )
|
||||
(*it)->renderRank();
|
||||
|
||||
if( m_group_highlight != -1 )
|
||||
{
|
||||
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
|
||||
glEnable( GL_BLEND );
|
||||
|
||||
std::vector<HEntity>::iterator it;
|
||||
for( it = m_groups[m_group_highlight].begin(); it < m_groups[m_group_highlight].end(); it++ )
|
||||
(*it)->renderRank();
|
||||
|
||||
glDisable( GL_BLEND );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CSelectedEntities::renderOverlays()
|
||||
{
|
||||
@ -793,7 +816,49 @@ void CMouseoverEntities::renderStaminaBars()
|
||||
|
||||
glDisable( GL_BLEND );
|
||||
}
|
||||
void CMouseoverEntities::renderRanks()
|
||||
{
|
||||
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
|
||||
glEnable( GL_BLEND );
|
||||
|
||||
std::vector<SMouseoverFader>::iterator it;
|
||||
for( it = m_mouseover.begin(); it < m_mouseover.end(); it++ )
|
||||
it->entity->renderRank();
|
||||
|
||||
glDisable( GL_BLEND );
|
||||
}
|
||||
|
||||
|
||||
int CSelectedEntities::loadRankTextures()
|
||||
{
|
||||
VFSUtil::FileList ranklist;
|
||||
VFSUtil::FindFiles( "art/textures/ui/session/icons", 0, ranklist );
|
||||
for ( std::vector<CStr>::iterator it = ranklist.begin(); it != ranklist.end(); it++ )
|
||||
{
|
||||
if ( !tex_is_known_extension(*it) )
|
||||
{
|
||||
LOG(ERROR, "Unknown rank texture extension", "%s", *it);
|
||||
continue;
|
||||
}
|
||||
Handle tmp = ogl_tex_load(*it);
|
||||
if (tmp <= 0)
|
||||
{
|
||||
LOG(ERROR, "Rank Textures", "loadRankTextures failed on \"%s\"", *it);
|
||||
return tmp;
|
||||
}
|
||||
m_rankTextures[it->AfterLast("/")] = tmp;
|
||||
RETURN_ERR(ogl_tex_upload(tmp));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void CSelectedEntities::destroyRankTextures()
|
||||
{
|
||||
for ( std::map<CStr, Handle>::iterator it=m_rankTextures.begin(); it != m_rankTextures.end(); it++ )
|
||||
{
|
||||
ogl_tex_free(it->second);
|
||||
it->second = 0;
|
||||
}
|
||||
}
|
||||
void CMouseoverEntities::renderOverlays()
|
||||
{
|
||||
CCamera *pCamera=g_Game->GetView()->GetCamera();
|
||||
@ -869,6 +934,14 @@ void FireWorldClickEvent(uint button, int clicks)
|
||||
g_Mouseover.m_target,
|
||||
(uint)g_Mouseover.m_worldposition.x,
|
||||
(uint)g_Mouseover.m_worldposition.y);
|
||||
|
||||
//Reset duplication flag- after this, it isn't the same order
|
||||
std::vector<HEntity>::iterator it=g_Selection.m_selected.begin();
|
||||
for ( ; it != g_Selection.m_selected.end(); it++ )
|
||||
{
|
||||
if ( (*it)->m_formation >= 0)
|
||||
(*it)->GetFormation()->SetDuplication(false);
|
||||
}
|
||||
}
|
||||
|
||||
void MouseButtonUpHandler(const SDL_Event *ev, int clicks)
|
||||
@ -968,7 +1041,6 @@ InReaction interactInputHandler( const SDL_Event* ev )
|
||||
if( g_Selection.m_selected.size() )
|
||||
pView->SetCameraTarget( g_Selection.getSelectionPosition() );
|
||||
break;
|
||||
|
||||
case HOTKEY_CAMERA_UNIT_VIEW:
|
||||
{
|
||||
if ( pView->IsAttached() )
|
||||
|
@ -36,6 +36,12 @@ struct CSelectedEntities : public Singleton<CSelectedEntities>
|
||||
m_secondaryAction = -1;
|
||||
m_selectionChanged = true;
|
||||
m_mouseOverMM = false;
|
||||
|
||||
loadRankTextures();
|
||||
}
|
||||
~CSelectedEntities()
|
||||
{
|
||||
destroyRankTextures();
|
||||
}
|
||||
std::vector<HEntity> m_selected;
|
||||
std::vector<HEntity> m_groups[MAX_GROUPS];
|
||||
@ -71,6 +77,11 @@ struct CSelectedEntities : public Singleton<CSelectedEntities>
|
||||
void renderOverlays();
|
||||
void renderHealthBars();
|
||||
void renderStaminaBars();
|
||||
void renderRanks();
|
||||
|
||||
void destroyRankTextures();
|
||||
int loadRankTextures();
|
||||
std::map<CStr, Handle> m_rankTextures;
|
||||
};
|
||||
|
||||
// CMouseoverEntities: the singleton containing entities the mouse is currently hovering over or bandboxing
|
||||
@ -121,6 +132,7 @@ struct CMouseoverEntities : public Singleton<CMouseoverEntities>
|
||||
void renderOverlays();
|
||||
void renderHealthBars();
|
||||
void renderStaminaBars();
|
||||
void renderRanks();
|
||||
|
||||
bool isBandbox() { return( m_bandbox ); }
|
||||
void startBandbox( u16 x, u16 y );
|
||||
|
@ -63,6 +63,8 @@ enum ENetMessageType
|
||||
NMT_Produce,
|
||||
NMT_Run,
|
||||
NMT_NotifyRequest,
|
||||
NMT_FormationGoto,
|
||||
NMT_FormationGeneric,
|
||||
|
||||
NMT_COMMAND_LAST,
|
||||
/* Post-Game Stage */
|
||||
@ -244,6 +246,16 @@ DERIVE_NMT_CLASS_(NetCommand, NotifyRequest)
|
||||
NMT_FIELD_INT(m_Action, u32, 4)
|
||||
END_NMT_CLASS()
|
||||
|
||||
DERIVE_NMT_CLASS_(NetCommand, FormationGoto)
|
||||
NMT_FIELD_INT(m_TargetX, u32, 2)
|
||||
NMT_FIELD_INT(m_TargetY, u32, 2)
|
||||
END_NMT_CLASS()
|
||||
|
||||
DERIVE_NMT_CLASS_(NetCommand, FormationGeneric)
|
||||
NMT_FIELD(HEntity, m_Target)
|
||||
NMT_FIELD_INT(m_Action, u32, 4)
|
||||
END_NMT_CLASS()
|
||||
|
||||
END_NMTS()
|
||||
|
||||
#else
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include <map>
|
||||
|
||||
#include "Entity.h"
|
||||
|
||||
#include "Vector2D.h"
|
||||
#define ALLNETMSGS_IMPLEMENT
|
||||
|
||||
#include "NetMessage.h"
|
||||
@ -84,6 +84,8 @@ void CNetMessage::ScriptingInit()
|
||||
def(NMT_Generic);
|
||||
def(NMT_Produce);
|
||||
def(NMT_NotifyRequest);
|
||||
def(NMT_FormationGoto);
|
||||
def(NMT_FormationGeneric);
|
||||
}
|
||||
|
||||
CNetCommand *CNetMessage::CommandFromJSArgs(const CEntityList &entities, JSContext *cx, uintN argc, jsval *argv)
|
||||
@ -201,10 +203,12 @@ CNetCommand *CNetMessage::CommandFromJSArgs(const CEntityList &entities, JSConte
|
||||
PositionMessage(Run)
|
||||
PositionMessage(Patrol)
|
||||
PositionMessage(AddWaypoint)
|
||||
|
||||
PositionMessage(FormationGoto)
|
||||
// NMT_Generic, target, action
|
||||
EntityIntMessage(Generic)
|
||||
EntityIntMessage(NotifyRequest)
|
||||
EntityIntMessage(FormationGeneric)
|
||||
|
||||
|
||||
// NMT_Produce, type, name
|
||||
ProduceMessage(Produce)
|
||||
@ -214,3 +218,137 @@ CNetCommand *CNetMessage::CommandFromJSArgs(const CEntityList &entities, JSConte
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
CNetCommand *CNetMessage::CastCommand(CNetMessage*& message, const CEntityList& entities, const ENetMessageType type)
|
||||
{
|
||||
#define CopyPositionMessage(_msg) \
|
||||
case NMT_ ## _msg: \
|
||||
{ \
|
||||
C##_msg *msg = new C##_msg(); \
|
||||
C##_msg *castmsg = static_cast<C##_msg*>(message); \
|
||||
msg->m_TargetX = castmsg->m_TargetX; \
|
||||
msg->m_TargetY = castmsg->m_TargetY; \
|
||||
msg->m_Entities = entities; \
|
||||
return msg; \
|
||||
}
|
||||
|
||||
#define CopyEntityMessage(_msg) \
|
||||
case NMT_ ## _msg: \
|
||||
{ \
|
||||
C##_msg *msg = new C##_msg(); \
|
||||
C##_msg *castmsg = static_cast<C##_msg*>(message); \
|
||||
msg->m_Entities = entities; \
|
||||
msg->m_Target = castmsg->Target; \
|
||||
return msg; \
|
||||
}
|
||||
|
||||
#define CopyEntityIntMessage(_msg) \
|
||||
case NMT_ ## _msg: \
|
||||
{ \
|
||||
C##_msg *msg = new C##_msg(); \
|
||||
C##_msg *castmsg = static_cast<C##_msg*>(message); \
|
||||
msg->m_Entities = entities; \
|
||||
msg->m_Target = castmsg->m_Target; \
|
||||
msg->m_Action = castmsg->m_Action; \
|
||||
return msg; \
|
||||
}
|
||||
|
||||
#define CopyProduceMessage(_msg) \
|
||||
case NMT_ ## _msg: \
|
||||
{ \
|
||||
C##_msg *msg = new C##_msg(); \
|
||||
C##_msg *castmsg = static_cast<C##_msg*>(message); \
|
||||
msg->m_Entities = entities; \
|
||||
msg->m_Name = castmsg->m_Name; \
|
||||
msg->m_Type = castmsg->m_Type; \
|
||||
return msg; \
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
CopyPositionMessage(Goto)
|
||||
CopyPositionMessage(Run)
|
||||
CopyPositionMessage(Patrol)
|
||||
CopyPositionMessage(AddWaypoint)
|
||||
CopyPositionMessage(FormationGoto)
|
||||
|
||||
CopyEntityIntMessage(Generic)
|
||||
CopyEntityIntMessage(NotifyRequest)
|
||||
CopyEntityIntMessage(FormationGeneric)
|
||||
|
||||
CopyProduceMessage(Produce)
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
CNetMessage *CNetMessage::CreatePositionMessage( const CEntityList& entities, const ENetMessageType type, CVector2D pos )
|
||||
{
|
||||
#define PosMessage(_msg) \
|
||||
case NMT_ ## _msg: \
|
||||
{ \
|
||||
C##_msg* msg = new C##_msg(); \
|
||||
msg->m_Entities = entities; \
|
||||
msg->m_TargetX = pos.x; \
|
||||
msg->m_TargetY = pos.y; \
|
||||
return msg; \
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
PosMessage(Goto)
|
||||
PosMessage(Run)
|
||||
PosMessage(Patrol)
|
||||
PosMessage(AddWaypoint)
|
||||
PosMessage(FormationGoto)
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
CNetMessage *CNetMessage::CreateEntityIntMessage( const CEntityList& entities, const ENetMessageType type, HEntity& target, int action )
|
||||
{
|
||||
#define EntMessage(_msg) \
|
||||
case NMT_ ## _msg: \
|
||||
{ \
|
||||
C##_msg* msg = new C##_msg(); \
|
||||
msg->m_Entities = entities; \
|
||||
msg->m_Target = target; \
|
||||
msg->m_Action = action; \
|
||||
return msg; \
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
EntMessage(Generic)
|
||||
EntMessage(NotifyRequest)
|
||||
EntMessage(FormationGeneric)
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
CNetMessage *CNetMessage::CreateProduceMessage( const CEntityList& entities, const ENetMessageType type, int proType, CStrW name )
|
||||
{
|
||||
#define ProMessage(_msg)\
|
||||
case NMT_ ## _msg: \
|
||||
{ \
|
||||
C##_msg* msg = new C##_msg(); \
|
||||
msg->m_Entities = entities; \
|
||||
msg->m_Type = proType; \
|
||||
msg->m_Name = name; \
|
||||
return msg; \
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
ProMessage(Produce)
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#undef ALLNETMSGS_DONT_CREATE_NMTS
|
||||
|
||||
class CNetCommand;
|
||||
class CVector2D;
|
||||
struct CEntityList;
|
||||
|
||||
/**
|
||||
@ -74,6 +75,13 @@ public:
|
||||
static void ScriptingInit();
|
||||
|
||||
static CNetCommand *CommandFromJSArgs(const CEntityList &entities, JSContext* cx, uintN argc, jsval* argv);
|
||||
//This, in a sense, a netmessage typecast. It copies the target data from "message", creates
|
||||
//a message of type "type", and assigns the message entities as "entities".
|
||||
static CNetCommand *CastCommand(CNetMessage*& message, const CEntityList &entities, const ENetMessageType type);
|
||||
//These can create a net message without JS args
|
||||
static CNetMessage *CreatePositionMessage( const CEntityList& entities, const ENetMessageType type, CVector2D pos );
|
||||
static CNetMessage *CreateEntityIntMessage( const CEntityList& entities, const ENetMessageType type, HEntity& target, int action );
|
||||
static CNetMessage *CreateProduceMessage( const CEntityList& entities, const ENetMessageType type, int proType, CStrW name );
|
||||
};
|
||||
|
||||
typedef CNetMessage * (*NetMessageDeserializer) (const u8 *buffer, uint length);
|
||||
|
@ -135,7 +135,7 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow)
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
|
||||
// render everything fullbright
|
||||
// set up texture environment for base pass
|
||||
MICROLOG(L"base splat textures");
|
||||
@ -154,13 +154,13 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow)
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
|
||||
static const float one[4] = { 1.f, 1.f, 1.f, 1.f };
|
||||
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, one);
|
||||
|
||||
|
||||
for(uint i = 0; i < m->visiblePatches.size(); ++i)
|
||||
{
|
||||
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData();
|
||||
patchdata->RenderBase(true); // with LOS color
|
||||
}
|
||||
|
||||
|
||||
// render blends
|
||||
// switch on the composite alpha map texture
|
||||
(void)ogl_tex_bind(g_Renderer.m_hCompositeAlphaMap, 1);
|
||||
@ -185,14 +185,14 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow)
|
||||
|
||||
// no need to write to the depth buffer a second time
|
||||
glDepthMask(0);
|
||||
|
||||
|
||||
// render blend passes for each patch
|
||||
for(uint i = 0; i < m->visiblePatches.size(); ++i)
|
||||
{
|
||||
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData();
|
||||
patchdata->RenderBlends();
|
||||
}
|
||||
|
||||
|
||||
// Disable second texcoord array
|
||||
pglClientActiveTextureARB(GL_TEXTURE1);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
@ -201,7 +201,7 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow)
|
||||
const CLightEnv& lightEnv = g_Renderer.GetLightEnv();
|
||||
|
||||
glBlendFunc(GL_DST_COLOR, GL_ZERO);
|
||||
|
||||
|
||||
if (!shadow)
|
||||
{
|
||||
pglActiveTextureARB(GL_TEXTURE0);
|
||||
@ -219,6 +219,7 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow)
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
|
||||
|
||||
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, &lightEnv.m_TerrainAmbientColor.X);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -275,6 +276,7 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow)
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
|
||||
|
||||
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, &lightEnv.m_TerrainAmbientColor.X);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -301,36 +303,39 @@ void TerrainRenderer::RenderTerrain(ShadowMap* shadow)
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
|
||||
|
||||
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, &lightEnv.m_TerrainAmbientColor.X);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pglActiveTextureARB(GL_TEXTURE0);
|
||||
pglClientActiveTextureARB(GL_TEXTURE0);
|
||||
|
||||
|
||||
for (uint i = 0; i < m->visiblePatches.size(); ++i)
|
||||
{
|
||||
CPatchRData* patchdata = (CPatchRData*)m->visiblePatches[i]->GetRenderData();
|
||||
patchdata->RenderStreams(STREAM_POS|STREAM_COLOR|STREAM_POSTOUV0, false);
|
||||
}
|
||||
|
||||
|
||||
glMatrixMode(GL_TEXTURE);
|
||||
glLoadIdentity();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
// restore OpenGL state
|
||||
g_Renderer.BindTexture(2,0);
|
||||
if ( shadow )
|
||||
{
|
||||
if ( shadow->GetUseDepthTexture() )
|
||||
g_Renderer.BindTexture(2,0);
|
||||
}
|
||||
g_Renderer.BindTexture(1,0);
|
||||
|
||||
pglClientActiveTextureARB(GL_TEXTURE0);
|
||||
pglActiveTextureARB(GL_TEXTURE0);
|
||||
|
||||
glDepthMask(1);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -370,7 +375,8 @@ void TerrainRenderer::RenderWater()
|
||||
{
|
||||
PROFILE(" render water ");
|
||||
|
||||
//Fresnel effect
|
||||
|
||||
//(Crappy) fresnel effect
|
||||
CCamera* Camera=g_Game->GetView()->GetCamera();
|
||||
CVector3D CamFace=Camera->m_Orientation.GetIn();
|
||||
CamFace.Normalize();
|
||||
@ -385,6 +391,7 @@ void TerrainRenderer::RenderWater()
|
||||
int mapSize = terrain->GetVerticesPerSide();
|
||||
CLOSManager* losMgr = g_Game->GetWorld()->GetLOSManager();
|
||||
WaterManager* WaterMgr = g_Renderer.GetWaterManager();
|
||||
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
|
||||
@ -414,7 +421,7 @@ void TerrainRenderer::RenderWater()
|
||||
|
||||
// Set the proper LOD bias
|
||||
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
|
||||
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
for(size_t i=0; i<m->visiblePatches.size(); i++)
|
||||
@ -433,7 +440,7 @@ void TerrainRenderer::RenderWater()
|
||||
for(int j=0; j<4; j++)
|
||||
{
|
||||
float terrainHeight = terrain->getVertexGroundLevel(x + DX[j], z + DZ[j]);
|
||||
if(terrainHeight < WaterMgr->m_WaterHeight)
|
||||
if( terrainHeight < WaterMgr->m_WaterHeight )
|
||||
{
|
||||
shouldRender = true;
|
||||
break;
|
||||
@ -481,12 +488,10 @@ void TerrainRenderer::RenderWater()
|
||||
} //end of x loop
|
||||
} //end of z loop
|
||||
}
|
||||
|
||||
glEnd();
|
||||
|
||||
glLoadIdentity();
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
glDepthMask(GL_TRUE);
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
|
@ -21,6 +21,7 @@ enum EEventType
|
||||
EVENT_PREPARE_ORDER,
|
||||
EVENT_ORDER_TRANSITION,
|
||||
EVENT_NOTIFICATION,
|
||||
EVENT_FORMATION,
|
||||
EVENT_LAST,
|
||||
|
||||
// Projectile events
|
||||
@ -47,7 +48,8 @@ static const wchar_t* const EventNames[EVENT_LAST] =
|
||||
/* EVENT_TARGET_CHANGED */ L"onTargetChanged", /* If this unit is selected and the mouseover object changes */
|
||||
/* EVENT_PREPARE_ORDER */ L"onPrepareOrder", /* To check if a unit can execute a given order */
|
||||
/* EVENT_ORDER_TRANSITION */ L"onOrderTransition", /* When we change orders (sometimes...) */
|
||||
/* EVENT_NOTIFICATION */ L"onNotification" /*When we receive a notification */
|
||||
/* EVENT_NOTIFICATION */ L"onNotification", /*When we receive a notification */
|
||||
/* EVENT_FORMATION */ L"onFormation" /* When this unit does something with a formation */
|
||||
};
|
||||
|
||||
#endif // #ifndef EVENTTYPES_H__
|
||||
|
@ -13,7 +13,9 @@
|
||||
#include "EntityHandles.h"
|
||||
#include "Entity.h"
|
||||
#include "EntityManager.h"
|
||||
|
||||
#include "BaseEntityCollection.h"
|
||||
#include "EntityFormation.h"
|
||||
#include "Scheduler.h"
|
||||
#include "timer.h"
|
||||
#include "LightEnv.h"
|
||||
@ -40,6 +42,8 @@
|
||||
#include "graphics/scripting/JSInterface_LightEnv.h"
|
||||
#include "scripting/JSConversions.h"
|
||||
#include "renderer/WaterManager.h"
|
||||
#include "simulation/FormationManager.h"
|
||||
|
||||
#ifndef NO_GUI
|
||||
# include "gui/scripting/JSInterface_IGUIObject.h"
|
||||
#endif
|
||||
@ -88,7 +92,6 @@ extern bool g_TerrainModified;
|
||||
return JS_FALSE;\
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Output
|
||||
//-----------------------------------------------------------------------------
|
||||
@ -189,7 +192,27 @@ JSBool getEntityTemplate( JSContext* cx, JSObject*, uint argc, jsval* argv, jsva
|
||||
*rval = OBJECT_TO_JSVAL( v->GetScript() );
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
//Used to create net messages for formations--msgList.front() is the original message. see issueCommand
|
||||
void CreateFormationMessage( std::vector<CNetMessage*>& msgList, CNetMessage*& msg, HEntity formationEnt )
|
||||
{
|
||||
if ( formationEnt->GetFormation()->IsDuplication() || msgList.empty() )
|
||||
return;
|
||||
|
||||
CNetMessage* tmp;
|
||||
ENetMessageType type=msg->GetType();
|
||||
CEntityList formation = formationEnt->GetFormation()->GetEntityList();
|
||||
if ( type == NMT_Goto || type == NMT_Run )
|
||||
{
|
||||
//formationEnt->GetFormation()->BaseToMovement();
|
||||
tmp = CNetMessage::CastCommand(msg, formation, NMT_FormationGoto);
|
||||
}
|
||||
else if ( type == NMT_Generic )
|
||||
tmp = CNetMessage::CastCommand(msg, formation, NMT_FormationGeneric);
|
||||
else
|
||||
return;
|
||||
|
||||
msgList.push_back(tmp);
|
||||
}
|
||||
|
||||
// Issue a command (network message) to an entity or collection.
|
||||
// params: either an entity- or entity collection object, message ID [int],
|
||||
@ -200,7 +223,7 @@ JSBool issueCommand( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rv
|
||||
REQUIRE_MIN_PARAMS(2, issueCommand);
|
||||
debug_assert(JSVAL_IS_OBJECT(argv[0]));
|
||||
*rval = JSVAL_NULL;
|
||||
|
||||
|
||||
CEntityList entities;
|
||||
|
||||
if (JS_GetClass(JSVAL_TO_OBJECT(argv[0])) == &CEntity::JSI_class)
|
||||
@ -215,18 +238,42 @@ JSBool issueCommand( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rv
|
||||
entities[i]->DestroyListeners( entities[i]->m_currentListener );
|
||||
entities[i]->m_currentListener = NULL;
|
||||
}
|
||||
CNetMessage *msg = CNetMessage::CommandFromJSArgs(entities, cx, argc-1, argv+1);
|
||||
if (msg)
|
||||
std::vector<CNetMessage*> messages;
|
||||
CNetMessage* msg = CNetMessage::CommandFromJSArgs(entities, cx, argc-1, argv+1);
|
||||
if (!msg)
|
||||
return JS_TRUE;
|
||||
|
||||
messages.push_back(msg);
|
||||
//Generate messages for formations
|
||||
for (size_t i=0; i < entities.size(); i++ )
|
||||
{
|
||||
g_Console->InsertMessage(L"issueCommand: %hs", msg->GetString().c_str());
|
||||
g_Game->GetSimulation()->QueueLocalCommand(msg);
|
||||
*rval = g_ScriptingHost.UCStringToValue(msg->GetString());
|
||||
if ( entities[i]->m_formation >= 0)
|
||||
{
|
||||
CEntityFormation* formation = entities[i]->GetFormation();
|
||||
bool duplicate = formation->IsDuplication();
|
||||
|
||||
if ( formation->IsLocked() && !duplicate)
|
||||
{
|
||||
formation->SelectAllUnits();
|
||||
CreateFormationMessage(messages, msg, entities[i]);
|
||||
formation->SetDuplication(true);
|
||||
entities.erase( entities.begin()+i ); //we don't want to be in two orders
|
||||
}
|
||||
else if ( duplicate )
|
||||
entities.erase( entities.begin()+i );
|
||||
}
|
||||
}
|
||||
|
||||
for ( std::vector<CNetMessage*>::iterator it=messages.begin(); it != messages.end(); it++ )
|
||||
{
|
||||
g_Console->InsertMessage(L"issueCommand: %hs", (*it)->GetString().c_str());
|
||||
g_Game->GetSimulation()->QueueLocalCommand(*it);
|
||||
*rval = g_ScriptingHost.UCStringToValue((*it)->GetString());
|
||||
}
|
||||
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Events
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -38,6 +38,9 @@ CBaseEntity::CBaseEntity()
|
||||
AddProperty( L"traits.stamina.max", &m_staminaMax );
|
||||
AddProperty( L"traits.stamina.bar_height", &m_staminaBarHeight );
|
||||
AddProperty( L"traits.stamina.bar_size", &m_staminaBarSize );
|
||||
AddProperty( L"traits.rank.size", &m_rankSize );
|
||||
AddProperty( L"traits.rank.height", &m_rankHeight );
|
||||
AddProperty( L"traits.rank.name", &m_rankName );
|
||||
AddProperty( L"traits.minimap.type", &m_minimapType );
|
||||
AddProperty( L"traits.minimap.red", &m_minimapR );
|
||||
AddProperty( L"traits.minimap.green", &m_minimapG );
|
||||
|
@ -72,6 +72,11 @@ public:
|
||||
float m_healthRegenStart;
|
||||
float m_healthDecayRate;
|
||||
|
||||
//Rank properties
|
||||
float m_rankHeight;
|
||||
int m_rankSize;
|
||||
CStr m_rankName;
|
||||
|
||||
// Minimap properties
|
||||
CStrW m_minimapType;
|
||||
int m_minimapR;
|
||||
|
187
source/simulation/BaseFormation.cpp
Normal file
187
source/simulation/BaseFormation.cpp
Normal file
@ -0,0 +1,187 @@
|
||||
#include "precompiled.h"
|
||||
#include "BaseFormation.h"
|
||||
#include "CLogger.h"
|
||||
#include "CStr.h"
|
||||
|
||||
#define LOG_CATEGORY "Formation"
|
||||
|
||||
CBaseFormation::CBaseFormation()
|
||||
{
|
||||
m_numSlots = 0;
|
||||
}
|
||||
bool CBaseFormation::loadXML(CStr filename)
|
||||
{
|
||||
CXeromyces XeroFile;
|
||||
|
||||
if (XeroFile.Load(filename) != PSRETURN_OK)
|
||||
return false;
|
||||
|
||||
#define EL(x) int el_##x = XeroFile.getElementID(#x)
|
||||
#define AT(x) int at_##x = XeroFile.getAttributeID(#x)
|
||||
EL(formation);
|
||||
EL(fl);
|
||||
EL(rk);
|
||||
EL(blank);
|
||||
|
||||
AT(tag);
|
||||
AT(bonus);
|
||||
AT(bonustype);
|
||||
AT(bonusval);
|
||||
AT(penalty);
|
||||
AT(penaltytype);
|
||||
AT(penaltyval);
|
||||
AT(required);
|
||||
AT(next);
|
||||
AT(prior);
|
||||
AT(movement);
|
||||
AT(filespacing);
|
||||
AT(rankspacing);
|
||||
AT(category);
|
||||
AT(order);
|
||||
#undef AT
|
||||
#undef EL
|
||||
|
||||
XMBElement Root = XeroFile.getRoot();
|
||||
if( Root.getNodeName() != el_formation )
|
||||
{
|
||||
LOG( ERROR, LOG_CATEGORY, "CBaseFormation::LoadXML: XML root was not \"Formation\" in file %s. Load failed.", filename.c_str() );
|
||||
return( false );
|
||||
}
|
||||
|
||||
//Load in single attributes
|
||||
XMBAttributeList Attributes = Root.getAttributes();
|
||||
for ( int i=0; i<Attributes.Count; ++i )
|
||||
{
|
||||
XMBAttribute Attr = Attributes.item(i);
|
||||
if ( Attr.Name == at_tag )
|
||||
m_tag = CStr(Attr.Value);
|
||||
else if ( Attr.Name == at_bonus )
|
||||
m_bonus = CStr(Attr.Value);
|
||||
else if ( Attr.Name == at_bonustype )
|
||||
m_bonusType = CStr(Attr.Value);
|
||||
else if ( Attr.Name == at_bonusval )
|
||||
m_bonusVal = CStr(Attr.Value).ToFloat();
|
||||
else if ( Attr.Name == at_penalty )
|
||||
m_penalty = CStr(Attr.Value);
|
||||
else if ( Attr.Name == at_penaltytype )
|
||||
m_penaltyType = CStr(Attr.Value);
|
||||
else if ( Attr.Name == at_penaltyval )
|
||||
m_penaltyVal = CStr(Attr.Value).ToFloat();
|
||||
else if ( Attr.Name == at_required)
|
||||
m_required = CStr(Attr.Value).ToInt();
|
||||
else if ( Attr.Name == at_next )
|
||||
m_next = CStr(Attr.Value);
|
||||
else if ( Attr.Name == at_prior )
|
||||
m_prior = CStr(Attr.Value);
|
||||
else if ( Attr.Name == at_movement )
|
||||
m_movement = CStr(Attr.Value);
|
||||
else if ( Attr.Name == at_rankspacing )
|
||||
m_rankSpacing = CStr(Attr.Value).ToFloat();
|
||||
else if ( Attr.Name == at_filespacing )
|
||||
m_fileSpacing = CStr(Attr.Value).ToFloat();
|
||||
else
|
||||
{
|
||||
CStr invAttr = XeroFile.getAttributeString(Attr.Name);
|
||||
LOG( ERROR, LOG_CATEGORY, "CBaseFormation::LoadXML: Invalid attribute defined in formation file %s. Load failed.", filename.c_str() );
|
||||
return( false );
|
||||
}
|
||||
}
|
||||
|
||||
XMBElementList RootChildren = Root.getChildNodes();
|
||||
int file=0;
|
||||
int rank=0;
|
||||
int maxrank=0;
|
||||
|
||||
//Read in files and ranks
|
||||
for (int i = 0; i < RootChildren.Count; ++i)
|
||||
{
|
||||
XMBElement RootChild = RootChildren.item(i);
|
||||
int ChildName = RootChild.getNodeName();
|
||||
|
||||
if ( ChildName == el_fl )
|
||||
{
|
||||
rank = 0;
|
||||
XMBAttributeList FileAttribList = RootChild.getAttributes();
|
||||
//Load default category
|
||||
CStr FileCatValue = FileAttribList.getNamedItem(at_category);
|
||||
|
||||
//Specific slots in this file (row)
|
||||
XMBElementList RankNodes = RootChild.getChildNodes();
|
||||
for ( int r=0; r<RankNodes.Count; ++r )
|
||||
{
|
||||
XMBElement Rank = RankNodes.item(r);
|
||||
if ( Rank.getNodeName() == el_blank )
|
||||
{
|
||||
++rank;
|
||||
continue;
|
||||
}
|
||||
else if ( Rank.getNodeName() != el_rk )
|
||||
return false;
|
||||
//error
|
||||
|
||||
XMBAttributeList RankAttribList = Rank.getAttributes();
|
||||
int order = CStr( RankAttribList.getNamedItem(at_order) ).ToInt();
|
||||
CStr category = CStr( RankAttribList.getNamedItem(at_category) );
|
||||
|
||||
if( order <= 0 )
|
||||
{
|
||||
LOG( ERROR, LOG_CATEGORY, "CBaseFormation::LoadXML: Invalid (negative number or 0) order defined in formation file %s. The game will try to continue anyway.", filename.c_str() );
|
||||
continue;
|
||||
}
|
||||
--order; //We need this to be in line with arrays, so start at 0
|
||||
//if ( category.length() )
|
||||
//AssignCategory(order, category);
|
||||
//else
|
||||
AssignCategory(order, FileCatValue);
|
||||
|
||||
m_slots[order].fileOff = file * m_fileSpacing;
|
||||
m_slots[order].rankOff = rank * m_rankSpacing;
|
||||
++m_numSlots;
|
||||
++rank;
|
||||
}
|
||||
if ( rank > maxrank )
|
||||
maxrank = rank;
|
||||
++file;
|
||||
} //if el_fl
|
||||
|
||||
else if ( ChildName == el_blank )
|
||||
++file;
|
||||
}
|
||||
|
||||
float centerx = maxrank * m_rankSpacing / 2.0f;
|
||||
float centery = file * m_fileSpacing / 2.0f;
|
||||
|
||||
//Here we check to make sure no order was skipped over. If so, failure, because we rely
|
||||
//on a linearly accessible slots in entityformation.cpp.
|
||||
for ( int i=0; i<m_numSlots; ++i )
|
||||
{
|
||||
if ( m_slots.find(i) == m_slots.end() )
|
||||
{
|
||||
LOG( ERROR, LOG_CATEGORY, "CBaseFormation::LoadXML: Missing orders in %s. Load failed.", filename.c_str() );
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_slots[i].rankOff = m_slots[i].rankOff - centerx;
|
||||
m_slots[i].fileOff = m_slots[i].fileOff - centery;
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void CBaseFormation::AssignCategory(int order, CStr category)
|
||||
{
|
||||
category.Remove( CStr(",") );
|
||||
category = category + " "; //So the final word will be pushed as well
|
||||
CStr temp;
|
||||
|
||||
//Push categories until last space
|
||||
while ( ( temp = category.BeforeFirst(" ") ) != "" )
|
||||
{
|
||||
m_slots[order].category.push_back(temp);
|
||||
//Don't use remove because certain categories could be substrings of others
|
||||
size_t off = category.find(temp);
|
||||
category.erase( off, temp.length() );
|
||||
}
|
||||
}
|
67
source/simulation/BaseFormation.h
Normal file
67
source/simulation/BaseFormation.h
Normal file
@ -0,0 +1,67 @@
|
||||
//Andrew aka pyrolink
|
||||
//ajdecker1022@msn.com
|
||||
//This class loads all the formation data needs to create an instance of a particular formation.
|
||||
|
||||
#ifndef BASEFORMATION_INCLUDED
|
||||
#define BASEFORMATION_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include "XML/Xeromyces.h"
|
||||
|
||||
class CStr;
|
||||
|
||||
class CBaseFormation
|
||||
{
|
||||
friend class CFormationManager;
|
||||
friend class CBaseFormationCollection;
|
||||
friend class CEntityFormation;
|
||||
|
||||
struct FormationSlot
|
||||
{
|
||||
float fileOff; //Distance between files of this slot and the formation's center
|
||||
float rankOff;
|
||||
std::vector<CStr> category;
|
||||
};
|
||||
|
||||
public:
|
||||
CBaseFormation();
|
||||
~CBaseFormation(){}
|
||||
|
||||
CStr GetBonus(){ return m_bonus; }
|
||||
CStr GetBonusType(){ return m_bonusType; }
|
||||
float GetBonusVal(){ return m_bonusVal; }
|
||||
|
||||
CStr GetPenalty(){ return m_penalty; }
|
||||
CStr GetPenaltyType(){ return m_penaltyType; }
|
||||
float GetPenaltyVal(){ return m_penaltyVal; }
|
||||
|
||||
private:
|
||||
|
||||
CStr m_tag;
|
||||
CStr m_bonus;
|
||||
CStr m_bonusType;
|
||||
float m_bonusVal;
|
||||
|
||||
CStr m_penalty;
|
||||
CStr m_penaltyType;
|
||||
float m_penaltyVal;
|
||||
|
||||
int m_required;
|
||||
CStr m_next;
|
||||
CStr m_prior;
|
||||
CStr m_movement;
|
||||
float m_fileSpacing;
|
||||
float m_rankSpacing;
|
||||
|
||||
int m_numSlots; //how many possible slots in this formation
|
||||
int m_numRanks;
|
||||
int m_numFiles;
|
||||
|
||||
//The key is the "order" of the slot
|
||||
std::map<int, FormationSlot> m_slots;
|
||||
|
||||
bool loadXML(CStr filename);
|
||||
void AssignCategory(int order, CStr category); //takes care of formatting strings
|
||||
};
|
||||
#endif
|
77
source/simulation/BaseFormationCollection.cpp
Normal file
77
source/simulation/BaseFormationCollection.cpp
Normal file
@ -0,0 +1,77 @@
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "BaseFormationCollection.h"
|
||||
#include "ObjectManager.h"
|
||||
#include "Model.h"
|
||||
#include "CLogger.h"
|
||||
#include "VFSUtil.h"
|
||||
|
||||
#define LOG_CATEGORY "formation"
|
||||
|
||||
|
||||
void CBaseFormationCollection::LoadFile( const char* path )
|
||||
{
|
||||
// Build the formation name -> filename mapping. This is done so that
|
||||
// the formation 'x' can be in units/x.xml, structures/x.xml, etc, and
|
||||
// we don't have to search every directory for x.xml.
|
||||
|
||||
// Extract the filename out of the path+name+extension.
|
||||
// Equivalent to /.*\/(.*)\.xml/, but not as pretty.
|
||||
CStrW tag = CStr(path).AfterLast("/").BeforeLast(".xml");
|
||||
m_templateFilenames[tag] = path;
|
||||
}
|
||||
|
||||
static void LoadFormationThunk( const char* path, const DirEnt* UNUSED(ent), void* context )
|
||||
{
|
||||
CBaseFormationCollection* this_ = (CBaseFormationCollection*)context;
|
||||
this_->LoadFile(path);
|
||||
}
|
||||
|
||||
int CBaseFormationCollection::loadTemplates()
|
||||
{
|
||||
// Load all files in entities/formations and subdirectories.
|
||||
THROW_ERR( VFSUtil::EnumDirEnts( "entities/formations", VFSUtil::RECURSIVE, "*.xml",
|
||||
LoadFormationThunk, this ) );
|
||||
return 0;
|
||||
}
|
||||
|
||||
CBaseFormation* CBaseFormationCollection::getTemplate( CStrW name )
|
||||
{
|
||||
// Check whether this template has already been loaded
|
||||
templateMap::iterator it = m_templates.find( name );
|
||||
if( it != m_templates.end() )
|
||||
return( it->second );
|
||||
|
||||
// Find the filename corresponding to this template
|
||||
templateFilenameMap::iterator filename_it = m_templateFilenames.find( name );
|
||||
if( filename_it == m_templateFilenames.end() )
|
||||
return( NULL );
|
||||
|
||||
CStr path( filename_it->second );
|
||||
|
||||
//Try to load to the formation
|
||||
CBaseFormation* newTemplate = new CBaseFormation();
|
||||
if( !newTemplate->loadXML( path ) )
|
||||
{
|
||||
LOG(ERROR, LOG_CATEGORY, "CBaseFormationCollection::loadTemplates(): Couldn't load template \"%s\"", path.c_str());
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
LOG(NORMAL, LOG_CATEGORY, "CBaseFormationCollection::loadTemplates(): Loaded template \"%s\"", path.c_str());
|
||||
m_templates[name] = newTemplate;
|
||||
|
||||
return newTemplate;
|
||||
}
|
||||
|
||||
void CBaseFormationCollection::getBaseFormationNames( std::vector<CStrW>& names )
|
||||
{
|
||||
for( templateFilenameMap::iterator it = m_templateFilenames.begin(); it != m_templateFilenames.end(); ++it )
|
||||
if( ! (it->first.Length() > 8 && it->first.Left(8) == L"template"))
|
||||
names.push_back( it->first );
|
||||
}
|
||||
|
||||
CBaseFormationCollection::~CBaseFormationCollection()
|
||||
{
|
||||
for( templateMap::iterator it = m_templates.begin(); it != m_templates.end(); ++it )
|
||||
delete( it->second );
|
||||
}
|
37
source/simulation/BaseFormationCollection.h
Normal file
37
source/simulation/BaseFormationCollection.h
Normal file
@ -0,0 +1,37 @@
|
||||
//BaseFormationCollection.h
|
||||
//Andrew aka pyrolink: ajdecker1022@msn.com
|
||||
|
||||
//Nearly identical to BaseEntityCollection and associates i.e. BaseEntity, Entity.
|
||||
//This is the manager of the entity formation "templates"
|
||||
|
||||
|
||||
#ifndef BASEFORM_COLLECTION_INCLUDED
|
||||
#define BASEFORM_COLLECTION_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
#include "CStr.h"
|
||||
#include "Singleton.h"
|
||||
#include "BaseFormation.h"
|
||||
|
||||
#define g_EntityFormationCollection CBaseFormationCollection::GetSingleton()
|
||||
|
||||
|
||||
class CBaseFormationCollection : public Singleton<CBaseFormationCollection>
|
||||
{
|
||||
|
||||
typedef std::map<CStrW, CBaseFormation*> templateMap;
|
||||
typedef std::map<CStrW, CStr> templateFilenameMap;
|
||||
templateMap m_templates;
|
||||
templateFilenameMap m_templateFilenames;
|
||||
public:
|
||||
~CBaseFormationCollection();
|
||||
CBaseFormation* getTemplate( CStrW formationType );
|
||||
int loadTemplates();
|
||||
void LoadFile( const char* path );
|
||||
|
||||
// Create a list of the names of all base entities, excluding template_*,
|
||||
// for display in ScEd's entity-selection box.
|
||||
void getBaseFormationNames( std::vector<CStrW>& names );
|
||||
};
|
||||
|
||||
#endif
|
@ -21,8 +21,12 @@
|
||||
#include "MathUtil.h"
|
||||
#include "CConsole.h"
|
||||
#include "renderer/WaterManager.h"
|
||||
#include "EntityFormation.h"
|
||||
#include "simulation/FormationManager.h"
|
||||
#include "BaseFormation.h"
|
||||
#include "graphics/GameView.h"
|
||||
|
||||
|
||||
extern CConsole* g_Console;
|
||||
extern int g_xres, g_yres;
|
||||
|
||||
@ -64,6 +68,9 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, cons
|
||||
AddProperty( L"traits.stamina.max", &m_staminaMax );
|
||||
AddProperty( L"traits.stamina.bar_height", &m_staminaBarHeight );
|
||||
AddProperty( L"traits.stamina.bar_size", &m_staminaBarSize );
|
||||
AddProperty( L"traits.rank.size", &m_rankSize );
|
||||
AddProperty( L"traits.rank.height", &m_rankHeight );
|
||||
AddProperty( L"traits.rank.name", &m_rankName );
|
||||
AddProperty( L"traits.minimap.type", &m_minimapType );
|
||||
AddProperty( L"traits.minimap.red", &m_minimapR );
|
||||
AddProperty( L"traits.minimap.green", &m_minimapG );
|
||||
@ -121,7 +128,9 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, cons
|
||||
m_lastCombatTime = 0;
|
||||
m_currentNotification = 0;
|
||||
m_currentListener = NULL;
|
||||
|
||||
|
||||
m_formationSlot = -1;
|
||||
m_formation = -1;
|
||||
m_grouped = -1;
|
||||
|
||||
m_building = building;
|
||||
@ -151,6 +160,9 @@ CEntity::~CEntity()
|
||||
delete it->second;
|
||||
}
|
||||
m_auras.clear();
|
||||
|
||||
CEntity* remove = this;
|
||||
g_FormationManager.RemoveUnit(remove);
|
||||
}
|
||||
|
||||
void CEntity::loadBase()
|
||||
@ -192,8 +204,11 @@ void CEntity::loadBase()
|
||||
void CEntity::kill()
|
||||
{
|
||||
g_Selection.removeAll( me );
|
||||
|
||||
if( m_bounds )
|
||||
|
||||
CEntity* remove = this;
|
||||
g_FormationManager.RemoveUnit(remove);
|
||||
|
||||
if( m_bounds )
|
||||
delete( m_bounds );
|
||||
m_bounds = NULL;
|
||||
|
||||
@ -208,8 +223,9 @@ void CEntity::kill()
|
||||
delete( m_actor );
|
||||
m_actor = NULL;
|
||||
}
|
||||
|
||||
|
||||
updateCollisionPatch();
|
||||
|
||||
|
||||
me = HEntity(); // will deallocate the entity, assuming nobody else has a reference to it
|
||||
}
|
||||
@ -679,7 +695,17 @@ void CEntity::DestroyListeners( CEntity* target )
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
CEntityFormation* CEntity::GetFormation()
|
||||
{
|
||||
if ( m_formation < 0 )
|
||||
return NULL;
|
||||
return g_FormationManager.GetFormation(m_formation);
|
||||
}
|
||||
void CEntity::DispatchFormationEvent( int type )
|
||||
{
|
||||
CFormationEvent evt( type );
|
||||
DispatchEvent( &evt );
|
||||
}
|
||||
void CEntity::repath()
|
||||
{
|
||||
CVector2D destination;
|
||||
@ -1051,6 +1077,46 @@ void CEntity::renderStaminaBar()
|
||||
|
||||
glEnd();
|
||||
}
|
||||
void CEntity::renderRank()
|
||||
{
|
||||
if( !m_bounds )
|
||||
return;
|
||||
if( m_rankHeight < 0 )
|
||||
return; // negative bar height means don't display stamina bar
|
||||
//Check for valid texture
|
||||
if( g_Selection.m_rankTextures.find( m_rankName ) == g_Selection.m_rankTextures.end() )
|
||||
return;
|
||||
|
||||
CCamera *g_Camera=g_Game->GetView()->GetCamera();
|
||||
|
||||
float sx, sy;
|
||||
CVector3D above;
|
||||
above.X = m_position.X;
|
||||
above.Z = m_position.Z;
|
||||
above.Y = getAnchorLevel(m_position.X, m_position.Z) + m_rankHeight;
|
||||
g_Camera->GetScreenCoordinates(above, sx, sy);
|
||||
int size = m_rankSize/2;
|
||||
|
||||
float x1 = sx - size;
|
||||
float x2 = sx + size;
|
||||
float y1 = g_yres - (sy - size); //top
|
||||
float y2 = g_yres - (sy + size); //bottom
|
||||
|
||||
ogl_tex_bind(g_Selection.m_rankTextures[m_rankName]);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE);
|
||||
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, g_Renderer.m_Options.m_LodBias);
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
|
||||
glTexCoord2f(1.0f, 0.0f); glVertex3f( x2, y2, 0 );
|
||||
glTexCoord2f(1.0f, 1.0f); glVertex3f( x2, y1, 0 );
|
||||
glTexCoord2f(0.0f, 1.0f); glVertex3f( x1, y1,0 );
|
||||
glTexCoord2f(0.0f, 0.0f); glVertex3f( x1, y2, 0 );
|
||||
|
||||
glEnd();
|
||||
}
|
||||
|
||||
|
||||
void CEntity::CalculateRun(float timestep)
|
||||
{
|
||||
@ -1105,7 +1171,14 @@ void CEntity::ScriptingInit()
|
||||
AddMethod<jsval, &CEntity::TriggerRun>( "triggerRun", 1 );
|
||||
AddMethod<jsval, &CEntity::SetRun>( "setRun", 1 );
|
||||
AddMethod<jsval, &CEntity::GetRunState>( "getRunState", 0 );
|
||||
|
||||
AddMethod<bool, &CEntity::IsInFormation>( "isInFormation", 0 );
|
||||
AddMethod<jsval, &CEntity::GetFormationBonus>( "getFormationBonus", 0 );
|
||||
AddMethod<jsval, &CEntity::GetFormationBonusType>( "getFormationBonusType", 0 );
|
||||
AddMethod<jsval, &CEntity::GetFormationBonusVal>( "getFormationBonusVal", 0 );
|
||||
AddMethod<jsval, &CEntity::GetFormationPenalty>( "getFormationPenalty", 0 );
|
||||
AddMethod<jsval, &CEntity::GetFormationPenaltyType>( "getFormationPenaltyType", 0 );
|
||||
AddMethod<jsval, &CEntity::GetFormationPenaltyVal>( "getFormationPenaltyVal", 0 );
|
||||
AddMethod<bool, &CEntity::IsInClass>( "isInClass", 1 );
|
||||
|
||||
AddClassProperty( L"template", (CBaseEntity* CEntity::*)&CEntity::m_base, false, (NotifyFn)&CEntity::loadBase );
|
||||
AddClassProperty( L"traits.id.classes", (GetFn)&CEntity::getClassSet, (SetFn)&CEntity::setClassSet );
|
||||
@ -1354,6 +1427,7 @@ bool CEntity::Kill( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(arg
|
||||
clearOrders();
|
||||
|
||||
g_EntityManager.SetDeath(true);
|
||||
|
||||
|
||||
if( m_actor )
|
||||
{
|
||||
@ -1702,3 +1776,37 @@ jsval CEntity::GetRunState( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UN
|
||||
{
|
||||
return m_isRunning;
|
||||
}
|
||||
jsval CEntity::GetFormationPenalty( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
|
||||
{
|
||||
return ToJSVal( GetFormation()->GetBase()->GetPenalty() );
|
||||
}
|
||||
jsval CEntity::GetFormationPenaltyType( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
|
||||
{
|
||||
return ToJSVal( GetFormation()->GetBase()->GetPenaltyType() );
|
||||
}
|
||||
jsval CEntity::GetFormationPenaltyVal( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
|
||||
{
|
||||
return ToJSVal( GetFormation()->GetBase()->GetPenaltyVal() );
|
||||
}
|
||||
jsval CEntity::GetFormationBonus( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
|
||||
{
|
||||
return ToJSVal( GetFormation()->GetBase()->GetBonus() );
|
||||
}
|
||||
jsval CEntity::GetFormationBonusType( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
|
||||
{
|
||||
return ToJSVal( GetFormation()->GetBase()->GetBonusType() );
|
||||
}
|
||||
jsval CEntity::GetFormationBonusVal( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
|
||||
{
|
||||
return ToJSVal( GetFormation()->GetBase()->GetBonusVal() );
|
||||
}
|
||||
bool CEntity::IsInClass( JSContext* cx, uintN argc, jsval* argv )
|
||||
{
|
||||
if( argc < 1 )
|
||||
{
|
||||
JS_ReportError( cx, "Too few parameters" );
|
||||
return( false );
|
||||
}
|
||||
CStr test = ToPrimitive<CStr>( argv[0] );
|
||||
return m_classes.IsMember( test );
|
||||
}
|
@ -29,7 +29,6 @@
|
||||
#include <deque>
|
||||
#include "scripting/ScriptableComplex.h"
|
||||
#include "Player.h"
|
||||
|
||||
#include "Vector2D.h"
|
||||
#include "Vector3D.h"
|
||||
#include "UnitManager.h"
|
||||
@ -47,6 +46,8 @@ class CUnit;
|
||||
class CAura;
|
||||
class CProductionQueue;
|
||||
|
||||
class CEntityFormation;
|
||||
|
||||
// TODO MT: Put this is /some/ sort of order...
|
||||
|
||||
class CEntity : public CJSComplex<CEntity>, public IEventTarget
|
||||
@ -88,6 +89,8 @@ public:
|
||||
|
||||
bool m_selected;
|
||||
i32 m_grouped;
|
||||
int m_formation; //Indice of which formation we're in
|
||||
int m_formationSlot; //The slot of the above formation
|
||||
|
||||
// If this unit has been removed from the gameworld but has still
|
||||
// has references.
|
||||
@ -117,6 +120,11 @@ public:
|
||||
float m_healthMax;
|
||||
float m_healthBarHeight;
|
||||
int m_healthBarSize;
|
||||
|
||||
//Rank properties
|
||||
float m_rankHeight;
|
||||
int m_rankSize;
|
||||
CStr m_rankName;
|
||||
|
||||
bool m_healthDecay;
|
||||
|
||||
@ -255,6 +263,7 @@ public:
|
||||
void renderSelectionOutline( float alpha = 1.0f );
|
||||
void renderHealthBar();
|
||||
void renderStaminaBar();
|
||||
void renderRank();
|
||||
|
||||
// After a collision, recalc the path to the next fixed waypoint.
|
||||
void repath();
|
||||
@ -278,7 +287,17 @@ public:
|
||||
|
||||
void DispatchNotification( CEntityOrder order, int type );
|
||||
void DestroyListeners( CEntity* target );
|
||||
|
||||
|
||||
CEntityFormation* GetFormation();
|
||||
bool IsInClass( JSContext* cx, uintN argc, jsval* argv );
|
||||
jsval GetFormationPenalty( JSContext* cx, uintN argc, jsval* argv );
|
||||
jsval GetFormationPenaltyType( JSContext* cx, uintN argc, jsval* argv );
|
||||
jsval GetFormationPenaltyVal( JSContext* cx, uintN argc, jsval* argv );
|
||||
|
||||
jsval GetFormationBonus( JSContext* cx, uintN argc, jsval* argv );
|
||||
jsval GetFormationBonusType( JSContext* cx, uintN argc, jsval* argv );
|
||||
jsval GetFormationBonusVal( JSContext* cx, uintN argc, jsval* argv );
|
||||
void DispatchFormationEvent( int type );
|
||||
// Script constructor
|
||||
|
||||
static JSBool Construct( JSContext* cx, JSObject* obj, uint argc, jsval* argv, jsval* rval );
|
||||
@ -304,6 +323,11 @@ public:
|
||||
bool ForceCheckListeners( JSContext* cx, uintN argc, jsval* argv );
|
||||
void CheckListeners( int type, CEntity *target );
|
||||
|
||||
bool IsInFormation( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) )
|
||||
{
|
||||
return ( m_formation != NULL ? true : false );
|
||||
}
|
||||
|
||||
bool Order( JSContext* cx, uintN argc, jsval* argv, bool Queued );
|
||||
inline bool OrderSingle( JSContext* cx, uintN argc, jsval* argv )
|
||||
{
|
||||
|
220
source/simulation/EntityFormation.cpp
Normal file
220
source/simulation/EntityFormation.cpp
Normal file
@ -0,0 +1,220 @@
|
||||
#include "precompiled.h"
|
||||
#include "EntityFormation.h"
|
||||
#include "BaseFormationCollection.h"
|
||||
#include "FormationManager.h"
|
||||
#include "Simulation.h"
|
||||
#include "ps\Game.h"
|
||||
#include "ps\Interact.h"
|
||||
#include "ps\Network\NetMessage.h"
|
||||
|
||||
CEntityFormation::CEntityFormation( CBaseFormation*& base, size_t index )
|
||||
{
|
||||
if (!base)
|
||||
return;
|
||||
|
||||
m_self = m_base = base;
|
||||
m_numEntities=0;
|
||||
m_speed=0.0f;
|
||||
m_orientation = 0.0f;
|
||||
|
||||
m_position.x = m_position.y = -1.0f;
|
||||
m_duplication=false;
|
||||
m_entities.resize(m_base->m_numSlots);
|
||||
|
||||
m_index = (int)index;
|
||||
}
|
||||
CEntityFormation::~CEntityFormation()
|
||||
{
|
||||
for ( int i=0; i<m_base->m_numSlots; ++i )
|
||||
{
|
||||
if ( m_entities[i] )
|
||||
{
|
||||
m_entities[i]->m_formation = -1;
|
||||
m_entities[i]->m_formationSlot = -1;
|
||||
m_entities[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
void CEntityFormation::SwitchBase( CBaseFormation*& base )
|
||||
{
|
||||
std::vector<CEntity*> copy;
|
||||
copy.resize( m_base->m_numSlots );
|
||||
for ( int i=0; i < m_base->m_numSlots; ++i )
|
||||
{
|
||||
if ( !m_entities[i] )
|
||||
continue;
|
||||
copy.push_back( m_entities[i] );
|
||||
RemoveUnit( m_entities[i] );
|
||||
}
|
||||
m_self = m_base = base;
|
||||
m_entities.resize(m_base->m_numSlots);
|
||||
ResetAllEntities();
|
||||
|
||||
for ( std::vector<CEntity*>::iterator it=copy.begin(); it != copy.end(); it++ )
|
||||
g_FormationManager.AddUnit(*it, m_index);
|
||||
}
|
||||
bool CEntityFormation::AddUnit( CEntity*& entity )
|
||||
{
|
||||
debug_assert( entity );
|
||||
//Add the unit to the most appropriate slot
|
||||
for (int i=0; i<m_base->m_numSlots; ++i)
|
||||
{
|
||||
if ( IsBetterUnit( i, entity ) )
|
||||
{
|
||||
if ( m_entities[i] )
|
||||
{
|
||||
CEntity* prev = m_entities[i];
|
||||
RemoveUnit( m_entities[i] );
|
||||
m_entities[i] = entity;
|
||||
AddUnit( prev );
|
||||
}
|
||||
|
||||
m_entities[i] = entity;
|
||||
++m_numEntities;
|
||||
entity->m_formation = m_index;
|
||||
entity->m_formationSlot = i;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void CEntityFormation::RemoveUnit( CEntity*& entity )
|
||||
{
|
||||
if ( !IsValidOrder(entity->m_formationSlot) || !entity )
|
||||
return;
|
||||
|
||||
m_entities[entity->m_formationSlot] = NULL;
|
||||
entity->m_formation = -1;
|
||||
entity->m_formationSlot = -1;
|
||||
|
||||
--m_numEntities;
|
||||
//UpdateFormation();
|
||||
}
|
||||
bool CEntityFormation::IsSlotAppropriate( int order, CEntity*& entity )
|
||||
{
|
||||
debug_assert( entity );
|
||||
if ( !IsValidOrder(order) )
|
||||
return false;
|
||||
for ( size_t idx=0; idx < m_base->m_slots[order].category.size(); ++idx )
|
||||
{
|
||||
if ( entity->m_classes.IsMember( m_base->m_slots[order].category[idx] ) )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool CEntityFormation::IsBetterUnit( int order, CEntity*& entity )
|
||||
{
|
||||
if ( !( IsValidOrder(order) || entity ) )
|
||||
return false;
|
||||
//Adding to an empty slot, check if we're elligible
|
||||
if ( !m_entities[order] )
|
||||
return IsSlotAppropriate(order, entity);
|
||||
|
||||
//Go through each category and check both units' classes. Must be done in order
|
||||
//because categories are listed in order of importance
|
||||
for ( size_t idx=0; idx < m_base->m_slots[order].category.size(); ++idx )
|
||||
{
|
||||
CStr cat = m_base->m_slots[order].category[idx];
|
||||
bool current=false;
|
||||
bool newEnt=false;
|
||||
|
||||
current = m_entities[order]->m_classes.IsMember( cat );
|
||||
newEnt = entity->m_classes.IsMember( cat );
|
||||
|
||||
if ( current != newEnt )
|
||||
return newEnt;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CEntityFormation::UpdateFormation()
|
||||
{
|
||||
//Get the entities in the right order (as in, ordered correctly and in the right order/slot)
|
||||
for ( int i=1; i<m_base->m_numSlots; ++i )
|
||||
{
|
||||
if ( !m_entities[i] )
|
||||
continue;
|
||||
|
||||
//Go through slots with higher order
|
||||
for ( int j=i-1; j > 0; --j )
|
||||
{
|
||||
if ( IsBetterUnit( j, m_entities[i] ) )
|
||||
{
|
||||
CEntity* temp = m_entities[j];
|
||||
m_entities[j] = m_entities[i];
|
||||
m_entities[i] = temp;
|
||||
|
||||
int tmpSlot = m_entities[i]->m_formationSlot;
|
||||
m_entities[i]->m_formationSlot = m_entities[j]->m_formationSlot;
|
||||
m_entities[j]->m_formationSlot = tmpSlot;
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
CEntityList entities = GetEntityList();
|
||||
CNetMessage* msg = CNetMessage::CreatePositionMessage( entities, NMT_FormationGoto, m_position );
|
||||
g_Game->GetSimulation()->QueueLocalCommand(msg);
|
||||
|
||||
}
|
||||
|
||||
void CEntityFormation::ResetAllEntities()
|
||||
{
|
||||
for ( int i=0; i<m_base->m_numSlots; ++i )
|
||||
m_entities[i] = NULL;
|
||||
}
|
||||
void CEntityFormation::SelectAllUnits()
|
||||
{
|
||||
for ( int i=0; i<m_base->m_numSlots; ++i )
|
||||
{
|
||||
if ( m_entities[i] && !g_Selection.isSelected(m_entities[i]->me) )
|
||||
g_Selection.addSelection( m_entities[i]->me );
|
||||
}
|
||||
}
|
||||
|
||||
CEntityList CEntityFormation::GetEntityList()
|
||||
{
|
||||
CEntityList ret;
|
||||
for ( int i=0; i<m_base->m_numSlots; i++ )
|
||||
{
|
||||
if ( m_entities[i] )
|
||||
ret.push_back( m_entities[i]->me);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
CVector2D CEntityFormation::GetSlotPosition( int order )
|
||||
{
|
||||
if ( IsValidOrder(order) )
|
||||
return CVector2D ( m_base->m_slots[order].rankOff, m_base->m_slots[order].fileOff );
|
||||
return CVector2D(-1, -1);
|
||||
}
|
||||
void CEntityFormation::BaseToMovement()
|
||||
{
|
||||
CBaseFormation* tmp = m_self;
|
||||
CBaseFormation* move = g_EntityFormationCollection.getTemplate( m_base->m_movement );
|
||||
if (!move)
|
||||
return;
|
||||
SwitchBase( move );
|
||||
//SwitchBase resets m_self, so reset it so we can return to the correct base later
|
||||
m_self = tmp;
|
||||
}
|
||||
void CEntityFormation::ResetIndex( size_t index )
|
||||
{
|
||||
m_index = (int)index;
|
||||
|
||||
for ( size_t i=0; i< m_entities.size(); ++i )
|
||||
{
|
||||
if ( m_entities[i] )
|
||||
m_entities[i]->m_formation = m_index;
|
||||
}
|
||||
}
|
||||
/*
|
||||
inline void CEntityFormation::SetDuplication( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* argv )
|
||||
{
|
||||
m_duplication=ToPrimitive<bool>( argv[0] );
|
||||
}
|
||||
inline bool CEntityFormation::IsDuplication( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv))
|
||||
{
|
||||
return m_duplication;
|
||||
}*/
|
73
source/simulation/EntityFormation.h
Normal file
73
source/simulation/EntityFormation.h
Normal file
@ -0,0 +1,73 @@
|
||||
//Andrew aka pyrolink
|
||||
//ajdecker1022@msn.com
|
||||
//Instances of this class contain the actual information about in-game formations.
|
||||
//It is based off of BaseFormation.cpp and uses it as a reference as to what can and cannot
|
||||
//be done in this formation. This is represented as m_base.
|
||||
|
||||
#ifndef ENTITYFORMATION_INCLUDED
|
||||
#define ENTITYFORMATION_INCLUDED
|
||||
|
||||
#include "BaseFormation.h"
|
||||
#include "EntitySupport.h"
|
||||
#include "Vector2D.h"
|
||||
|
||||
class CVector2D;
|
||||
class CEntity;
|
||||
struct SClassSet;
|
||||
|
||||
class CEntityFormation
|
||||
{
|
||||
friend class CFormationManager;
|
||||
public:
|
||||
CEntityFormation( CBaseFormation*& base, size_t index );
|
||||
~CEntityFormation();
|
||||
|
||||
int GetEntityCount() { return m_numEntities; }
|
||||
float GetSpeed() { return m_speed; }
|
||||
int GetSlotCount() { return m_base->m_numSlots; }
|
||||
|
||||
CEntityList GetEntityList();
|
||||
CVector2D GetSlotPosition( int order );
|
||||
CVector2D GetPosition() { return m_position; }
|
||||
CBaseFormation* GetBase() { return m_base; }
|
||||
void BaseToMovement();
|
||||
|
||||
void SelectAllUnits();
|
||||
|
||||
inline void SetDuplication( bool duplicate ) { m_duplication=duplicate; }
|
||||
inline bool IsDuplication() { return m_duplication; }
|
||||
//inline void SetDuplication( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) );
|
||||
//inline bool IsDuplication( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) );
|
||||
inline void SetLock( bool lock ){ m_locked=lock; }
|
||||
inline bool IsLocked() { return m_locked; }
|
||||
inline bool IsValidOrder(int order) { return ( order >= 0 && order < m_base->m_numSlots ); }
|
||||
|
||||
private:
|
||||
int m_numEntities;
|
||||
int m_index;
|
||||
float m_speed; //speed of slowest unit
|
||||
float m_orientation; //Our orientation angle. Used for rotation.
|
||||
CVector2D m_position;
|
||||
|
||||
bool m_locked;
|
||||
//Prevents other selected units from reordering the formation after one has already done it.
|
||||
bool m_duplication;
|
||||
|
||||
CBaseFormation* m_base;
|
||||
CBaseFormation* m_self; //Keeps track of base (referred to during movement switching)
|
||||
std::vector<CEntity*> m_entities; //number of units currently in this formation
|
||||
|
||||
|
||||
bool AddUnit( CEntity*& entity );
|
||||
void RemoveUnit( CEntity*& entity );
|
||||
bool IsSlotAppropriate( int order, CEntity*& entity ); //If empty, can we use this slot?
|
||||
bool IsBetterUnit( int order, CEntity*& entity );
|
||||
|
||||
void UpdateFormation();
|
||||
void SwitchBase( CBaseFormation*& base );
|
||||
//void UpdateGridOffset();
|
||||
|
||||
void ResetIndex( size_t index );
|
||||
void ResetAllEntities(); //Sets all handles to invalid
|
||||
};
|
||||
#endif
|
@ -74,7 +74,6 @@ public:
|
||||
};
|
||||
|
||||
|
||||
|
||||
class CEntityOrder
|
||||
{
|
||||
public:
|
||||
|
@ -97,3 +97,8 @@ CEventNotification::CEventNotification( CEntityOrder order, int notifyType ) : C
|
||||
AddLocalProperty( L"target", &m_target );
|
||||
AddLocalProperty( L"location", &m_location );
|
||||
}
|
||||
CFormationEvent::CFormationEvent( int type ) : CScriptEvent( L"formationEvent", EVENT_FORMATION, true )
|
||||
{
|
||||
(int&) m_formationEvent = type;
|
||||
AddLocalProperty( L"formationEvent", &m_formationEvent );
|
||||
}
|
||||
|
@ -105,5 +105,19 @@ class CEventNotification : public CScriptEvent
|
||||
public:
|
||||
CEventNotification( CEntityOrder order, int notifyType );
|
||||
};
|
||||
class CFormationEvent : public CScriptEvent
|
||||
{
|
||||
int m_formationEvent;
|
||||
|
||||
public:
|
||||
CFormationEvent( int type );
|
||||
|
||||
enum FormationEventType
|
||||
{
|
||||
FORMATION_ENTER,
|
||||
FORMATION_LEAVE,
|
||||
|
||||
FORMATION_LAST
|
||||
};
|
||||
};
|
||||
#endif
|
||||
|
133
source/simulation/FormationManager.cpp
Normal file
133
source/simulation/FormationManager.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
#include "precompiled.h"
|
||||
|
||||
#include "FormationManager.h"
|
||||
#include "Entity.h"
|
||||
#include "CStr.h"
|
||||
#include "BaseFormation.h"
|
||||
#include "EntityFormation.h"
|
||||
|
||||
#include "ps/Vector2D.h"
|
||||
|
||||
CFormationManager::~CFormationManager()
|
||||
{
|
||||
for ( size_t i=0; i<m_formations.size(); i++ )
|
||||
delete m_formations[i];
|
||||
}
|
||||
void CFormationManager::CreateFormation( CStrW& name, CEntityList& entities )
|
||||
{
|
||||
if ( entities.empty() )
|
||||
{
|
||||
debug_warn("Attempting to create a formation with no entities");
|
||||
return;
|
||||
}
|
||||
CBaseFormation* base = g_EntityFormationCollection.getTemplate(name);
|
||||
if (!base)
|
||||
return;
|
||||
if ( entities.size() < (size_t)base->m_required )
|
||||
return;
|
||||
|
||||
CEntityFormation* tmp = new CEntityFormation( base, m_formations.size() );
|
||||
m_formations.push_back( tmp );
|
||||
CEntityList accepted = AddUnitList( entities, (int)m_formations.size()-1 );
|
||||
|
||||
//Find average position
|
||||
CVector3D average( 0.0f, 0.0f, 0.0f );
|
||||
for ( CEntityList::iterator it=accepted.begin(); it != accepted.end(); it++ )
|
||||
average += (*it)->m_position;
|
||||
average = average * ( 1.0f / (float)entities.size() );
|
||||
CVector2D average2D(average.X, average.Z);
|
||||
|
||||
m_formations.back()->m_position = average2D;
|
||||
m_formations.back()->UpdateFormation();
|
||||
|
||||
if ( accepted.size() < (size_t)base->m_required )
|
||||
DestroyFormation( m_formations.size()-1 );
|
||||
}
|
||||
void CFormationManager::DestroyFormation( size_t form )
|
||||
{
|
||||
if ( form < 0 || form >= m_formations.size())
|
||||
{
|
||||
debug_warn("CFormationManager::DestroyFormation--invalid entity");
|
||||
return;
|
||||
}
|
||||
FormIterator it=m_formations.begin() + form;
|
||||
CEntityList entities = (*it)->GetEntityList();
|
||||
//Notify the script that we've "left" the formation
|
||||
for ( size_t i=0; i<entities.size(); i++ )
|
||||
{
|
||||
CEntity* entity = entities[i];
|
||||
entity->DispatchFormationEvent( CFormationEvent::FORMATION_LEAVE );
|
||||
(*it)->RemoveUnit(entity);
|
||||
}
|
||||
delete *it;
|
||||
*it = NULL;
|
||||
m_formations.erase( it );
|
||||
UpdateIndexes( form );
|
||||
}
|
||||
bool CFormationManager::AddUnit( CEntity*& entity, int& form )
|
||||
{
|
||||
if ( !IsValidFormation(form) )
|
||||
return false;
|
||||
|
||||
if ( entity->m_formation > -1 )
|
||||
{
|
||||
if ( !RemoveUnit( entity ) )
|
||||
--form;
|
||||
}
|
||||
FormIterator it = m_formations.begin() + form;
|
||||
//Adding too many?
|
||||
if ( (*it)->m_numEntities == (*it)->m_base->m_numSlots )
|
||||
{
|
||||
CBaseFormation* next = g_EntityFormationCollection.getTemplate((*it)->m_base->m_next);
|
||||
if (next)
|
||||
(*it)->SwitchBase( next );
|
||||
}
|
||||
if ( (*it)->AddUnit(entity) ) //This unit might be better
|
||||
{
|
||||
entity->DispatchFormationEvent( CFormationEvent::FORMATION_ENTER );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
CEntityList CFormationManager::AddUnitList( CEntityList& entities, int form )
|
||||
{
|
||||
CEntityList accepted;
|
||||
for ( CEntityList::iterator it=entities.begin(); it != entities.end(); it++ )
|
||||
{
|
||||
CEntity* entity = *it;
|
||||
if ( AddUnit( entity, form ) )
|
||||
accepted.push_back( *it );
|
||||
}
|
||||
return accepted;
|
||||
}
|
||||
bool CFormationManager::RemoveUnit( CEntity*& entity )
|
||||
{
|
||||
if ( !IsValidFormation(entity->m_formation) )
|
||||
return true;
|
||||
|
||||
FormIterator it = m_formations.begin() + entity->m_formation;
|
||||
if ( (*it)->m_numEntities == (*it)->m_base->m_required )
|
||||
{
|
||||
CBaseFormation* prior = g_EntityFormationCollection.getTemplate((*it)->m_base->m_prior);
|
||||
//Disband formation
|
||||
if (!prior)
|
||||
{
|
||||
DestroyFormation( entity->m_formation );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
entity->DispatchFormationEvent( CFormationEvent::FORMATION_LEAVE );
|
||||
(*it)->RemoveUnit( entity );
|
||||
return true;
|
||||
}
|
||||
CEntityFormation* CFormationManager::GetFormation(int form)
|
||||
{
|
||||
if ( IsValidFormation(form) )
|
||||
return m_formations[form];
|
||||
return NULL;
|
||||
}
|
||||
void CFormationManager::UpdateIndexes( size_t update )
|
||||
{
|
||||
for ( ; update < m_formations.size(); update++ )
|
||||
m_formations[update]->ResetIndex( update );
|
||||
}
|
43
source/simulation/FormationManager.h
Normal file
43
source/simulation/FormationManager.h
Normal file
@ -0,0 +1,43 @@
|
||||
//Andrew aka pyrolink
|
||||
//ajdecker1022@msn.com
|
||||
//This keeps track of all the formations exisisting in the game.
|
||||
|
||||
#include "ps/Singleton.h"
|
||||
#include "BaseFormationCollection.h"
|
||||
#include "scripting/DOMEvent.h"
|
||||
|
||||
#define g_FormationManager CFormationManager::GetSingleton()
|
||||
|
||||
class CEntity;
|
||||
class CStr;
|
||||
class CBaseFormation;
|
||||
class CVector2D;
|
||||
class CEntityFormation;
|
||||
|
||||
struct CEntityList;
|
||||
|
||||
class CFormationManager : public Singleton<CFormationManager>
|
||||
{
|
||||
#define FormIterator std::vector<CEntityFormation*>::iterator
|
||||
|
||||
public:
|
||||
CFormationManager() {}
|
||||
~CFormationManager();
|
||||
void CreateFormation( CStrW& name, CEntityList& entities );
|
||||
//entity is any unit in the formation
|
||||
void DestroyFormation( size_t form );
|
||||
inline bool IsValidFormation( int index )
|
||||
{
|
||||
return ((size_t)index < m_formations.size() && index >= 0);
|
||||
}
|
||||
bool AddUnit( CEntity*& entity, int& form );
|
||||
CEntityList AddUnitList( CEntityList& entities, int form );
|
||||
|
||||
//Returns false if the formation is destroyed
|
||||
bool RemoveUnit( CEntity*& entity );
|
||||
CEntityFormation* GetFormation(int form);
|
||||
void UpdateIndexes( size_t update );
|
||||
|
||||
private:
|
||||
std::vector<CEntityFormation*> m_formations;
|
||||
};
|
@ -22,6 +22,7 @@
|
||||
#include "GameAttributes.h"
|
||||
#include "renderer/Renderer.h"
|
||||
#include "renderer/WaterManager.h"
|
||||
#include "EntityFormation.h"
|
||||
|
||||
#include "gui/CGUI.h"
|
||||
|
||||
@ -171,6 +172,48 @@ void RandomizeLocations(CEntityOrder order, const vector <HEntity> &entities, bo
|
||||
}
|
||||
}
|
||||
|
||||
void FormationLocations(CEntityOrder order, const vector <HEntity> &entities, bool clearQueue)
|
||||
{
|
||||
CVector2D upvec(0.0f, 1.0f);
|
||||
vector<HEntity>::const_iterator it = entities.begin();
|
||||
CEntityFormation* formation = (*it)->GetFormation();
|
||||
|
||||
|
||||
for (; it != entities.end(); it++)
|
||||
{
|
||||
CEntityOrder orderCopy = order;
|
||||
CVector2D posDelta = orderCopy.m_data[0].location - formation->GetPosition();
|
||||
CVector2D formDelta = formation->GetSlotPosition( (*it)->m_formationSlot );
|
||||
|
||||
posDelta = posDelta.normalize();
|
||||
//Rotate the slot position's offset vector according to the rotation of posDelta.
|
||||
CVector2D rotDelta;
|
||||
float deltaCos = posDelta.dot(upvec);
|
||||
float deltaSin = sinf( acosf(deltaCos) );
|
||||
rotDelta.x = formDelta.x * deltaCos - formDelta.y * deltaSin;
|
||||
rotDelta.y = formDelta.x * deltaSin + formDelta.y * deltaCos;
|
||||
|
||||
orderCopy.m_data[0].location += rotDelta;
|
||||
|
||||
// Clamp it to within the map, just in case.
|
||||
float mapsize = (float)g_Game->GetWorld()->GetTerrain()->GetVerticesPerSide() * CELL_SIZE;
|
||||
|
||||
if( orderCopy.m_data[0].location.x < 0.0f )
|
||||
orderCopy.m_data[0].location.x = 0.0f;
|
||||
if( orderCopy.m_data[0].location.x >= mapsize )
|
||||
orderCopy.m_data[0].location.x = mapsize;
|
||||
if( orderCopy.m_data[0].location.y < 0.0f )
|
||||
orderCopy.m_data[0].location.y = 0.0f;
|
||||
if( orderCopy.m_data[0].location.y >= mapsize )
|
||||
orderCopy.m_data[0].location.y = mapsize;
|
||||
|
||||
if( clearQueue )
|
||||
(*it)->clearOrders();
|
||||
|
||||
(*it)->pushOrder( orderCopy );
|
||||
}
|
||||
}
|
||||
|
||||
void QueueOrder(CEntityOrder order, const vector <HEntity> &entities, bool clearQueue)
|
||||
{
|
||||
vector<HEntity>::const_iterator it;
|
||||
@ -197,6 +240,14 @@ uint CSimulation::TranslateMessage(CNetMessage* pMsg, uint clientMask, void* UNU
|
||||
order.m_data[0].location.y=(float)msg->m_TargetY; \
|
||||
RandomizeLocations(order, msg->m_Entities, clearQueue); \
|
||||
} while(0)
|
||||
#define ENTITY_POSITION_FORM(_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; \
|
||||
FormationLocations(order, msg->m_Entities, clearQueue); \
|
||||
} while(0)
|
||||
#define ENTITY_ENTITY(_msg, _order) do\
|
||||
{ \
|
||||
_msg *msg=(_msg *)pMsg; \
|
||||
@ -259,6 +310,13 @@ uint CSimulation::TranslateMessage(CNetMessage* pMsg, uint clientMask, void* UNU
|
||||
case NMT_Goto:
|
||||
ENTITY_POSITION(CGoto, ORDER_GOTO);
|
||||
break;
|
||||
case NMT_FormationGoto:
|
||||
ENTITY_POSITION_FORM(CFormationGoto, ORDER_GOTO);
|
||||
break;
|
||||
//TODO: make formation move to within range of target and then attack normally
|
||||
case NMT_FormationGeneric:
|
||||
ENTITY_ENTITY_INT(CFormationGeneric, ORDER_GENERIC);
|
||||
break;
|
||||
case NMT_Run:
|
||||
ENTITY_POSITION(CRun, ORDER_RUN);
|
||||
break;
|
||||
@ -274,6 +332,7 @@ uint CSimulation::TranslateMessage(CNetMessage* pMsg, uint clientMask, void* UNU
|
||||
case NMT_NotifyRequest:
|
||||
ENTITY_ENTITY_INT(CNotifyRequest, ORDER_NOTIFY_REQUEST);
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return clientMask;
|
||||
|
Loading…
Reference in New Issue
Block a user