mirror of
https://gitea.wildfiregames.com/0ad/0ad.git
synced 2026-09-21 20:06:40 +00:00
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:
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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...) */
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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 );
|
||||
|
||||
|
||||
@@ -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];
|
||||
};
|
||||
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user