1
0
forked from 0ad/0ad

#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:
pyrolink 2006-03-31 03:30:34 +00:00
parent 87780e328a
commit 3408b078b7
26 changed files with 1408 additions and 43 deletions

View File

@ -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];

View File

@ -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.)

View File

@ -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() )

View File

@ -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 );

View File

@ -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

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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__

View File

@ -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
//-----------------------------------------------------------------------------

View File

@ -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 );

View File

@ -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;

View 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() );
}
}

View 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

View 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 );
}

View 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

View File

@ -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 );
}

View File

@ -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 )
{

View 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;
}*/

View 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

View File

@ -74,7 +74,6 @@ public:
};
class CEntityOrder
{
public:

View File

@ -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 );
}

View File

@ -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

View 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 );
}

View 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;
};

View File

@ -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;