Added production queue system and associated events. It might still need some extra features but it should be functional.

This was SVN commit r3547.
This commit is contained in:
Matei
2006-02-22 22:45:16 +00:00
parent 022c84f43d
commit 7a4aeb69ed
18 changed files with 403 additions and 90 deletions
@@ -39,7 +39,7 @@
<Script File="entities/template_entity_script.js" />
<Script File="entities/template_entity_script.js" />
<Event On="Initialize" Function="entityInit" />
<Event On="PrepareOrder" Function="entityEventPrepareOrder" />
</Entity>
@@ -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...
@@ -27,4 +27,8 @@
</Traits>
<Script File="entities/template_entity_script.js" />
<Event On="StartProduction" Function="entityStartProduction" />
<Event On="CancelProduction" Function="entityCancelProduction" />
<Event On="FinishProduction" Function="entityFinishProduction" />
</Entity>
@@ -25,7 +25,7 @@
<Loot>
<XP>800</XP>
<Food>1</Food>
<wood>1</wood>
<Wood>1</Wood>
<Stone>1</Stone>
<Ore>1</Ore>
</Loot>
+3
View File
@@ -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);
+6
View File
@@ -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)
+23 -1
View File
@@ -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<int>(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<CStrW>(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;
+7 -1
View File
@@ -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...) */
+35 -11
View File
@@ -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<CStrW>(argv[2]);
newOrder.m_data[1].data = ToPrimitive<int>(argv[1]);
}
catch( PSERROR_Scripting_ConversionFailed )
{
JS_ReportError( cx, "Invalid parameter types" );
return( false );
}
break;
default:
JS_ReportError( cx, "Invalid order type" );
return( false );
+7 -5
View File
@@ -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<CEntity>, public IEventTarget
typedef STL_HASH_MAP<int, SEntityAction> ActionTable;
typedef std::set<CAura*> 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 );
+7 -1
View File
@@ -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];
};
+24 -13
View File
@@ -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 );
}
-40
View File
@@ -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<SAuraInstance> m_Influenced;
SAuraData m_Data;
size_t m_Recharge;
};
#endif
+33 -9
View File
@@ -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;
+31 -4
View File
@@ -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
+137
View File
@@ -0,0 +1,137 @@
#include "precompiled.h"
#include "ProductionQueue.h"
#include "EntityManager.h"
#include <algorithm>
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<CProductionItem>::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<int>( 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<int>( 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<jsval, &CProductionQueue::JSI_Get>( "get", 1 );
AddMethod<bool, &CProductionQueue::JSI_Cancel>( "cancel", 1 );
CJSObject<CProductionQueue>::ScriptingInit("ProductionQueue");
}
+47
View File
@@ -0,0 +1,47 @@
#include "Entity.h"
#include "EntityHandles.h"
#include <vector>
#ifndef __PRODUCTIONQUEUE_H__
#define __PRODUCTIONQUEUE_H__
class CProductionItem : public CJSObject<CProductionItem>
{
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<CProductionQueue>
{
CEntity* m_owner;
std::vector<CProductionItem*> 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
+11
View File
@@ -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;