From f2e867e2396c7d5e899aed424c28b342fab11f91 Mon Sep 17 00:00:00 2001 From: pyrolink Date: Sat, 8 Apr 2006 22:34:54 +0000 Subject: [PATCH] #Bar borders and angle penalty -Changes to notifications. They take different parameters now-see template_entity_script.js. You can choose to destroy the notifiers yourself in the script (useful for idle) -Added "idle" event with registerIdle and registerDamage to assist with the angle penalty. -Bar border stuff -Angle penalty is set up but untested-it just needs to use this.getAttackDirections() to find the number of directions the entity is being attacked from. The penalty is specified in template_unit There is a problem when the game exits-it attempts to destroy the notifiers in entity.cpp's constructor, where it calls DestroyAllNotifiers(). The problem is that the notifiers don't exist any longer because they've been destroyed. I would fix it but I'm leaving for vacation (Jason told me it was OK to comitt). Hope it isn't too much of a problem. This was SVN commit r3732. --- source/ps/GameSetup/GameSetup.cpp | 14 +- source/ps/Interact.cpp | 77 +++-- source/ps/Interact.h | 12 +- source/scripting/EventTypes.h | 4 +- source/scripting/ScriptGlue.cpp | 54 +++- source/simulation/BaseEntity.cpp | 13 +- source/simulation/BaseEntity.h | 19 +- source/simulation/BaseFormation.cpp | 46 ++- source/simulation/BaseFormation.h | 12 + source/simulation/Entity.cpp | 334 +++++++++++++++----- source/simulation/Entity.h | 38 ++- source/simulation/EntityFormation.cpp | 13 +- source/simulation/EntityFormation.h | 14 +- source/simulation/EntityOrders.h | 5 +- source/simulation/EntityStateProcessing.cpp | 4 +- source/simulation/EventHandlers.cpp | 15 +- source/simulation/EventHandlers.h | 13 + source/simulation/FormationManager.cpp | 12 +- source/simulation/FormationManager.h | 3 +- 19 files changed, 552 insertions(+), 150 deletions(-) diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp index e09948dbea..bab7a3e4e8 100644 --- a/source/ps/GameSetup/GameSetup.cpp +++ b/source/ps/GameSetup/GameSetup.cpp @@ -352,12 +352,18 @@ void Render() glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); + + g_Selection.renderRanks(); + g_Mouseover.renderRanks(); + g_Mouseover.renderBarBorders(); + g_Selection.renderBarBorders(); + glDisable( GL_TEXTURE_2D ); g_Mouseover.renderHealthBars(); g_Selection.renderHealthBars(); g_Mouseover.renderStaminaBars(); g_Selection.renderStaminaBars(); - g_Selection.renderRanks(); - g_Mouseover.renderRanks(); + glEnable( GL_TEXTURE_2D ); + glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); @@ -502,6 +508,8 @@ static void InitScripting() g_ScriptingHost.DefineConstant( "FORMATION_ENTER", CFormationEvent::FORMATION_ENTER ); g_ScriptingHost.DefineConstant( "FORMATION_LEAVE", CFormationEvent::FORMATION_LEAVE ); + g_ScriptingHost.DefineConstant( "FORMATION_DAMAGE", CFormationEvent::FORMATION_DAMAGE ); + g_ScriptingHost.DefineConstant( "FORMATION_ATTACK", CFormationEvent::FORMATION_ATTACK ); g_ScriptingHost.DefineConstant( "NOTIFY_NONE", CEntityListener::NOTIFY_NONE ); g_ScriptingHost.DefineConstant( "NOTIFY_GOTO", CEntityListener::NOTIFY_GOTO ); @@ -513,6 +521,8 @@ 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( "NOTIFY_IDLE", CEntityListener::NOTIFY_IDLE ); + g_ScriptingHost.DefineConstant( "NOTIFY_ALL", CEntityListener::NOTIFY_ALL ); g_ScriptingHost.DefineConstant( "ORDER_NONE", -1 ); g_ScriptingHost.DefineConstant( "ORDER_GOTO", CEntityOrder::ORDER_GOTO ); diff --git a/source/ps/Interact.cpp b/source/ps/Interact.cpp index bd6a622bb4..389cc0b4be 100755 --- a/source/ps/Interact.cpp +++ b/source/ps/Interact.cpp @@ -118,6 +118,25 @@ void CSelectedEntities::renderStaminaBars() glDisable( GL_BLEND ); } } +void CSelectedEntities::renderBarBorders() +{ + std::vector::iterator it; + for( it = m_selected.begin(); it < m_selected.end(); it++ ) + (*it)->renderBarBorders(); + + if( m_group_highlight != -1 ) + { + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glEnable( GL_BLEND ); + + std::vector::iterator it; + for( it = m_groups[m_group_highlight].begin(); it < m_groups[m_group_highlight].end(); it++ ) + (*it)->renderBarBorders(); + + glDisable( GL_BLEND ); + } +} + void CSelectedEntities::renderRanks() { std::vector::iterator it; @@ -828,34 +847,48 @@ void CMouseoverEntities::renderRanks() glDisable( GL_BLEND ); } - - -int CSelectedEntities::loadRankTextures() +void CMouseoverEntities::renderBarBorders() { - VFSUtil::FileList ranklist; - VFSUtil::FindFiles( "art/textures/ui/session/icons", 0, ranklist ); - for ( std::vector::iterator it = ranklist.begin(); it != ranklist.end(); it++ ) + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glEnable( GL_BLEND ); + + std::vector::iterator it; + for( it = m_mouseover.begin(); it < m_mouseover.end(); it++ ) + it->entity->renderBarBorders(); + + glDisable( GL_BLEND ); +} +static void LoadUnitUIThunk( const char* path, const DirEnt* UNUSED(ent), void* context ) +{ + std::map* textures = (std::map*) context; + CStr name(path); + + if ( !tex_is_known_extension(path) ) { - const char* filename = it->c_str(); - if ( !tex_is_known_extension(filename) ) - { - LOG(ERROR, LOG_CATEGORY, "Unknown rank texture extension (%s)", filename); - continue; - } - Handle ht = ogl_tex_load(filename); - if (ht <= 0) - { - LOG(ERROR, LOG_CATEGORY, "loadRankTextures failed on \"%s\"", filename); - return ht; - } - m_rankTextures[it->AfterLast("/")] = ht; - RETURN_ERR(ogl_tex_upload(ht)); + if ( name.BeforeLast(".") == name ) //this is a directory (contains no ".") + return; + LOG(ERROR, "Unknown rank texture extension (%s)", path); + return; } + Handle tmp = ogl_tex_load(path); + if (tmp <= 0) + { + LOG(ERROR, "Rank Textures", "loadRankTextures failed on \"%s\"", path); + return; + } + name.Remove("art/textures/ui/session/icons/"); //Names are relative to this directory + (*textures)[name] = tmp; + ogl_tex_upload(tmp); +} +int CSelectedEntities::loadUnitUITextures() +{ + THROW_ERR( VFSUtil::EnumDirEnts( "art/textures/ui/session/icons/", VFSUtil::RECURSIVE, + NULL, LoadUnitUIThunk, &m_unitUITextures ) ); return 0; } -void CSelectedEntities::destroyRankTextures() +void CSelectedEntities::destroyUnitUITextures() { - for ( std::map::iterator it=m_rankTextures.begin(); it != m_rankTextures.end(); it++ ) + for ( std::map::iterator it=m_unitUITextures.begin(); it != m_unitUITextures.end(); it++ ) { ogl_tex_free(it->second); it->second = 0; diff --git a/source/ps/Interact.h b/source/ps/Interact.h index 05c19dfc08..81f4e5e1de 100755 --- a/source/ps/Interact.h +++ b/source/ps/Interact.h @@ -37,11 +37,11 @@ struct CSelectedEntities : public Singleton m_selectionChanged = true; m_mouseOverMM = false; - loadRankTextures(); + loadUnitUITextures(); } ~CSelectedEntities() { - destroyRankTextures(); + destroyUnitUITextures(); } std::vector m_selected; std::vector m_groups[MAX_GROUPS]; @@ -78,10 +78,11 @@ struct CSelectedEntities : public Singleton void renderHealthBars(); void renderStaminaBars(); void renderRanks(); + void renderBarBorders(); - void destroyRankTextures(); - int loadRankTextures(); - std::map m_rankTextures; + void destroyUnitUITextures(); + int loadUnitUITextures(); + std::map m_unitUITextures; }; // CMouseoverEntities: the singleton containing entities the mouse is currently hovering over or bandboxing @@ -133,6 +134,7 @@ struct CMouseoverEntities : public Singleton void renderHealthBars(); void renderStaminaBars(); void renderRanks(); + void renderBarBorders(); bool isBandbox() { return( m_bandbox ); } void startBandbox( u16 x, u16 y ); diff --git a/source/scripting/EventTypes.h b/source/scripting/EventTypes.h index 30e2adabf6..973696cacc 100644 --- a/source/scripting/EventTypes.h +++ b/source/scripting/EventTypes.h @@ -22,6 +22,7 @@ enum EEventType EVENT_ORDER_TRANSITION, EVENT_NOTIFICATION, EVENT_FORMATION, + EVENT_IDLE, EVENT_LAST, // Projectile events @@ -49,7 +50,8 @@ static const wchar_t* const EventNames[EVENT_LAST] = /* 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_FORMATION */ L"onFormation" /* When this unit does something with a formation */ + /* EVENT_FORMATION */ L"onFormation", /* When this unit does something with a formation */ + /* EVENT_IDLE */ L"onIdle" /* When this unit becomes idle, do something */ }; #endif // #ifndef EVENTTYPES_H__ diff --git a/source/scripting/ScriptGlue.cpp b/source/scripting/ScriptGlue.cpp index 821d336882..1b9c4bf100 100755 --- a/source/scripting/ScriptGlue.cpp +++ b/source/scripting/ScriptGlue.cpp @@ -231,12 +231,11 @@ JSBool issueCommand( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rv else entities = *EntityCollection::RetrieveSet(cx, JSVAL_TO_OBJECT(argv[0])); - //Destroy old listeners if we're explicitly being reassigned + //Destroy old notifiers if we're explicitly being reassigned for ( size_t i=0; i < entities.size(); i++) { - if ( entities[i]->m_currentListener ) - entities[i]->DestroyListeners( entities[i]->m_currentListener ); - entities[i]->m_currentListener = NULL; + if ( entities[i]->m_destroyNotifiers ) + entities[i]->DestroyAllNotifiers(); } std::vector messages; CNetMessage* msg = CNetMessage::CommandFromJSArgs(entities, cx, argc-1, argv+1); @@ -273,6 +272,48 @@ JSBool issueCommand( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rv return JS_TRUE; } +//Formation stuff +JSBool createEntityFormation( JSContext* cx, JSObject* UNUSED(obj), uint argc, jsval* argv, jsval* rval ) +{ + REQUIRE_MIN_PARAMS(2, createEntityFormation); + + CEntityList entities = *EntityCollection::RetrieveSet(cx, JSVAL_TO_OBJECT(argv[0])); + CStrW name = ToPrimitive( argv[1] ); + g_FormationManager.CreateFormation( entities, name ); + *rval = JSVAL_VOID; + return JS_TRUE; + +} +JSBool removeFromFormation( JSContext* cx, JSObject* UNUSED(obj), uint argc, jsval* argv, jsval* rval ) +{ + REQUIRE_MIN_PARAMS(1, removeFromFormation); + + CEntityList entities; + if (JS_GetClass(JSVAL_TO_OBJECT(argv[0])) == &CEntity::JSI_class) + entities.push_back( (ToNative(argv[0])) ->me); + else + entities = *EntityCollection::RetrieveSet(cx, JSVAL_TO_OBJECT(argv[0])); + + *rval = g_FormationManager.RemoveUnitList(entities) ? JS_TRUE : JS_FALSE; + return JS_TRUE; +} +JSBool lockEntityFormation( JSContext* cx, JSObject* UNUSED(obj), uint argc, jsval* argv, jsval* rval ) +{ + REQUIRE_MIN_PARAMS(1, lockEntityFormation); + + CEntity* entity = ToNative( argv[0] ); + entity->GetFormation()->SetLock( ToPrimitive( argv[1] ) ); + *rval = JSVAL_VOID; + return JS_TRUE; +} +JSBool isFormationLocked( JSContext* cx, JSObject* UNUSED(obj), uint argc, jsval* argv, jsval* rval ) +{ + REQUIRE_MIN_PARAMS(1, isFormationLocked); + + CEntity* entity = ToNative( argv[0] ); + *rval = entity->GetFormation()->IsLocked() ? JS_TRUE : JS_FALSE; + return JS_TRUE; +} //----------------------------------------------------------------------------- // Events @@ -1072,6 +1113,11 @@ JSFunctionSpec ScriptFunctionTable[] = JS_FUNC(issueCommand, issueCommand, 2) JS_FUNC(startPlacing, startPlacing, 1) + JS_FUNC(createEntityFormation, createEntityFormation, 2) + JS_FUNC(removeFromFormation, removeFromFormation, 1) + JS_FUNC(lockEntityFormation, lockEntityFormation, 1) + JS_FUNC(isFormationLocked, isFormationLocked, 1) + // Camera JS_FUNC(setCameraTarget, setCameraTarget, 1) diff --git a/source/simulation/BaseEntity.cpp b/source/simulation/BaseEntity.cpp index 146c248eb0..4429db6505 100755 --- a/source/simulation/BaseEntity.cpp +++ b/source/simulation/BaseEntity.cpp @@ -31,14 +31,25 @@ CBaseEntity::CBaseEntity() AddProperty( L"traits.health.max", &m_healthMax ); AddProperty( L"traits.health.bar_height", &m_healthBarHeight ); AddProperty( L"traits.health.bar_size", &m_healthBarSize ); + AddProperty( L"traits.health.bar_width", &m_healthBarWidth ); + AddProperty( L"traits.health.border_height", &m_healthBorderHeight); + AddProperty( L"traits.health.border_width", &m_healthBorderWidth ); + AddProperty( L"traits.health.border_name", &m_healthBorderName ); AddProperty( L"traits.health.regen_rate", &m_healthRegenRate ); AddProperty( L"traits.health.regen_start", &m_healthRegenStart ); AddProperty( L"traits.health.decay_rate", &m_healthDecayRate ); + AddProperty( L"traits.stamina.curr", &m_staminaCurr ); 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.stamina.bar_width", &m_staminaBarWidth ); + AddProperty( L"traits.stamina.border_height", &m_staminaBorderHeight); + AddProperty( L"traits.stamina.border_width", &m_staminaBorderWidth ); + AddProperty( L"traits.stamina.border_name", &m_staminaBorderName ); + AddProperty( L"traits.angle_penalty.sectors", &m_sectorDivs ); + AddProperty( L"traits.angle_penalty.value", &m_sectorPenalty ); + AddProperty( L"traits.rank.width", &m_rankWidth ); AddProperty( L"traits.rank.height", &m_rankHeight ); AddProperty( L"traits.rank.name", &m_rankName ); AddProperty( L"traits.minimap.type", &m_minimapType ); diff --git a/source/simulation/BaseEntity.h b/source/simulation/BaseEntity.h index 7859623c9a..33e0c4562b 100755 --- a/source/simulation/BaseEntity.h +++ b/source/simulation/BaseEntity.h @@ -61,20 +61,30 @@ public: float m_staminaMax; float m_staminaBarHeight; int m_staminaBarSize; - + float m_staminaBarWidth; + + int m_staminaBorderWidth; + int m_staminaBorderHeight; + CStr m_staminaBorderName; + // HP properties float m_healthCurr; float m_healthMax; float m_healthBarHeight; int m_healthBarSize; + float m_healthBarWidth; + int m_healthBorderWidth; + int m_healthBorderHeight; + CStr m_healthBorderName; + float m_healthRegenRate; float m_healthRegenStart; float m_healthDecayRate; //Rank properties - float m_rankHeight; - int m_rankSize; + float m_rankWidth; + int m_rankHeight; CStr m_rankName; // Minimap properties @@ -100,6 +110,9 @@ public: SEntityAction m_run; SEntityAction m_generic; + int m_sectorDivs; + float m_sectorPenalty; + float m_turningRadius; CScriptObject m_EventHandlers[EVENT_LAST]; diff --git a/source/simulation/BaseFormation.cpp b/source/simulation/BaseFormation.cpp index dd4bfdfdc8..2bed1bfe7d 100644 --- a/source/simulation/BaseFormation.cpp +++ b/source/simulation/BaseFormation.cpp @@ -2,6 +2,7 @@ #include "BaseFormation.h" #include "CLogger.h" #include "CStr.h" +#include "maths\MathUtil.h" #define LOG_CATEGORY "Formation" @@ -30,6 +31,11 @@ bool CBaseFormation::loadXML(CStr filename) AT(penalty); AT(penaltytype); AT(penaltyval); + AT(anglepenalty); + AT(anglepenaltydivs); + AT(anglepenaltytype); + AT(anglepenaltyval); + AT(required); AT(next); AT(prior); @@ -53,36 +59,46 @@ bool CBaseFormation::loadXML(CStr filename) for ( int i=0; i m_slots; + std::vector m_angleValues; //cosine of angle divisions bool loadXML(CStr filename); void AssignCategory(int order, CStr category); //takes care of formatting strings diff --git a/source/simulation/Entity.cpp b/source/simulation/Entity.cpp index 0f7623e90c..13e6b9bcd9 100755 --- a/source/simulation/Entity.cpp +++ b/source/simulation/Entity.cpp @@ -61,14 +61,25 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, cons AddProperty( L"traits.health.max", &m_healthMax ); AddProperty( L"traits.health.bar_height", &m_healthBarHeight ); AddProperty( L"traits.health.bar_size", &m_healthBarSize ); + AddProperty( L"traits.health.bar_width", &m_healthBarWidth ); + AddProperty( L"traits.health.border_height", &m_healthBorderHeight); + AddProperty( L"traits.health.border_width", &m_healthBorderWidth ); + AddProperty( L"traits.health.border_name", &m_healthBorderName ); AddProperty( L"traits.health.regen_rate", &m_healthRegenRate ); AddProperty( L"traits.health.regen_start", &m_healthRegenStart ); AddProperty( L"traits.health.decay_rate", &m_healthDecayRate ); + AddProperty( L"traits.stamina.curr", &m_staminaCurr ); 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.stamina.bar_width", &m_staminaBarWidth ); + AddProperty( L"traits.stamina.border_height", &m_staminaBorderHeight); + AddProperty( L"traits.stamina.border_width", &m_staminaBorderWidth ); + AddProperty( L"traits.stamina.border_name", &m_staminaBorderName ); + AddProperty( L"traits.angle_penalty.sectors", &m_sectorDivs); + AddProperty( L"traits.angle_penalty.value", &m_sectorPenalty ); + AddProperty( L"traits.rank.width", &m_rankWidth ); AddProperty( L"traits.rank.height", &m_rankHeight ); AddProperty( L"traits.rank.name", &m_rankName ); AddProperty( L"traits.minimap.type", &m_minimapType ); @@ -89,6 +100,18 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, cons AddProperty( EventNames[t], &m_EventHandlers[t], false ); AddHandler( t, &m_EventHandlers[t] ); } + if ( m_sectorDivs >= 0 ) + { + m_sectorAngles.resize(m_sectorDivs); + m_sectorValues.resize(m_sectorDivs); + float step = DEGTORAD(360.0f / m_sectorDivs); + + for ( int i=0; iDestroyNotifier( this ); + DestroyAllNotifiers(); + CEntity* remove = this; g_FormationManager.RemoveUnit(remove); } @@ -207,6 +235,8 @@ void CEntity::kill() CEntity* remove = this; g_FormationManager.RemoveUnit(remove); + + DestroyAllNotifiers(); if( m_bounds ) delete( m_bounds ); @@ -662,9 +692,21 @@ void CEntity::Tick() void CEntity::clearOrders() { - m_orderQueue.clear(); + if ( m_orderQueue.empty() ) + return; + CIdleEvent evt( m_orderQueue.front(), m_currentNotification ); + DispatchEvent(&evt); + m_orderQueue.clear(); } +void CEntity::popOrder() +{ + if ( m_orderQueue.empty() ) + return; + CIdleEvent evt( m_orderQueue.front(), m_currentNotification ); + DispatchEvent(&evt); + m_orderQueue.pop_front(); +} void CEntity::pushOrder( CEntityOrder& order ) { CEventPrepareOrder evt( order.m_data[0].entity, order.m_type, order.m_data[1].data, order.m_data[0].string ); @@ -683,17 +725,36 @@ void CEntity::DispatchNotification( CEntityOrder order, int type ) CEventNotification evt( order, type ); DispatchEvent( &evt ); } -void CEntity::DestroyListeners( CEntity* target ) +int CEntity::DestroyNotifier( CEntity* target ) { - if (target->m_listeners.empty()) - return; - - for ( size_t i=0; i < target->m_listeners.size(); i++) + if (target->m_listeners.empty() || !m_destroyNotifiers) + return 0; + //Stop listening + for ( size_t i=0; i < target->m_listeners.size(); i++ ) { if ( target->m_listeners[i].m_sender == this ) target->m_listeners.erase(target->m_listeners.begin() + i); } - + int removed=0; + //Get rid of our copy + for ( size_t i=0; i < target->m_notifiers.size(); i++ ) + { + if ( m_notifiers[i] == target ) + { + m_notifiers.erase(m_notifiers.begin() + i); + ++removed; + } + } + return removed; +} +void CEntity::DestroyAllNotifiers() +{ + debug_assert(m_destroyNotifiers); + //Make them stop listening to us + if ( m_notifiers.empty() ) + return; + for ( size_t i=0; iGetView()->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) + height; + g_Camera.GetScreenCoordinates(above, sx, sy); + return CVector2D( sx, sy ); +} +void CEntity::renderBarBorders() +{ + pglActiveTextureARB( GL_TEXTURE0_ARB ); + + 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); + + if ( g_Selection.m_unitUITextures.find(m_healthBorderName) != g_Selection.m_unitUITextures.end() ) + { + ogl_tex_bind( g_Selection.m_unitUITextures[m_healthBorderName] ); + CVector2D pos = getScreenCoords( m_healthBarHeight ); + + float left = pos.x - m_healthBorderWidth/2; + float right = pos.x + m_healthBorderWidth/2; + pos.y = g_yres - pos.y; + float bottom = pos.y + m_healthBorderHeight/2; + float top = pos.y - m_healthBorderHeight/2; + + glBegin(GL_QUADS); + + glTexCoord2f(0.0f, 0.0f); glVertex3f( left, bottom, 0 ); + glTexCoord2f(0.0f, 1.0f); glVertex3f( left, top, 0 ); + glTexCoord2f(1.0f, 1.0f); glVertex3f( right, top, 0 ); + glTexCoord2f(1.0f, 0.0f); glVertex3f( right, bottom, 0 ); + + glEnd(); + } + if ( g_Selection.m_unitUITextures.find(m_staminaBorderName) != g_Selection.m_unitUITextures.end() ) + { + ogl_tex_bind( g_Selection.m_unitUITextures[m_staminaBorderName] ); + CVector2D pos = getScreenCoords( m_staminaBarHeight ); + float left = pos.x - m_staminaBorderWidth/2; + float right = pos.x + m_staminaBorderWidth/2; + pos.y = g_yres - pos.y; + float bottom = pos.y + m_staminaBorderHeight/2; + float top = pos.y - m_staminaBorderHeight/2; + + glBegin(GL_QUADS); + + glTexCoord2f(0.0f, 0.0f); glVertex3f( left, bottom, 0 ); + glTexCoord2f(0.0f, 1.0f); glVertex3f( left, top, 0 ); + glTexCoord2f(1.0f, 1.0f); glVertex3f( right, top, 0 ); + glTexCoord2f(1.0f, 0.0f); glVertex3f( right, bottom, 0 ); + + glEnd(); + } +} void CEntity::renderHealthBar() { if( !m_bounds ) return; if( m_healthBarHeight < 0 ) return; // negative bar height means don't display health bar - - 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_healthBarHeight; - g_Camera.GetScreenCoordinates(above, sx, sy); + + CVector2D pos = getScreenCoords( m_healthBarHeight ); float fraction = clamp(m_healthCurr / m_healthMax, 0.0f, 1.0f); - - float x1 = sx - m_healthBarSize/2; - float x2 = sx + m_healthBarSize/2; - float y = g_yres - sy; - - glBegin(GL_LINES); + float x1 = pos.x - m_healthBarSize/2; + float x2 = pos.x + m_healthBarSize/2; + float y = g_yres - pos.y; + + glLineWidth( m_healthBarWidth ); + glBegin(GL_LINES); // green part of bar glColor3f( 0, 1, 0 ); @@ -1038,6 +1151,9 @@ void CEntity::renderHealthBar() glVertex3f( x2, y, 0 ); glEnd(); + + glLineWidth(1.0f); + } void CEntity::renderStaminaBar() @@ -1047,20 +1163,13 @@ void CEntity::renderStaminaBar() if( m_staminaBarHeight < 0 ) return; // negative bar height means don't display stamina bar - 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_staminaBarHeight; - g_Camera->GetScreenCoordinates(above, sx, sy); + CVector2D pos = getScreenCoords( m_staminaBarHeight ); float fraction = clamp(m_staminaCurr / m_staminaMax, 0.0f, 1.0f); - - float x1 = sx - m_staminaBarSize/2; - float x2 = sx + m_staminaBarSize/2; - float y = g_yres - sy; - + float x1 = pos.x - m_staminaBarSize/2; + float x2 = pos.x + m_staminaBarSize/2; + float y = g_yres - pos.y; + + glLineWidth( m_staminaBarWidth ); glBegin(GL_LINES); // blue part of bar @@ -1076,17 +1185,18 @@ void CEntity::renderStaminaBar() glVertex3f( x2, y, 0 ); glEnd(); + glLineWidth(1.0f); } void CEntity::renderRank() { if( !m_bounds ) return; if( m_rankHeight < 0 ) - return; // negative bar height means don't display stamina bar + return; // negative height means don't display stamina bar //Check for valid texture - if( g_Selection.m_rankTextures.find( m_rankName ) == g_Selection.m_rankTextures.end() ) + if( g_Selection.m_unitUITextures.find( m_rankName ) == g_Selection.m_unitUITextures.end() ) return; - + CCamera *g_Camera=g_Game->GetView()->GetCamera(); float sx, sy; @@ -1095,14 +1205,14 @@ void CEntity::renderRank() 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; + int size = m_rankWidth/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]); + + ogl_tex_bind(g_Selection.m_unitUITextures[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); @@ -1167,7 +1277,9 @@ void CEntity::ScriptingInit() AddMethod( "removeAura", 1 ); AddMethod( "setActionParams", 5 ); AddMethod( "forceCheckListeners", 2 ); - AddMethod( "requestNotification", 3 ); + AddMethod( "requestNotification", 4 ); + AddMethod( "destroyNotifier", 1 ); + AddMethod( "destroyAllNotifiers", 0 ); AddMethod( "triggerRun", 1 ); AddMethod( "setRun", 1 ); AddMethod( "getRunState", 0 ); @@ -1178,7 +1290,8 @@ void CEntity::ScriptingInit() AddMethod( "getFormationPenalty", 0 ); AddMethod( "getFormationPenaltyType", 0 ); AddMethod( "getFormationPenaltyVal", 0 ); - AddMethod( "isInClass", 1 ); + AddMethod( "registerDamage", 0 ); + AddMethod( "registerIdle", 0 ); AddClassProperty( L"template", (CBaseEntity* CEntity::*)&CEntity::m_base, false, (NotifyFn)&CEntity::loadBase ); AddClassProperty( L"traits.id.classes", (GetFn)&CEntity::getClassSet, (SetFn)&CEntity::setClassSet ); @@ -1311,11 +1424,12 @@ bool CEntity::Order( JSContext* cx, uintN argc, jsval* argv, bool Queued ) //It's not a notification order if ( argc == 3 ) { - if ( m_currentListener ) - DestroyListeners( m_currentListener ); - m_currentListener = NULL; + if ( m_destroyNotifiers ) + { + m_currentRequest=0; + DestroyAllNotifiers(); + } } - break; case CEntityOrder::ORDER_GENERIC: if( argc < 3 ) @@ -1342,9 +1456,11 @@ bool CEntity::Order( JSContext* cx, uintN argc, jsval* argv, bool Queued ) //It's not a notification order if ( argc == 3 ) { - if ( m_currentListener ) - DestroyListeners( m_currentListener ); - m_currentListener = NULL; + if ( m_destroyNotifiers ) + { + m_currentRequest=0; + DestroyAllNotifiers(); + } } break; case CEntityOrder::ORDER_PRODUCE: @@ -1627,32 +1743,35 @@ jsval CEntity::SetActionParams( JSContext* UNUSED(cx), uintN argc, jsval* argv ) bool CEntity::RequestNotification( JSContext* cx, uintN argc, jsval* argv ) { - if( argc < 3 ) + if( argc < 4 ) { JS_ReportError( cx, "Too few parameters" ); return( false ); } CEntityListener notify; - notify.m_sender = this; - //(Convert from int to enum) CEntity *target = ToNative( argv[0] ); + (int&)notify.m_type = ToPrimitive( argv[1] ); + bool tmpDestroyNotifiers = ToPrimitive( argv[2] ); + bool m_destroyNotifiers = !ToPrimitive( argv[3] ); + if (target == this) return false; - - *( (int*) &(notify.m_type) ) = ToPrimitive( argv[1] ); + notify.m_sender = this; + //Clean up old requests - if ( ToPrimitive( argv[2] ) && !target->m_listeners.empty() ) - DestroyListeners( target ); - - if ( target != m_currentListener && m_currentListener ) - DestroyListeners( m_currentListener ); + if ( tmpDestroyNotifiers ) + DestroyAllNotifiers(); + //If new request is not the same and we're destroy notifiers, reset + else if ( !(notify.m_type & m_currentRequest) && m_destroyNotifiers ) + DestroyAllNotifiers(); - m_currentListener = target; + m_currentRequest = notify.m_type; + m_notifiers.push_back( target ); + int result = target->m_currentNotification & notify.m_type; //If our target isn't stationary and it's doing something we want to follow, send notification - int result = target->m_currentNotification & notify.m_type; if ( result && !target->m_orderQueue.empty() ) { CEntityOrder order = target->m_orderQueue.front(); @@ -1667,11 +1786,6 @@ bool CEntity::RequestNotification( JSContext* cx, uintN argc, jsval* argv ) case CEntityListener::NOTIFY_ATTACK: case CEntityListener::NOTIFY_GATHER: case CEntityListener::NOTIFY_DAMAGE: - if( argc < 2 ) - { - JS_ReportError( cx, "Too few parameters" ); - } - DispatchNotification( order, result ); break; default: @@ -1713,11 +1827,13 @@ bool CEntity::ForceCheckListeners( JSContext *cx, uintN argc, jsval* argv ) case CEntityListener::NOTIFY_ATTACK: case CEntityListener::NOTIFY_GATHER: case CEntityListener::NOTIFY_DAMAGE: + case CEntityListener::NOTIFY_IDLE: //target should be 'this' m_listeners[i].m_sender->DispatchNotification( order, result ); - + break; + default: JS_ReportError( cx, "Invalid order type" ); - continue; + break; } } } @@ -1754,6 +1870,21 @@ void CEntity::CheckListeners( int type, CEntity *target) } } } +jsval CEntity::DestroyAllNotifiers( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) +{ + DestroyAllNotifiers(); + return JS_TRUE; +} +jsval CEntity::DestroyNotifier( JSContext* cx, uintN argc, jsval* argv ) +{ + if ( argc < 1 ) + { + JS_ReportError(cx, "too few parameters: CEntity::DestroyNotifier"); + return JS_FALSE; + } + DestroyNotifier( ToNative( argv[0] ) ); + return JS_TRUE; +} jsval CEntity::TriggerRun( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) { @@ -1800,13 +1931,64 @@ jsval CEntity::GetFormationBonusVal( JSContext* UNUSED(cx), uintN UNUSED(argc), { return ToJSVal( GetFormation()->GetBase()->GetBonusVal() ); } -bool CEntity::IsInClass( JSContext* cx, uintN argc, jsval* argv ) + +jsval CEntity::RegisterDamage( JSContext* cx, uintN argc, jsval* argv ) { - if( argc < 1 ) + if ( argc < 1 ) { JS_ReportError( cx, "Too few parameters" ); return( false ); } - CStr test = ToPrimitive( argv[0] ); - return m_classes.IsMember( test ); -} \ No newline at end of file + CEntity* inflictor = ToNative( argv[0] ); + CVector2D up(1.0f, 0.0f); + CVector2D pos = CVector2D( inflictor->m_position.X, inflictor->m_position.Z ); + CVector2D posDelta = (pos - m_position).normalize(); + + float angle = up.dot(posDelta); + //Find what section it is between and "activate" it + for ( int i=0; im_sectorDivs ) + m_sectorValues[i] = true; + else if ( angle > m_sectorAngles[i] && angle < m_sectorAngles[i+1] ) + m_sectorValues[i] = true; + } + return JS_TRUE; +} +jsval CEntity::RegisterIdle( JSContext* cx, uintN argc, jsval* argv ) +{ + if ( argc < 1 ) + { + JS_ReportError( cx, "Too few parameters" ); + return( false ); + } + CEntity* idleEntity = ToNative( argv[0] ); + + CVector2D up(1.0f, 0.0f); + CVector2D pos = CVector2D( idleEntity->m_position.X, idleEntity->m_position.Z ); + CVector2D posDelta = (pos - m_position).normalize(); + + float angle = up.dot(posDelta); + //Find what section it is between and "activate" it + for ( int i=0; im_sectorDivs ) + m_sectorValues[i] = false; + else if ( angle > m_sectorAngles[i] && angle < m_sectorAngles[i+1] ) + m_sectorValues[i] = false; + } + return JS_TRUE; +} +jsval CEntity::GetAttackDirections( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) +{ + int directions=0; + + for ( std::vector::iterator it=m_sectorValues.begin(); it != m_sectorValues.end(); it++ ) + { + if ( *it ) + ++directions; + } + return ToJSVal( directions ); +} diff --git a/source/simulation/Entity.h b/source/simulation/Entity.h index 1710020668..74e7e52b3d 100755 --- a/source/simulation/Entity.h +++ b/source/simulation/Entity.h @@ -114,16 +114,26 @@ public: float m_staminaMax; float m_staminaBarHeight; int m_staminaBarSize; + float m_staminaBarWidth; + + int m_staminaBorderWidth; + int m_staminaBorderHeight; + CStr m_staminaBorderName; // HP properties float m_healthCurr; float m_healthMax; float m_healthBarHeight; int m_healthBarSize; + float m_healthBarWidth; + + int m_healthBorderWidth; + int m_healthBorderHeight; + CStr m_healthBorderName; //Rank properties float m_rankHeight; - int m_rankSize; + int m_rankWidth; CStr m_rankName; bool m_healthDecay; @@ -189,8 +199,16 @@ public: std::deque m_orderQueue; std::deque m_listeners; - int m_currentNotification; - CEntity* m_currentListener; + + std::vector m_notifiers; + int m_currentNotification; //Current order in the form of a notification code + int m_currentRequest; //Notification we our notifiers are sending + bool m_destroyNotifiers; //True: we destroy them. False: the script does. + + std::vector m_sectorValues; + std::vector m_sectorAngles; + int m_sectorDivs; + float m_sectorPenalty; private: CEntity( CBaseEntity* base, CVector3D position, float orientation, const std::set& actorSelections, CStrW building = L"" ); @@ -261,10 +279,11 @@ public: // Things like selection circles and debug info - possibly move to gui if/when it becomes responsible for (and capable of) it. void render(); void renderSelectionOutline( float alpha = 1.0f ); + void renderBarBorders( ); void renderHealthBar(); void renderStaminaBar(); void renderRank(); - + CVector2D getScreenCoords( float height ); // After a collision, recalc the path to the next fixed waypoint. void repath(); @@ -283,10 +302,12 @@ public: void checkExtant(); // Existence void clearOrders(); + void popOrder(); //Use this if and order has finished instead of m_orderQueue.pop_front() void pushOrder( CEntityOrder& order ); void DispatchNotification( CEntityOrder order, int type ); - void DestroyListeners( CEntity* target ); + int DestroyNotifier( CEntity* target ); //Stop notifier from sending to us + void DestroyAllNotifiers(); CEntityFormation* GetFormation(); bool IsInClass( JSContext* cx, uintN argc, jsval* argv ); @@ -297,7 +318,12 @@ public: 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 ); + + jsval RegisterDamage( JSContext* cx, uintN argc, jsval* argv ); + jsval RegisterIdle( JSContext* cx, uintN argc, jsval* argv ); + jsval GetAttackDirections( JSContext* cx, uintN argc, jsval* argv ); // Script constructor static JSBool Construct( JSContext* cx, JSObject* obj, uint argc, jsval* argv, jsval* rval ); @@ -322,6 +348,8 @@ public: //Just in case we want to explicitly check the listeners without waiting for the order to be pushed bool ForceCheckListeners( JSContext* cx, uintN argc, jsval* argv ); void CheckListeners( int type, CEntity *target ); + jsval DestroyAllNotifiers( JSContext* cx, uintN argc, jsval* argv ); + jsval DestroyNotifier( JSContext* cx, uintN argc, jsval* argv ); bool IsInFormation( JSContext* UNUSED(cx), uintN UNUSED(argc), jsval* UNUSED(argv) ) { diff --git a/source/simulation/EntityFormation.cpp b/source/simulation/EntityFormation.cpp index 93ea0584f2..22d39aed30 100644 --- a/source/simulation/EntityFormation.cpp +++ b/source/simulation/EntityFormation.cpp @@ -91,19 +91,21 @@ void CEntityFormation::RemoveUnit( CEntity*& entity ) --m_numEntities; //UpdateFormation(); } -bool CEntityFormation::IsSlotAppropriate( int order, CEntity*& entity ) +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] ) ) + CStr tmp( m_base->m_slots[order].category[idx] ); + if ( entity->m_classes.IsMember( tmp ) ) return true; } return false; } -bool CEntityFormation::IsBetterUnit( int order, CEntity*& entity ) +bool CEntityFormation::IsBetterUnit( int order, CEntity* entity ) { if ( !( IsValidOrder(order) || entity ) ) return false; @@ -163,6 +165,11 @@ void CEntityFormation::ResetAllEntities() for ( int i=0; im_numSlots; ++i ) m_entities[i] = NULL; } +void CEntityFormation::ResetAngleDivs() +{ + for ( int i=0; im_anglePenaltyDivs; ++i ) + m_angleDivs[i] = false; +} void CEntityFormation::SelectAllUnits() { for ( int i=0; im_numSlots; ++i ) diff --git a/source/simulation/EntityFormation.h b/source/simulation/EntityFormation.h index 5ef21e5fbd..f7a587dbcb 100644 --- a/source/simulation/EntityFormation.h +++ b/source/simulation/EntityFormation.h @@ -36,8 +36,6 @@ public: 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 ); } @@ -55,19 +53,21 @@ private: CBaseFormation* m_base; CBaseFormation* m_self; //Keeps track of base (referred to during movement switching) - std::vector m_entities; //number of units currently in this formation - + std::vector m_entities; //number of units currently in this formation + std::vector m_angleDivs; //attack direction penalty-true=being attacked from sector + std::vector m_angleVals; + 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 ); + 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 + void ResetAngleDivs(); }; #endif \ No newline at end of file diff --git a/source/simulation/EntityOrders.h b/source/simulation/EntityOrders.h index c7cc2292e2..cba4326f37 100755 --- a/source/simulation/EntityOrders.h +++ b/source/simulation/EntityOrders.h @@ -66,7 +66,10 @@ public: NOTIFY_ESCORT = 0x0F, //GOTO | ATTACK | DAMAGE NOTIFY_HEAL = 0x10, - NOTIFY_GATHER = 0x20 + NOTIFY_GATHER = 0x20, + + NOTIFY_IDLE = 0x40, + NOTIFY_ALL = 0x7F } m_type; diff --git a/source/simulation/EntityStateProcessing.cpp b/source/simulation/EntityStateProcessing.cpp index 0754e7494b..7dacc503e2 100755 --- a/source/simulation/EntityStateProcessing.cpp +++ b/source/simulation/EntityStateProcessing.cpp @@ -379,7 +379,7 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times if(!DispatchEvent( contactEvent )) { // Cancel current order - m_orderQueue.pop_front(); + popOrder(); m_isRunning = false; m_shouldRun = false; m_actor->SetEntitySelection( L"idle" ); @@ -406,7 +406,7 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times //TODO: eventually when stances/formations are implemented, if applicable (e.g. not //heal or if defensive stance), the unit should expand and continue the order. - m_orderQueue.pop_front(); + popOrder(); m_isRunning = false; m_shouldRun = false; return( false ); diff --git a/source/simulation/EventHandlers.cpp b/source/simulation/EventHandlers.cpp index 198ad101d4..29664d0b79 100755 --- a/source/simulation/EventHandlers.cpp +++ b/source/simulation/EventHandlers.cpp @@ -97,8 +97,21 @@ 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 ) +CFormationEvent::CFormationEvent( int type ) : CScriptEvent( L"formationEvent", EVENT_FORMATION, true ) { (int&) m_formationEvent = type; AddLocalProperty( L"formationEvent", &m_formationEvent ); } +CIdleEvent::CIdleEvent( CEntityOrder order, int notifyType ) : CScriptEvent( L"idleEvent", EVENT_IDLE, false ) +{ + m_notifyType = notifyType; + m_orderType = order.m_type; + m_target = order.m_data[0].entity; + CVector3D convert( order.m_data[0].location.x, 0.0f, order.m_data[0].location.y ); + m_location = convert; + + AddLocalProperty( L"notifyType", &m_notifyType ); + AddLocalProperty( L"orderType", &m_orderType ); + AddLocalProperty( L"target", &m_target ); + AddLocalProperty( L"location", &m_location ); +} \ No newline at end of file diff --git a/source/simulation/EventHandlers.h b/source/simulation/EventHandlers.h index 13c8a1ea7c..4d04282083 100755 --- a/source/simulation/EventHandlers.h +++ b/source/simulation/EventHandlers.h @@ -116,8 +116,21 @@ public: { FORMATION_ENTER, FORMATION_LEAVE, + FORMATION_DAMAGE, + FORMATION_ATTACK, FORMATION_LAST }; }; +class CIdleEvent : public CScriptEvent +{ + int m_notifyType; //previous order in notification code form + int m_orderType; + int m_action; //previous order in terms of generic order action + CVector3D m_location; + CEntity* m_target; +public: + CIdleEvent( CEntityOrder order, int notifyType ); +}; + #endif diff --git a/source/simulation/FormationManager.cpp b/source/simulation/FormationManager.cpp index af63714dd8..a5421e1b29 100644 --- a/source/simulation/FormationManager.cpp +++ b/source/simulation/FormationManager.cpp @@ -13,7 +13,7 @@ CFormationManager::~CFormationManager() for ( size_t i=0; iRemoveUnit( entity ); return true; } +bool CFormationManager::RemoveUnitList( CEntityList& entities ) +{ + for ( CEntityList::iterator it=entities.begin(); it != entities.end(); it++ ) + { + CEntity* entity = *it; + if ( !RemoveUnit(entity) ) + return false; + } + return true; +} CEntityFormation* CFormationManager::GetFormation(int form) { if ( IsValidFormation(form) ) diff --git a/source/simulation/FormationManager.h b/source/simulation/FormationManager.h index f59ed82902..b4fc6ae0cb 100644 --- a/source/simulation/FormationManager.h +++ b/source/simulation/FormationManager.h @@ -23,7 +23,7 @@ class CFormationManager : public Singleton public: CFormationManager() {} ~CFormationManager(); - void CreateFormation( CStrW& name, CEntityList& entities ); + void CreateFormation( CEntityList& entities, CStrW& name ); //entity is any unit in the formation void DestroyFormation( size_t form ); inline bool IsValidFormation( int index ) @@ -35,6 +35,7 @@ public: //Returns false if the formation is destroyed bool RemoveUnit( CEntity*& entity ); + bool RemoveUnitList( CEntityList& entities ); CEntityFormation* GetFormation(int form); void UpdateIndexes( size_t update );