diff --git a/binaries/data/mods/official/entities/template_entity.xml b/binaries/data/mods/official/entities/template_entity.xml
index a8c144f6ee..130f3f0015 100755
--- a/binaries/data/mods/official/entities/template_entity.xml
+++ b/binaries/data/mods/official/entities/template_entity.xml
@@ -39,7 +39,7 @@
-
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/template_entity_script.js b/binaries/data/mods/official/entities/template_entity_script.js
index c63a8d70a1..63944c4147 100644
--- a/binaries/data/mods/official/entities/template_entity_script.js
+++ b/binaries/data/mods/official/entities/template_entity_script.js
@@ -729,7 +729,7 @@ function entityEventPrepareOrder( evt )
evt.preventDefault();
return;
}
-
+
//evt.notifySource is the entity order data will be obtained from, so if we're attacking and we
//want our listeners to copy us, then we will use our own order as the source.
@@ -762,7 +762,7 @@ function entityEventPrepareOrder( evt )
case ACTION_ATTACK:
case ACTION_ATTACK_RANGED:
evt.action = getAttackAction( this, evt.target );
- if ( action == ACTION_NONE )
+ if ( evt.action == ACTION_NONE )
evt.preventDefault();
evt.notifyType = NOTIFY_ATTACK;
break;
@@ -779,15 +779,38 @@ function entityEventPrepareOrder( evt )
break;
}
break;
+
+ case ORDER_PRODUCE:
+ evt.notifyType = NOTIFY_NONE;
+ break;
default:
- //evt.preventDefault();
+ console.write("Unknown order type " + evt.orderType + "; ignoring.");
+ evt.preventDefault();
break;
}
}
// ====================================================================
+function entityStartProduction( evt )
+{
+ console.write("StartProduction: " + evt.productionType + " " + evt.name);
+ evt.time = 5.0;
+}
+
+function entityFinishProduction( evt )
+{
+ console.write("FinishProduction: " + evt.productionType + " " + evt.name);
+}
+
+function entityCancelProduction( evt )
+{
+ console.write("CancelProduction: " + evt.productionType + " " + evt.name);
+}
+
+// ====================================================================
+
function entityAddCreateQueue( template, tab, list )
{
// Make sure we have a queue to put things in...
diff --git a/binaries/data/mods/official/entities/template_structure.xml b/binaries/data/mods/official/entities/template_structure.xml
index 7a19edc013..ae2ec52730 100644
--- a/binaries/data/mods/official/entities/template_structure.xml
+++ b/binaries/data/mods/official/entities/template_structure.xml
@@ -27,4 +27,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/binaries/data/mods/official/entities/template_unit_infantry.xml b/binaries/data/mods/official/entities/template_unit_infantry.xml
index c35eb560fa..d5d0f8fa62 100644
--- a/binaries/data/mods/official/entities/template_unit_infantry.xml
+++ b/binaries/data/mods/official/entities/template_unit_infantry.xml
@@ -25,7 +25,7 @@
800
1
- 1
+ 1
1
1
diff --git a/source/ps/GameSetup/GameSetup.cpp b/source/ps/GameSetup/GameSetup.cpp
index 9fe06111b0..01d241c98c 100644
--- a/source/ps/GameSetup/GameSetup.cpp
+++ b/source/ps/GameSetup/GameSetup.cpp
@@ -495,6 +495,7 @@ static void InitScripting()
CJSProgressTimer::ScriptingInit();
CProjectile::ScriptingInit();
+ g_ScriptingHost.DefineConstant( "NOTIFY_NONE", CEntityListener::NOTIFY_NONE );
g_ScriptingHost.DefineConstant( "NOTIFY_GOTO", CEntityListener::NOTIFY_GOTO );
g_ScriptingHost.DefineConstant( "NOTIFY_RUN", CEntityListener::NOTIFY_RUN );
g_ScriptingHost.DefineConstant( "NOTIFY_FOLLOW", CEntityListener::NOTIFY_FOLLOW );
@@ -510,6 +511,8 @@ static void InitScripting()
g_ScriptingHost.DefineConstant( "ORDER_RUN", CEntityOrder::ORDER_RUN );
g_ScriptingHost.DefineConstant( "ORDER_PATROL", CEntityOrder::ORDER_PATROL );
g_ScriptingHost.DefineConstant( "ORDER_GENERIC", CEntityOrder::ORDER_GENERIC );
+ g_ScriptingHost.DefineConstant( "ORDER_PRODUCE", CEntityOrder::ORDER_PRODUCE );
+
#define REG_JS_CONSTANT(_name) g_ScriptingHost.DefineConstant(#_name, _name)
REG_JS_CONSTANT(SDL_BUTTON_LEFT);
diff --git a/source/ps/Network/AllNetMessages.h b/source/ps/Network/AllNetMessages.h
index 64003ca62a..7ef5bcda41 100755
--- a/source/ps/Network/AllNetMessages.h
+++ b/source/ps/Network/AllNetMessages.h
@@ -60,6 +60,7 @@ enum ENetMessageType
NMT_Patrol,
NMT_AddWaypoint,
NMT_Generic,
+ NMT_Produce,
NMT_Run,
NMT_NotifyRequest,
@@ -233,6 +234,11 @@ DERIVE_NMT_CLASS_(NetCommand, Generic)
NMT_FIELD_INT(m_Action, u32, 4)
END_NMT_CLASS()
+DERIVE_NMT_CLASS_(NetCommand, Produce)
+ NMT_FIELD_INT(m_Type, u32, 4)
+ NMT_FIELD(CStrW, m_Name)
+END_NMT_CLASS()
+
DERIVE_NMT_CLASS_(NetCommand, NotifyRequest)
NMT_FIELD(HEntity, m_Target)
NMT_FIELD_INT(m_Action, u32, 4)
diff --git a/source/ps/Network/NetMessage.cpp b/source/ps/Network/NetMessage.cpp
index 5c963909eb..83eb71b4b2 100755
--- a/source/ps/Network/NetMessage.cpp
+++ b/source/ps/Network/NetMessage.cpp
@@ -82,6 +82,7 @@ void CNetMessage::ScriptingInit()
def(NMT_Patrol);
def(NMT_AddWaypoint);
def(NMT_Generic);
+ def(NMT_Produce);
def(NMT_NotifyRequest);
}
@@ -142,6 +143,15 @@ CNetCommand *CNetMessage::CommandFromJSArgs(const CEntityList &entities, JSConte
int val=ToPrimitive(argv[argIndex++]); \
_msg->_field=val; \
)
+ #define ReadString(_msg, _field) \
+ STMT(\
+ if (argIndex+1 > argc) \
+ ArgumentCountError(); \
+ if (!JSVAL_IS_STRING(argv[argIndex])) \
+ ArgumentTypeError(); \
+ CStrW val=ToPrimitive(argv[argIndex++]); \
+ _msg->_field=val; \
+ )
#define PositionMessage(_msg) \
case NMT_ ## _msg: \
@@ -171,6 +181,15 @@ CNetCommand *CNetMessage::CommandFromJSArgs(const CEntityList &entities, JSConte
return msg; \
}
+ #define ProduceMessage(_msg) \
+ case NMT_ ## _msg: \
+ { \
+ C##_msg *msg = new C##_msg(); \
+ ReadInt(msg, m_Type); \
+ ReadString(msg, m_Name); \
+ return msg; \
+ }
+
// argIndex, incremented by reading macros. We have already "eaten" the
// first argument (message type)
uint argIndex = 1;
@@ -178,14 +197,17 @@ CNetCommand *CNetMessage::CommandFromJSArgs(const CEntityList &entities, JSConte
{
// NMT_Goto, targetX, targetY
PositionMessage(Goto)
-
PositionMessage(Run)
PositionMessage(Patrol)
PositionMessage(AddWaypoint)
+ // NMT_Generic, target, action
EntityIntMessage(Generic)
EntityIntMessage(NotifyRequest)
+ // NMT_Produce, type, name
+ ProduceMessage(Produce)
+
default:
JS_ReportError(cx, "Invalid order type");
return NULL;
diff --git a/source/scripting/EventTypes.h b/source/scripting/EventTypes.h
index 76fc6a6f33..8b3986314a 100644
--- a/source/scripting/EventTypes.h
+++ b/source/scripting/EventTypes.h
@@ -13,6 +13,9 @@ enum EEventType
EVENT_INITIALIZE = 0,
EVENT_TICK,
EVENT_GENERIC,
+ EVENT_START_PRODUCTION,
+ EVENT_CANCEL_PRODUCTION,
+ EVENT_FINISH_PRODUCTION,
EVENT_TARGET_CHANGED,
EVENT_PREPARE_ORDER,
EVENT_ORDER_TRANSITION,
@@ -35,7 +38,10 @@ static const wchar_t* const EventNames[EVENT_LAST] =
{
/* EVENT_INITIALIZE */ L"onInitialize",
/* EVENT_TICK */ L"onTick",
- /* EVENT_GENERIC */ L"onGeneric",
+ /* EVENT_GENERIC */ L"onGeneric", /* For generic actions on a target unit, like attack or gather */
+ /* EVENT_START_PRODUCTION */ L"onStartProduction", /* We're about to start training/researching something (deduct resources, etc) */
+ /* EVENT_CANCEL_PRODUCTION */ L"onCancelProduction", /* Something in production has been cancelled */
+ /* EVENT_FINISH_PRODUCTION */ L"onFinishProduction", /* We've finished something in production */
/* 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...) */
diff --git a/source/simulation/Entity.cpp b/source/simulation/Entity.cpp
index cdf06aabcb..4d1474716b 100755
--- a/source/simulation/Entity.cpp
+++ b/source/simulation/Entity.cpp
@@ -9,6 +9,7 @@
#include "BaseEntityCollection.h"
#include "Unit.h"
#include "Aura.h"
+#include "ProductionQueue.h"
#include "Renderer.h"
#include "Model.h"
#include "Terrain.h"
@@ -71,6 +72,9 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation )
AddProperty( L"traits.vision.permanent", &m_permanent );
AddProperty( L"last_combat_time", &m_lastCombatTime );
+ m_productionQueue = new CProductionQueue( this );
+ AddProperty( L"production_queue", m_productionQueue );
+
for( int t = 0; t < EVENT_LAST; t++ )
{
AddProperty( EventNames[t], &m_EventHandlers[t], false );
@@ -135,6 +139,8 @@ CEntity::~CEntity()
delete( m_bounds );
}
+ delete m_productionQueue;
+
for( AuraTable::iterator it = m_auras.begin(); it != m_auras.end(); it++ )
{
delete it->second;
@@ -331,6 +337,8 @@ void CEntity::update( size_t timestep )
m_frameCheck = 0;
}
+ m_productionQueue->Update( timestep );
+
// Note: aura processing is done before state processing because the state
// processing code is filled with all kinds of returns
@@ -400,6 +408,10 @@ void CEntity::update( size_t timestep )
break;
updateCollisionPatch();
return;
+ case CEntityOrder::ORDER_PRODUCE:
+ processProduce( current );
+ m_orderQueue.pop_front();
+ break;
case CEntityOrder::ORDER_GENERIC_NOPATHING:
if( processGenericNoPathing( current, timestep ) )
break;
@@ -421,7 +433,7 @@ void CEntity::update( size_t timestep )
break;
default:
debug_warn("Invalid entity order" );
- }
+ }
}
PROFILE_END( "state processing" );
@@ -625,21 +637,17 @@ void CEntity::clearOrders()
void CEntity::pushOrder( CEntityOrder& order )
{
- //Replace acceptsOrder because we need the notification data after the order is pushed
- CEventPrepareOrder evt( order.m_data[0].entity, order.m_type, order.m_data[1].data );
+ CEventPrepareOrder evt( order.m_data[0].entity, order.m_type, order.m_data[1].data, order.m_data[0].string );
if( DispatchEvent(&evt) )
{
- m_orderQueue.push_back( order );
- CheckListeners( evt.m_notifyType, evt.m_notifySource );
+ m_orderQueue.push_back( order );
+ if(evt.m_notifyType != CEntityListener::NOTIFY_NONE)
+ {
+ CheckListeners( evt.m_notifyType, evt.m_notifySource );
+ }
}
}
-bool CEntity::acceptsOrder( int orderType, CEntity* orderTarget, int action )
-{
- CEventPrepareOrder evt( orderTarget, orderType, action );
- return ( DispatchEvent(&evt) );
-}
-
void CEntity::DispatchNotification( CEntityOrder order, int type )
{
CEventNotification evt( order, type );
@@ -1248,6 +1256,22 @@ bool CEntity::Order( JSContext* cx, uintN argc, jsval* argv, bool Queued )
m_currentListener = NULL;
}
break;
+ case CEntityOrder::ORDER_PRODUCE:
+ if( argc < 3 )
+ {
+ JS_ReportError( cx, "Too few parameters" );
+ return( false );
+ }
+ try {
+ newOrder.m_data[0].string = ToPrimitive(argv[2]);
+ newOrder.m_data[1].data = ToPrimitive(argv[1]);
+ }
+ catch( PSERROR_Scripting_ConversionFailed )
+ {
+ JS_ReportError( cx, "Invalid parameter types" );
+ return( false );
+ }
+ break;
default:
JS_ReportError( cx, "Invalid order type" );
return( false );
diff --git a/source/simulation/Entity.h b/source/simulation/Entity.h
index b0ae1290aa..56789ba1b9 100755
--- a/source/simulation/Entity.h
+++ b/source/simulation/Entity.h
@@ -45,6 +45,7 @@ class CBaseEntity;
class CBoundingObject;
class CUnit;
class CAura;
+class CProductionQueue;
// TODO MT: Put this is /some/ sort of order...
@@ -56,11 +57,10 @@ class CEntity : public CJSComplex, public IEventTarget
typedef STL_HASH_MAP ActionTable;
typedef std::set AuraSet;
-private:
+public:
// The player that owns this entity
CPlayer* m_player;
-public:
// Intrinsic properties
CBaseEntity* m_base;
@@ -70,6 +70,9 @@ public:
// The class types this entity has
SClassSet m_classes;
+ // Production queue
+ CProductionQueue* m_productionQueue;
+
float m_speed;
float m_turningRadius;
float m_runRegenRate;
@@ -187,6 +190,8 @@ private:
bool processGeneric( CEntityOrder* current, size_t timestep_milli );
bool processGenericNoPathing( CEntityOrder* current, size_t timestep_milli );
+
+ bool processProduce( CEntityOrder* order );
bool processGotoNoPathing( CEntityOrder* current, size_t timestep_milli );
bool processGoto( CEntityOrder* current, size_t timestep_milli );
@@ -262,9 +267,6 @@ public:
void checkGroup(); // Groups
void checkExtant(); // Existence
- // Returns whether the entity is capable of performing the given orderType on the target.
- bool acceptsOrder( int orderType, CEntity* orderTarget, int action );
-
void clearOrders();
void pushOrder( CEntityOrder& order );
diff --git a/source/simulation/EntityOrders.h b/source/simulation/EntityOrders.h
index 9da8535074..81e4ba4d7e 100755
--- a/source/simulation/EntityOrders.h
+++ b/source/simulation/EntityOrders.h
@@ -40,11 +40,13 @@
#include "Vector2D.h"
#include "scripting/DOMEvent.h"
+// An order data field, which could represent different things depending on the type of order.
struct SOrderData
{
CVector2D location;
- u64 data; // miscellaneous
HEntity entity;
+ CStrW string;
+ u64 data; // could be recast as a double or int
};
class CEntityListener
@@ -52,6 +54,8 @@ class CEntityListener
public:
enum
{
+ NOTIFY_NONE = 0x00,
+
NOTIFY_GOTO = 0x01,
NOTIFY_RUN = 0x02,
NOTIFY_FOLLOW = 0x03, //GOTO | RUN
@@ -85,9 +89,11 @@ public:
ORDER_PATH_END_MARKER,
ORDER_GENERIC,
ORDER_GENERIC_NOPATHING,
+ ORDER_PRODUCE,
ORDER_NOTIFY_REQUEST,
ORDER_LAST
} m_type;
+
SOrderData m_data[ORDER_MAX_DATA];
};
diff --git a/source/simulation/EntityStateProcessing.cpp b/source/simulation/EntityStateProcessing.cpp
index ca9884713b..666188f9fb 100755
--- a/source/simulation/EntityStateProcessing.cpp
+++ b/source/simulation/EntityStateProcessing.cpp
@@ -8,6 +8,7 @@
#include "ObjectEntry.h"
#include "SkeletonAnimDef.h" // Animation duration
#include "Unit.h"
+#include "ProductionQueue.h"
#include "MathUtil.h"
#include "Collision.h"
@@ -325,8 +326,6 @@ bool CEntity::processGotoNoPathing( CEntityOrder* current, size_t timestep_milli
// Handles processing common to (at the moment) gather and melee attack actions
bool CEntity::processContactAction( CEntityOrder* current, size_t UNUSED(timestep_millis), int transition, SEntityAction* action )
{
- //m_orderQueue.pop_front();
-
if( !current->m_data[0].entity || !current->m_data[0].entity->m_extant )
return( false );
current->m_data[0].location = current->m_data[0].entity->m_position;
@@ -336,7 +335,6 @@ bool CEntity::processContactAction( CEntityOrder* current, size_t UNUSED(timeste
{
(int&)current->m_type = transition;
m_isRunning = false;
- //m_orderQueue.push_front(*current); // Seems to be needed since we do a pop above
return( true );
}
@@ -403,16 +401,15 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times
}
if( ( m_fsm_cyclepos <= action->m_Speed ) && ( nextpos > action->m_Speed ) )
{
- // Fire!
m_actor->HideAmmunition();
- DispatchEvent( contactEvent );
- // Note that, at the moment, we don't care if the action succeeds or fails -
- // we could check for failure, then abort the animation.
- // It depends what we think is worse: stopping an animation halfway through,
- // or playing the animation without getting a game effect.
-
- // Could also check again here if the entity still exists, is in range, etc..
- // and cancel if not, but we'll see how it looks without that, first.
+ if(!DispatchEvent( contactEvent ))
+ {
+ // Cancel current order
+ m_orderQueue.pop_front();
+ m_isRunning = false;
+ m_shouldRun = false;
+ return( false );
+ }
}
if( nextpos >= ( action->m_Speed * 2 ) )
@@ -427,7 +424,7 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times
return( false );
}
- // Target's dead (or exhausted)? Then our work here is done.
+ // Target's dead (or exhausted), or we cancelled? Then our work here is done.
if( !current->m_data[0].entity || !current->m_data[0].entity->m_extant )
{
//TODO: eventually when stances/formations are implemented, if applicable (e.g. not
@@ -698,3 +695,17 @@ bool CEntity::processPatrol( CEntityOrder* current, size_t UNUSED(timestep_milli
m_orderQueue.push_back( repeat_patrol );
return( true );
}
+
+bool CEntity::processProduce( CEntityOrder* order )
+{
+ int type = order->m_data[1].data;
+ CStrW name = order->m_data[0].string;
+ debug_printf("Trying to produce %d %ls\n", type, name.c_str() );
+ CEventStartProduction evt( type, name );
+ if( DispatchEvent( &evt ) && evt.GetTime() >= 0 )
+ {
+ debug_printf("Production started, time is %f\n", evt.GetTime());
+ m_productionQueue->AddItem( type, name, evt.GetTime() );
+ }
+ return( false );
+}
diff --git a/source/simulation/EntitySupport.h b/source/simulation/EntitySupport.h
index 556b7ade92..29abe528f6 100755
--- a/source/simulation/EntitySupport.h
+++ b/source/simulation/EntitySupport.h
@@ -52,44 +52,4 @@ struct SClassSet
}
};
-struct SAuraData
-{
- enum Allegiance
- {
- SELF = 1,
- PLAYER = 2,
- GAIA = 4,
- ALLY = 8,
- ENEMY = 16,
- };
- static const ssize_t DURATION_RADIUS = -1;
- static const ssize_t DURATION_PERMANENT = -2;
- SClassSet::ClassSet m_Affects;
- Allegiance m_Allegiance;
- float m_Radius;
- size_t m_Time;
- size_t m_Cooldown;
- float m_Hitpoints;
- size_t m_Duration;
- bool m_Cumulative;
- CScriptObject m_BeginEffect;
- CScriptObject m_EndEffect;
-};
-
-struct SAuraInstance
-{
- HEntity m_Influenced;
- size_t m_EnteredRange;
- size_t m_LastInRange;
- size_t m_Applied;
- inline CEntity* GetEntity() const { return( m_Influenced ); }
-};
-
-struct SAura
-{
- std::vector m_Influenced;
- SAuraData m_Data;
- size_t m_Recharge;
-};
-
#endif
diff --git a/source/simulation/EventHandlers.cpp b/source/simulation/EventHandlers.cpp
index 9de5fc8be1..d8fc4875db 100755
--- a/source/simulation/EventHandlers.cpp
+++ b/source/simulation/EventHandlers.cpp
@@ -10,13 +10,34 @@ CEventGeneric::CEventGeneric( CEntity* target, int action ) : CScriptEvent( L"ge
AddLocalProperty( L"action", &m_action );
}
-/*CEventDamage::CEventDamage( CEntity* inflictor, CDamageType* damage ) : CScriptEvent( L"takesDamage", EVENT_DAMAGE, true )
+CEventStartProduction::CEventStartProduction( int productionType, const CStrW& name )
+ : CScriptEvent( L"startProduction", EVENT_START_PRODUCTION, true)
{
- m_inflictor = inflictor;
- m_damage = damage;
- AddLocalProperty( L"inflictor", &m_inflictor, true );
- AddLocalProperty( L"damage", &m_damage );
-}*/
+ m_productionType = productionType;
+ m_name = name;
+ m_time = -1;
+ AddLocalProperty( L"productionType", &m_productionType );
+ AddLocalProperty( L"name", &m_name );
+ AddLocalProperty( L"time", &m_time );
+}
+
+CEventFinishProduction::CEventFinishProduction( int productionType, const CStrW& name )
+ : CScriptEvent( L"finishProduction", EVENT_FINISH_PRODUCTION, true)
+{
+ m_productionType = productionType;
+ m_name = name;
+ AddLocalProperty( L"productionType", &m_productionType );
+ AddLocalProperty( L"name", &m_name );
+}
+
+CEventCancelProduction::CEventCancelProduction( int productionType, const CStrW& name )
+ : CScriptEvent( L"cancelProduction", EVENT_CANCEL_PRODUCTION, true)
+{
+ m_productionType = productionType;
+ m_name = name;
+ AddLocalProperty( L"productionType", &m_productionType );
+ AddLocalProperty( L"name", &m_name );
+}
CEventTargetChanged::CEventTargetChanged( CEntity* target ) : CScriptEvent( L"targetChanged", EVENT_TARGET_CHANGED, false )
{
@@ -29,7 +50,6 @@ CEventTargetChanged::CEventTargetChanged( CEntity* target ) : CScriptEvent( L"ta
m_secondaryAction = 0;
m_secondaryCursor = L"arrow-default";
-
AddLocalProperty( L"target", &m_target, true );
AddLocalProperty( L"defaultOrder", &m_defaultOrder );
AddLocalProperty( L"defaultAction", &m_defaultAction );
@@ -39,19 +59,23 @@ CEventTargetChanged::CEventTargetChanged( CEntity* target ) : CScriptEvent( L"ta
AddLocalProperty( L"secondaryAction", &m_secondaryAction );
}
-CEventPrepareOrder::CEventPrepareOrder( CEntity* target, int orderType, int action ) : CScriptEvent( L"prepareOrder", EVENT_PREPARE_ORDER, true )
+CEventPrepareOrder::CEventPrepareOrder( CEntity* target, int orderType, int action, const CStrW& name)
+ : CScriptEvent( L"prepareOrder", EVENT_PREPARE_ORDER, true )
{
m_target = target;
m_orderType = orderType;
m_action = action;
+ m_name = name;
AddLocalProperty( L"target", &m_target, true );
AddLocalProperty( L"orderType", &m_orderType, true );
AddLocalProperty( L"action", &m_action );
+ AddLocalProperty( L"name", &m_name );
AddLocalProperty( L"notifyType", &m_notifyType );
AddLocalProperty( L"notifySource", &m_notifySource );
}
-CEventOrderTransition::CEventOrderTransition( int orderPrevious, int orderCurrent, CEntity*& target, CVector3D& worldPosition ) : CScriptEvent( L"orderTransition", EVENT_ORDER_TRANSITION, true )
+CEventOrderTransition::CEventOrderTransition( int orderPrevious, int orderCurrent, CEntity*& target, CVector3D& worldPosition )
+ : CScriptEvent( L"orderTransition", EVENT_ORDER_TRANSITION, true )
{
m_orderPrevious = orderPrevious;
m_orderCurrent = orderCurrent;
diff --git a/source/simulation/EventHandlers.h b/source/simulation/EventHandlers.h
index 76ddb6c9e4..7d443555dc 100755
--- a/source/simulation/EventHandlers.h
+++ b/source/simulation/EventHandlers.h
@@ -26,7 +26,33 @@ class CEventGeneric : public CScriptEvent
CEntity* m_target;
int m_action;
public:
- CEventGeneric( CEntity* target, int m_action );
+ CEventGeneric( CEntity* target, int action );
+};
+
+class CEventStartProduction : public CScriptEvent
+{
+ int m_productionType;
+ CStrW m_name;
+ float m_time;
+public:
+ CEventStartProduction( int productionType, const CStrW& name );
+ inline float GetTime() { return m_time; }
+};
+
+class CEventFinishProduction : public CScriptEvent
+{
+ int m_productionType;
+ CStrW m_name;
+public:
+ CEventFinishProduction( int productionType, const CStrW& name );
+};
+
+class CEventCancelProduction : public CScriptEvent
+{
+ int m_productionType;
+ CStrW m_name;
+public:
+ CEventCancelProduction( int productionType, const CStrW& name );
};
class CEventTargetChanged : public CScriptEvent
@@ -44,13 +70,14 @@ public:
class CEventPrepareOrder : public CScriptEvent
{
+public:
CEntity* m_target;
int m_orderType;
-public:
+ int m_action;
+ CStrW m_name;
CEntity* m_notifySource;
int m_notifyType;
- int m_action;
- CEventPrepareOrder( CEntity* target, int orderType, int action );
+ CEventPrepareOrder( CEntity* target, int orderType, int action, const CStrW& name );
};
class CEventOrderTransition : public CScriptEvent
diff --git a/source/simulation/ProductionQueue.cpp b/source/simulation/ProductionQueue.cpp
new file mode 100644
index 0000000000..93f4e5ad0e
--- /dev/null
+++ b/source/simulation/ProductionQueue.cpp
@@ -0,0 +1,137 @@
+#include "precompiled.h"
+#include "ProductionQueue.h"
+#include "EntityManager.h"
+#include
+
+using namespace std;
+
+// CProductionItem
+
+CProductionItem::CProductionItem( int type, const CStrW& name, float totalTime )
+ : m_type(type), m_name(name), m_totalTime(totalTime), m_elapsedTime(0)
+{
+ ONCE( ScriptingInit(); );
+}
+
+CProductionItem::~CProductionItem()
+{
+}
+
+void CProductionItem::Update( size_t timestep )
+{
+ m_elapsedTime = min( m_totalTime, m_elapsedTime + timestep/1000.0f );
+}
+
+bool CProductionItem::IsComplete()
+{
+ return( m_elapsedTime == m_totalTime );
+}
+
+float CProductionItem::GetProgress()
+{
+ return( m_totalTime==0 ? 1.0 : m_elapsedTime/m_totalTime );
+}
+
+jsval CProductionItem::JSI_GetProgress( JSContext* UNUSED(cx) )
+{
+ return ToJSVal( GetProgress() );
+}
+
+void CProductionItem::ScriptingInit()
+{
+ AddProperty(L"type", &CProductionItem::m_type, true);
+ AddProperty(L"name", &CProductionItem::m_name, true);
+ AddProperty(L"elapsedTime", &CProductionItem::m_elapsedTime, true);
+ AddProperty(L"totalTime", &CProductionItem::m_totalTime, true);
+ AddProperty(L"progress", (GetFn)&CProductionItem::JSI_GetProgress);
+ CJSObject::ScriptingInit("ProductionItem");
+}
+
+// CProductionQueue
+
+CProductionQueue::CProductionQueue( CEntity* owner )
+ : m_owner(owner)
+{
+ ONCE( ScriptingInit(); );
+}
+
+CProductionQueue::~CProductionQueue()
+{
+ for( size_t i=0; i < m_items.size(); ++i )
+ {
+ delete m_items[i];
+ }
+}
+
+void CProductionQueue::AddItem( int type, const CStrW& name, float totalTime )
+{
+ m_items.push_back( new CProductionItem( type, name, totalTime ) );
+}
+
+void CProductionQueue::Update( size_t timestep )
+{
+ if( !m_items.empty() )
+ {
+ CProductionItem* front = m_items.front();
+ front->Update( timestep );
+ if( front->IsComplete() )
+ {
+ debug_printf("Production complete!\n");
+ CEventFinishProduction evt( front->m_type, front->m_name );
+ m_owner->DispatchEvent( &evt );
+ m_items.erase( m_items.begin() );
+ delete front;
+ }
+ // TODO: Think about what we want to happen when there are multiple productions
+ // with totalTime=0 at the front (pop them all at once or do one per update?)
+ }
+}
+
+jsval CProductionQueue::JSI_GetLength( JSContext* UNUSED(cx) )
+{
+ return ToJSVal( (int) m_items.size() );
+}
+
+jsval CProductionQueue::JSI_Get( JSContext* cx, uintN argc, jsval* argv )
+{
+ debug_assert( argc == 1 );
+ debug_assert( JSVAL_IS_INT(argv[0]) );
+
+ int index = ToPrimitive( argv[0] );
+
+ if(index < 0 || index >= m_items.size() )
+ {
+ JS_ReportError( cx, "Production queue index out of bounds: %d", index );
+ return JSVAL_NULL;
+ }
+
+ return ToJSVal( m_items[index] );
+}
+
+bool CProductionQueue::JSI_Cancel( JSContext* cx, uintN argc, jsval* argv )
+{
+ debug_assert( argc == 1 );
+ debug_assert( JSVAL_IS_INT(argv[0]) );
+
+ int index = ToPrimitive( argv[0] );
+
+ if(index < 0 || index >= m_items.size() )
+ {
+ JS_ReportError( cx, "Production queue index out of bounds: %d", index );
+ return false;
+ }
+
+ CProductionItem* item = m_items[index];
+ CEventCancelProduction evt( item->m_type, item->m_name );
+ m_owner->DispatchEvent( &evt );
+ m_items.erase( m_items.begin() + index );
+ return true;
+}
+
+void CProductionQueue::ScriptingInit()
+{
+ AddProperty(L"length", (GetFn)&CProductionQueue::JSI_GetLength);
+ AddMethod( "get", 1 );
+ AddMethod( "cancel", 1 );
+ CJSObject::ScriptingInit("ProductionQueue");
+}
diff --git a/source/simulation/ProductionQueue.h b/source/simulation/ProductionQueue.h
new file mode 100644
index 0000000000..7bb68dd695
--- /dev/null
+++ b/source/simulation/ProductionQueue.h
@@ -0,0 +1,47 @@
+#include "Entity.h"
+#include "EntityHandles.h"
+#include
+
+#ifndef __PRODUCTIONQUEUE_H__
+#define __PRODUCTIONQUEUE_H__
+
+class CProductionItem : public CJSObject
+{
+public:
+ int m_type;
+ CStrW m_name;
+ float m_totalTime; // how long this production takes
+ float m_elapsedTime; // how long we've been working on this production
+
+ CProductionItem( int type, const CStrW& name, float totalTime );
+ ~CProductionItem();
+
+ void Update( size_t timestep );
+
+ bool IsComplete();
+ float GetProgress();
+
+ jsval JSI_GetProgress( JSContext* cx );
+
+ static void ScriptingInit();
+};
+
+class CProductionQueue : public CJSObject
+{
+ CEntity* m_owner;
+ std::vector m_items;
+public:
+ CProductionQueue( CEntity* owner );
+ ~CProductionQueue();
+
+ void AddItem( int type, const CStrW& name, float totalTime );
+ void Update( size_t timestep );
+
+ jsval JSI_GetLength( JSContext* cx );
+ jsval JSI_Get( JSContext* cx, uintN argc, jsval* argv );
+ bool JSI_Cancel( JSContext* cx, uintN argc, jsval* argv );
+
+ static void ScriptingInit();
+};
+
+#endif
diff --git a/source/simulation/Simulation.cpp b/source/simulation/Simulation.cpp
index 71bc460a8d..127d62aca2 100755
--- a/source/simulation/Simulation.cpp
+++ b/source/simulation/Simulation.cpp
@@ -212,6 +212,14 @@ uint CSimulation::TranslateMessage(CNetMessage* pMsg, uint clientMask, void* UNU
order.m_data[1].data=msg->m_Action; \
QueueOrder(order, msg->m_Entities, clearQueue); \
} while(0)
+#define ENTITY_INT_STRING(_msg, _order) do\
+ { \
+ _msg *msg=(_msg *)pMsg; \
+ order.m_type=CEntityOrder::_order; \
+ order.m_data[0].string=msg->m_Name; \
+ order.m_data[1].data=msg->m_Type; \
+ QueueOrder(order, msg->m_Entities, clearQueue); \
+ } while(0)
switch (pMsg->GetType())
{
@@ -260,6 +268,9 @@ uint CSimulation::TranslateMessage(CNetMessage* pMsg, uint clientMask, void* UNU
case NMT_Generic:
ENTITY_ENTITY_INT(CGeneric, ORDER_GENERIC);
break;
+ case NMT_Produce:
+ ENTITY_INT_STRING(CProduce, ORDER_PRODUCE);
+ break;
case NMT_NotifyRequest:
ENTITY_ENTITY_INT(CNotifyRequest, ORDER_NOTIFY_REQUEST);
break;