Script integration work - valueOf() is no longer required most of the time; also property-change watches and enumeration over entities (but not yet other native objects), rudimentary beginnings of a global events system, and adjusted the status orb to update only on changes.
This was SVN commit r2157.
This commit is contained in:
parent
498d58ee61
commit
bcabe3aa53
@ -28,7 +28,7 @@ function entity_event_gather( evt )
|
||||
console.write( evt.target.traits.supply.type);
|
||||
console.write( evt.target.traits.supply.type.toString().toUpperCase() );
|
||||
evt.target.traits.supply.curr -= gather_amt;
|
||||
this.player.resource.valueOf()[evt.target.traits.supply.type.toString().toUpperCase()] += gather_amt;
|
||||
this.player.resource[evt.target.traits.supply.type.toString().toUpperCase()] += gather_amt;
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,16 +132,28 @@ function entity_event_targetchanged( evt )
|
||||
// string) first.
|
||||
|
||||
evt.defaultAction = ORDER_GOTO;
|
||||
evt.defaultCursor = "arrow-default";
|
||||
if( evt.target )
|
||||
{
|
||||
if( this.actions.attack &&
|
||||
( evt.target.traits.id.civ_code != "gaia" ) &&
|
||||
( evt.target.traits.id.civ_code.toString() != this.traits.id.civ_code.toString() ) )
|
||||
( evt.target.player != gaiaPlayer ) &&
|
||||
( evt.target.player != this.player ) )
|
||||
{
|
||||
evt.defaultAction = ORDER_ATTACK;
|
||||
evt.defaultCursor = "action-attack";
|
||||
}
|
||||
if( this.actions.gather && evt.target.traits.supply &&
|
||||
this.actions.gather[evt.target.traits.supply.type] &&
|
||||
( ( evt.target.traits.supply.curr > 0 ) || ( evt.target.traits.supply.max == 0 ) ) )
|
||||
{
|
||||
evt.defaultAction = ORDER_GATHER;
|
||||
if( evt.target.traits.supply.type == "wood" )
|
||||
{
|
||||
evt.defaultCursor = "action-chop";
|
||||
}
|
||||
else
|
||||
evt.defaultCursor = "action-mine";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -161,11 +173,8 @@ function entity_event_prepareorder( evt )
|
||||
evt.preventDefault();
|
||||
break;
|
||||
case ORDER_ATTACK:
|
||||
// If we can't attack, we're not targeting a unit, or that unit is the same civ as us.
|
||||
// (Should of course be same /player/ as us - not ready yet.)
|
||||
if( !this.actions.attack ||
|
||||
!evt.target ||
|
||||
( evt.target.traits.id.civ_code.toString() == this.traits.id.civ_code.toString() ) )
|
||||
!evt.target )
|
||||
evt.preventDefault();
|
||||
break;
|
||||
case ORDER_GATHER:
|
||||
@ -191,10 +200,11 @@ function entity_add_create_queue( template, list, tab )
|
||||
comboTemplate.tab = tab;
|
||||
|
||||
// Append to the end of this queue
|
||||
this.actions.create.queue.valueOf().push( comboTemplate );
|
||||
|
||||
this.actions.create.queue.push( template );
|
||||
|
||||
// If we're not already building something...
|
||||
if( !this.actions.create.progress || !this.actions.create.progress.valueOf() )
|
||||
if( !this.actions.create.progress )
|
||||
{
|
||||
console.write( "Starting work on (unqueued) ", template.tag );
|
||||
// Start the progress timer.
|
||||
@ -218,7 +228,7 @@ function entity_create_complete()
|
||||
// at http://www.mozilla.org/js/language/E262-3.pdf. Bit technical but
|
||||
// the sections on 'Native ECMAScript Objects' are quite useful)
|
||||
|
||||
var template = this.actions.create.queue.valueOf().shift();
|
||||
var template = this.actions.create.queue.shift();
|
||||
|
||||
// Code to find a free space around an object is tedious and slow, so
|
||||
// I wrote it in C. Takes the template object so it can determine how
|
||||
@ -232,26 +242,26 @@ function entity_create_complete()
|
||||
// Oh well. The player's just lost all the resources and time they put into
|
||||
// construction - serves them right for not paying attention to the land
|
||||
// around their barracks, doesn't it?
|
||||
return;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
created = new Entity( template, position );
|
||||
|
||||
// Above shouldn't ever fail, but just in case...
|
||||
if( !created )
|
||||
return;
|
||||
|
||||
if( created )
|
||||
{
|
||||
console.write( "Created: ", template.tag );
|
||||
|
||||
// Entities start under Gaia control - make the controller
|
||||
// the same as our controller
|
||||
created.player = this.player;
|
||||
|
||||
}
|
||||
}
|
||||
// If there's something else in the build queue...
|
||||
if( this.actions.create.queue.valueOf().length > 0 )
|
||||
if( this.actions.create.queue.length > 0 )
|
||||
{
|
||||
// Start on the next item.
|
||||
template = this.actions.create.queue.valueOf()[0];
|
||||
template = this.actions.create.queue[0];
|
||||
console.write( "Starting work on (queued) ", template.tag );
|
||||
this.actions.create.progress = new ProgressTimer( template.traits.creation.time, this.actions.create.construct / 1000, entity_create_complete, this )
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ function setupSession()
|
||||
CreateResources();
|
||||
|
||||
// Start refreshing the session controls.
|
||||
setInterval( getObjectInfo, 1, 1000 );
|
||||
setInterval( getObjectInfo, 1, 100 );
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
|
@ -463,6 +463,12 @@ function PressCommandButton(GUIObject, list, tab)
|
||||
|
||||
function UpdateCommandButtons()
|
||||
{
|
||||
if( shouldUpdateStat( "actions.create.list" ) )
|
||||
{
|
||||
// Everything in this block is tied to properties in
|
||||
// actions.create.list, the above check should limit the
|
||||
// number of times this update is needlessly made.
|
||||
|
||||
// Update train/research/build lists.
|
||||
listCounter = 1;
|
||||
unitArray = UpdateList(action_tab_train, listCounter); if (unitArray != 0) listCounter++;
|
||||
@ -471,7 +477,10 @@ function UpdateCommandButtons()
|
||||
techArray = UpdateList(action_tab_research, listCounter); if (techArray != 0) listCounter++;
|
||||
formationArray = UpdateList(action_tab_formation, listCounter); if (formationArray != 0) listCounter++;
|
||||
stanceArray = UpdateList(action_tab_stance, listCounter); if (stanceArray != 0) listCounter++;
|
||||
}
|
||||
|
||||
if( shouldUpdateStat( "actions" ) )
|
||||
{
|
||||
// Update commands.
|
||||
commandCounter = SN_STATUS_PANE_COMMAND.tab.max;
|
||||
commandCounter = UpdateCommand(action_attack, commandCounter);
|
||||
@ -489,6 +498,7 @@ function UpdateCommandButtons()
|
||||
// If this slot could possibly contain a list, hide that too.
|
||||
GUIObjectHide("SN_STATUS_PANE_COMMAND_" + commandClearLoop + "_GROUP");
|
||||
}
|
||||
}
|
||||
|
||||
// Update production queue.
|
||||
GUIObject = getGUIObjectByName("SN_STATUS_PANE_COMMAND_PROGRESS");
|
||||
@ -521,27 +531,125 @@ function UpdateCommandButtons()
|
||||
|
||||
// ====================================================================
|
||||
|
||||
// Update-on-alteration trickery...
|
||||
|
||||
// We don't really want to update every single time we get a
|
||||
// selection-changed or property-changed event; that could happen
|
||||
// a lot. Instead, use this bunch of globals to cache any changes
|
||||
// that happened between GUI updates.
|
||||
|
||||
// This boolean determines whether the selection has been changed.
|
||||
var selectionChanged = false;
|
||||
|
||||
// This boolean determines what the template of the selected object
|
||||
// was when last we looked
|
||||
var selectionTemplate = null;
|
||||
|
||||
// This array holds the name of all properties that need to be updated
|
||||
var selectionPropertiesChanged = new Array();
|
||||
|
||||
// This array holds a list of all the objects we hold property-change
|
||||
// watches on
|
||||
var propertyWatches = new Array();
|
||||
|
||||
// This function resets all the update variables, above
|
||||
function resetUpdateVars()
|
||||
{
|
||||
if( selectionChanged )
|
||||
{
|
||||
for( watchedObject in propertyWatches )
|
||||
propertyWatches[watchedObject].unwatchAll( selectionWatchHandler ); // Remove the handler
|
||||
|
||||
propertyWatches = new Array();
|
||||
if( selection[0] )
|
||||
{
|
||||
// Watch the object itself
|
||||
selection[0].watchAll( selectionWatchHandler );
|
||||
propertyWatches.push( selection[0] );
|
||||
// And every parent back up the tree (changes there will affect
|
||||
// displayed properties via inheritance)
|
||||
var parent = selection[0].template
|
||||
while( parent )
|
||||
{
|
||||
parent.watchAll( selectionWatchHandler );
|
||||
propertyWatches.push( selection[0] );
|
||||
parent = parent.parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
selectionChanged = false;
|
||||
if( selection[0] )
|
||||
{
|
||||
selectionTemplate = selection[0].template;
|
||||
}
|
||||
else
|
||||
selectionTemplate = null;
|
||||
|
||||
selectionPropertiesChanged = new Array();
|
||||
}
|
||||
|
||||
// This function returns whether we should update a particular statistic
|
||||
// in the GUI (e.g. "actions.attack") - this should happen if: the selection
|
||||
// changed, the selection had its template altered (changing lots of stuff)
|
||||
// or an assignment has been made to that stat or any property within that
|
||||
// stat.
|
||||
function shouldUpdateStat( statname )
|
||||
{
|
||||
if( selectionChanged || ( selectionTemplate != selection[0].template ) )
|
||||
return( true );
|
||||
for( var property in selectionPropertiesChanged )
|
||||
{
|
||||
// If property starts with statname
|
||||
if( selectionPropertiesChanged[property].substring( 0, statname.length ) == statname )
|
||||
return( true );
|
||||
}
|
||||
return( false );
|
||||
}
|
||||
|
||||
// This function is a handler for the 'selectionChanged' event,
|
||||
// it updates the selectionChanged flag
|
||||
function selectionChangedHandler()
|
||||
{
|
||||
selectionChanged = true;
|
||||
}
|
||||
|
||||
// Register it.
|
||||
addGlobalHandler( "selectionChanged", selectionChangedHandler );
|
||||
|
||||
// This function is a handler for a watch event; it updates the
|
||||
// selectionPropertiesChanged array
|
||||
function selectionWatchHandler( propname, oldvalue, newvalue )
|
||||
{
|
||||
selectionPropertiesChanged.push( propname );
|
||||
// This bit's important (watches allow the handler to change the value
|
||||
// before it gets written; we don't want to affect things, so make sure
|
||||
// the value we send back is the one that was going to be written)
|
||||
return( newvalue );
|
||||
}
|
||||
|
||||
function UpdateStatusOrb()
|
||||
{
|
||||
// Update heading.
|
||||
if( shouldUpdateStat( "player" ) || shouldUpdateStat( "traits.id.civ" ) )
|
||||
{
|
||||
GUIObject = getGUIObjectByName("SN_STATUS_PANE_HEADING");
|
||||
GUIObject.caption = selection[0].player.name; // Player name (placeholder; replace with proper callsign).
|
||||
if (selection[0].traits.id.civ)
|
||||
GUIObject.caption += " [icon=bullet_icon] " + selection[0].traits.id.civ;
|
||||
}
|
||||
|
||||
// Update name text.
|
||||
if( shouldUpdateStat( "traits.id" ) )
|
||||
{
|
||||
GUIObject = getGUIObjectByName("SN_STATUS_PANE_NAME1");
|
||||
GUIObject.caption = "";
|
||||
|
||||
// Personal name.
|
||||
if (selection[0].traits.id.personal && selection[0].traits.id.personal != "")
|
||||
{
|
||||
GUIObject.caption += selection[0].traits.id.personal + "\n";
|
||||
}
|
||||
// Generic name.
|
||||
if (selection[0].traits.id.generic)
|
||||
{
|
||||
GUIObject.caption += selection[0].traits.id.generic + "\n";
|
||||
}
|
||||
// Specific/ranked name.
|
||||
if (selection[0].traits.id.ranked)
|
||||
{
|
||||
@ -554,11 +662,15 @@ function UpdateStatusOrb()
|
||||
GUIObject.caption += selection[0].traits.id.specific + "\n";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if( shouldUpdateStat( "traits.id.icon" ) )
|
||||
{
|
||||
// Update portrait
|
||||
if (selection[0].traits.id.icon)
|
||||
setPortrait("SN_STATUS_PANE_PORTRAIT", selection[0].traits.id.icon, selection[0].traits.id.civ_code, selection[0].traits.id.icon_cell);
|
||||
|
||||
}
|
||||
if( shouldUpdateStat( "traits.up.rank" ) )
|
||||
{
|
||||
// Update rank.
|
||||
GUIObject = getGUIObjectByName("SN_STATUS_PANE_ICON_RANK");
|
||||
if (selection[0].traits.up.rank > 1)
|
||||
@ -568,7 +680,9 @@ function UpdateStatusOrb()
|
||||
}
|
||||
else
|
||||
GUIObject.sprite = "";
|
||||
|
||||
}
|
||||
if( shouldUpdateStat( "traits.health" ) )
|
||||
{
|
||||
// Update hitpoints
|
||||
if (selection[0].traits.health.curr && selection[0].traits.health.max)
|
||||
{
|
||||
@ -582,7 +696,10 @@ function UpdateStatusOrb()
|
||||
getGUIObjectByName("SN_STATUS_PANE_ICON_HP_TEXT").hidden = true;
|
||||
getGUIObjectByName("SN_STATUS_PANE_ICON_HP_BAR").hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
if( shouldUpdateStat( "traits.up" ) )
|
||||
{
|
||||
// Update upgrade points
|
||||
if (selection[0].traits.up && selection[0].traits.up.curr && selection[0].traits.up.req)
|
||||
{
|
||||
@ -596,7 +713,9 @@ function UpdateStatusOrb()
|
||||
getGUIObjectByName("SN_STATUS_PANE_ICON_XP_TEXT").hidden = true;
|
||||
getGUIObjectByName("SN_STATUS_PANE_ICON_XP_BAR").hidden = true;
|
||||
}
|
||||
|
||||
}
|
||||
if( shouldUpdateStat( "traits.garrison" ) )
|
||||
{
|
||||
// Update Supply/Garrison
|
||||
GUIObject = getGUIObjectByName("SN_STATUS_PANE_2STAT");
|
||||
GUIObject.caption = '';
|
||||
@ -608,6 +727,11 @@ function UpdateStatusOrb()
|
||||
GUIObject.caption += '[icon="icon_statistic_garrison"] [color="100 100 255"]' + selection[0].traits.garrison.curr + '/' + selection[0].traits.garrison.max + '[/color] ';
|
||||
}
|
||||
}
|
||||
}
|
||||
if( shouldUpdateStat( "traits.supply" ) )
|
||||
{
|
||||
GUIObject = getGUIObjectByName("SN_STATUS_PANE_2STAT");
|
||||
GUIObject.caption = '';
|
||||
|
||||
if (selection[0].traits.supply)
|
||||
{
|
||||
@ -620,8 +744,11 @@ function UpdateStatusOrb()
|
||||
GUIObject.caption += '[icon="icon_resource_' + selection[0].traits.supply.type + '"] [color="100 100 255"]' + selection[0].traits.supply.curr + '/' + selection[0].traits.supply.max + '[/color] ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update Attack stats
|
||||
if( shouldUpdateStat( "actions.attack" ) )
|
||||
{
|
||||
if (selection[0].actions.attack && selection[0].actions.attack.damage && selection[0].actions.attack.damage > 0)
|
||||
getGUIObjectByName("SN_STATUS_PANE_STAT1").caption = '[icon="icon_statistic_attack"]' + selection[0].actions.attack.damage;
|
||||
else
|
||||
@ -651,8 +778,10 @@ function UpdateStatusOrb()
|
||||
getGUIObjectByName("SN_STATUS_PANE_STAT6").caption = '[icon="icon_statistic_accuracy"]' + Math.round(selection[0].actions.attack.accuracy*100) + '%';
|
||||
else
|
||||
getGUIObjectByName("SN_STATUS_PANE_STAT6").caption = "";
|
||||
|
||||
}
|
||||
// Update Armour & Other stats
|
||||
if( shouldUpdateStat( "traits.armour" ) )
|
||||
{
|
||||
if (selection[0].traits.armour && selection[0].traits.armour.value && selection[0].traits.armour.value > 0)
|
||||
getGUIObjectByName("SN_STATUS_PANE_STAT7").caption = '[icon="icon_statistic_armour"]' + selection[0].traits.armour.value;
|
||||
else getGUIObjectByName("SN_STATUS_PANE_STAT7").caption = "";
|
||||
@ -665,18 +794,26 @@ function UpdateStatusOrb()
|
||||
if (selection[0].traits.armour && selection[0].traits.armour.crush && selection[0].traits.armour.crush > 0)
|
||||
getGUIObjectByName("SN_STATUS_PANE_STAT10").caption = '[icon="icon_statistic_crush"]' + Math.round(selection[0].traits.armour.crush*100) + '%';
|
||||
else getGUIObjectByName("SN_STATUS_PANE_STAT10").caption = "";
|
||||
|
||||
}
|
||||
if( shouldUpdateStat( "actions.move" ) )
|
||||
{
|
||||
if (selection[0].actions.move && selection[0].actions.move.speed)
|
||||
getGUIObjectByName("SN_STATUS_PANE_STAT11").caption = '[icon="icon_statistic_speed"]' + selection[0].actions.move.speed;
|
||||
else getGUIObjectByName("SN_STATUS_PANE_STAT11").caption = "";
|
||||
}
|
||||
if( shouldUpdateStat( "traits.vision" ) )
|
||||
{
|
||||
if (selection[0].traits.vision && selection[0].traits.vision.los)
|
||||
getGUIObjectByName("SN_STATUS_PANE_STAT12").caption = '[icon="icon_statistic_los"]' + selection[0].traits.vision.los;
|
||||
else getGUIObjectByName("SN_STATUS_PANE_STAT12").caption = "";
|
||||
}
|
||||
|
||||
// Reveal Status Orb
|
||||
getGUIObjectByName("session_status_orb").hidden = false;
|
||||
|
||||
// Update Command Buttons.
|
||||
UpdateCommandButtons();
|
||||
|
||||
resetUpdateVars();
|
||||
}
|
||||
|
||||
|
@ -199,7 +199,7 @@ void CModel::CalcAnimatedObjectBound(CSkeletonAnimDef* anim,CBound& result)
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// BuildAnimation: load raw animation frame animation from given file, and build a
|
||||
// animation specific to this model
|
||||
CSkeletonAnim* CModel::BuildAnimation(const char* filename,float speed)
|
||||
CSkeletonAnim* CModel::BuildAnimation(const char* filename,float speed,size_t actionpos)
|
||||
{
|
||||
CSkeletonAnimDef* def=g_SkelAnimMan.GetAnimation(filename);
|
||||
if (!def) return 0;
|
||||
@ -207,6 +207,9 @@ CSkeletonAnim* CModel::BuildAnimation(const char* filename,float speed)
|
||||
CSkeletonAnim* anim=new CSkeletonAnim;
|
||||
anim->m_AnimDef=def;
|
||||
anim->m_Speed=speed;
|
||||
anim->m_ActionPos=actionpos;
|
||||
if( actionpos > anim->m_AnimDef->GetDuration() )
|
||||
anim->m_ActionPos = anim->m_AnimDef->GetDuration();
|
||||
anim->m_ObjectBounds.SetEmpty();
|
||||
InvalidateBounds();
|
||||
|
||||
|
@ -102,7 +102,7 @@ public:
|
||||
|
||||
// load raw animation frame animation from given file, and build a
|
||||
// animation specific to this model
|
||||
CSkeletonAnim* BuildAnimation(const char* filename,float speed);
|
||||
CSkeletonAnim* BuildAnimation(const char* filename,float speed,size_t actionpos);
|
||||
|
||||
// add a prop to the model on the given point
|
||||
void AddProp(SPropPoint* point,CModel* model);
|
||||
|
@ -159,6 +159,7 @@ bool CObjectBase::Load(const char* filename)
|
||||
AT(file);
|
||||
AT(name);
|
||||
AT(speed);
|
||||
AT(actionpos);
|
||||
AT(attachpoint);
|
||||
AT(actor);
|
||||
AT(frequency);
|
||||
@ -220,6 +221,11 @@ bool CObjectBase::Load(const char* filename)
|
||||
anim.m_Speed = CStr(ae.Value).ToInt() / 100.f;
|
||||
if (anim.m_Speed <= 0.0) anim.m_Speed = 1.0f;
|
||||
}
|
||||
else if (ae.Name == at_actionpos)
|
||||
{
|
||||
anim.m_ActionPos = CStr(ae.Value).ToInt();
|
||||
if (anim.m_ActionPos < 0) anim.m_ActionPos = 0;
|
||||
}
|
||||
else
|
||||
; // unrecognised element
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ public:
|
||||
|
||||
struct Anim {
|
||||
// constructor
|
||||
Anim() : m_Speed(1), m_AnimData(0) {}
|
||||
Anim() : m_Speed(1), m_ActionPos( 0 ), m_AnimData(0) {}
|
||||
|
||||
// name of the animation - "Idle", "Run", etc
|
||||
CStr m_AnimName;
|
||||
@ -21,6 +21,8 @@ public:
|
||||
CStr m_FileName;
|
||||
// animation speed, as specified in XML actor file
|
||||
float m_Speed;
|
||||
// time during the animation at which the interesting bit happens (msec)
|
||||
size_t m_ActionPos;
|
||||
// the animation data, specific to the this model
|
||||
CSkeletonAnim* m_AnimData;
|
||||
};
|
||||
|
@ -144,7 +144,7 @@ bool CObjectEntry::BuildRandomVariant(CObjectBase::variation_key& vars, CObjectB
|
||||
if (m_Animations[t].m_FileName.Length() > 0)
|
||||
{
|
||||
const char* animfilename = m_Animations[t].m_FileName;
|
||||
m_Animations[t].m_AnimData = m_Model->BuildAnimation(animfilename, m_Animations[t].m_Speed);
|
||||
m_Animations[t].m_AnimData = m_Model->BuildAnimation(animfilename, m_Animations[t].m_Speed, m_Animations[t].m_ActionPos);
|
||||
|
||||
CStr AnimNameLC = m_Animations[t].m_AnimName.LowerCase();
|
||||
|
||||
|
@ -23,6 +23,8 @@ public:
|
||||
CSkeletonAnimDef* m_AnimDef;
|
||||
// speed at which this animation runs
|
||||
float m_Speed;
|
||||
// time during the animation at which the interesting bit happens (msec)
|
||||
size_t m_ActionPos;
|
||||
// object space bounds of the model when this animation is applied to it
|
||||
CBound m_ObjectBounds;
|
||||
};
|
||||
|
@ -58,6 +58,7 @@
|
||||
#include "StringConvert.h"
|
||||
|
||||
#include "scripting/ScriptingHost.h"
|
||||
#include "scripting/GameEvents.h"
|
||||
#include "scripting/JSInterface_Entity.h"
|
||||
#include "scripting/JSInterface_BaseEntity.h"
|
||||
#include "scripting/JSInterface_Vector3D.h"
|
||||
@ -789,7 +790,7 @@ TIMER(InitScripting)
|
||||
|
||||
PlayerCollection::Init( "PlayerCollection" );
|
||||
CDamageType::ScriptingInit();
|
||||
CJSPropertyAccessor<CEntity>::ScriptingInit(); // <-- Doesn't really matter which we use, but we know CJSPropertyAccessor<T> is already being compiled for T = CEntity.
|
||||
CJSComplexPropertyAccessor<CEntity>::ScriptingInit(); // <-- Doesn't really matter which we use, but we know CJSPropertyAccessor<T> is already being compiled for T = CEntity.
|
||||
CScriptEvent::ScriptingInit();
|
||||
CJSProgressTimer::ScriptingInit();
|
||||
|
||||
@ -801,6 +802,8 @@ TIMER(InitScripting)
|
||||
|
||||
JSI_Camera::init();
|
||||
JSI_Console::init();
|
||||
|
||||
new CGameEvents;
|
||||
}
|
||||
|
||||
|
||||
@ -1069,6 +1072,7 @@ static void Shutdown()
|
||||
// delete &g_EntityManager;
|
||||
|
||||
delete &g_GameAttributes;
|
||||
delete &g_JSGameEvents;
|
||||
|
||||
delete &g_EntityTemplateCollection;
|
||||
|
||||
|
@ -21,8 +21,8 @@ CPlayerSlot::CPlayerSlot(int slotID, CPlayer *pPlayer):
|
||||
);
|
||||
|
||||
//AddProperty(L"session", (GetFn)&CPlayerSlot::JSI_GetSession);
|
||||
AddReadOnlyProperty(L"session", &m_pSession);
|
||||
AddReadOnlyProperty(L"player", &m_pPlayer);
|
||||
AddLocalProperty(L"session", &m_pSession, true );
|
||||
AddLocalProperty(L"player", &m_pPlayer, true );
|
||||
}
|
||||
|
||||
CPlayerSlot::~CPlayerSlot()
|
||||
@ -34,7 +34,7 @@ void CPlayerSlot::ScriptingInit()
|
||||
AddMethod<bool, &CPlayerSlot::JSI_AssignToSession>("assignToSession", 1);
|
||||
AddMethod<bool, &CPlayerSlot::JSI_AssignLocal>("assignLocal", 0);
|
||||
AddMethod<bool, &CPlayerSlot::JSI_AssignOpen>("assignOpen", 0);
|
||||
AddClassProperty(L"assignment", (GetFn)&CPlayerSlot::JSI_GetAssignment);
|
||||
AddProperty(L"assignment", (GetFn)&CPlayerSlot::JSI_GetAssignment);
|
||||
// AddMethod<bool, &CPlayerSlot::JSI_AssignAI>("assignAI", <num_args>);
|
||||
|
||||
CJSObject<CPlayerSlot>::ScriptingInit("PlayerSlot");
|
||||
@ -258,7 +258,7 @@ void CGameAttributes::ScriptingInit()
|
||||
PlayerSlotArray_JS::Construct, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
AddMethod<jsval, &CGameAttributes::JSI_GetOpenSlot>("getOpenSlot", 0);
|
||||
AddClassProperty(L"slots", (GetFn)&CGameAttributes::JSI_GetPlayerSlots);
|
||||
AddProperty(L"slots", (GetFn)&CGameAttributes::JSI_GetPlayerSlots);
|
||||
|
||||
CJSObject<CGameAttributes>::ScriptingInit("GameAttributes");
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "BoundingObjects.h"
|
||||
#include "Unit.h"
|
||||
#include "Model.h"
|
||||
#include "scripting/GameEvents.h"
|
||||
|
||||
extern CConsole* g_Console;
|
||||
extern int g_mouse_x, g_mouse_y;
|
||||
@ -120,10 +121,12 @@ void CSelectedEntities::renderOverlays()
|
||||
glDisable( GL_BLEND );
|
||||
}
|
||||
|
||||
/*
|
||||
glLoadIdentity();
|
||||
glTranslatef( (float)( g_mouse_x + 16 ), (float)( g_Renderer.GetHeight() - g_mouse_y - 8 ), 0.0f );
|
||||
glScalef( 1.0f, -1.0f, 1.0f );
|
||||
glColor4f( 1.0f, 1.0f, 1.0f, 0.5f );
|
||||
|
||||
switch( m_contextOrder )
|
||||
{
|
||||
case CEntityOrder::ORDER_GOTO:
|
||||
@ -139,6 +142,7 @@ void CSelectedEntities::renderOverlays()
|
||||
glwprintf( L"Gather" );
|
||||
break;
|
||||
}
|
||||
*/
|
||||
|
||||
glDisable( GL_TEXTURE_2D );
|
||||
glPopMatrix();
|
||||
@ -350,6 +354,14 @@ CVector3D CSelectedEntities::getGroupPosition( i8 groupid )
|
||||
|
||||
void CSelectedEntities::update()
|
||||
{
|
||||
static std::vector<HEntity> lastSelection;
|
||||
|
||||
if( !( m_selected == lastSelection ) )
|
||||
{
|
||||
g_JSGameEvents.FireSelectionChanged( m_selectionChanged );
|
||||
lastSelection = m_selected;
|
||||
}
|
||||
|
||||
if( m_selectionChanged || g_Mouseover.m_targetChanged )
|
||||
{
|
||||
// Can't order anything off the map
|
||||
@ -362,6 +374,8 @@ void CSelectedEntities::update()
|
||||
// Quick count to see which is the modal default order.
|
||||
|
||||
int defaultPoll[CEntityOrder::ORDER_LAST];
|
||||
std::map<CStrW, int, CStrW_hash_compare> defaultCursor[CEntityOrder::ORDER_LAST];
|
||||
|
||||
int t, vote;
|
||||
for( t = 0; t < CEntityOrder::ORDER_LAST; t++ )
|
||||
defaultPoll[t] = 0;
|
||||
@ -369,9 +383,15 @@ void CSelectedEntities::update()
|
||||
std::vector<HEntity>::iterator it;
|
||||
for( it = m_selected.begin(); it < m_selected.end(); it++ )
|
||||
{
|
||||
vote = (*it)->defaultOrder( g_Mouseover.m_target );
|
||||
CEventTargetChanged evt( g_Mouseover.m_target );
|
||||
(*it)->DispatchEvent( &evt );
|
||||
vote = evt.m_defaultAction;
|
||||
|
||||
if( ( vote >= 0 ) && ( vote < CEntityOrder::ORDER_LAST ) )
|
||||
{
|
||||
defaultPoll[vote]++;
|
||||
defaultCursor[vote][evt.m_defaultCursor]++;
|
||||
}
|
||||
}
|
||||
|
||||
vote = -1;
|
||||
@ -381,15 +401,16 @@ void CSelectedEntities::update()
|
||||
vote = t;
|
||||
}
|
||||
|
||||
std::map<CStrW, int, CStrW_hash_compare>::iterator itv;
|
||||
m_contextOrder = vote;
|
||||
switch( m_contextOrder )
|
||||
|
||||
// Now find the most appropriate cursor
|
||||
t = 0;
|
||||
for( itv = defaultCursor[vote].begin(); itv != defaultCursor[vote].end(); itv++ )
|
||||
if( itv->second > t )
|
||||
{
|
||||
case CEntityOrder::ORDER_ATTACK_MELEE:
|
||||
g_CursorName = "action-attack";
|
||||
break;
|
||||
default:
|
||||
g_CursorName = "arrow-default";
|
||||
break;
|
||||
t = itv->second;
|
||||
g_CursorName = itv->first;
|
||||
}
|
||||
|
||||
m_selectionChanged = false;
|
||||
|
@ -22,13 +22,13 @@ CNetClient::CServerSession::CServerSession(int sessionID, const CStrW &name):
|
||||
m_Name(name)
|
||||
{
|
||||
ONCE( ScriptingInit(); );
|
||||
|
||||
AddReadOnlyProperty(L"id", &m_SessionID);
|
||||
AddReadOnlyProperty(L"name", &m_Name);
|
||||
}
|
||||
|
||||
void CNetClient::CServerSession::ScriptingInit()
|
||||
{
|
||||
AddProperty(L"id", &CNetClient::CServerSession::m_SessionID, true);
|
||||
AddProperty(L"name", &CNetClient::CServerSession::m_Name, true);
|
||||
|
||||
CJSObject<CServerSession>::ScriptingInit("NetClient_ServerSession");
|
||||
}
|
||||
|
||||
@ -57,18 +57,18 @@ void CNetClient::ScriptingInit()
|
||||
|
||||
|
||||
|
||||
AddClassProperty(L"onStartGame", &CNetClient::m_OnStartGame);
|
||||
AddClassProperty(L"onChat", &CNetClient::m_OnChat);
|
||||
AddClassProperty(L"onConnectComplete", &CNetClient::m_OnConnectComplete);
|
||||
AddClassProperty(L"onDisconnect", &CNetClient::m_OnDisconnect);
|
||||
AddClassProperty(L"onClientConnect", &CNetClient::m_OnClientConnect);
|
||||
AddClassProperty(L"onClientDisconnect", &CNetClient::m_OnClientDisconnect);
|
||||
AddProperty(L"onStartGame", &CNetClient::m_OnStartGame);
|
||||
AddProperty(L"onChat", &CNetClient::m_OnChat);
|
||||
AddProperty(L"onConnectComplete", &CNetClient::m_OnConnectComplete);
|
||||
AddProperty(L"onDisconnect", &CNetClient::m_OnDisconnect);
|
||||
AddProperty(L"onClientConnect", &CNetClient::m_OnClientConnect);
|
||||
AddProperty(L"onClientDisconnect", &CNetClient::m_OnClientDisconnect);
|
||||
|
||||
|
||||
AddClassProperty(L"password", &CNetClient::m_Password);
|
||||
AddClassProperty<CStrW>(L"playerName", &CNetClient::m_Name);
|
||||
AddProperty(L"password", &CNetClient::m_Password);
|
||||
AddProperty<CStrW>(L"playerName", &CNetClient::m_Name);
|
||||
|
||||
AddClassProperty(L"sessions", &CNetClient::m_JSI_ServerSessions);
|
||||
AddProperty(L"sessions", &CNetClient::m_JSI_ServerSessions);
|
||||
CJSMap<SessionMap>::ScriptingInit("NetClient_SessionMap");
|
||||
CJSObject<CNetClient>::ScriptingInit("NetClient");
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ class CStartGameEvent: public CScriptEvent
|
||||
{
|
||||
public:
|
||||
CStartGameEvent():
|
||||
CScriptEvent(L"startGame", false, NET_JS_EVENT_START_GAME)
|
||||
CScriptEvent(L"startGame", NET_JS_EVENT_START_GAME, false)
|
||||
{}
|
||||
};
|
||||
|
||||
@ -29,12 +29,12 @@ class CChatEvent: public CScriptEvent
|
||||
|
||||
public:
|
||||
CChatEvent(CStrW sender, CStrW message):
|
||||
CScriptEvent(L"chat", false, NET_JS_EVENT_CHAT),
|
||||
CScriptEvent(L"chat", NET_JS_EVENT_CHAT, false ),
|
||||
m_Sender(sender),
|
||||
m_Message(message)
|
||||
{
|
||||
AddReadOnlyProperty(L"sender", &m_Sender);
|
||||
AddReadOnlyProperty(L"message", &m_Message);
|
||||
AddLocalProperty(L"sender", &m_Sender, true);
|
||||
AddLocalProperty(L"message", &m_Message, true);
|
||||
}
|
||||
};
|
||||
|
||||
@ -45,12 +45,12 @@ class CConnectCompleteEvent: public CScriptEvent
|
||||
|
||||
public:
|
||||
CConnectCompleteEvent(CStrW message, bool success):
|
||||
CScriptEvent(L"connectComplete", false, NET_JS_EVENT_CONNECT_COMPLETE),
|
||||
CScriptEvent(L"connectComplete", NET_JS_EVENT_CONNECT_COMPLETE, false),
|
||||
m_Message(message),
|
||||
m_Success(success)
|
||||
{
|
||||
AddReadOnlyProperty(L"message", &m_Message);
|
||||
AddReadOnlyProperty(L"success", &m_Success);
|
||||
AddLocalProperty(L"message", &m_Message, true);
|
||||
AddLocalProperty(L"success", &m_Success, true);
|
||||
}
|
||||
};
|
||||
|
||||
@ -60,10 +60,10 @@ class CDisconnectEvent: public CScriptEvent
|
||||
|
||||
public:
|
||||
CDisconnectEvent(CStrW message):
|
||||
CScriptEvent(L"disconnect", false, NET_JS_EVENT_DISCONNECT),
|
||||
CScriptEvent(L"disconnect", NET_JS_EVENT_DISCONNECT, false),
|
||||
m_Message(message)
|
||||
{
|
||||
AddReadOnlyProperty(L"message", &m_Message);
|
||||
AddLocalProperty(L"message", &m_Message, true);
|
||||
}
|
||||
};
|
||||
|
||||
@ -76,15 +76,15 @@ class CClientConnectDisconnectCommon: public CScriptEvent
|
||||
public:
|
||||
CClientConnectDisconnectCommon(const wchar_t* eventName, int eventType,
|
||||
int sessionID, const CStrW &name, CNetServerSession *pSession):
|
||||
CScriptEvent(L"clientConnect", false, NET_JS_EVENT_CLIENT_CONNECT),
|
||||
CScriptEvent(L"clientConnect", NET_JS_EVENT_CLIENT_CONNECT, false),
|
||||
m_SessionID(sessionID),
|
||||
m_Name(name),
|
||||
m_pSession(pSession)
|
||||
{
|
||||
AddReadOnlyProperty(L"id", &m_SessionID);
|
||||
AddReadOnlyProperty(L"name", &m_Name);
|
||||
AddLocalProperty(L"id", &m_SessionID, true);
|
||||
AddLocalProperty(L"name", &m_Name, true);
|
||||
if (m_pSession)
|
||||
AddReadOnlyProperty(L"session", &m_pSession);
|
||||
AddLocalProperty(L"session", &m_pSession, true);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -60,18 +60,6 @@ CNetServer::CNetServer(CGame *pGame, CGameAttributes *pGameAttribs):
|
||||
ScriptingInit();
|
||||
);
|
||||
|
||||
AddProperty(L"sessions", &m_JSI_Sessions);
|
||||
|
||||
AddProperty(L"serverPlayerName", &m_ServerPlayerName);
|
||||
AddProperty(L"serverName", &m_ServerName);
|
||||
AddProperty(L"welcomeMessage", &m_WelcomeMessage);
|
||||
|
||||
AddProperty(L"port", &m_Port);
|
||||
|
||||
AddProperty(L"onChat", &m_OnChat);
|
||||
AddProperty(L"onClientConnect", &m_OnClientConnect);
|
||||
AddProperty(L"onClientDisconnect", &m_OnClientDisconnect);
|
||||
|
||||
m_pGameAttributes->SetUpdateCallback(AttributeUpdate, this);
|
||||
m_pGameAttributes->SetPlayerUpdateCallback(PlayerAttributeUpdate, this);
|
||||
m_pGameAttributes->SetPlayerSlotAssignmentCallback(PlayerSlotAssignmentCallback, this);
|
||||
@ -102,6 +90,18 @@ void CNetServer::ScriptingInit()
|
||||
|
||||
AddMethod<bool, &CNetServer::JSI_Open>("open", 0);
|
||||
|
||||
AddProperty(L"sessions", &CNetServer::m_JSI_Sessions);
|
||||
|
||||
AddProperty(L"serverPlayerName", &CNetServer::m_ServerPlayerName);
|
||||
AddProperty(L"serverName", &CNetServer::m_ServerName);
|
||||
AddProperty(L"welcomeMessage", &CNetServer::m_WelcomeMessage);
|
||||
|
||||
AddProperty(L"port", &CNetServer::m_Port);
|
||||
|
||||
AddProperty(L"onChat", &CNetServer::m_OnChat);
|
||||
AddProperty(L"onClientConnect", &CNetServer::m_OnClientConnect);
|
||||
AddProperty(L"onClientDisconnect", &CNetServer::m_OnClientDisconnect);
|
||||
|
||||
CJSObject<CNetServer>::ScriptingInit("NetServer");
|
||||
}
|
||||
|
||||
|
@ -19,9 +19,6 @@ CNetServerSession::CNetServerSession(CNetServer *pServer, CSocketInternal *pInt,
|
||||
ONCE(
|
||||
ScriptingInit();
|
||||
);
|
||||
|
||||
AddProperty(L"id", &m_ID);
|
||||
AddProperty(L"name", &m_Name);
|
||||
}
|
||||
|
||||
CNetServerSession::~CNetServerSession()
|
||||
@ -209,6 +206,9 @@ void CNetServerSession::ScriptingInit()
|
||||
AddMethod<bool, &CNetServerSession::JSI_Close>("close", 0);
|
||||
|
||||
CJSObject<CNetServerSession>::ScriptingInit("NetSession");
|
||||
// Hope this doesn't break anything...
|
||||
AddProperty( L"id", &CNetServerSession::m_ID );
|
||||
AddProperty( L"name", (CStrW CNetServerSession::*)&CNetServerSession::m_Name );
|
||||
}
|
||||
|
||||
bool CNetServerSession::JSI_Close(JSContext *cx, uintN argc, jsval *argv)
|
||||
|
@ -35,12 +35,12 @@ void CPlayer::ScriptingInit()
|
||||
AddMethod<jsval, &CPlayer::JSI_ToString>( "toString", 0 );
|
||||
AddMethod<jsval, &CPlayer::JSI_SetColour>( "setColour", 1);
|
||||
|
||||
AddReadOnlyClassProperty( L"id", &CPlayer::m_PlayerID );
|
||||
AddProperty( L"id", &CPlayer::m_PlayerID, true );
|
||||
// MT: Work out how this fits with the Synched stuff...
|
||||
|
||||
// AddClassProperty( L"name", &CPlayer::m_Name );
|
||||
// AddClassProperty( L"colour", &CPlayer::m_Colour );
|
||||
AddClassProperty( L"controlled", (IJSObject::GetFn)JSI_GetControlledEntities );
|
||||
AddProperty( L"controlled", (IJSObject::GetFn)JSI_GetControlledEntities );
|
||||
|
||||
CJSObject<CPlayer>::ScriptingInit( "Player" );
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ bool CProfileNode::Return()
|
||||
|
||||
void CProfileNode::ScriptingInit()
|
||||
{
|
||||
AddClassProperty( L"name", (IJSObject::GetFn)CProfileNode::JS_GetName );
|
||||
AddProperty( L"name", (IJSObject::GetFn)CProfileNode::JS_GetName );
|
||||
/*
|
||||
AddReadOnlyClassProperty( L"callsTotal", &CProfileNode::calls_total );
|
||||
AddReadOnlyClassProperty( L"callsPerFrame", &CProfileNode::calls_frame_last );
|
||||
|
@ -1,19 +1,157 @@
|
||||
#include "precompiled.h"
|
||||
#include "DOMEvent.h"
|
||||
#include "timer.h"
|
||||
#include "Profile.h"
|
||||
#include "ScriptObject.h"
|
||||
|
||||
CScriptEvent::CScriptEvent( const CStrW& Type, bool Cancelable, unsigned int TypeCode )
|
||||
IEventTarget::~IEventTarget()
|
||||
{
|
||||
m_Type = Type; m_TypeCode = TypeCode; m_Cancelable = Cancelable; m_Cancelled = false; m_Timestamp = (long)( get_time() * 1000.0 );
|
||||
AddReadOnlyProperty( L"type", &m_Type );
|
||||
AddReadOnlyProperty( L"cancelable", &m_Cancelable );
|
||||
AddReadOnlyProperty( L"timeStamp", &m_Timestamp );
|
||||
HandlerMap::iterator it;
|
||||
for( it = m_Handlers_name.begin(); it != m_Handlers_name.end(); it++ )
|
||||
delete( it->second );
|
||||
}
|
||||
|
||||
bool IEventTarget::_DispatchEvent( CScriptEvent* evt, IEventTarget* target )
|
||||
{
|
||||
if( before && before->_DispatchEvent( evt, target ) )
|
||||
return( true ); // Stop propagation.
|
||||
|
||||
evt->m_CurrentTarget = this;
|
||||
|
||||
HandlerList::iterator it;
|
||||
for( it = m_Handlers_id[evt->m_TypeCode].begin(); it != m_Handlers_id[evt->m_TypeCode].end(); it++ )
|
||||
{
|
||||
DOMEventHandler id = *it;
|
||||
if( id && id->DispatchEvent( GetScriptExecContext( target ), evt ) )
|
||||
return( true );
|
||||
}
|
||||
|
||||
HandlerRange range = m_Handlers_name.equal_range( evt->m_Type );
|
||||
HandlerMap::iterator itm;
|
||||
for( itm = range.first; itm != range.second; itm++ )
|
||||
{
|
||||
DOMEventHandler id = itm->second;
|
||||
if( id && id->DispatchEvent( GetScriptExecContext( target ), evt ) )
|
||||
return( true );
|
||||
}
|
||||
|
||||
if( after && after->_DispatchEvent( evt, target ) )
|
||||
return( true ); // Stop propagation.
|
||||
|
||||
return( false );
|
||||
}
|
||||
|
||||
bool IEventTarget::DispatchEvent( CScriptEvent* evt )
|
||||
{
|
||||
const char* data = g_Profiler.InternString( "script: " + (CStr8)evt->m_Type );
|
||||
g_Profiler.StartScript( data );
|
||||
evt->m_Target = this;
|
||||
_DispatchEvent( evt, this );
|
||||
g_Profiler.Stop();
|
||||
return( !evt->m_Cancelled );
|
||||
}
|
||||
|
||||
bool IEventTarget::AddHandler( int TypeCode, DOMEventHandler handler )
|
||||
{
|
||||
HandlerList::iterator it;
|
||||
for( it = m_Handlers_id[TypeCode].begin(); it != m_Handlers_id[TypeCode].end(); it++ )
|
||||
if( **it == *handler ) return( false );
|
||||
m_Handlers_id[TypeCode].push_back( handler );
|
||||
return( true );
|
||||
}
|
||||
|
||||
bool IEventTarget::AddHandler( CStrW TypeString, DOMEventHandler handler )
|
||||
{
|
||||
HandlerMap::iterator it;
|
||||
HandlerRange range = m_Handlers_name.equal_range( TypeString );
|
||||
for( it = range.first; it != range.second; it++ )
|
||||
if( *( it->second ) == *handler ) return( false );
|
||||
m_Handlers_name.insert( HandlerMap::value_type( TypeString, handler ) );
|
||||
return( true );
|
||||
}
|
||||
|
||||
bool IEventTarget::RemoveHandler( int TypeCode, DOMEventHandler handler )
|
||||
{
|
||||
HandlerList::iterator it;
|
||||
for( it = m_Handlers_id[TypeCode].begin(); it != m_Handlers_id[TypeCode].end(); it++ )
|
||||
if( **it == *handler )
|
||||
{
|
||||
m_Handlers_id[TypeCode].erase( it );
|
||||
return( true );
|
||||
}
|
||||
|
||||
return( false );
|
||||
}
|
||||
|
||||
bool IEventTarget::RemoveHandler( CStrW TypeString, DOMEventHandler handler )
|
||||
{
|
||||
HandlerMap::iterator it;
|
||||
HandlerRange range = m_Handlers_name.equal_range( TypeString );
|
||||
for( it = range.first; it != range.second; it++ )
|
||||
if( *( it->second ) == *handler )
|
||||
{
|
||||
delete( it->second );
|
||||
m_Handlers_name.erase( it );
|
||||
return( true );
|
||||
}
|
||||
|
||||
return( false );
|
||||
}
|
||||
|
||||
bool IEventTarget::AddHandlerJS( JSContext* cx, uintN argc, jsval* argv )
|
||||
{
|
||||
assert( argc >= 2 );
|
||||
DOMEventHandler handler = new CScriptObject( argv[1] );
|
||||
if( !handler->Defined() )
|
||||
{
|
||||
delete( handler );
|
||||
return( false );
|
||||
}
|
||||
if( !AddHandler( ToPrimitive<CStrW>( argv[0] ), handler ) )
|
||||
{
|
||||
delete( handler );
|
||||
return( false );
|
||||
}
|
||||
return( true );
|
||||
}
|
||||
|
||||
bool IEventTarget::RemoveHandlerJS( JSContext* cx, uintN argc, jsval* argv )
|
||||
{
|
||||
assert( argc >= 2 );
|
||||
DOMEventHandler handler = new CScriptObject( argv[1] );
|
||||
if( !handler->Defined() )
|
||||
{
|
||||
delete( handler );
|
||||
return( false );
|
||||
}
|
||||
if( !RemoveHandler( ToPrimitive<CStrW>( argv[0] ), handler ) )
|
||||
{
|
||||
delete( handler );
|
||||
return( false );
|
||||
}
|
||||
delete( handler );
|
||||
return( true );
|
||||
}
|
||||
|
||||
CScriptEvent::CScriptEvent( const CStrW& Type, unsigned int TypeCode, bool Cancelable, bool Blockable )
|
||||
{
|
||||
m_Type = Type; m_TypeCode = TypeCode;
|
||||
m_Cancelable = Cancelable; m_Cancelled = false;
|
||||
m_Blockable = Blockable; m_Blocked = false;
|
||||
m_Timestamp = (long)( get_time() * 1000.0 );
|
||||
}
|
||||
|
||||
void CScriptEvent::ScriptingInit()
|
||||
{
|
||||
AddMethod<jsval, &CScriptEvent::ToString>( "toString", 0 );
|
||||
AddMethod<jsval, &CScriptEvent::PreventDefault>( "preventDefault", 0 );
|
||||
AddMethod<jsval, &CScriptEvent::PreventDefault>( "cancel", 0 );
|
||||
AddMethod<jsval, StopPropagation>( "stopPropagation", 0 );
|
||||
|
||||
AddProperty( L"type", &CScriptEvent::m_Type, true );
|
||||
AddProperty( L"cancelable", &CScriptEvent::m_Cancelable, true );
|
||||
AddProperty( L"blockable", &CScriptEvent::m_Blockable, true );
|
||||
AddProperty( L"timestamp", &CScriptEvent::m_Timestamp, true );
|
||||
|
||||
CJSObject<CScriptEvent>::ScriptingInit( "Event" );
|
||||
}
|
||||
@ -25,6 +163,13 @@ jsval CScriptEvent::PreventDefault( JSContext* cx, uintN argc, jsval* argv )
|
||||
return( JSVAL_VOID );
|
||||
}
|
||||
|
||||
jsval CScriptEvent::StopPropagation( JSContext* cx, uintN argc, jsval* argv )
|
||||
{
|
||||
if( m_Blockable )
|
||||
m_Blocked = true;
|
||||
return( JSVAL_VOID );
|
||||
}
|
||||
|
||||
jsval CScriptEvent::ToString( JSContext* cx, uintN argc, jsval* argv )
|
||||
{
|
||||
wchar_t buffer[256];
|
||||
|
@ -9,6 +9,56 @@
|
||||
|
||||
#include "ScriptableObject.h"
|
||||
|
||||
#include "EventTypes.h"
|
||||
|
||||
class CScriptObject;
|
||||
class CScriptEvent;
|
||||
|
||||
typedef CScriptObject* DOMEventHandler;
|
||||
|
||||
class IEventTarget
|
||||
{
|
||||
// Return 'true' if we should stop propagating.
|
||||
bool _DispatchEvent( CScriptEvent* evt, IEventTarget* target );
|
||||
|
||||
// Events dispatched to this object are sent here before being processed.
|
||||
IEventTarget* before;
|
||||
// Events dispatched to this object are sent here after being processed.
|
||||
IEventTarget* after;
|
||||
|
||||
typedef std::vector<DOMEventHandler> HandlerList;
|
||||
HandlerList m_Handlers_id[EVENT_LAST];
|
||||
typedef STL_HASH_MULTIMAP<CStrW, DOMEventHandler, CStrW_hash_compare> HandlerMap;
|
||||
HandlerMap m_Handlers_name;
|
||||
typedef std::pair<HandlerMap::iterator, HandlerMap::iterator> HandlerRange;
|
||||
public:
|
||||
IEventTarget()
|
||||
{
|
||||
before = NULL;
|
||||
after = NULL;
|
||||
}
|
||||
~IEventTarget();
|
||||
inline void SetPriorObject( IEventTarget* obj ) { before = obj; }
|
||||
inline void SetNextObject( IEventTarget* obj ) { after = obj; }
|
||||
|
||||
// Returns false if the handler was already present
|
||||
bool AddHandler( int TypeCode, DOMEventHandler handler );
|
||||
bool AddHandler( CStrW TypeString, DOMEventHandler handler );
|
||||
// Returns false if the handler was not present
|
||||
bool RemoveHandler( int TypeCode, DOMEventHandler handler );
|
||||
bool RemoveHandler( CStrW TypeString, DOMEventHandler handler );
|
||||
|
||||
bool AddHandlerJS( JSContext* cx, uintN argc, jsval* argv );
|
||||
bool RemoveHandlerJS( JSContext* cx, uintN argc, jsval* argv );
|
||||
|
||||
// Return the JSObject* we'd like to be the 'this' object
|
||||
// when executing the handler. The argument is the object
|
||||
// to which the event is targeted.
|
||||
virtual JSObject* GetScriptExecContext( IEventTarget* target ) = 0;
|
||||
|
||||
bool DispatchEvent( CScriptEvent* evt );
|
||||
};
|
||||
|
||||
class CScriptEvent : public CJSObject<CScriptEvent>
|
||||
{
|
||||
public:
|
||||
@ -20,10 +70,10 @@ public:
|
||||
};
|
||||
|
||||
// Target (currently unused)
|
||||
// EventTarget* m_Target;
|
||||
IEventTarget* m_Target;
|
||||
|
||||
// Listening object currently being processed (currently unused)
|
||||
// EventTarget* m_CurrentTarget;
|
||||
IEventTarget* m_CurrentTarget;
|
||||
|
||||
// Phase type (currently unused)
|
||||
// EPhaseType m_EventPhase;
|
||||
@ -34,6 +84,9 @@ public:
|
||||
// Can be cancelled (default actions prevented)
|
||||
bool m_Cancelable;
|
||||
|
||||
// Can be blocked (prevented from propogating along the handler chain)
|
||||
bool m_Blockable;
|
||||
|
||||
// Timestamp (milliseconds since epoch (start of game?))
|
||||
i32 m_Timestamp;
|
||||
|
||||
@ -46,13 +99,17 @@ public:
|
||||
// Has been cancelled?
|
||||
bool m_Cancelled;
|
||||
|
||||
// Has it been blocked (won't be sent to any more handlers)
|
||||
bool m_Blocked;
|
||||
|
||||
// --
|
||||
|
||||
jsval ToString( JSContext* cx, uintN argc, jsval* argv );
|
||||
jsval PreventDefault( JSContext* cx, uintN argc, jsval* argv );
|
||||
jsval StopPropagation( JSContext* cx, uintN argc, jsval* argv );
|
||||
|
||||
public:
|
||||
CScriptEvent( const CStrW& Type, bool Cancelable, unsigned int TypeCode = (unsigned int)-1 );
|
||||
CScriptEvent( const CStrW& Type, unsigned int TypeCode = (unsigned int)-1, bool Cancelable = true, bool Blockable = true );
|
||||
static void ScriptingInit();
|
||||
};
|
||||
|
||||
|
@ -62,13 +62,6 @@ template<typename T> bool ToPrimitive( JSContext* cx, jsval v, T*& Storage )
|
||||
return( true );
|
||||
}
|
||||
|
||||
/*
|
||||
template<typename T> JSObject* ToScript( T** Native )
|
||||
{
|
||||
return( ToScript( *Native ) );
|
||||
}
|
||||
*/
|
||||
|
||||
template<typename T> inline T ToPrimitive( JSContext* cx, jsval v ) { T Temp; ToPrimitive( cx, v, Temp ); return( Temp ); }
|
||||
template<typename T> inline T ToPrimitive( jsval v ) { return( ToPrimitive<T>( g_ScriptingHost.GetContext(), v ) ); }
|
||||
|
||||
|
@ -48,10 +48,10 @@ void SColour::SColourInit( float _r, float _g, float _b, float _a )
|
||||
void SColour::ScriptingInit()
|
||||
{
|
||||
AddMethod<jsval, &SColour::ToString>( "toString", 0 );
|
||||
AddClassProperty<float>( L"r", (float IJSObject::*)&SColour::r );
|
||||
AddClassProperty<float>( L"g", (float IJSObject::*)&SColour::g );
|
||||
AddClassProperty<float>( L"b", (float IJSObject::*)&SColour::b );
|
||||
AddClassProperty<float>( L"a", (float IJSObject::*)&SColour::a );
|
||||
AddProperty<float>( L"r", (float IJSObject::*)&SColour::r );
|
||||
AddProperty<float>( L"g", (float IJSObject::*)&SColour::g );
|
||||
AddProperty<float>( L"b", (float IJSObject::*)&SColour::b );
|
||||
AddProperty<float>( L"a", (float IJSObject::*)&SColour::a );
|
||||
|
||||
CJSObject<SColour>::ScriptingInit( "Colour", SColour::Construct, 3 );
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "timer.h"
|
||||
#include "LightEnv.h"
|
||||
#include "MapWriter.h"
|
||||
#include "GameEvents.h"
|
||||
|
||||
#include "Game.h"
|
||||
#include "Network/Server.h"
|
||||
@ -59,6 +60,8 @@ JSFunctionSpec ScriptFunctionTable[] =
|
||||
{"getGlobal", getGlobal, 0, 0, 0 },
|
||||
{"getGUIGlobal", getGUIGlobal, 0, 0, 0 },
|
||||
{"setCursor", setCursor, 1, 0, 0 },
|
||||
{"addGlobalHandler", AddGlobalHandler, 2, 0, 0 },
|
||||
{"removeGlobalHandler", RemoveGlobalHandler, 2, 0, 0 },
|
||||
{"setCameraTarget", setCameraTarget, 1, 0, 0 },
|
||||
{"startGame", startGame, 0, 0, 0 },
|
||||
{"endGame", endGame, 0, 0, 0 },
|
||||
@ -227,6 +230,18 @@ JSBool SetLocalPlayer( JSContext* context, JSObject* obj, jsval id, jsval* vp )
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
JSBool AddGlobalHandler( JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval )
|
||||
{
|
||||
*rval = BOOLEAN_TO_JSVAL( g_JSGameEvents.AddHandlerJS( cx, argc, argv ) );
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
JSBool RemoveGlobalHandler( JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval )
|
||||
{
|
||||
*rval = BOOLEAN_TO_JSVAL( g_JSGameEvents.RemoveHandlerJS( cx, argc, argv ) );
|
||||
return( JS_TRUE );
|
||||
}
|
||||
|
||||
JSBool setCameraTarget( JSContext* context, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval )
|
||||
{
|
||||
CVector3D* target;
|
||||
|
@ -20,7 +20,11 @@ JSBool GetPlayerSet( JSContext* context, JSObject* globalObject, jsval id, jsval
|
||||
JSBool GetLocalPlayer( JSContext* context, JSObject* globalObject, jsval id, jsval* vp );
|
||||
JSBool SetLocalPlayer( JSContext* context, JSObject* globalObject, jsval id, jsval* vp );
|
||||
JSBool GetGaiaPlayer( JSContext* context, JSObject* globalObject, jsval id, jsval* vp );
|
||||
JSBool GetLocalPlayer( JSContext* context, JSObject* globalObject, jsval argv, jsval* vp );
|
||||
// JSBool SetGaiaPlayer( JSContext* context, JSObject* globalObject, jsval argv, jsval* vp );
|
||||
|
||||
// Events system
|
||||
JSFunc AddGlobalHandler;
|
||||
JSFunc RemoveGlobalHandler;
|
||||
|
||||
// Camera
|
||||
JSFunc setCameraTarget;
|
||||
|
@ -4,36 +4,6 @@
|
||||
//
|
||||
// Mark Thompson (mark@wildfiregames.com / mot20@cam.ac.uk)
|
||||
//
|
||||
// General idea:
|
||||
//
|
||||
// IJSProperty is the interface representing a property of an object.
|
||||
// Objects contain a mapping of names->IJSProperties
|
||||
// Some IJSProperties wrap C++ variables that are declared by the engine
|
||||
// Others wrap pairs of getter/setter functions
|
||||
// Most, however, wrap a jsval and are defined by scripts and XML files.
|
||||
// Objects may also have a parent object. If an attempt is made to
|
||||
// access a property that doesn't exist in this object, the parent's object
|
||||
// is checked, and so on.
|
||||
// To allow this parent system to work for C++ properties, when a C++
|
||||
// is set on an object, it's also set on any object that inherits it
|
||||
// (Unless that object specifies its own value for that property, or
|
||||
// the property is marked as being uninheritable)
|
||||
|
||||
// Objects and properties may be flagged read-only, causing all attempts to
|
||||
// set values to become no-ops. If an object is read-only, all properties are
|
||||
// - even ones that are flagged as writable.
|
||||
|
||||
// Usage: Create a class CSomething inheriting CJSObject<CSomething>
|
||||
// In CSomething's constructor, add properties to the new object with
|
||||
// AddProperty( name, pointer-to-C++-variable ).
|
||||
// Also, ScriptingInit( "Some Name" ) must be called at initialization
|
||||
// - put it in main. There's also AddMethod<ReturnType, NativeFunction>( Name,
|
||||
// MinArgs ) - call that at initialization, too.
|
||||
// If you include data members or functions that return types that JSConversions.h
|
||||
// doesn't handle sensibly, you need to make it do so.
|
||||
// If you're looking for examples, DOMEvent.h is the simplest user of this class
|
||||
// CBaseEntity and CEntity do also (and use other stuff not mentioned above)
|
||||
// but are more complex.
|
||||
|
||||
#include "scripting/ScriptingHost.h"
|
||||
#include "JSConversions.h"
|
||||
@ -41,72 +11,27 @@
|
||||
#ifndef SCRIPTABLE_INCLUDED
|
||||
#define SCRIPTABLE_INCLUDED
|
||||
|
||||
#define ALLOW_NONSHARED_NATIVES
|
||||
|
||||
class IJSObject;
|
||||
|
||||
class IJSProperty
|
||||
{
|
||||
public:
|
||||
|
||||
bool m_AllowsInheritance;
|
||||
bool m_Inherited;
|
||||
bool m_Intrinsic;
|
||||
|
||||
// This is to make sure that all the fields are initialized at construction
|
||||
inline IJSProperty():
|
||||
m_AllowsInheritance(true),
|
||||
m_Inherited(true),
|
||||
m_Intrinsic(true)
|
||||
{}
|
||||
|
||||
virtual jsval Get( JSContext* cx, IJSObject* owner ) = 0;
|
||||
virtual void Set( JSContext* cx, IJSObject* owner, jsval Value ) = 0;
|
||||
|
||||
// Copies the data directly out of a parent property
|
||||
// Warning: Don't use if you're not certain the properties are not of the same type.
|
||||
virtual void ImmediateCopy( IJSObject* CopyTo, IJSObject* CopyFrom, IJSProperty* CopyProperty ) = 0;
|
||||
|
||||
jsval Get( IJSObject* owner ) { return( Get( g_ScriptingHost.GetContext(), owner ) ); }
|
||||
void Set( IJSObject* owner, jsval Value ) { return( Set( g_ScriptingHost.GetContext(), owner, Value ) ); }
|
||||
|
||||
virtual ~IJSProperty() {}
|
||||
virtual jsval Get( JSContext* cx, IJSObject* obj ) = 0;
|
||||
virtual void Set( JSContext* cx, IJSObject* obj, jsval value ) = 0;
|
||||
};
|
||||
|
||||
class IJSObject
|
||||
{
|
||||
// Make copy constructor and assignment operator private - since copying of
|
||||
// these objects is unsafe unless done specially.
|
||||
// These will never be implemented (they are, after all, here to *prevent*
|
||||
// copying)
|
||||
IJSObject(const IJSObject &other);
|
||||
IJSObject& operator=(const IJSObject &other);
|
||||
|
||||
public:
|
||||
typedef STL_HASH_MAP<CStrW, IJSProperty*, CStrW_hash_compare> PropertyTable;
|
||||
typedef std::vector<IJSObject*> InheritorsList;
|
||||
|
||||
// Used for freshen/update
|
||||
typedef void (IJSObject::*NotifyFn)();
|
||||
|
||||
// Property getters and setters
|
||||
typedef jsval (IJSObject::*GetFn)();
|
||||
typedef void (IJSObject::*SetFn)( jsval );
|
||||
typedef void (IJSObject::*SetFn)( jsval value );
|
||||
|
||||
// Properties of this object
|
||||
PropertyTable m_Properties;
|
||||
|
||||
// Parent object
|
||||
IJSObject* m_Parent;
|
||||
|
||||
// Objects that inherit from this
|
||||
InheritorsList m_Inheritors;
|
||||
|
||||
// Set the base, and rebuild
|
||||
void SetBase( IJSObject* m_Parent );
|
||||
|
||||
// Rebuild any intrinsic (mapped-to-C++-variable) properties
|
||||
virtual void Rebuild() = 0;
|
||||
|
||||
// Check for a property
|
||||
// Return a pointer to a property, if it exists
|
||||
virtual IJSProperty* HasProperty( CStrW PropertyName ) = 0;
|
||||
|
||||
// Retrieve the value of a property (returning false if that property is not defined)
|
||||
@ -121,263 +46,49 @@ public:
|
||||
|
||||
template<typename T, bool ReadOnly = false> class CJSObject;
|
||||
|
||||
template<typename T> class CJSPropertyAccessor
|
||||
{
|
||||
T* m_Owner;
|
||||
CStrW m_PropertyRoot;
|
||||
template<typename Q, bool ReadOnly> friend class CJSObject;
|
||||
|
||||
public:
|
||||
CJSPropertyAccessor( T* Owner, CStrW PropertyRoot )
|
||||
{
|
||||
m_Owner = Owner;
|
||||
m_PropertyRoot = PropertyRoot;
|
||||
}
|
||||
static JSObject* CreateAccessor( JSContext* cx, T* Owner, CStrW PropertyRoot )
|
||||
{
|
||||
JSObject* Accessor = JS_NewObject( cx, &JSI_Class, NULL, NULL );
|
||||
JS_SetPrivate( cx, Accessor, new CJSPropertyAccessor( Owner, PropertyRoot ) );
|
||||
|
||||
return( Accessor );
|
||||
}
|
||||
static JSBool JSGetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp )
|
||||
{
|
||||
CJSPropertyAccessor* Instance = (CJSPropertyAccessor*)JS_GetPrivate( cx, obj );
|
||||
if( !Instance ) return( JS_TRUE );
|
||||
|
||||
CStrW PropName = Instance->m_PropertyRoot + CStrW( L"." ) + g_ScriptingHost.ValueToUCString( id );
|
||||
|
||||
Instance->m_Owner->GetProperty( cx, PropName, vp );
|
||||
|
||||
return( JS_TRUE );
|
||||
}
|
||||
static JSBool JSSetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp )
|
||||
{
|
||||
CJSPropertyAccessor* Instance = (CJSPropertyAccessor*)JS_GetPrivate( cx, obj );
|
||||
if( !Instance ) return( JS_TRUE );
|
||||
|
||||
CStrW PropName = g_ScriptingHost.ValueToUCString( id );
|
||||
|
||||
Instance->m_Owner->SetProperty( cx, Instance->m_PropertyRoot + CStrW( L"." ) + PropName, vp );
|
||||
|
||||
return( JS_TRUE );
|
||||
}
|
||||
static JSBool JSPrimitive( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval )
|
||||
{
|
||||
CJSPropertyAccessor* Instance = (CJSPropertyAccessor*)JS_GetPrivate( cx, obj );
|
||||
if( !Instance ) return( JS_TRUE );
|
||||
|
||||
// Check all along the inheritance tree
|
||||
// Possible optimization: Store the hashed value over the lookups
|
||||
IJSObject* Target = Instance->m_Owner;
|
||||
IJSProperty* Property;
|
||||
|
||||
while( Target )
|
||||
{
|
||||
Property = Target->HasProperty( Instance->m_PropertyRoot );
|
||||
if( Property )
|
||||
{
|
||||
*rval = Property->Get( cx, Target );
|
||||
break;
|
||||
}
|
||||
Target = Target->m_Parent;
|
||||
}
|
||||
|
||||
return( JS_TRUE );
|
||||
}
|
||||
static JSBool JSToString( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval )
|
||||
{
|
||||
CJSPropertyAccessor* Instance = (CJSPropertyAccessor*)JS_GetPrivate( cx, obj );
|
||||
if( !Instance ) return( JS_TRUE );
|
||||
|
||||
// Check all along the inheritance tree
|
||||
// TODO: Optimization: Store the hashed value over the lookups
|
||||
IJSObject* Target = Instance->m_Owner;
|
||||
IJSProperty* Property;
|
||||
JSString* str;
|
||||
|
||||
while( Target )
|
||||
{
|
||||
Property = Target->HasProperty( Instance->m_PropertyRoot );
|
||||
if( Property )
|
||||
{
|
||||
str = JS_ValueToString( cx, Property->Get( cx, Target ) );
|
||||
break;
|
||||
}
|
||||
Target = Target->m_Parent;
|
||||
}
|
||||
|
||||
*rval = STRING_TO_JSVAL( str );
|
||||
|
||||
return( JS_TRUE );
|
||||
}
|
||||
static JSClass JSI_Class;
|
||||
static void ScriptingInit()
|
||||
{
|
||||
JSFunctionSpec JSI_methods[] = { { "valueOf", JSPrimitive, 0, 0, 0 }, { "toString", JSToString, 0, 0, 0 }, { 0 } };
|
||||
JSPropertySpec JSI_props[] = { { 0 } };
|
||||
|
||||
g_ScriptingHost.DefineCustomObjectType( &JSI_Class, NULL, 0, JSI_props, JSI_methods, NULL, NULL );
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> JSClass CJSPropertyAccessor<T>::JSI_Class = {
|
||||
"Property", JSCLASS_HAS_PRIVATE,
|
||||
JS_PropertyStub, JS_PropertyStub,
|
||||
JSGetProperty, JSSetProperty,
|
||||
JS_EnumerateStub, JS_ResolveStub,
|
||||
JS_ConvertStub, JS_FinalizeStub,
|
||||
NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
|
||||
template<typename T, bool ReadOnly> class CJSSharedProperty : public IJSProperty
|
||||
template<typename T, bool ReadOnly> class CJSProperty : public IJSProperty
|
||||
{
|
||||
T IJSObject::*m_Data;
|
||||
|
||||
// Function on Owner to call after value is changed
|
||||
IJSObject::NotifyFn m_Update;
|
||||
|
||||
// Function on Owner to call before reading or writing the value
|
||||
IJSObject::NotifyFn m_Freshen;
|
||||
|
||||
public:
|
||||
CJSSharedProperty( T IJSObject::*Data, bool AllowsInheritance = false, IJSObject::NotifyFn Update = NULL, IJSObject::NotifyFn Freshen = NULL )
|
||||
CJSProperty( T IJSObject::*Data )
|
||||
{
|
||||
m_Data = Data;
|
||||
m_AllowsInheritance = AllowsInheritance;
|
||||
m_Update = Update;
|
||||
m_Freshen = Freshen;
|
||||
m_Intrinsic = true;
|
||||
m_Inherited = true;
|
||||
}
|
||||
jsval Get( JSContext* cx, IJSObject* owner )
|
||||
{
|
||||
if( m_Freshen ) (owner->*m_Freshen)();
|
||||
return( ToJSVal( owner->*m_Data ) );
|
||||
}
|
||||
void ImmediateCopy( IJSObject* CopyTo, IJSObject* CopyFrom, IJSProperty* CopyProperty )
|
||||
{
|
||||
debug_warn( "Inheritance not supported for CJSSharedProperties" );
|
||||
}
|
||||
void Set( JSContext* cx, IJSObject* owner, jsval Value )
|
||||
{
|
||||
if( !ReadOnly )
|
||||
{
|
||||
if( m_Freshen ) (owner->*m_Freshen)();
|
||||
if( ToPrimitive( cx, Value, owner->*m_Data ) )
|
||||
if( m_Update ) (owner->*m_Update)();
|
||||
ToPrimitive( cx, Value, owner->*m_Data );
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename T, bool ReadOnly> class CJSProperty : public IJSProperty
|
||||
#ifdef ALLOW_NONSHARED_NATIVES
|
||||
|
||||
template<typename T, bool ReadOnly> class CJSNonsharedProperty : public IJSProperty
|
||||
{
|
||||
T* m_Data;
|
||||
|
||||
// Function on Owner to call after value is changed
|
||||
IJSObject::NotifyFn m_Update;
|
||||
|
||||
// Function on Owner to call before reading or writing the value
|
||||
IJSObject::NotifyFn m_Freshen;
|
||||
|
||||
public:
|
||||
CJSProperty( T* Data, bool AllowsInheritance = false, IJSObject::NotifyFn Update = NULL, IJSObject::NotifyFn Freshen = NULL )
|
||||
CJSNonsharedProperty( T* Data )
|
||||
{
|
||||
m_Data = Data;
|
||||
m_AllowsInheritance = AllowsInheritance;
|
||||
m_Update = Update;
|
||||
m_Freshen = Freshen;
|
||||
m_Intrinsic = true;
|
||||
}
|
||||
jsval Get( JSContext* cx, IJSObject* owner )
|
||||
{
|
||||
if( m_Freshen ) (owner->*m_Freshen)();
|
||||
return( ToJSVal( *m_Data ) );
|
||||
}
|
||||
void ImmediateCopy( IJSObject* CopyTo, IJSObject* CopyFrom, IJSProperty* CopyProperty )
|
||||
{
|
||||
*m_Data = *( ( (CJSProperty*)CopyProperty )->m_Data );
|
||||
}
|
||||
void Set( JSContext* cx, IJSObject* owner, jsval Value )
|
||||
{
|
||||
if( !ReadOnly )
|
||||
{
|
||||
if( m_Freshen ) (owner->*m_Freshen)();
|
||||
if( ToPrimitive( cx, Value, *m_Data ) )
|
||||
if( m_Update ) (owner->*m_Update)();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class CJSReflector
|
||||
{
|
||||
template<typename Q, bool ReadOnly> friend class CJSObject;
|
||||
JSObject* m_JSAccessor;
|
||||
};
|
||||
|
||||
class CJSDynamicProperty : public IJSProperty
|
||||
{
|
||||
template<typename Q, bool ReadOnly> friend class CJSObject;
|
||||
|
||||
JSObject* m_JSAccessor;
|
||||
public:
|
||||
CJSDynamicProperty()
|
||||
{
|
||||
m_JSAccessor = NULL;
|
||||
m_Intrinsic = false;
|
||||
m_Inherited = false;
|
||||
ToPrimitive( cx, Value, *m_Data );
|
||||
}
|
||||
};
|
||||
|
||||
class CJSValProperty : public CJSDynamicProperty
|
||||
{
|
||||
template<typename Q, bool ReadOnly> friend class CJSObject;
|
||||
|
||||
jsval m_Data;
|
||||
|
||||
public:
|
||||
CJSValProperty( jsval Data, bool Inherited )
|
||||
{
|
||||
m_Inherited = Inherited;
|
||||
m_Data = Data;
|
||||
Root();
|
||||
}
|
||||
~CJSValProperty()
|
||||
{
|
||||
Uproot();
|
||||
}
|
||||
void Root()
|
||||
{
|
||||
if( JSVAL_IS_GCTHING( m_Data ) )
|
||||
#ifndef NDEBUG
|
||||
JS_AddNamedRoot( g_ScriptingHost.GetContext(), (void*)&m_Data, "jsval property" );
|
||||
#else
|
||||
JS_AddRoot( g_ScriptingHost.GetContext(), (void*)&m_Data );
|
||||
#endif
|
||||
}
|
||||
void Uproot()
|
||||
{
|
||||
if( JSVAL_IS_GCTHING( m_Data ) )
|
||||
JS_RemoveRoot( g_ScriptingHost.GetContext(), (void*)&m_Data );
|
||||
}
|
||||
jsval Get( JSContext* cx, IJSObject* owner )
|
||||
{
|
||||
return( m_Data );
|
||||
}
|
||||
void Set( JSContext* cx, IJSObject* owner, jsval Value )
|
||||
{
|
||||
Uproot();
|
||||
m_Data = Value;
|
||||
Root();
|
||||
}
|
||||
void ImmediateCopy( IJSObject* CopyTo, IJSObject* CopyFrom, IJSProperty* CopyProperty )
|
||||
{
|
||||
assert( 0 && "ImmediateCopy called on a CJSValProperty (something's gone wrong with the inheritance on this object)" );
|
||||
}
|
||||
};
|
||||
#endif /* ALLOW_NONSHARED_NATIVES */
|
||||
|
||||
class CJSFunctionProperty : public IJSProperty
|
||||
{
|
||||
@ -390,28 +101,59 @@ class CJSFunctionProperty : public IJSProperty
|
||||
public:
|
||||
CJSFunctionProperty( IJSObject::GetFn Getter, IJSObject::SetFn Setter )
|
||||
{
|
||||
m_Inherited = false;
|
||||
m_Intrinsic = true;
|
||||
m_Getter = Getter;
|
||||
m_Setter = Setter;
|
||||
// Must at least be able to read
|
||||
assert( m_Getter );
|
||||
}
|
||||
jsval Get( JSContext* cx, IJSObject* owner )
|
||||
jsval Get( JSContext* cx, IJSObject* obj )
|
||||
{
|
||||
return( (owner->*m_Getter)() );
|
||||
return( (obj->*m_Getter)() );
|
||||
}
|
||||
void Set( JSContext* cx, IJSObject* owner, jsval Value )
|
||||
void Set( JSContext* cx, IJSObject* obj, jsval value )
|
||||
{
|
||||
if( m_Setter )
|
||||
(owner->*m_Setter)( Value );
|
||||
}
|
||||
void ImmediateCopy( IJSObject* CopyTo, IJSObject* CopyFrom, IJSProperty* CopyProperty )
|
||||
{
|
||||
assert( 0 && "ImmediateCopy called on a property wrapping getter/setter functions (something's gone wrong with the inheritance for this object)" );
|
||||
(obj->*m_Setter)( value );
|
||||
}
|
||||
};
|
||||
|
||||
class CJSValProperty : public IJSProperty
|
||||
{
|
||||
template<typename Q, bool ReadOnly> friend class CJSObject;
|
||||
|
||||
jsval m_Data;
|
||||
|
||||
public:
|
||||
CJSValProperty( jsval Data )
|
||||
{
|
||||
m_Data = Data;
|
||||
Root();
|
||||
}
|
||||
~CJSValProperty()
|
||||
{
|
||||
Uproot();
|
||||
}
|
||||
void Root()
|
||||
{
|
||||
if( JSVAL_IS_GCTHING( m_Data ) )
|
||||
JS_AddRoot( g_ScriptingHost.GetContext(), (void*)&m_Data );
|
||||
}
|
||||
void Uproot()
|
||||
{
|
||||
if( JSVAL_IS_GCTHING( m_Data ) )
|
||||
JS_RemoveRoot( g_ScriptingHost.GetContext(), (void*)&m_Data );
|
||||
}
|
||||
jsval Get( JSContext* cx, IJSObject* object )
|
||||
{
|
||||
return( m_Data );
|
||||
}
|
||||
void Set( JSContext* cx, IJSObject* owner, jsval value )
|
||||
{
|
||||
Uproot();
|
||||
m_Data = value;
|
||||
Root();
|
||||
}
|
||||
};
|
||||
|
||||
// Wrapper around native functions that are attached to CJSObjects
|
||||
|
||||
@ -432,14 +174,17 @@ public:
|
||||
|
||||
template<typename T, bool ReadOnly> class CJSObject : public IJSObject
|
||||
{
|
||||
typedef STL_HASH_MAP<CStrW, CJSReflector*, CStrW_hash_compare> ReflectorTable;
|
||||
|
||||
// This object
|
||||
JSObject* m_JS;
|
||||
|
||||
ReflectorTable m_Reflectors;
|
||||
|
||||
public:
|
||||
static JSClass JSI_class;
|
||||
protected:
|
||||
// The properties defined by the engine
|
||||
static PropertyTable m_NativeProperties;
|
||||
#ifdef ALLOW_NONSHARED_NATIVES
|
||||
PropertyTable m_NonsharedProperties;
|
||||
#endif
|
||||
// Properties added by script
|
||||
PropertyTable m_ScriptProperties;
|
||||
|
||||
// Whether native code is responsible for managing this object.
|
||||
// Script constructors should clear this *BEFORE* creating a JS
|
||||
@ -447,8 +192,45 @@ public:
|
||||
|
||||
bool m_EngineOwned;
|
||||
|
||||
public:
|
||||
// Property getters and setters
|
||||
typedef jsval (T::*TGetFn)();
|
||||
typedef void (T::*TSetFn)( jsval value );
|
||||
|
||||
static JSClass JSI_class;
|
||||
|
||||
static void ScriptingInit( const char* ClassName, JSNative Constructor = NULL, uintN ConstructorMinArgs = 0 )
|
||||
{
|
||||
JSFunctionSpec* JSI_methods = new JSFunctionSpec[ m_Methods.size() + 1 ];
|
||||
unsigned int MethodID;
|
||||
for( MethodID = 0; MethodID < m_Methods.size(); MethodID++ )
|
||||
JSI_methods[MethodID] = m_Methods[MethodID];
|
||||
|
||||
JSI_methods[MethodID].name = 0;
|
||||
|
||||
JSI_class.name = ClassName;
|
||||
g_ScriptingHost.DefineCustomObjectType( &JSI_class, Constructor, ConstructorMinArgs, JSI_props, JSI_methods, NULL, NULL );
|
||||
|
||||
delete[]( JSI_methods );
|
||||
|
||||
atexit( ScriptingShutdown );
|
||||
}
|
||||
static void ScriptingShutdown()
|
||||
{
|
||||
PropertyTable::iterator it;
|
||||
for( it = m_NativeProperties.begin(); it != m_NativeProperties.end(); it++ )
|
||||
delete( it->second );
|
||||
}
|
||||
|
||||
// JS Property access
|
||||
bool GetProperty( JSContext* cx, CStrW PropertyName, jsval* vp );
|
||||
bool GetProperty( JSContext* cx, CStrW PropertyName, jsval* vp )
|
||||
{
|
||||
IJSProperty* Property = HasProperty( PropertyName );
|
||||
if( Property )
|
||||
*vp = Property->Get( cx, this );
|
||||
|
||||
return( true );
|
||||
}
|
||||
void SetProperty( JSContext* cx, CStrW PropertyName, jsval* vp )
|
||||
{
|
||||
if( !ReadOnly )
|
||||
@ -459,29 +241,6 @@ public:
|
||||
{
|
||||
// Already exists
|
||||
prop->Set( cx, this, *vp );
|
||||
|
||||
prop->m_Inherited = false;
|
||||
|
||||
// If it's a C++ property, reflect this change in objects that inherit this.
|
||||
if( prop->m_AllowsInheritance && prop->m_Intrinsic )
|
||||
{
|
||||
InheritorsList UpdateSet( m_Inheritors );
|
||||
|
||||
while( !UpdateSet.empty() )
|
||||
{
|
||||
IJSObject* UpdateObj = UpdateSet.back();
|
||||
UpdateSet.pop_back();
|
||||
IJSProperty* UpdateProp = UpdateObj->HasProperty( PropertyName );
|
||||
// Property must exist, also be a C++ property, and not have its value specified.
|
||||
if( UpdateProp && UpdateProp->m_Intrinsic && UpdateProp->m_Inherited )
|
||||
{
|
||||
UpdateProp->Set( cx, this, *vp );
|
||||
InheritorsList::iterator it2;
|
||||
for( it2 = UpdateObj->m_Inheritors.begin(); it2 != UpdateObj->m_Inheritors.end(); it2++ )
|
||||
UpdateSet.push_back( *it2 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -491,9 +250,110 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
IJSProperty* HasProperty( CStrW PropertyName )
|
||||
{
|
||||
PropertyTable::iterator it;
|
||||
|
||||
// Engine-defined properties take precedence
|
||||
it = m_NativeProperties.find( PropertyName );
|
||||
if( it != m_NativeProperties.end() )
|
||||
return( it->second );
|
||||
// Next are the object-local engine-defined properties
|
||||
// (if they're compiled in)
|
||||
#ifdef ALLOW_NONSHARED_NATIVES
|
||||
it = m_NonsharedProperties.find( PropertyName );
|
||||
if( it != m_NonsharedProperties.end() )
|
||||
return( it->second );
|
||||
#endif
|
||||
// Then check in script properties
|
||||
it = m_ScriptProperties.find( PropertyName );
|
||||
if( it != m_ScriptProperties.end() )
|
||||
return( it->second );
|
||||
|
||||
// Otherwise not found
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
void AddProperty( CStrW PropertyName, jsval Value )
|
||||
{
|
||||
assert( !HasProperty( PropertyName ) );
|
||||
CJSValProperty* newProp = new CJSValProperty( Value );
|
||||
m_ScriptProperties[PropertyName] = newProp;
|
||||
}
|
||||
void AddProperty( CStrW PropertyName, CStrW Value )
|
||||
{
|
||||
AddProperty( PropertyName, JSParseString( Value ) );
|
||||
}
|
||||
static void AddProperty( CStrW PropertyName, TGetFn Getter, TSetFn Setter = NULL )
|
||||
{
|
||||
m_NativeProperties[PropertyName] = new CJSFunctionProperty( (GetFn)Getter, (SetFn)Setter );
|
||||
}
|
||||
template<typename ReturnType, ReturnType (T::*NativeFunction)( JSContext* cx, uintN argc, jsval* argv )>
|
||||
static void AddMethod( const char* Name, uintN MinArgs )
|
||||
{
|
||||
JSFunctionSpec FnInfo = { Name, CNativeFunction<T, ReadOnly, ReturnType, NativeFunction>::JSFunction, MinArgs, 0, 0 };
|
||||
m_Methods.push_back( FnInfo );
|
||||
}
|
||||
template<typename PropType> static void AddProperty( CStrW PropertyName, PropType T::*Native, bool PropReadOnly = ReadOnly )
|
||||
{
|
||||
IJSProperty* prop;
|
||||
if( PropReadOnly )
|
||||
{
|
||||
prop = new CJSProperty<PropType, true>( (PropType IJSObject::*)Native );
|
||||
}
|
||||
else
|
||||
prop = new CJSProperty<PropType, ReadOnly>( (PropType IJSObject::*)Native );
|
||||
m_NativeProperties[PropertyName] = prop;
|
||||
}
|
||||
#ifdef ALLOW_NONSHARED_NATIVES
|
||||
template<typename PropType> void AddLocalProperty( CStrW PropertyName, PropType* Native, bool PropReadOnly = ReadOnly )
|
||||
{
|
||||
IJSProperty* prop;
|
||||
if( PropReadOnly )
|
||||
{
|
||||
prop = new CJSNonsharedProperty<PropType, true>( Native );
|
||||
}
|
||||
else
|
||||
prop = new CJSNonsharedProperty<PropType, ReadOnly>( Native );
|
||||
m_NonsharedProperties[PropertyName] = prop;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Object operations
|
||||
JSObject* GetScript()
|
||||
{
|
||||
if( !m_JS )
|
||||
CreateScriptObject();
|
||||
return( m_JS );
|
||||
}
|
||||
// Creating and releasing script objects is done automatically most of the time, but you
|
||||
// can do it explicitly.
|
||||
void CreateScriptObject()
|
||||
{
|
||||
if( !m_JS )
|
||||
{
|
||||
m_JS = JS_NewObject( g_ScriptingHost.GetContext(), &JSI_class, NULL, NULL );
|
||||
if( m_EngineOwned )
|
||||
JS_AddRoot( g_ScriptingHost.GetContext(), (void*)&m_JS );
|
||||
|
||||
JS_SetPrivate( g_ScriptingHost.GetContext(), m_JS, (T*)this );
|
||||
}
|
||||
}
|
||||
void ReleaseScriptObject()
|
||||
{
|
||||
if( m_JS )
|
||||
{
|
||||
JS_SetPrivate( g_ScriptingHost.GetContext(), m_JS, NULL );
|
||||
if( m_EngineOwned )
|
||||
JS_RemoveRoot( g_ScriptingHost.GetContext(), &m_JS );
|
||||
m_JS = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Functions that must be provided to JavaScript
|
||||
// Functions and data that must be provided to JavaScript
|
||||
//
|
||||
private:
|
||||
static JSBool JSGetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp )
|
||||
{
|
||||
T* Instance = ToNative<T>( cx, obj );
|
||||
@ -519,28 +379,6 @@ public:
|
||||
|
||||
return( JS_TRUE );
|
||||
}
|
||||
static void ScriptingInit( const char* ClassName, JSNative Constructor = NULL, uintN ConstructorMinArgs = 0 )
|
||||
{
|
||||
JSFunctionSpec* JSI_methods = new JSFunctionSpec[ m_Methods.size() + 1 ];
|
||||
unsigned int MethodID;
|
||||
for( MethodID = 0; MethodID < m_Methods.size(); MethodID++ )
|
||||
JSI_methods[MethodID] = m_Methods[MethodID];
|
||||
|
||||
JSI_methods[MethodID].name = 0;
|
||||
|
||||
JSI_class.name = ClassName;
|
||||
g_ScriptingHost.DefineCustomObjectType( &JSI_class, Constructor, ConstructorMinArgs, JSI_props, JSI_methods, NULL, NULL );
|
||||
|
||||
delete[]( JSI_methods );
|
||||
|
||||
atexit( ScriptingShutdown );
|
||||
}
|
||||
static void ScriptingShutdown()
|
||||
{
|
||||
PropertyTable::iterator it;
|
||||
for( it = m_IntrinsicProperties.begin(); it != m_IntrinsicProperties.end(); it++ )
|
||||
delete( it->second );
|
||||
}
|
||||
static void DefaultFinalize( JSContext *cx, JSObject *obj )
|
||||
{
|
||||
T* Instance = ToNative<T>( cx, obj );
|
||||
@ -551,52 +389,14 @@ public:
|
||||
JS_SetPrivate( cx, obj, NULL );
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
JSObject* GetScript()
|
||||
{
|
||||
if( !m_JS )
|
||||
CreateScriptObject();
|
||||
return( m_JS );
|
||||
}
|
||||
// Creating and releasing script objects is done automatically most of the time, but you
|
||||
// can do it explicitly.
|
||||
void CreateScriptObject()
|
||||
{
|
||||
if( !m_JS )
|
||||
{
|
||||
m_JS = JS_NewObject( g_ScriptingHost.GetContext(), &JSI_class, NULL, NULL );
|
||||
if( m_EngineOwned )
|
||||
{
|
||||
#ifndef NDEBUG // Name the GC roots something more useful than 'ScriptableObject.h'
|
||||
JS_AddNamedRoot( g_ScriptingHost.GetContext(), (void*)&m_JS, JSI_class.name );
|
||||
#else
|
||||
JS_AddRoot( g_ScriptingHost.GetContext(), (void*)&m_JS );
|
||||
#endif
|
||||
}
|
||||
JS_SetPrivate( g_ScriptingHost.GetContext(), m_JS, (T*)this );
|
||||
}
|
||||
}
|
||||
void ReleaseScriptObject()
|
||||
{
|
||||
if( m_JS )
|
||||
{
|
||||
JS_SetPrivate( g_ScriptingHost.GetContext(), m_JS, NULL );
|
||||
if( m_EngineOwned )
|
||||
JS_RemoveRoot( g_ScriptingHost.GetContext(), &m_JS );
|
||||
m_JS = NULL;
|
||||
}
|
||||
}
|
||||
private:
|
||||
static JSPropertySpec JSI_props[];
|
||||
static std::vector<JSFunctionSpec> m_Methods;
|
||||
static PropertyTable m_IntrinsicProperties;
|
||||
|
||||
|
||||
public:
|
||||
// Standard constructors/destructors
|
||||
CJSObject()
|
||||
{
|
||||
m_Parent = NULL;
|
||||
m_JS = NULL;
|
||||
m_EngineOwned = true;
|
||||
}
|
||||
@ -607,163 +407,15 @@ public:
|
||||
void Shutdown()
|
||||
{
|
||||
PropertyTable::iterator it;
|
||||
for( it = m_Properties.begin(); it != m_Properties.end(); it++ )
|
||||
{
|
||||
if( !it->second->m_Intrinsic )
|
||||
{
|
||||
CJSDynamicProperty* extProp = (CJSValProperty*)it->second;
|
||||
if( extProp->m_JSAccessor )
|
||||
{
|
||||
CJSPropertyAccessor< CJSObject<T, ReadOnly> >* accessor = (CJSPropertyAccessor< CJSObject<T, ReadOnly> >*)JS_GetPrivate( g_ScriptingHost.GetContext(), extProp->m_JSAccessor );
|
||||
assert( accessor );
|
||||
delete( accessor );
|
||||
JS_SetPrivate( g_ScriptingHost.GetContext(), extProp->m_JSAccessor, NULL );
|
||||
JS_RemoveRoot( g_ScriptingHost.GetContext(), &( extProp->m_JSAccessor ) );
|
||||
}
|
||||
}
|
||||
for( it = m_ScriptProperties.begin(); it != m_ScriptProperties.end(); it++ )
|
||||
delete( it->second );
|
||||
}
|
||||
|
||||
|
||||
ReflectorTable::iterator it_a;
|
||||
for( it_a = m_Reflectors.begin(); it_a != m_Reflectors.end(); it_a++ )
|
||||
{
|
||||
CJSPropertyAccessor< CJSObject<T, ReadOnly> >* accessor = (CJSPropertyAccessor< CJSObject<T, ReadOnly> >*)JS_GetPrivate( g_ScriptingHost.GetContext(), it_a->second->m_JSAccessor );
|
||||
assert( accessor );
|
||||
delete( accessor );
|
||||
JS_SetPrivate( g_ScriptingHost.GetContext(), it_a->second->m_JSAccessor, NULL );
|
||||
JS_RemoveRoot( g_ScriptingHost.GetContext(), &( it_a->second->m_JSAccessor ) );
|
||||
delete( it_a->second );
|
||||
}
|
||||
m_ScriptProperties.clear();
|
||||
ReleaseScriptObject();
|
||||
}
|
||||
void SetBase( IJSObject* Parent )
|
||||
{
|
||||
if( m_Parent )
|
||||
{
|
||||
// Remove this from the list of our parent's inheritors
|
||||
InheritorsList::iterator it;
|
||||
for( it = m_Parent->m_Inheritors.begin(); it != m_Parent->m_Inheritors.end(); it++ )
|
||||
if( (*it) == this )
|
||||
{
|
||||
m_Parent->m_Inheritors.erase( it );
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_Parent = Parent;
|
||||
if( m_Parent )
|
||||
{
|
||||
// Place this in the list of our parent's inheritors
|
||||
m_Parent->m_Inheritors.push_back( this );
|
||||
Rebuild();
|
||||
}
|
||||
}
|
||||
void Rebuild()
|
||||
{
|
||||
PropertyTable::iterator it;
|
||||
// For each intrinsic property we have,
|
||||
for( it = m_Properties.begin(); it != m_Properties.end(); it++ )
|
||||
{
|
||||
if( !it->second->m_Intrinsic || !it->second->m_Inherited )
|
||||
continue;
|
||||
|
||||
// Attempt to locate it in the parent
|
||||
IJSProperty* cp = m_Parent->HasProperty( it->first );
|
||||
|
||||
// If it doesn't have it, we've inherited from an object of a different type
|
||||
// This isn't allowed at the moment; but I don't have an totally convincing
|
||||
// reason for forbidding it entirely. Mind, I can't think of any use for it,
|
||||
// either.
|
||||
// If it can be inherited, inherit it.
|
||||
if( cp && cp->m_AllowsInheritance )
|
||||
{
|
||||
assert( cp->m_Intrinsic );
|
||||
it->second->ImmediateCopy( this, m_Parent, cp );
|
||||
}
|
||||
}
|
||||
// Do the same for the shared properties table, too
|
||||
for( it = m_IntrinsicProperties.begin(); it != m_IntrinsicProperties.end(); it++ )
|
||||
{
|
||||
if( !it->second->m_Inherited )
|
||||
continue;
|
||||
|
||||
IJSProperty* cp = m_Parent->HasProperty( it->first );
|
||||
|
||||
if( cp && cp->m_AllowsInheritance )
|
||||
{
|
||||
assert( cp->m_Intrinsic );
|
||||
it->second->ImmediateCopy( this, m_Parent, cp );
|
||||
}
|
||||
}
|
||||
|
||||
// Now recurse.
|
||||
InheritorsList::iterator c;
|
||||
for( c = m_Inheritors.begin(); c != m_Inheritors.end(); c++ )
|
||||
(*c)->Rebuild();
|
||||
|
||||
}
|
||||
IJSProperty* HasProperty( CStrW PropertyName )
|
||||
{
|
||||
PropertyTable::iterator it;
|
||||
it = T::m_IntrinsicProperties.find( PropertyName );
|
||||
if( it != T::m_IntrinsicProperties.end() )
|
||||
return( it->second );
|
||||
|
||||
it = m_Properties.find( PropertyName );
|
||||
if( it != m_Properties.end() )
|
||||
return( it->second );
|
||||
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
void AddProperty( CStrW PropertyName, jsval Value )
|
||||
{
|
||||
assert( !HasProperty( PropertyName ) );
|
||||
CJSDynamicProperty* newProp = new CJSValProperty( Value, false );
|
||||
m_Properties[PropertyName] = newProp;
|
||||
|
||||
ReflectorTable::iterator it;
|
||||
it = m_Reflectors.find( PropertyName );
|
||||
if( it != m_Reflectors.end() )
|
||||
{
|
||||
// We had an accessor pointing to this property before it was defined.
|
||||
newProp->m_JSAccessor = it->second->m_JSAccessor;
|
||||
JS_RemoveRoot( g_ScriptingHost.GetContext(), &( it->second->m_JSAccessor ) );
|
||||
JS_AddRoot( g_ScriptingHost.GetContext(), &( newProp->m_JSAccessor ) );
|
||||
#ifdef ALLOW_NONSHARED_NATIVES
|
||||
for( it = m_NonsharedProperties.begin(); it != m_NonsharedProperties.end(); it++ )
|
||||
delete( it->second );
|
||||
m_Reflectors.erase( it );
|
||||
}
|
||||
}
|
||||
|
||||
void AddProperty( CStrW PropertyName, CStrW Value )
|
||||
{
|
||||
AddProperty( PropertyName, JSParseString( Value ) );
|
||||
}
|
||||
static void AddClassProperty( CStrW PropertyName, GetFn Getter, SetFn Setter = NULL )
|
||||
{
|
||||
T::m_IntrinsicProperties[PropertyName] = new CJSFunctionProperty( Getter, Setter );
|
||||
}
|
||||
template<typename ReturnType, ReturnType (T::*NativeFunction)( JSContext* cx, uintN argc, jsval* argv )>
|
||||
static void AddMethod( const char* Name, uintN MinArgs )
|
||||
{
|
||||
JSFunctionSpec FnInfo = { Name, CNativeFunction<T, ReadOnly, ReturnType, NativeFunction>::JSFunction, MinArgs, 0, 0 };
|
||||
T::m_Methods.push_back( FnInfo );
|
||||
}
|
||||
template<typename PropType> static void AddClassProperty( CStrW PropertyName, PropType T::*Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL )
|
||||
{
|
||||
T::m_IntrinsicProperties[PropertyName] = new CJSSharedProperty<PropType, ReadOnly>( (PropType IJSObject::*)Native, PropAllowInheritance, Update, Refresh );
|
||||
}
|
||||
template<typename PropType> static void AddReadOnlyClassProperty( CStrW PropertyName, PropType T::*Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL )
|
||||
{
|
||||
T::m_IntrinsicProperties[PropertyName] = new CJSSharedProperty<PropType, true>( (PropType IJSObject::*)Native, PropAllowInheritance, Update, Refresh );
|
||||
}
|
||||
template<typename PropType> void AddProperty( CStrW PropertyName, PropType* Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL )
|
||||
{
|
||||
m_Properties[PropertyName] = new CJSProperty<PropType, ReadOnly>( Native, PropAllowInheritance, Update, Refresh );
|
||||
}
|
||||
template<typename PropType> void AddReadOnlyProperty( CStrW PropertyName, PropType* Native, bool PropAllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL )
|
||||
{
|
||||
m_Properties[PropertyName] = new CJSProperty<PropType, true>( Native, PropAllowInheritance, Update, Refresh );
|
||||
m_NonsharedProperties.clear();
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
@ -781,72 +433,8 @@ template<typename T, bool ReadOnly> JSPropertySpec CJSObject<T, ReadOnly>::JSI_p
|
||||
};
|
||||
|
||||
template<typename T, bool ReadOnly> std::vector<JSFunctionSpec> CJSObject<T, ReadOnly>::m_Methods;
|
||||
template<typename T, bool ReadOnly> typename CJSObject<typename T, ReadOnly>::PropertyTable CJSObject<T, ReadOnly>::m_IntrinsicProperties;
|
||||
|
||||
template<typename T, bool ReadOnly> bool CJSObject<T, ReadOnly>::GetProperty( JSContext* cx, CStrW PropertyName, jsval* vp )
|
||||
{
|
||||
IJSProperty* Property = HasProperty( PropertyName );
|
||||
if( Property && Property->m_Intrinsic )
|
||||
{
|
||||
*vp = Property->Get( cx, this );
|
||||
}
|
||||
else
|
||||
{
|
||||
CJSDynamicProperty* extProp;
|
||||
|
||||
if( Property )
|
||||
{
|
||||
extProp = (CJSValProperty*)Property;
|
||||
|
||||
if( !extProp->m_JSAccessor )
|
||||
{
|
||||
extProp->m_JSAccessor = CJSPropertyAccessor< CJSObject<T, ReadOnly> >::CreateAccessor( cx, this, PropertyName );
|
||||
JS_AddNamedRoot( cx, &extProp->m_JSAccessor, "property accessor" );
|
||||
}
|
||||
|
||||
*vp = OBJECT_TO_JSVAL( extProp->m_JSAccessor );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check to see if it exists on a parent
|
||||
IJSObject* check = m_Parent;
|
||||
while( check )
|
||||
{
|
||||
if( check->HasProperty( PropertyName ) ) break;
|
||||
check = check->m_Parent;
|
||||
}
|
||||
|
||||
if( !check )
|
||||
return( false );
|
||||
|
||||
// FIXME: Fiddle a way so this /doesn't/ require multiple kilobytes
|
||||
// of memory. Can't think of any better way to do it yet. Problem is
|
||||
// that script may access a property that isn't defined locally, but
|
||||
// is defined by an ancestor. We can't return an accessor to the
|
||||
// ancestor's property, because then if it's altered it affects that
|
||||
// object, not this. At the moment, creating a 'reflector' property
|
||||
// accessor that references /this/ object to be returned to script.
|
||||
|
||||
// (N.B. Can't just put JSObjects* in the table -> table entries can
|
||||
// move -> root no longer refers to the JSObject.)
|
||||
|
||||
ReflectorTable::iterator it;
|
||||
it = m_Reflectors.find( PropertyName );
|
||||
|
||||
if( it == m_Reflectors.end() )
|
||||
{
|
||||
CJSReflector* reflector = new CJSReflector();
|
||||
reflector->m_JSAccessor = CJSPropertyAccessor< CJSObject<T, ReadOnly> >::CreateAccessor( cx, this, PropertyName );
|
||||
JS_AddRoot( cx, &reflector->m_JSAccessor );
|
||||
m_Reflectors.insert( std::pair<CStrW, CJSReflector*>( PropertyName, reflector ) );
|
||||
*vp = OBJECT_TO_JSVAL( reflector->m_JSAccessor );
|
||||
}
|
||||
else
|
||||
*vp = OBJECT_TO_JSVAL( it->second->m_JSAccessor );
|
||||
}
|
||||
}
|
||||
return( true );
|
||||
}
|
||||
template<typename T, bool ReadOnly> typename CJSObject<typename T, ReadOnly>::PropertyTable CJSObject<T, ReadOnly>::m_NativeProperties;
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -114,8 +114,6 @@ struct CSynchedJSObjectBase
|
||||
m_Owner(owner),
|
||||
m_Update(update)
|
||||
{
|
||||
m_AllowsInheritance = false;
|
||||
m_Intrinsic = true;
|
||||
}
|
||||
};
|
||||
|
||||
@ -140,7 +138,7 @@ protected:
|
||||
template <typename T> void AddSynchedProperty(CStrW name, T *native, UpdateFn update=NULL)
|
||||
{
|
||||
ISynchedJSProperty *prop=new CSynchedJSProperty<T>(name, native, this, update);
|
||||
m_Properties[name]=prop;
|
||||
m_NonsharedProperties[name]=prop;
|
||||
m_SynchedProperties[name]=prop;
|
||||
}
|
||||
};
|
||||
|
@ -24,7 +24,10 @@ CBaseEntity::CBaseEntity()
|
||||
AddProperty( L"traits.corpse", &m_corpse );
|
||||
|
||||
for( int t = 0; t < EVENT_LAST; t++ )
|
||||
AddProperty( EventNames[t], &m_EventHandlers[t] );
|
||||
{
|
||||
AddProperty( EventNames[t], &m_EventHandlers[t], false );
|
||||
AddHandler( t, &m_EventHandlers[t] );
|
||||
}
|
||||
|
||||
// Initialize, make life a little easier on the scriptors
|
||||
m_speed = m_turningRadius = m_meleeRange = m_meleeRangeMin = 0.0f;
|
||||
@ -62,6 +65,7 @@ void CBaseEntity::loadBase()
|
||||
}
|
||||
|
||||
SetBase( m_base );
|
||||
SetNextObject( m_base );
|
||||
}
|
||||
|
||||
bool CBaseEntity::loadXML( CStr filename )
|
||||
@ -204,7 +208,7 @@ void CBaseEntity::XMLLoadProperty( const CXeromyces& XeroFile, const XMBElement&
|
||||
// Add a property, put the node text into it.
|
||||
CStrW PropertyName = BasePropertyName + CStr8( XeroFile.getElementString( Source.getNodeName() ) );
|
||||
|
||||
IJSProperty* Existing = HasProperty( PropertyName );
|
||||
IJSComplexProperty* Existing = HasProperty( PropertyName );
|
||||
if( Existing )
|
||||
{
|
||||
if( !Existing->m_Intrinsic )
|
||||
@ -264,11 +268,16 @@ void CBaseEntity::ScriptingInit()
|
||||
{
|
||||
AddMethod<jsval, &CBaseEntity::ToString>( "toString", 0 );
|
||||
|
||||
CJSObject<CBaseEntity>::ScriptingInit( "EntityTemplate" );
|
||||
CJSComplex<CBaseEntity>::ScriptingInit( "EntityTemplate" );
|
||||
}
|
||||
|
||||
// Script-bound functions
|
||||
|
||||
JSObject* CBaseEntity::GetScriptExecContext( IEventTarget* target )
|
||||
{
|
||||
return( target->GetScriptExecContext( target ) );
|
||||
}
|
||||
|
||||
jsval CBaseEntity::ToString( JSContext* cx, uintN argc, jsval* argv )
|
||||
{
|
||||
wchar_t buffer[256];
|
||||
|
@ -21,13 +21,13 @@
|
||||
#include "CStr.h"
|
||||
#include "ObjectEntry.h"
|
||||
|
||||
#include "scripting/ScriptableObject.h"
|
||||
#include "scripting/ScriptableComplex.h"
|
||||
#include "BoundingObjects.h"
|
||||
#include "EventHandlers.h"
|
||||
#include "ScriptObject.h"
|
||||
#include "Xeromyces.h"
|
||||
|
||||
class CBaseEntity : public CJSObject<CBaseEntity>
|
||||
class CBaseEntity : public CJSComplex<CBaseEntity>, public IEventTarget
|
||||
{
|
||||
public:
|
||||
CBaseEntity();
|
||||
@ -65,6 +65,9 @@ public:
|
||||
|
||||
// Script-bound functions
|
||||
|
||||
// Get script execution contexts - always run in the context of the entity that fired it.
|
||||
JSObject* GetScriptExecContext( IEventTarget* target );
|
||||
|
||||
jsval ToString( JSContext* cx, uintN argc, jsval* argv );
|
||||
|
||||
static void ScriptingInit();
|
||||
|
@ -41,7 +41,10 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation )
|
||||
AddProperty( L"player", &m_player );
|
||||
|
||||
for( int t = 0; t < EVENT_LAST; t++ )
|
||||
AddProperty( EventNames[t], &m_EventHandlers[t] );
|
||||
{
|
||||
AddProperty( EventNames[t], &m_EventHandlers[t], false );
|
||||
AddHandler( t, &m_EventHandlers[t] );
|
||||
}
|
||||
|
||||
// Set our parent unit and build us an actor.
|
||||
m_actor = NULL;
|
||||
@ -102,6 +105,7 @@ void CEntity::loadBase()
|
||||
// Set up our instance data
|
||||
|
||||
SetBase( m_base );
|
||||
SetNextObject( m_base );
|
||||
|
||||
if( m_base->m_bound_type == CBoundingObject::BOUND_CIRCLE )
|
||||
{
|
||||
@ -326,16 +330,6 @@ void CEntity::dispatch( const CMessage* msg )
|
||||
}
|
||||
*/
|
||||
|
||||
bool CEntity::DispatchEvent( CScriptEvent* evt )
|
||||
{
|
||||
const char* data = g_Profiler.InternString( "script: " + (CStr8)evt->m_Type );
|
||||
|
||||
g_Profiler.StartScript( data );
|
||||
bool rval = m_EventHandlers[evt->m_TypeCode].DispatchEvent( GetScript(), evt );
|
||||
g_Profiler.Stop();
|
||||
return( rval );
|
||||
}
|
||||
|
||||
void CEntity::clearOrders()
|
||||
{
|
||||
m_orderQueue.clear();
|
||||
@ -346,13 +340,6 @@ void CEntity::pushOrder( CEntityOrder& order )
|
||||
m_orderQueue.push_back( order );
|
||||
}
|
||||
|
||||
int CEntity::defaultOrder( CEntity* orderTarget )
|
||||
{
|
||||
CEventTargetChanged evt( orderTarget );
|
||||
DispatchEvent( &evt );
|
||||
return( evt.m_defaultAction );
|
||||
}
|
||||
|
||||
bool CEntity::acceptsOrder( int orderType, CEntity* orderTarget )
|
||||
{
|
||||
CEventPrepareOrder evt( orderTarget, orderType );
|
||||
@ -624,7 +611,7 @@ void CEntity::ScriptingInit()
|
||||
|
||||
AddClassProperty( L"template", (CBaseEntity* CEntity::*)&CEntity::m_base, false, (NotifyFn)&CEntity::loadBase );
|
||||
|
||||
CJSObject<CEntity>::ScriptingInit( "Entity", Construct, 2 );
|
||||
CJSComplex<CEntity>::ScriptingInit( "Entity", Construct, 2 );
|
||||
}
|
||||
|
||||
// Script constructor
|
||||
|
@ -32,7 +32,7 @@
|
||||
#define ENTITY_INCLUDED
|
||||
|
||||
#include <deque>
|
||||
#include "scripting/ScriptableObject.h"
|
||||
#include "scripting/ScriptableComplex.h"
|
||||
#include "Player.h"
|
||||
|
||||
#include "Vector2D.h"
|
||||
@ -52,7 +52,7 @@ class CUnit;
|
||||
|
||||
// TODO MT: Put this is /some/ sort of order...
|
||||
|
||||
class CEntity : public CJSObject<CEntity>
|
||||
class CEntity : public CJSComplex<CEntity>, public IEventTarget
|
||||
{
|
||||
friend class CEntityManager;
|
||||
private:
|
||||
@ -96,22 +96,15 @@ public:
|
||||
|
||||
//-- Scripts
|
||||
|
||||
// Get script execution contexts - always run in the context of the entity that fired it.
|
||||
JSObject* GetScriptExecContext( IEventTarget* target ) { return( ((CEntity*)target)->GetScript() ); }
|
||||
|
||||
// EventListener adaptation?
|
||||
//typedef std::vector<CScriptObject> ExtendedHandlerList;
|
||||
//typedef STL_HASH_MAP<CStrW, HandlerList, CStrW_hash_compare> ExtendedHandlerTable;
|
||||
|
||||
CScriptObject m_EventHandlers[EVENT_LAST];
|
||||
|
||||
// EventListener adaptation?
|
||||
/*
|
||||
void AddListener( const CStrW& EventType, CScriptObject* Handler )
|
||||
{
|
||||
m_EventHandlers[EventType].push_back( Handler );
|
||||
}
|
||||
*/
|
||||
|
||||
bool DispatchEvent( CScriptEvent* evt );
|
||||
|
||||
CUnit* m_actor;
|
||||
|
||||
// State transition in the FSM (animations should be reset)
|
||||
@ -185,8 +178,6 @@ public:
|
||||
void checkGroup(); // Groups
|
||||
void checkExtant(); // Existence
|
||||
|
||||
// Returns the default action of the entity upon the target (or -1 if none apply)
|
||||
int CEntity::defaultOrder( CEntity* orderTarget );
|
||||
// Returns whether the entity is capable of performing the given orderType on the target.
|
||||
bool acceptsOrder( int orderType, CEntity* orderTarget );
|
||||
|
||||
|
@ -299,6 +299,8 @@ bool CEntity::processContactAction( CEntityOrder* current, size_t timestep_milli
|
||||
}
|
||||
bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t timestep_millis, CSkeletonAnim* animation, CScriptEvent* contactEvent, float range, float minRange = 0.0f )
|
||||
{
|
||||
static size_t cyclepos = 0;
|
||||
|
||||
// Target's dead (or exhausted)? Then our work here is done.
|
||||
if( !current->m_data[0].entity || !current->m_data[0].entity->m_extant )
|
||||
{
|
||||
@ -308,9 +310,27 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times
|
||||
|
||||
if( m_actor )
|
||||
{
|
||||
// Still playing attack animation? Suspend processing.
|
||||
if( m_actor->GetModel()->GetAnimation() == animation )
|
||||
if( animation && ( m_actor->GetModel()->GetAnimation() == animation ) )
|
||||
{
|
||||
size_t cyclenext = cyclepos + timestep_millis;
|
||||
if( ( cyclepos <= animation->m_ActionPos ) &&
|
||||
( cyclenext > animation->m_ActionPos ) )
|
||||
{
|
||||
// Actually execute the action script in the sim frame
|
||||
// that contains the animation's 'action point' (as
|
||||
// specified by the artist that created it)
|
||||
if( !DispatchEvent( contactEvent ) )
|
||||
{
|
||||
// The script is cancelling the attack/gather action.
|
||||
|
||||
// Cancel the animation (will probably cause a graphical
|
||||
// glitch, but can't be helped)
|
||||
m_actor->GetModel()->SetAnimation( m_actor->GetObject()->m_WalkAnim );
|
||||
}
|
||||
}
|
||||
cyclepos = cyclenext;
|
||||
return( false );
|
||||
}
|
||||
|
||||
// Just transitioned? No animation? (=> melee just finished) Play walk.
|
||||
if( m_transition || !m_actor->GetModel()->GetAnimation() )
|
||||
@ -396,8 +416,12 @@ bool CEntity::processContactActionNoPathing( CEntityOrder* current, size_t times
|
||||
m_ahead = delta.normalize();
|
||||
}
|
||||
|
||||
if( DispatchEvent( contactEvent ) && m_actor )
|
||||
// Start the animation. Actual damage/gather will be done in a
|
||||
// few hundred msec, at the 'action point' of the animation we're
|
||||
// now setting.
|
||||
|
||||
m_actor->GetModel()->SetAnimation( animation, true );
|
||||
cyclepos = 0;
|
||||
|
||||
return( false );
|
||||
}
|
||||
|
@ -35,10 +35,10 @@ public:
|
||||
}
|
||||
static void ScriptingInit()
|
||||
{
|
||||
AddClassProperty<float>( L"crush", &CDamageType::m_Crush );
|
||||
AddClassProperty<float>( L"hack", &CDamageType::m_Hack );
|
||||
AddClassProperty<float>( L"pierce", &CDamageType::m_Pierce );
|
||||
AddClassProperty<float>( L"typeless", &CDamageType::m_Typeless );
|
||||
AddProperty<float>( L"crush", &CDamageType::m_Crush );
|
||||
AddProperty<float>( L"hack", &CDamageType::m_Hack );
|
||||
AddProperty<float>( L"pierce", &CDamageType::m_Pierce );
|
||||
AddProperty<float>( L"typeless", &CDamageType::m_Typeless );
|
||||
CJSObject<CDamageType>::ScriptingInit( "DamageType", Construct, 3 );
|
||||
}
|
||||
static JSBool Construct( JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval )
|
||||
|
@ -2,50 +2,52 @@
|
||||
#include "EventHandlers.h"
|
||||
#include "Entity.h"
|
||||
|
||||
CEventAttack::CEventAttack( CEntity* target ) : CScriptEvent( L"attack", true, EVENT_ATTACK )
|
||||
CEventAttack::CEventAttack( CEntity* target ) : CScriptEvent( L"attack", EVENT_ATTACK, true )
|
||||
{
|
||||
m_target = target;
|
||||
AddProperty( L"target", &m_target );
|
||||
AddLocalProperty( L"target", &m_target );
|
||||
}
|
||||
|
||||
CEventGather::CEventGather( CEntity* target ) : CScriptEvent( L"gather", true, EVENT_GATHER )
|
||||
CEventGather::CEventGather( CEntity* target ) : CScriptEvent( L"gather", EVENT_GATHER, true )
|
||||
{
|
||||
m_target = target;
|
||||
AddProperty( L"target", &m_target );
|
||||
AddLocalProperty( L"target", &m_target );
|
||||
}
|
||||
|
||||
CEventDamage::CEventDamage( CEntity* inflictor, CDamageType* damage ) : CScriptEvent( L"takesDamage", true, EVENT_DAMAGE )
|
||||
CEventDamage::CEventDamage( CEntity* inflictor, CDamageType* damage ) : CScriptEvent( L"takesDamage", EVENT_DAMAGE, true )
|
||||
{
|
||||
m_inflictor = inflictor;
|
||||
m_damage = damage;
|
||||
AddReadOnlyProperty( L"inflictor", &m_inflictor );
|
||||
AddProperty( L"damage", &m_damage );
|
||||
AddLocalProperty( L"inflictor", &m_inflictor, true );
|
||||
AddLocalProperty( L"damage", &m_damage );
|
||||
}
|
||||
|
||||
CEventTargetChanged::CEventTargetChanged( CEntity* target ) : CScriptEvent( L"targetChanged", false, EVENT_TARGET_CHANGED )
|
||||
CEventTargetChanged::CEventTargetChanged( CEntity* target ) : CScriptEvent( L"targetChanged", EVENT_TARGET_CHANGED, false )
|
||||
{
|
||||
m_target = target;
|
||||
m_defaultAction = -1;
|
||||
AddReadOnlyProperty( L"target", &m_target );
|
||||
AddProperty( L"defaultAction", &m_defaultAction );
|
||||
m_defaultCursor = L"arrow-default";
|
||||
AddLocalProperty( L"target", &m_target, true );
|
||||
AddLocalProperty( L"defaultAction", &m_defaultAction );
|
||||
AddLocalProperty( L"defaultCursor", &m_defaultCursor );
|
||||
}
|
||||
|
||||
CEventPrepareOrder::CEventPrepareOrder( CEntity* target, int orderType ) : CScriptEvent( L"prepareOrder", true, EVENT_PREPARE_ORDER )
|
||||
CEventPrepareOrder::CEventPrepareOrder( CEntity* target, int orderType ) : CScriptEvent( L"prepareOrder", EVENT_PREPARE_ORDER, true )
|
||||
{
|
||||
m_target = target;
|
||||
m_orderType = orderType;
|
||||
AddReadOnlyProperty( L"target", &m_target );
|
||||
AddReadOnlyProperty( L"orderType", &m_orderType );
|
||||
AddLocalProperty( L"target", &m_target, true );
|
||||
AddLocalProperty( L"orderType", &m_orderType, true );
|
||||
}
|
||||
|
||||
CEventOrderTransition::CEventOrderTransition( int orderPrevious, int orderCurrent, CEntity*& target, CVector3D& worldPosition ) : CScriptEvent( L"orderTransition", true, EVENT_ORDER_TRANSITION )
|
||||
CEventOrderTransition::CEventOrderTransition( int orderPrevious, int orderCurrent, CEntity*& target, CVector3D& worldPosition ) : CScriptEvent( L"orderTransition", EVENT_ORDER_TRANSITION, true )
|
||||
{
|
||||
m_orderPrevious = orderPrevious;
|
||||
m_orderCurrent = orderCurrent;
|
||||
m_target = ⌖
|
||||
m_worldPosition = &worldPosition;
|
||||
AddReadOnlyProperty( L"orderPrevious", &m_orderPrevious );
|
||||
AddProperty( L"orderCurrent", &m_orderCurrent );
|
||||
AddProperty( L"target", m_target );
|
||||
AddProperty( L"position", m_worldPosition );
|
||||
AddLocalProperty( L"orderPrevious", &m_orderPrevious, true );
|
||||
AddLocalProperty( L"orderCurrent", &m_orderCurrent );
|
||||
AddLocalProperty( L"target", m_target );
|
||||
AddLocalProperty( L"position", m_worldPosition );
|
||||
}
|
||||
|
@ -10,41 +10,16 @@
|
||||
|
||||
class CDamageType;
|
||||
|
||||
enum EEventType
|
||||
{
|
||||
EVENT_INITIALIZE,
|
||||
EVENT_TICK,
|
||||
EVENT_ATTACK,
|
||||
EVENT_GATHER,
|
||||
EVENT_DAMAGE,
|
||||
EVENT_TARGET_CHANGED,
|
||||
EVENT_PREPARE_ORDER,
|
||||
EVENT_ORDER_TRANSITION,
|
||||
EVENT_LAST,
|
||||
};
|
||||
|
||||
static const wchar_t* EventNames[] =
|
||||
{
|
||||
/* EVENT_INITIALIZE */ L"onInitialize",
|
||||
/* EVENT_TICK */ L"onTick",
|
||||
/* EVENT_ATTACK */ L"onAttack", /* This unit is the one doing the attacking... */
|
||||
/* EVENT_GATHER */ L"onGather", /* This unit is the one doing the gathering... */
|
||||
/* EVENT_DAMAGE */ L"onTakesDamage",
|
||||
/* 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...) */
|
||||
};
|
||||
|
||||
class CEventInitialize : public CScriptEvent
|
||||
{
|
||||
public:
|
||||
CEventInitialize() : CScriptEvent( L"initialize", false, EVENT_INITIALIZE ) {}
|
||||
CEventInitialize() : CScriptEvent( L"initialize", EVENT_INITIALIZE, false ) {}
|
||||
};
|
||||
|
||||
class CEventTick : public CScriptEvent
|
||||
{
|
||||
public:
|
||||
CEventTick() : CScriptEvent( L"tick", false, EVENT_TICK ) {}
|
||||
CEventTick() : CScriptEvent( L"tick", EVENT_TICK, false ) {}
|
||||
};
|
||||
|
||||
class CEventAttack : public CScriptEvent
|
||||
@ -74,6 +49,7 @@ class CEventTargetChanged : public CScriptEvent
|
||||
CEntity* m_target;
|
||||
public:
|
||||
int m_defaultAction;
|
||||
CStrW m_defaultCursor;
|
||||
CEventTargetChanged( CEntity* target );
|
||||
};
|
||||
|
||||
|
@ -121,9 +121,9 @@ CJSProgressTimer::CJSProgressTimer( double Max, double Increment, JSFunction* Ca
|
||||
|
||||
void CJSProgressTimer::ScriptingInit()
|
||||
{
|
||||
AddClassProperty( L"max", &CJSProgressTimer::m_Max );
|
||||
AddClassProperty( L"current", &CJSProgressTimer::m_Current );
|
||||
AddClassProperty( L"increment", &CJSProgressTimer::m_Increment );
|
||||
AddProperty( L"max", &CJSProgressTimer::m_Max );
|
||||
AddProperty( L"current", &CJSProgressTimer::m_Current );
|
||||
AddProperty( L"increment", &CJSProgressTimer::m_Increment );
|
||||
|
||||
CJSObject<CJSProgressTimer>::ScriptingInit( "ProgressTimer", Construct, 2 );
|
||||
}
|
||||
|
@ -76,15 +76,15 @@ JSObject* CScriptObject::GetFunctionObject()
|
||||
// Executes a script attached to a JS object.
|
||||
// Returns false if the script isn't defined, if the script can't be executed,
|
||||
// otherwise true. Script return value is in rval.
|
||||
bool CScriptObject::Run( JSObject* Context, jsval* rval )
|
||||
bool CScriptObject::Run( JSObject* Context, jsval* rval, uintN argc, jsval* argv )
|
||||
{
|
||||
if( !Function )
|
||||
return( false );
|
||||
return( JS_TRUE == JS_CallFunction( g_ScriptingHost.GetContext(), Context, Function, 0, NULL, rval ) );
|
||||
return( JS_TRUE == JS_CallFunction( g_ScriptingHost.GetContext(), Context, Function, argc, argv, rval ) );
|
||||
}
|
||||
|
||||
// This variant casts script return value to a boolean, and passes it back.
|
||||
bool CScriptObject::Run( JSObject* Context )
|
||||
bool CScriptObject::Run( JSObject* Context, uintN argc, jsval* argv )
|
||||
{
|
||||
jsval Temp;
|
||||
if( !Run( Context, &Temp ) )
|
||||
@ -95,11 +95,13 @@ bool CScriptObject::Run( JSObject* Context )
|
||||
// Treat this as an event handler and dispatch an event to it. Return !evt->m_cancelled, as a convenience.
|
||||
bool CScriptObject::DispatchEvent( JSObject* Context, CScriptEvent* evt )
|
||||
{
|
||||
if( Function )
|
||||
{
|
||||
jsval Temp;
|
||||
jsval EventObject = OBJECT_TO_JSVAL( evt->GetScript() );
|
||||
if( Function )
|
||||
JS_CallFunction( g_ScriptingHost.GetContext(), Context, Function, 1, &EventObject, &Temp );
|
||||
return( !evt->m_Cancelled );
|
||||
}
|
||||
return( evt->m_Blocked );
|
||||
}
|
||||
|
||||
void CScriptObject::Compile( CStrW FileNameTag, CStrW FunctionBody )
|
||||
|
@ -37,15 +37,19 @@ public:
|
||||
return( Function != NULL );
|
||||
}
|
||||
|
||||
inline operator bool() { return( Function != NULL ); }
|
||||
inline bool operator!() { return( !Function ); }
|
||||
inline bool operator==( const CScriptObject& compare ) { return( Function == compare.Function ); }
|
||||
|
||||
// JSObject wrapping the function if it's defined, NULL if it isn't.
|
||||
JSObject* GetFunctionObject();
|
||||
|
||||
// Executes a script attached to a JS object.
|
||||
// Returns false if the script isn't defined, if the script can't be executed,
|
||||
// otherwise true. Script return value is in rval.
|
||||
bool Run( JSObject* Context, jsval* rval );
|
||||
bool Run( JSObject* Context, jsval* rval, uintN argc = 0, jsval* argv = NULL );
|
||||
// This variant casts script return value to a boolean, and passes it back.
|
||||
bool Run( JSObject* Context );
|
||||
bool Run( JSObject* Context, uintN argc = 0, jsval* argv = NULL );
|
||||
|
||||
// Treat this as an event handler and dispatch an event to it.
|
||||
bool DispatchEvent( JSObject* Context, CScriptEvent* evt );
|
||||
|
Loading…
Reference in New Issue
Block a user