diff --git a/source/graphics/Camera.cpp b/source/graphics/Camera.cpp index 25b4fa3275..ca24050817 100755 --- a/source/graphics/Camera.cpp +++ b/source/graphics/Camera.cpp @@ -237,7 +237,7 @@ void CCamera::LookAlong( CVector3D camera, CVector3D orientation, CVector3D up ) orientation.Normalize(); up.Normalize(); CVector3D s = orientation.Cross( up ); - // CVector3D u = s.Cross( orientation ); + m_Orientation._11 = -s.X; m_Orientation._12 = up.X; m_Orientation._13 = orientation.X; m_Orientation._14 = camera.X; m_Orientation._21 = -s.Y; m_Orientation._22 = up.Y; m_Orientation._23 = orientation.Y; m_Orientation._24 = camera.Y; m_Orientation._31 = -s.Z; m_Orientation._32 = up.Z; m_Orientation._33 = orientation.Z; m_Orientation._34 = camera.Z; diff --git a/source/graphics/Terrain.h b/source/graphics/Terrain.h index 2e62b356a2..61b761a574 100755 --- a/source/graphics/Terrain.h +++ b/source/graphics/Terrain.h @@ -32,11 +32,11 @@ public: inline bool isOnMap( float x, float y ) const { - return( ( x >= 0.0f ) && ( x <= (float)m_MapSize ) && ( y >= 0.0f ) && ( y <= (float)m_MapSize ) ); + return( ( x >= 0.0f ) && ( x <= (float)( m_MapSize * CELL_SIZE ) ) && ( y >= 0.0f ) && ( y <= (float)( m_MapSize * CELL_SIZE ) ) ); } inline bool isOnMap( const CVector2D& v ) const { - return( ( v.x >= 0.0f ) && ( v.x <= (float)m_MapSize ) && ( v.y >= 0.0f ) && ( v.y <= (float)m_MapSize ) ); + return( ( v.x >= 0.0f ) && ( v.x <= (float)( m_MapSize * CELL_SIZE ) ) && ( v.y >= 0.0f ) && ( v.y <= (float)( m_MapSize * CELL_SIZE ) ) ); } float getExactGroundLevel( float x, float y ) const ; inline float getExactGroundLevel( const CVector2D& v ) const { return( getExactGroundLevel( v.x, v.y ) ); } diff --git a/source/graphics/scripting/JSInterface_Camera.cpp b/source/graphics/scripting/JSInterface_Camera.cpp index d20ab970f9..b335b61e01 100755 --- a/source/graphics/scripting/JSInterface_Camera.cpp +++ b/source/graphics/scripting/JSInterface_Camera.cpp @@ -8,6 +8,7 @@ #include "Terrain.h" #include "Game.h" + JSClass JSI_Camera::JSI_class = { "Camera", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, @@ -39,45 +40,71 @@ void JSI_Camera::init() JSI_Camera::Camera_Info::Camera_Info() { - CMatrix3D orient; - orient.SetXRotation( DEGTORAD( 30 ) ); - orient.RotateY( DEGTORAD( -45 ) ); - orient.Translate( 100, 150, -100 ); + CMatrix3D Orient; + Orient.SetXRotation( DEGTORAD( 30 ) ); + Orient.RotateY( DEGTORAD( -45 ) ); + Orient.Translate( 100, 150, -100 ); - Camera_Info( orient.GetTranslation(), orient.GetIn(), orient.GetUp() ); + Camera_Info( (const CMatrix3D&)Orient ); } -JSI_Camera::Camera_Info::Camera_Info( const CVector3D& _position ) +JSI_Camera::Camera_Info::Camera_Info( const CVector3D& Position ) +{ + CMatrix3D Orient; + Orient.SetXRotation( DEGTORAD( 30 ) ); + Orient.RotateY( DEGTORAD( -45 ) ); + Orient.Translate( Position ); + + Camera_Info( (const CMatrix3D&)Orient ); +} + +JSI_Camera::Camera_Info::Camera_Info( const CVector3D& Position, const CVector3D& Orientation ) { Camera_Info(); - position = _position; + m_Data->LookAlong( Position, Orientation, CVector3D( 0.0f, 1.0f, 0.0f ) ); } -JSI_Camera::Camera_Info::Camera_Info( const CVector3D& _position, const CVector3D& _orientation ) +JSI_Camera::Camera_Info::Camera_Info( const CVector3D& Position, const CVector3D& Orientation, const CVector3D& Up ) { - Camera_Info( _position, _orientation, CVector3D( 0.0f, 1.0f, 0.0f ) ); + Camera_Info(); + m_Data->LookAlong( Position, Orientation, Up ); } -JSI_Camera::Camera_Info::Camera_Info( const CVector3D& _position, const CVector3D& _orientation, const CVector3D& _up ) +JSI_Camera::Camera_Info::Camera_Info( const CMatrix3D& Orientation ) { - position = _position; - orientation = _orientation; - up = _up; - copy = NULL; + m_Data = new CCamera(); + m_EngineOwned = false; + + m_Data->LookAlong( Orientation.GetTranslation(), Orientation.GetIn(), Orientation.GetUp() ); } -JSI_Camera::Camera_Info::Camera_Info( CCamera* _copy ) +JSI_Camera::Camera_Info::Camera_Info( CCamera* Reference ) { - copy = _copy; - position = copy->m_Orientation.GetTranslation(); - orientation = copy->m_Orientation.GetIn(); - up = copy->m_Orientation.GetUp(); + m_Data = Reference; + m_EngineOwned = true; } -void JSI_Camera::Camera_Info::update() +JSI_Camera::Camera_Info::~Camera_Info() { - if( copy ) - copy->LookAlong( position, orientation, up ); + if( !m_EngineOwned ) + delete( m_Data ); +} + +void JSI_Camera::Camera_Info::Freshen() +{ + m_sv_Position = m_Data->m_Orientation.GetTranslation(); + m_sv_Orientation = m_Data->m_Orientation.GetIn(); + m_sv_Up = m_Data->m_Orientation.GetUp(); +} + +void JSI_Camera::Camera_Info::Update() +{ + m_Data->LookAlong( m_sv_Position, m_sv_Orientation, m_sv_Up ); +} + +void JSI_Camera::Camera_Info::FreshenTarget() +{ + m_sv_Target = m_Data->GetFocus(); } JSBool JSI_Camera::getCamera( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) @@ -97,7 +124,9 @@ JSBool JSI_Camera::setCamera( JSContext* cx, JSObject* obj, jsval id, jsval* vp JS_ReportError( cx, "[Camera] Invalid object" ); } else - g_Game->GetView()->GetCamera()->LookAlong( cameraInfo->position, cameraInfo->orientation, cameraInfo->up ); + { + g_Game->GetView()->GetCamera()->m_Orientation = cameraInfo->m_Data->m_Orientation; + } return( JS_TRUE ); } @@ -117,14 +146,14 @@ JSBool JSI_Camera::getProperty( JSContext* cx, JSObject* obj, jsval id, jsval* v switch( g_ScriptingHost.ValueToInt( id ) ) { - case vector_position: d = &cameraInfo->position; break; - case vector_orientation: d = &cameraInfo->orientation; break; - case vector_up: d = &cameraInfo->up; break; - default: return( JS_FALSE ); + case vector_position: d = &cameraInfo->m_sv_Position; break; + case vector_orientation: d = &cameraInfo->m_sv_Orientation; break; + case vector_up: d = &cameraInfo->m_sv_Up; break; + default: return( JS_TRUE ); } JSObject* vector3d = JS_NewObject( g_ScriptingHost.getContext(), &JSI_Vector3D::JSI_class, NULL, NULL ); - JS_SetPrivate( g_ScriptingHost.getContext(), vector3d, new JSI_Vector3D::Vector3D_Info( d, cameraInfo, ( void( IBoundPropertyOwner::* )() )&Camera_Info::update ) ); + JS_SetPrivate( g_ScriptingHost.getContext(), vector3d, new JSI_Vector3D::Vector3D_Info( *d /*, cameraInfo, ( void( IPropertyOwner::* )() )&Camera_Info::Update, ( void( IPropertyOwner::* )() )&Camera_Info::Freshen */ ) ); *vp = OBJECT_TO_JSVAL( vector3d ); return( JS_TRUE ); } @@ -146,14 +175,16 @@ JSBool JSI_Camera::setProperty( JSContext* cx, JSObject* obj, jsval id, jsval* v if( JSVAL_IS_OBJECT( *vp ) && ( v = (JSI_Vector3D::Vector3D_Info*)JS_GetInstancePrivate( g_ScriptingHost.getContext(), vector3d, &JSI_Vector3D::JSI_class, NULL ) ) ) { + cameraInfo->Freshen(); + switch( g_ScriptingHost.ValueToInt( id ) ) { - case vector_position: cameraInfo->position = *( v->vector ); break; - case vector_orientation: cameraInfo->orientation = *( v->vector ); break; - case vector_up: cameraInfo->up = *( v->vector ); break; + case vector_position: cameraInfo->m_sv_Position = *( v->vector ); break; + case vector_orientation: cameraInfo->m_sv_Orientation = *( v->vector ); break; + case vector_up: cameraInfo->m_sv_Up = *( v->vector ); break; } - cameraInfo->update(); + cameraInfo->Update(); } return( JS_TRUE ); @@ -170,9 +201,9 @@ JSBool JSI_Camera::lookAt( JSContext* cx, JSObject* obj, unsigned int argc, jsva if( argc <= 3 ) { - cameraInfo->position = GETVECTOR( argv[0] ); - cameraInfo->orientation = ( GETVECTOR( argv[1] ) - GETVECTOR( argv[0] ) ); - cameraInfo->orientation.Normalize(); + cameraInfo->m_sv_Position = GETVECTOR( argv[0] ); + cameraInfo->m_sv_Orientation = ( GETVECTOR( argv[1] ) - GETVECTOR( argv[0] ) ); + cameraInfo->m_sv_Orientation.Normalize(); } else { @@ -183,16 +214,16 @@ JSBool JSI_Camera::lookAt( JSContext* cx, JSObject* obj, unsigned int argc, jsva if( argc == 2 ) { - cameraInfo->up = CVector3D( 0.0f, 1.0f, 0.0f ); + cameraInfo->m_sv_Up = CVector3D( 0.0f, 1.0f, 0.0f ); } else if( argc == 3 ) { - cameraInfo->up = GETVECTOR( argv[2] ); + cameraInfo->m_sv_Up = GETVECTOR( argv[2] ); } #undef GETVECTOR - cameraInfo->update(); + cameraInfo->Update(); *rval = JSVAL_TRUE; return( JS_TRUE ); @@ -204,20 +235,8 @@ JSBool JSI_Camera::getFocus( JSContext* cx, JSObject* obj, uintN argc, jsval* ar Camera_Info* cameraInfo = (Camera_Info*)JS_GetPrivate( cx, obj ); - CHFTracer tracer( g_Game->GetWorld()->GetTerrain() ); int x, z; - - CVector3D currentTarget; - - if( !tracer.RayIntersect( cameraInfo->position, cameraInfo->orientation, x, z, currentTarget ) ) - { - // Off the edge of the world? - // Work out where it /would/ hit, if the map were extended out to infinity with average height. - - currentTarget = cameraInfo->position + cameraInfo->orientation * ( ( 50.0f - cameraInfo->position.Y ) / cameraInfo->orientation.Y ); - } - JSObject* vector3d = JS_NewObject( g_ScriptingHost.getContext(), &JSI_Vector3D::JSI_class, NULL, NULL ); - JS_SetPrivate( g_ScriptingHost.getContext(), vector3d, new JSI_Vector3D::Vector3D_Info( currentTarget ) ); + JS_SetPrivate( g_ScriptingHost.getContext(), vector3d, new JSI_Vector3D::Vector3D_Info( cameraInfo->m_sv_Target /*, cameraInfo, NULL, ( void( IPropertyOwner::* )() )&Camera_Info::FreshenTarget */ ) ); *rval = OBJECT_TO_JSVAL( vector3d ); return( JS_TRUE ); } diff --git a/source/graphics/scripting/JSInterface_Camera.h b/source/graphics/scripting/JSInterface_Camera.h index 6df7dc44d9..79a40bf6c0 100755 --- a/source/graphics/scripting/JSInterface_Camera.h +++ b/source/graphics/scripting/JSInterface_Camera.h @@ -7,7 +7,6 @@ // Mark Thompson (mot20@cam.ac.uk / mark@wildfiregames.com) #include "scripting/ScriptingHost.h" -#include "EntityProperties.h" #include "Vector3D.h" #include "Camera.h" @@ -30,18 +29,37 @@ namespace JSI_Camera lookat }; - struct Camera_Info : public IBoundPropertyOwner + struct Camera_Info { - CVector3D position; - CVector3D orientation; - CVector3D up; - CCamera* copy; + CCamera* m_Data; + bool m_EngineOwned; + CVector3D m_sv_Position; + CVector3D m_sv_Orientation; + CVector3D m_sv_Up; + CVector3D m_sv_Target; + Camera_Info(); // Create a camera set to the initial view + + Camera_Info( const CVector3D& Position ); + Camera_Info( const CVector3D& Position, const CVector3D& Orientation ); + Camera_Info( const CVector3D& Position, const CVector3D& Orientation, const CVector3D& Up ); + Camera_Info( const CMatrix3D& Orientation ); + Camera_Info( CCamera* copy ); + + void Freshen(); + void Update(); + void FreshenTarget(); + + ~Camera_Info(); +/* + Camera_Info( const CVector3D& _position ); Camera_Info( const CVector3D& _position, const CVector3D& _orientation ); Camera_Info( const CVector3D& _position, const CVector3D& _orientation, const CVector3D& _up ); Camera_Info( CCamera* _copy ); void update(); + +*/ }; extern JSClass JSI_class; extern JSPropertySpec JSI_props[]; diff --git a/source/main.cpp b/source/main.cpp index 9e0ccbd60c..b4cbb02555 100755 --- a/source/main.cpp +++ b/source/main.cpp @@ -55,6 +55,7 @@ #include "scripting/JSInterface_Camera.h" #include "scripting/JSInterface_Selection.h" #include "scripting/JSInterface_Console.h" +#include "scripting/JSCollection.h" #include "gui/scripting/JSInterface_IGUIObject.h" #include "gui/scripting/JSInterface_GUITypes.h" @@ -103,7 +104,7 @@ static bool g_NoPBuffer=true; static bool g_FixedFrameTiming=false; static bool g_VSync = false; -CLightEnv g_LightEnv; +extern CLightEnv g_LightEnv; static bool g_EntGraph = false; @@ -636,12 +637,17 @@ static void InitScripting() new CScheduler; // Register the JavaScript interfaces with the runtime - JSI_Entity::init(); - JSI_BaseEntity::init(); + CEntity::ScriptingInit(); + + CBaseEntity::ScriptingInit(); + JSI_IGUIObject::init(); JSI_GUITypes::init(); JSI_Vector3D::init(); - JSI_Selection::init(); + // JSI_Selection::init(); + EntityCollection::Init( "EntityCollection" ); + CJSPropertyAccessor::ScriptingInit(); // <-- Doesn't really matter which we use, but we know CJSPropertyAccessor is already being compiled for T = CEntity. + JSI_Camera::init(); JSI_Console::init(); } @@ -740,12 +746,14 @@ static void Shutdown() delete &g_Mouseover; delete &g_Selection; - delete &g_ScriptingHost; delete &g_Pathfinder; // Managed by CWorld // delete &g_EntityManager; + delete &g_EntityTemplateCollection; + delete &g_ScriptingHost; + // destroy actor related stuff delete &g_UnitMan; delete &g_ObjMan; diff --git a/source/maths/Vector3D.h b/source/maths/Vector3D.h index 07c93ff62b..5ac681d9cd 100755 --- a/source/maths/Vector3D.h +++ b/source/maths/Vector3D.h @@ -15,6 +15,7 @@ #include #include "MathUtil.h" + class CVector3D { public: @@ -24,7 +25,7 @@ class CVector3D CVector3D () { X = 0.0f; Y = 0.0f; Z = 0.0f; } CVector3D (float x, float y, float z); - int operator ! () const ; + int operator!() const; float& operator[](int index) { return *((&X)+index); } const float& operator[](int index) const { return *((&X)+index); } @@ -59,8 +60,6 @@ class CVector3D //Returns length of the vector float GetLength () const; void Normalize (); - }; - -#endif +#endif \ No newline at end of file diff --git a/source/maths/scripting/JSInterface_Vector3D.cpp b/source/maths/scripting/JSInterface_Vector3D.cpp index 1397e8a6d9..38229d7423 100755 --- a/source/maths/scripting/JSInterface_Vector3D.cpp +++ b/source/maths/scripting/JSInterface_Vector3D.cpp @@ -48,17 +48,27 @@ JSI_Vector3D::Vector3D_Info::Vector3D_Info( const CVector3D& copy ) vector = new CVector3D( copy.X, copy.Y, copy.Z ); } -JSI_Vector3D::Vector3D_Info::Vector3D_Info( CVector3D* attach, IBoundPropertyOwner* _owner ) +JSI_Vector3D::Vector3D_Info::Vector3D_Info( CVector3D* attach, IPropertyOwner* _owner ) { owner = _owner; updateFn = NULL; + freshenFn = NULL; vector = attach; } -JSI_Vector3D::Vector3D_Info::Vector3D_Info( CVector3D* attach, IBoundPropertyOwner* _owner, void( IBoundPropertyOwner::*_updateFn )(void) ) +JSI_Vector3D::Vector3D_Info::Vector3D_Info( CVector3D* attach, IPropertyOwner* _owner, void( IPropertyOwner::*_updateFn )(void) ) { owner = _owner; updateFn = _updateFn; + freshenFn = NULL; + vector = attach; +} + +JSI_Vector3D::Vector3D_Info::Vector3D_Info( CVector3D* attach, IPropertyOwner* _owner, void( IPropertyOwner::*_updateFn )(void), void( IPropertyOwner::*_freshenFn )(void) ) +{ + owner = _owner; + updateFn = _updateFn; + freshenFn = _freshenFn; vector = attach; } @@ -78,8 +88,11 @@ JSBool JSI_Vector3D::getProperty( JSContext* cx, JSObject* obj, jsval id, jsval* JS_ReportError( cx, "[Vector3D] Invalid reference" ); return( JS_TRUE ); } + CVector3D* vectorData = vectorInfo->vector; + if( vectorInfo->owner && vectorInfo->freshenFn ) ( (vectorInfo->owner)->*(vectorInfo->freshenFn) )(); + switch( g_ScriptingHost.ValueToInt( id ) ) { case component_x: *vp = DOUBLE_TO_JSVAL( JS_NewDouble( cx, vectorData->X ) ); return( JS_TRUE ); @@ -103,6 +116,7 @@ JSBool JSI_Vector3D::setProperty( JSContext* cx, JSObject* obj, jsval id, jsval* } CVector3D* vectorData = vectorInfo->vector; + if( vectorInfo->owner && vectorInfo->freshenFn ) ( (vectorInfo->owner)->*(vectorInfo->freshenFn) )(); switch( g_ScriptingHost.ValueToInt( id ) ) { @@ -163,7 +177,11 @@ JSBool JSI_Vector3D::toString( JSContext* cx, JSObject* obj, uintN argc, jsval* { char buffer[256]; Vector3D_Info* vectorInfo = (Vector3D_Info*)JS_GetPrivate( cx, obj ); + if( !vectorInfo ) return( JS_TRUE ); + + if( vectorInfo->owner && vectorInfo->freshenFn ) ( (vectorInfo->owner)->*(vectorInfo->freshenFn) )(); + CVector3D* vectorData = vectorInfo->vector; snprintf( buffer, 256, "[object Vector3D: ( %f, %f, %f )]", vectorData->X, vectorData->Y, vectorData->Z ); buffer[255] = 0; diff --git a/source/maths/scripting/JSInterface_Vector3D.h b/source/maths/scripting/JSInterface_Vector3D.h index 5d7f747927..ca5b8a0add 100755 --- a/source/maths/scripting/JSInterface_Vector3D.h +++ b/source/maths/scripting/JSInterface_Vector3D.h @@ -13,7 +13,7 @@ #ifndef JSI_VECTOR3_INCLUDED #define JSI_VECTOR3_INCLUDED -class IBoundPropertyOwner; +class IPropertyOwner; namespace JSI_Vector3D { @@ -26,14 +26,16 @@ namespace JSI_Vector3D JSBool toString( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ); struct Vector3D_Info { - IBoundPropertyOwner* owner; - void ( IBoundPropertyOwner::*updateFn )(); + IPropertyOwner* owner; + void ( IPropertyOwner::*updateFn )(); + void ( IPropertyOwner::*freshenFn )(); CVector3D* vector; Vector3D_Info(); Vector3D_Info( float x, float y, float z ); Vector3D_Info( const CVector3D& copy ); - Vector3D_Info( CVector3D* attach, IBoundPropertyOwner* _owner ); - Vector3D_Info( CVector3D* attach, IBoundPropertyOwner* _owner, void (IBoundPropertyOwner::*_updateFn)() ); + Vector3D_Info( CVector3D* attach, IPropertyOwner* _owner ); + Vector3D_Info( CVector3D* attach, IPropertyOwner* _owner, void (IPropertyOwner::*_updateFn)() ); + Vector3D_Info( CVector3D* attach, IPropertyOwner* _owner, void (IPropertyOwner::*_updateFn)(), void (IPropertyOwner::*_freshenFn)() ); ~Vector3D_Info(); }; extern JSClass JSI_class; diff --git a/source/ps/AttributeMap.h b/source/ps/AttributeMap.h index 40e37f1801..4d59a5d362 100755 --- a/source/ps/AttributeMap.h +++ b/source/ps/AttributeMap.h @@ -3,7 +3,7 @@ #include "scripting/ScriptingHost.h" // Needed for STL_HASH_MAP detection -#include "EntityProperties.h" +//#include "scripting/JSProperty.h" namespace AttributeMap_JS { diff --git a/source/ps/Hotkey.cpp b/source/ps/Hotkey.cpp index 0b8637008b..fbbbbb940d 100755 --- a/source/ps/Hotkey.cpp +++ b/source/ps/Hotkey.cpp @@ -52,7 +52,7 @@ struct SHotkeyInfo // Will phase out the default shortcuts at sometime in the near future // (or, failing that, will update them so they can do the tricky stuff -// the config file can. +// the config file can.) static SHotkeyInfo hotkeyInfo[] = { diff --git a/source/ps/Interact.cpp b/source/ps/Interact.cpp index 410a7d11fa..a8b59fc81f 100755 --- a/source/ps/Interact.cpp +++ b/source/ps/Interact.cpp @@ -17,7 +17,7 @@ extern bool g_active; static const float SELECT_DBLCLICK_RATE = 0.5f; static const int ORDER_DELAY = 5; -void CSelectedEntities::addSelection( CEntity* entity ) +void CSelectedEntities::addSelection( HEntity entity ) { m_group = -1; assert( !isSelected( entity ) ); @@ -25,12 +25,12 @@ void CSelectedEntities::addSelection( CEntity* entity ) entity->m_selected = true; } -void CSelectedEntities::removeSelection( CEntity* entity ) +void CSelectedEntities::removeSelection( HEntity entity ) { m_group = -1; assert( isSelected( entity ) ); entity->m_selected = false; - std::vector::iterator it; + std::vector::iterator it; for( it = m_selected.begin(); it < m_selected.end(); it++ ) { if( (*it) == entity ) @@ -43,7 +43,7 @@ void CSelectedEntities::removeSelection( CEntity* entity ) void CSelectedEntities::renderSelectionOutlines() { - std::vector::iterator it; + std::vector::iterator it; for( it = m_selected.begin(); it < m_selected.end(); it++ ) (*it)->renderSelectionOutline(); @@ -52,7 +52,7 @@ void CSelectedEntities::renderSelectionOutlines() glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glEnable( GL_BLEND ); - std::vector::iterator it; + std::vector::iterator it; for( it = m_groups[m_group_highlight].begin(); it < m_groups[m_group_highlight].end(); it++ ) (*it)->renderSelectionOutline( 0.5f ); @@ -67,7 +67,7 @@ void CSelectedEntities::renderOverlays() glPushMatrix(); glEnable( GL_TEXTURE_2D ); - std::vector::iterator it; + std::vector::iterator it; for( it = m_selected.begin(); it < m_selected.end(); it++ ) { if( (*it)->m_grouped != -1 ) @@ -93,7 +93,7 @@ void CSelectedEntities::renderOverlays() glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glEnable( GL_BLEND ); - std::vector::iterator it; + std::vector::iterator it; for( it = m_groups[m_group_highlight].begin(); it < m_groups[m_group_highlight].end(); it++ ) { assert( (*it)->m_bounds ); @@ -132,7 +132,7 @@ void CSelectedEntities::renderOverlays() glPopMatrix(); } -void CSelectedEntities::setSelection( CEntity* entity ) +void CSelectedEntities::setSelection( HEntity entity ) { m_group = -1; clearSelection(); @@ -142,17 +142,17 @@ void CSelectedEntities::setSelection( CEntity* entity ) void CSelectedEntities::clearSelection() { m_group = -1; - std::vector::iterator it; + std::vector::iterator it; for( it = m_selected.begin(); it < m_selected.end(); it++ ) (*it)->m_selected = false; m_selected.clear(); } -void CSelectedEntities::removeAll( CEntity* entity ) +void CSelectedEntities::removeAll( HEntity entity ) { // Remove a reference to an entity from everywhere // (for use when said entity is being destroyed) - std::vector::iterator it; + std::vector::iterator it; for( it = m_selected.begin(); it < m_selected.end(); it++ ) { if( (*it) == entity ) @@ -161,7 +161,7 @@ void CSelectedEntities::removeAll( CEntity* entity ) break; } } - for( u8 group = 0; group < 10; group++ ) + for( u8 group = 0; group < MAX_GROUPS; group++ ) { for( it = m_groups[group].begin(); it < m_groups[group].end(); it++ ) { @@ -177,7 +177,7 @@ void CSelectedEntities::removeAll( CEntity* entity ) CVector3D CSelectedEntities::getSelectionPosition() { CVector3D avg; - std::vector::iterator it; + std::vector::iterator it; for( it = m_selected.begin(); it < m_selected.end(); it++ ) avg += (*it)->m_graphics_position; return( avg * ( 1.0f / m_selected.size() ) ); @@ -185,7 +185,7 @@ CVector3D CSelectedEntities::getSelectionPosition() void CSelectedEntities::saveGroup( i8 groupid ) { - std::vector::iterator it; + std::vector::iterator it; // Clear all entities in the group... for( it = m_groups[groupid].begin(); it < m_groups[groupid].end(); it++ ) (*it)->m_grouped = -1; @@ -197,8 +197,8 @@ void CSelectedEntities::saveGroup( i8 groupid ) { if( (*it)->m_grouped != -1 ) { - std::vector& group = m_groups[(*it)->m_grouped]; - std::vector::iterator it2; + std::vector& group = m_groups[(*it)->m_grouped]; + std::vector::iterator it2; for( it2 = group.begin(); it2 < group.end(); it2++ ) { if( (*it2) == &(**it) ) @@ -216,16 +216,16 @@ void CSelectedEntities::saveGroup( i8 groupid ) m_group = groupid; } -void CSelectedEntities::addToGroup( i8 groupid, CEntity* entity ) +void CSelectedEntities::addToGroup( i8 groupid, HEntity entity ) { - std::vector::iterator it; + std::vector::iterator it; // Remove selected entities from each group they're in, and flag them as // members of the new group if( entity->m_grouped != -1 ) { - std::vector& group = m_groups[(*it)->m_grouped]; - std::vector::iterator it2; + std::vector& group = m_groups[(*it)->m_grouped]; + std::vector::iterator it2; for( it2 = group.begin(); it2 < group.end(); it2++ ) { if( (*it2) == entity ) @@ -244,7 +244,7 @@ void CSelectedEntities::loadGroup( i8 groupid ) { clearSelection(); m_selected = m_groups[groupid]; - std::vector::iterator it; + std::vector::iterator it; for( it = m_selected.begin(); it < m_selected.end(); it++ ) (*it)->m_selected = true; m_group = groupid; @@ -252,7 +252,7 @@ void CSelectedEntities::loadGroup( i8 groupid ) void CSelectedEntities::addGroup( i8 groupid ) { - std::vector::iterator it; + std::vector::iterator it; for( it = m_groups[groupid].begin(); it < m_groups[groupid].end(); it++ ) { if( !isSelected( *it ) ) @@ -262,13 +262,13 @@ void CSelectedEntities::addGroup( i8 groupid ) (*it)->m_selected = true; } -void CSelectedEntities::changeGroup( CEntity* entity, i8 groupid ) +void CSelectedEntities::changeGroup( HEntity entity, i8 groupid ) { // Remove from current group i32 current = entity->m_grouped; if( current != -1 ) { - std::vector::iterator it; + std::vector::iterator it; for( it = m_groups[current].begin(); it < m_groups[current].end(); it++ ) { if( (*it) == entity ) @@ -283,9 +283,9 @@ void CSelectedEntities::changeGroup( CEntity* entity, i8 groupid ) entity->m_grouped = groupid; } -bool CSelectedEntities::isSelected( CEntity* entity ) +bool CSelectedEntities::isSelected( HEntity entity ) { - std::vector::iterator it; + std::vector::iterator it; for( it = m_selected.begin(); it < m_selected.end(); it++ ) { if( (*it) == entity ) @@ -321,7 +321,7 @@ int CSelectedEntities::getGroupCount( i8 groupid ) CVector3D CSelectedEntities::getGroupPosition( i8 groupid ) { CVector3D avg; - std::vector::iterator it; + std::vector::iterator it; for( it = m_groups[groupid].begin(); it < m_groups[groupid].end(); it++ ) avg += (*it)->m_graphics_position; return( avg * ( 1.0f / m_groups[groupid].size() ) ); @@ -394,7 +394,7 @@ bool CSelectedEntities::isContextValid( int contextOrder ) return( false ); // Check to see if any member of the selection supports this order type. - std::vector::iterator it; + std::vector::iterator it; for( it = m_selected.begin(); it < m_selected.end(); it++ ) if( (*it)->acceptsOrder( contextOrder, g_Mouseover.m_target ) ) return( true ); @@ -406,7 +406,7 @@ void CSelectedEntities::contextOrder( bool pushQueue ) CCamera *pCamera=g_Game->GetView()->GetCamera(); CTerrain *pTerrain=g_Game->GetWorld()->GetTerrain(); - std::vector::iterator it; + std::vector::iterator it; CEntityOrder context, contextRandomized; (int&)context.m_type = m_contextOrder; @@ -492,13 +492,16 @@ void CMouseoverEntities::update( float timestep ) pCamera->BuildCameraRay( origin, dir ); CUnit* hit = g_UnitMan.PickUnit( origin, dir ); - - m_target = NULL; m_worldposition = pCamera->GetWorldCoordinates(); - if( hit ) - m_target = hit->GetEntity(); + if( hit && hit->GetEntity() ) + { + m_target = hit->GetEntity()->me; + } + else + m_target = HEntity(); + if( m_viewall ) { // 'O' key. Show selection outlines for all player units on screen @@ -512,7 +515,7 @@ void CMouseoverEntities::update( float timestep ) std::vector::iterator it; for( it = onscreen->begin(); it < onscreen->end(); it++ ) - m_mouseover.push_back( SMouseoverFader( &(**it), m_fademaximum, false ) ); + m_mouseover.push_back( SMouseoverFader( *it, m_fademaximum, false ) ); delete( onscreen ); } @@ -573,7 +576,7 @@ void CMouseoverEntities::update( float timestep ) it2->isActive = true; } if( !found ) - m_mouseover.push_back( SMouseoverFader( &(**it), ( m_fadeinrate + m_fadeoutrate ) * timestep ) ); + m_mouseover.push_back( SMouseoverFader( *it, ( m_fadeinrate + m_fadeoutrate ) * timestep ) ); } } delete( onscreen ); @@ -615,7 +618,7 @@ void CMouseoverEntities::update( float timestep ) it++; continue; } } - if( !found && m_target ) + if( !found && (bool)m_target ) { float initial = m_fadeinrate * timestep; if( initial > m_fademaximum ) initial = m_fademaximum; @@ -653,7 +656,7 @@ void CMouseoverEntities::expandAcrossScreen() std::vector::iterator it; for( it = activeset->begin(); it < activeset->end(); it++ ) { - m_mouseover.push_back( SMouseoverFader( &(**it) ) ); + m_mouseover.push_back( SMouseoverFader( *it ) ); } delete( activeset ); } @@ -665,7 +668,7 @@ void CMouseoverEntities::expandAcrossWorld() std::vector::iterator it; for( it = activeset->begin(); it < activeset->end(); it++ ) { - m_mouseover.push_back( SMouseoverFader( &(**it) ) ); + m_mouseover.push_back( SMouseoverFader( *it ) ); } delete( activeset ); } @@ -749,7 +752,7 @@ int interactInputHandler( const SDL_Event* ev ) CTerrain *pTerrain=g_Game->GetWorld()->GetTerrain(); static float lastclicktime = 0.0f; - static CEntity* lastclickobject = NULL; + static HEntity lastclickobject; static u8 clicks = 0; static u16 button_down_x, button_down_y; diff --git a/source/ps/Interact.h b/source/ps/Interact.h index 2dbbfd20a8..7cb559fe28 100755 --- a/source/ps/Interact.h +++ b/source/ps/Interact.h @@ -23,24 +23,24 @@ struct CSelectedEntities : public Singleton { CSelectedEntities() { clearSelection(); m_group = -1; m_group_highlight = -1; m_contextOrder = -1; } - std::vector m_selected; - std::vector m_groups[MAX_GROUPS]; + std::vector m_selected; + std::vector m_groups[MAX_GROUPS]; i8 m_group, m_group_highlight; int m_contextOrder; - void addSelection( CEntity* entity ); - void removeSelection( CEntity* entity ); - void setSelection( CEntity* entity ); + void addSelection( HEntity entity ); + void removeSelection( HEntity entity ); + void setSelection( HEntity entity ); void clearSelection(); - void removeAll( CEntity* entity ); - bool isSelected( CEntity* entity ); + void removeAll( HEntity entity ); + bool isSelected( HEntity entity ); CVector3D getSelectionPosition(); - void addToGroup( i8 groupid, CEntity* entity ); + void addToGroup( i8 groupid, HEntity entity ); void saveGroup( i8 groupid ); void loadGroup( i8 groupid ); void addGroup( i8 groupid ); - void changeGroup( CEntity* entity, i8 groupid ); + void changeGroup( HEntity entity, i8 groupid ); void highlightGroup( i8 groupid ); void highlightNone(); int getGroupCount( i8 groupid ); @@ -62,10 +62,10 @@ struct CSelectedEntities : public Singleton struct SMouseoverFader { - CEntity* entity; + HEntity entity; float fade; bool isActive; - SMouseoverFader( CEntity* _entity, float _fade = 0.0f, bool _active = true ) : entity( _entity ), fade( _fade ), isActive( _active ) {} + SMouseoverFader( HEntity _entity, float _fade = 0.0f, bool _active = true ) : entity( _entity ), fade( _fade ), isActive( _active ) {} }; struct CMouseoverEntities : public Singleton @@ -74,7 +74,7 @@ struct CMouseoverEntities : public Singleton float m_fadeoutrate; float m_fademaximum; CVector2D m_worldposition; - CEntity* m_target; + HEntity m_target; bool m_bandbox, m_viewall; u16 m_x1, m_y1, m_x2, m_y2; @@ -87,7 +87,7 @@ struct CMouseoverEntities : public Singleton m_fadeoutrate = 2.0f; m_fademaximum = 0.5f; m_mouseover.clear(); - m_target = NULL; + m_target; } std::vector m_mouseover; void update( float timestep ); diff --git a/source/ps/scripting/JSInterface_Selection.cpp b/source/ps/scripting/JSInterface_Selection.cpp index 1549aafff7..17d0b06d75 100755 --- a/source/ps/scripting/JSInterface_Selection.cpp +++ b/source/ps/scripting/JSInterface_Selection.cpp @@ -4,177 +4,13 @@ #include "precompiled.h" #include "JSInterface_Selection.h" +#include "scripting/JSCollection.h" #include "Interact.h" -JSClass JSI_Selection::JSI_class = -{ - "EntityCollection", JSCLASS_HAS_PRIVATE, - JSI_Selection::addProperty, JSI_Selection::removeProperty, - JSI_Selection::getProperty, JSI_Selection::setProperty, - JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, JSI_Selection::finalize, - NULL, NULL, NULL, NULL -}; - -JSPropertySpec JSI_Selection::JSI_props[] = -{ - { 0 } -}; - -JSFunctionSpec JSI_Selection::JSI_methods[] = -{ - { "toString", JSI_Selection::toString, 0, 0, 0 }, - { 0 }, -}; - -JSBool JSI_Selection::addProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) -{ - if( !JSVAL_IS_INT( id ) ) - return( JS_TRUE ); - - int i = JSVAL_TO_INT( id ); - std::vector* set = (std::vector*)JS_GetPrivate( cx, obj ); - - // Invalid index and/or value is not an object. - - if( !set || !JSVAL_IS_OBJECT( *vp ) || ( i < 0 ) ) - return( JS_TRUE ); - - HEntity* e = (HEntity*)JS_GetInstancePrivate( cx, JSVAL_TO_OBJECT( *vp ), &JSI_Entity::JSI_class, NULL ); - - // Value is not an entity. - if( !e ) - return( JS_TRUE ); - - if( i >= (int)set->capacity() ) set->resize( i + 1 ); - - // Add to set. - set->insert( set->begin() + id, *e ); - return( JS_TRUE ); -} - -JSBool JSI_Selection::removeProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) -{ - if( !JSVAL_IS_INT( id ) ) - { - *vp = JSVAL_TRUE; - return( JS_TRUE ); - } - - int i = JSVAL_TO_INT( id ); - std::vector* set = (std::vector*)JS_GetPrivate( cx, obj ); - - // Invalid index and/or value is not an object. - - if( !set || !JSVAL_IS_OBJECT( *vp ) || ( i < 0 ) || ( i >= (int)set->size() ) ) - { - *vp = JSVAL_TRUE; - return( JS_TRUE ); - } - - // Remove from set. - set->erase( set->begin() + id ); - - *vp = JSVAL_TRUE; - return( JS_TRUE ); -} - -JSBool JSI_Selection::getProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) -{ - if( !JSVAL_IS_INT( id ) ) - return( JS_TRUE ); - - int i = JSVAL_TO_INT( id ); - std::vector* set = (std::vector*)JS_GetPrivate( cx, obj ); - - // Invalid index and/or value is not an object. - - if( !set || ( i < 0 ) || ( i >= (int)set->size() ) ) - { - *vp = JSVAL_NULL; - return( JS_TRUE ); - } - - // Retrieve from set. - JSObject* e = JS_NewObject( cx, &JSI_Entity::JSI_class, NULL, NULL ); - JS_SetPrivate( cx, e, new HEntity( set->at( i ) ) ); - - *vp = OBJECT_TO_JSVAL( e ); - return( JS_TRUE ); -} - -JSBool JSI_Selection::setProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) -{ - if( !JSVAL_IS_INT( id ) ) - return( JS_TRUE ); - - int i = JSVAL_TO_INT( id ); - std::vector* set = (std::vector*)JS_GetPrivate( cx, obj ); - - // Invalid index and/or value is not an object. - - if( !set || !JSVAL_IS_OBJECT( *vp ) || ( i < 0 ) || ( i >= (int)set->size() ) ) - return( JS_TRUE ); - - HEntity* e = (HEntity*)JS_GetInstancePrivate( cx, JSVAL_TO_OBJECT( *vp ), &JSI_Entity::JSI_class, NULL ); - - // Value is not an entity. - if( !e ) - { - JSObject* c = JS_NewObject( cx, &JSI_Entity::JSI_class, NULL, NULL ); - JS_SetPrivate( cx, c, new HEntity( set->at( i ) ) ); - - *vp = OBJECT_TO_JSVAL( e ); - return( JS_TRUE ); - } - - // Write into set. - set->at( id ) = *e; - - return( JS_TRUE ); -} - -void JSI_Selection::finalize( JSContext* cx, JSObject* obj ) -{ - std::vector* set = (std::vector*)JS_GetPrivate( cx, obj ); - if( set ) - delete( set ); -} - -void JSI_Selection::init() -{ - g_ScriptingHost.DefineCustomObjectType( &JSI_class, NULL, 0, JSI_props, JSI_methods, NULL, NULL ); -} - -JSBool JSI_Selection::toString( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ) -{ - std::vector* set = (std::vector*)JS_GetPrivate( cx, obj ); - - wchar_t buffer[256]; - int len=swprintf( buffer, 256, L"[object EntityCollection: %d entities]", set->size() ); - buffer[255] = 0; - if (len < 0 || len > 255) len=255; - utf16string u16str(buffer, buffer+len); - *rval = STRING_TO_JSVAL( JS_NewUCStringCopyZ( cx, u16str.c_str() ) ); - return( JS_TRUE ); -} - JSBool JSI_Selection::getSelection( JSContext* context, JSObject* globalObject, jsval id, jsval* vp ) { - std::vector* set = new std::vector; - std::vector::iterator it; + *vp = OBJECT_TO_JSVAL( EntityCollection::CreateReference( &( g_Selection.m_selected ) ) ); - for( it = g_Selection.m_selected.begin(); it != g_Selection.m_selected.end(); it++ ) - set->push_back( (*it)->me ); - - JSObject* selectionArray = JS_NewObject( context, &JSI_Selection::JSI_class, NULL, NULL ); - JS_SetPrivate( context, selectionArray, set ); - - JS_DefineFunction( context, selectionArray, "isOrderTypeValid", JSI_Selection::isValidContextOrder, 1, 0 ); - JS_DefineProperty( context, selectionArray, "orderType", INT_TO_JSVAL( g_Selection.m_contextOrder ), - JSI_Selection::getContextOrder, JSI_Selection::setContextOrder, 0 ); - - *vp = OBJECT_TO_JSVAL( selectionArray ); return( JS_TRUE ); } @@ -182,17 +18,17 @@ JSBool JSI_Selection::setSelection( JSContext* context, JSObject* globalObject, { if( !JSVAL_IS_OBJECT( *vp ) ) { - JS_ReportError( context, "Not a valid EntityCollection" ); + JS_ReportError( context, "Not a valid Collection" ); *vp = JSVAL_NULL; return( JS_TRUE ); } + JSObject* selectionArray = JSVAL_TO_OBJECT( *vp ); + EntityCollection::CJSCollectionData* Info = (EntityCollection::CJSCollectionData*)JS_GetInstancePrivate( context, JSVAL_TO_OBJECT( *vp ), &EntityCollection::JSI_class, NULL ); - std::vector* set = (std::vector*)JS_GetInstancePrivate( context, JSVAL_TO_OBJECT( *vp ), &JSI_Selection::JSI_class, NULL ); - - if( !set ) + if( !Info ) { - JS_ReportError( context, "Not a valid EntityCollection" ); + JS_ReportError( context, "Not a valid Collection" ); *vp = JSVAL_NULL; return( JS_TRUE ); } @@ -200,20 +36,8 @@ JSBool JSI_Selection::setSelection( JSContext* context, JSObject* globalObject, g_Selection.clearSelection(); std::vector::iterator it; - for( it = set->begin(); it < set->end(); it++ ) - g_Selection.addSelection( &(**it) ); -/* old-style load from array, here for reference - jsval entry; - JS_GetElement( context, selectionArray, i, &entry ); - JSObject* selection = JSVAL_TO_OBJECT( entry ); - HEntity* entity = NULL; - if( JSVAL_IS_OBJECT( entry ) && ( entity = (HEntity*)JS_GetInstancePrivate( context, JSVAL_TO_OBJECT( entry ), &JSI_Entity::JSI_class, NULL ) ) ) - { - g_Selection.addSelection( &(**entity) ); - } - else - JS_ReportError( context, "[Entity] Invalid reference" ); -*/ + for( it = Info->m_Data->begin(); it < Info->m_Data->end(); it++ ) + g_Selection.addSelection( *it ); return( JS_TRUE ); } @@ -224,17 +48,7 @@ JSBool JSI_Selection::getGroups( JSContext* context, JSObject* obj, jsval id, js for( i8 groupId = 0; groupId < MAX_GROUPS; groupId++ ) { - JSObject* group = JS_NewObject( context, &JSI_Selection::JSI_class, NULL, NULL ); - - std::vector* set = new std::vector; - std::vector::iterator it; - - for( it = g_Selection.m_groups[groupId].begin(); it < g_Selection.m_groups[groupId].end(); it++ ) - set->push_back( (*it)->me ); - - JS_SetPrivate( context, group, set ); - - jsval v = OBJECT_TO_JSVAL( group ); + jsval v = OBJECT_TO_JSVAL( EntityCollection::CreateReference( &( g_Selection.m_groups[groupId] ) ) ); JS_SetElement( context, groupsArray, groupId, &v ); } @@ -257,19 +71,21 @@ JSBool JSI_Selection::setGroups( JSContext* context, JSObject* obj, jsval id, js if( JS_GetElement( context, groupsArray, groupId, &v ) && JSVAL_IS_OBJECT( v ) ) { JSObject* group = JSVAL_TO_OBJECT( v ); - std::vector* set = (std::vector*)JS_GetInstancePrivate( context, group, &JSI_Selection::JSI_class, NULL ); - if( set ) + EntityCollection::CJSCollectionData* Info = (EntityCollection::CJSCollectionData*)JS_GetInstancePrivate( context, group, &EntityCollection::JSI_class, NULL ); + if( Info ) { g_Selection.m_groups[groupId].clear(); std::vector::iterator it; - for( it = set->begin(); it < set->end(); it++ ) - g_Selection.addToGroup( groupId, &(**it) ); + for( it = Info->m_Data->begin(); it < Info->m_Data->end(); it++ ) + g_Selection.addToGroup( groupId, *it ); + } } } + return( JS_TRUE ); - } +} JSBool JSI_Selection::isValidContextOrder( JSContext* context, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval ) { diff --git a/source/ps/scripting/JSInterface_Selection.h b/source/ps/scripting/JSInterface_Selection.h index c0826a581d..a13c4ab604 100755 --- a/source/ps/scripting/JSInterface_Selection.h +++ b/source/ps/scripting/JSInterface_Selection.h @@ -10,15 +10,6 @@ namespace JSI_Selection { - JSBool toString( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ); - extern JSClass JSI_class; - extern JSPropertySpec JSI_props[]; - extern JSFunctionSpec JSI_methods[]; - JSBool addProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ); - JSBool removeProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ); - JSBool getProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ); - JSBool setProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ); - void init(); void finalize( JSContext* cx, JSObject* obj ); diff --git a/source/scripting/JSConversions.cpp b/source/scripting/JSConversions.cpp new file mode 100755 index 0000000000..095f2d9ff8 --- /dev/null +++ b/source/scripting/JSConversions.cpp @@ -0,0 +1,216 @@ +#include "precompiled.h" +#include "JSConversions.h" +#include "Entity.h" +#include "ObjectManager.h" +#include "scripting/JSInterface_Vector3D.h" + +// HEntity + +template<> HEntity* ToNative( JSContext* cx, JSObject* obj ) +{ + CEntity* e = ToNative( cx, obj ); + return( &( e->me ) ); +} + +template<> JSObject* ToScript( HEntity* Native ) +{ + return( ToScript( &( **Native ) ) ); +} + +// CBaseEntity* + +template<> bool ToPrimitive( JSContext* cx, jsval v, CBaseEntity*& Storage ) +{ + if( !JSVAL_IS_OBJECT( v ) ) return( false ); + CBaseEntity* Data = (CBaseEntity*)JS_GetInstancePrivate( cx, JSVAL_TO_OBJECT( v ), &CBaseEntity::JSI_class, NULL ); + if( !Data ) return( false ); + Storage = Data; + return( true ); +} + +template<> JSObject* ToScript( CBaseEntity** Native ) +{ + return( ToScript( *Native ) ); +} + +// CObjectEntry* + +template<> bool ToPrimitive( JSContext* cx, jsval v, CObjectEntry*& Storage ) +{ + CStrW ActorName; + if( !ToPrimitive( cx, v, ActorName ) ) + return( false ); + Storage = g_ObjMan.FindObject( (CStr)ActorName ); + return( true ); +} + +template<> jsval ToJSVal( CObjectEntry*& Native ) +{ + if( !Native ) + return( ToJSVal( CStrW( L"[No actor]" ) ) ); + return( ToJSVal( CStrW( Native->m_Name ) ) ); +} + +// CVector3D + +template<> CVector3D* ToNative( JSContext* cx, JSObject* obj ) +{ + JSI_Vector3D::Vector3D_Info* v = (JSI_Vector3D::Vector3D_Info*)JS_GetPrivate( cx, obj ); + return( v->vector ); +} + +template<> JSObject* ToScript( CVector3D* Native ) +{ + JSObject* Script = JS_NewObject( g_ScriptingHost.GetContext(), &JSI_Vector3D::JSI_class, NULL, NULL ); + JS_SetPrivate( g_ScriptingHost.GetContext(), Script, new JSI_Vector3D::Vector3D_Info( *Native ) ); + return( Script ); +} + + +// CScriptObject + +template<> jsval ToJSVal( CScriptObject& Native ) +{ + if( Native.Type == CScriptObject::FUNCTION ) + return( OBJECT_TO_JSVAL( JS_GetFunctionObject( Native.Function ) ) ); + return( JSVAL_NULL ); +} + +template<> bool ToPrimitive( JSContext* cx, jsval v, CScriptObject& Storage ) +{ + Storage.SetJSVal( v ); + return( true ); +} + +// i32 + +template<> jsval ToJSVal( const i32& Native ) +{ + return( INT_TO_JSVAL( Native ) ); +} + +template<> jsval ToJSVal( i32& Native ) +{ + return( INT_TO_JSVAL( Native ) ); +} + +template<> bool ToPrimitive( JSContext* cx, jsval v, i32& Storage ) +{ + try + { + Storage = g_ScriptingHost.ValueToInt( v ); + } + catch( PSERROR_Scripting_ConversionFailed ) + { + return( false ); + } + return( true ); +} + +// double + +template<> jsval ToJSVal( const double& Native ) +{ + return( DOUBLE_TO_JSVAL( JS_NewDouble( g_ScriptingHost.getContext(), Native ) ) ); +} + +template<> jsval ToJSVal( double& Native ) +{ + return( DOUBLE_TO_JSVAL( JS_NewDouble( g_ScriptingHost.getContext(), Native ) ) ); +} + +template<> bool ToPrimitive( JSContext* cx, jsval v, double& Storage ) +{ + try + { + Storage = g_ScriptingHost.ValueToDouble( v ); + } + catch( PSERROR_Scripting_ConversionFailed ) + { + return( false ); + } + return( true ); +} + +// float + +template<> jsval ToJSVal( const float& Native ) +{ + return( DOUBLE_TO_JSVAL( JS_NewDouble( g_ScriptingHost.getContext(), Native ) ) ); +} + +template<> jsval ToJSVal( float& Native ) +{ + return( DOUBLE_TO_JSVAL( JS_NewDouble( g_ScriptingHost.getContext(), Native ) ) ); +} + +template<> bool ToPrimitive( JSContext* cx, jsval v, float& Storage ) +{ + try + { + Storage = (float)g_ScriptingHost.ValueToDouble( v ); + } + catch( PSERROR_Scripting_ConversionFailed ) + { + return( false ); + } + return( true ); +} + + +// bool + +template<> jsval ToJSVal( const bool& Native ) +{ + return( BOOLEAN_TO_JSVAL( Native ) ); +} + +template<> jsval ToJSVal( bool& Native ) +{ + return( BOOLEAN_TO_JSVAL( Native ) ); +} + +template<> bool ToPrimitive( JSContext* cx, jsval v, bool& Storage ) +{ + try + { + Storage = g_ScriptingHost.ValueToBool( v ); + } + catch( PSERROR_Scripting_ConversionFailed ) + { + return( false ); + } + return( true ); +} + +// CStrW +template<> bool ToPrimitive( JSContext* cx, jsval v, CStrW& Storage ) +{ + try + { + Storage = g_ScriptingHost.ValueToUCString( v ); + } + catch( PSERROR_Scripting_ConversionFailed ) + { + return( false ); + } + return( true ); +} + +template<> jsval ToJSVal( const CStrW& Native ) +{ + return( STRING_TO_JSVAL( JS_NewUCStringCopyZ( g_ScriptingHost.GetContext(), Native.c_str() ) ) ); +} + +template<> jsval ToJSVal( CStrW& Native ) +{ + return( STRING_TO_JSVAL( JS_NewUCStringCopyZ( g_ScriptingHost.GetContext(), Native.c_str() ) ) ); +} + +// jsval + +template<> jsval ToJSVal( const jsval& Native ) +{ + return( Native ); +} + diff --git a/source/scripting/JSConversions.h b/source/scripting/JSConversions.h new file mode 100755 index 0000000000..653db16231 --- /dev/null +++ b/source/scripting/JSConversions.h @@ -0,0 +1,116 @@ +// A general system of converting between native objects and their JavaScript representations +// Mark Thompson (mark@wildfiregames.com / mot20@cam.ac.uk) + +#ifndef JSCONVERSIONS_INCLUDED +#define JSCONVERSIONS_INCLUDED + +#include "scripting/ScriptingHost.h" + +class CEntity; +class HEntity; +class CBaseEntity; +class CStrW; +class CScriptObject; +class CObjectEntry; +class CVector3D; + +// ----- +// +// Defaults +// +// ----- + +template T* ToNative( JSContext* cx, JSObject* obj ) +{ + return( (T*)JS_GetInstancePrivate( cx, obj, &T::JSI_class, NULL ) ); +} + +template JSObject* ToScript( T* Native ) +{ + return( Native->GetScript() ); +} + +template T* ToNative( jsval v ) +{ + if( !JSVAL_IS_OBJECT( v ) ) return( NULL ); + return( ToNative( g_ScriptingHost.GetContext(), JSVAL_TO_OBJECT( v ) ) ); +} + +template bool ToPrimitive( JSContext* cx, jsval v, T& Storage ) +{ + T* Native = ToNative( v ); + if( !Native ) return( false ); + Storage = *Native; + return( true ); +} + +template inline T ToPrimitive( JSContext* cx, jsval v ) { T Temp; ToPrimitive( cx, v, Temp ); return( Temp ); } +template inline T ToPrimitive( jsval v ) { return( ToPrimitive( g_ScriptingHost.GetContext(), v ) ); } + +template jsval ToJSVal( T& Native ) +{ + return( OBJECT_TO_JSVAL( ToScript( &Native ) ) ); +} + +template jsval ToJSVal( const T& Native ); + +// ----- +// +// Overrides +// +// ----- + +// CVector3D +template<> CVector3D* ToNative( JSContext* cx, JSObject* obj ); +template<> JSObject* ToScript( CVector3D* Native ); + +// CBaseEntity +template<> bool ToPrimitive( JSContext* cx, jsval v, CBaseEntity*& Storage ); +template<> JSObject* ToScript( CBaseEntity** Native ); + +// CObjectEntry +template<> bool ToPrimitive( JSContext* cx, jsval v, CObjectEntry*& Storage ); +template<> jsval ToJSVal( CObjectEntry*& Native ); + +// HEntity +template<> HEntity* ToNative( JSContext* cx, JSObject* obj ); +template<> JSObject* ToScript( HEntity* Native ); + +// CScriptObject +template<> bool ToPrimitive( JSContext* cx, jsval v, CScriptObject& Storage ); +template<> jsval ToJSVal( CScriptObject& Native ); + +// i32 +template<> bool ToPrimitive( JSContext* cx, jsval v, i32& Storage ); +template<> jsval ToJSVal( const i32& Native ); +template<> jsval ToJSVal( i32& Native ); + +// double +template<> bool ToPrimitive( JSContext* cx, jsval v, double& Storage ); +template<> jsval ToJSVal( const double& Native ); +template<> jsval ToJSVal( double& Native ); + +// float +template<> bool ToPrimitive( JSContext* cx, jsval v, float& Storage ); +template<> jsval ToJSVal( const float& Native ); +template<> jsval ToJSVal( float& Native ); + +// bool +template<> bool ToPrimitive( JSContext* cx, jsval v, bool& Storage ); +template<> jsval ToJSVal( const bool& Native ); +template<> jsval ToJSVal( bool& Native ); + +// CStrW +template<> bool ToPrimitive( JSContext* cx, jsval v, CStrW& Storage ); +template<> jsval ToJSVal( const CStrW& Native ); +template<> jsval ToJSVal( CStrW& Native ); + +// jsval + +template<> jsval ToJSVal( const jsval& Native ); + + + + + +#endif \ No newline at end of file diff --git a/source/scripting/ScriptGlue.cpp b/source/scripting/ScriptGlue.cpp index 2ca7469435..77ad18710c 100755 --- a/source/scripting/ScriptGlue.cpp +++ b/source/scripting/ScriptGlue.cpp @@ -9,6 +9,7 @@ #include "EntityManager.h" #include "BaseEntityCollection.h" #include "Scheduler.h" +#include "timer.h" #include "Game.h" #include "Network/Server.h" @@ -17,6 +18,7 @@ #include "ps/i18n.h" #include "scripting/JSInterface_Entity.h" +#include "scripting/JSCollection.h" #include "scripting/JSInterface_BaseEntity.h" #include "scripting/JSInterface_Vector3D.h" #include "gui/scripting/JSInterface_IGUIObject.h" @@ -58,6 +60,7 @@ JSFunctionSpec ScriptFunctionTable[] = {"exit", exitProgram, 0, 0, 0 }, {"crash", crash, 0, 0, 0 }, + {"forceGC", forceGC, 0, 0, 0 }, {"_mem", js_mem, 0, 0, 0 }, // Intentionally undocumented {0, 0, 0, 0, 0}, }; @@ -76,6 +79,7 @@ JSPropertySpec ScriptGlobalTable[] = { "groups", GLOBAL_GROUPSARRAY, JSPROP_PERMANENT, JSI_Selection::getGroups, JSI_Selection::setGroups }, { "camera", GLOBAL_CAMERA, JSPROP_PERMANENT, JSI_Camera::getCamera, JSI_Camera::setCamera }, { "console", GLOBAL_CONSOLE, JSPROP_PERMANENT | JSPROP_READONLY, JSI_Console::getConsole, NULL }, + { "entities", 0, JSPROP_PERMANENT | JSPROP_READONLY, GetEntitySet, NULL }, { 0, 0, 0, 0, 0 }, }; @@ -128,8 +132,8 @@ JSBool getEntityByHandle( JSContext* context, JSObject* UNUSEDPARAM(globalObject *rval = JSVAL_NULL; return( JS_TRUE ); } - JSObject* entity = JS_NewObject( context, &JSI_Entity::JSI_class, NULL, NULL ); - JS_SetPrivate( context, entity, v ); + JSObject* entity = (*v)->GetScript(); + *rval = OBJECT_TO_JSVAL( entity ); return( JS_TRUE ); } @@ -152,12 +156,22 @@ JSBool getEntityTemplate( JSContext* context, JSObject* UNUSEDPARAM(globalObject if( !v ) { *rval = JSVAL_NULL; - JS_ReportError( context, "No such template: %ls", (const wchar_t*)templateName ); + JS_ReportError( context, "No such template: %s", CStr8(templateName).c_str() ); return( JS_TRUE ); } - JSObject* baseEntity = JS_NewObject( context, &JSI_BaseEntity::JSI_class, NULL, NULL ); - JS_SetPrivate( context, baseEntity, v ); - *rval = OBJECT_TO_JSVAL( baseEntity ); + + *rval = OBJECT_TO_JSVAL( v->GetScript() ); + + return( JS_TRUE ); +} + +JSBool GetEntitySet( JSContext* context, JSObject* globalObject, jsval argv, jsval* vp ) +{ + std::vector* extant = g_EntityManager.getExtant(); + + *vp = OBJECT_TO_JSVAL( EntityCollection::Create( *extant ) ); + delete( extant ); + return( JS_TRUE ); } @@ -245,6 +259,17 @@ JSBool cancelInterval( JSContext* UNUSEDPARAM(context), JSObject* UNUSEDPARAM(gl return( JS_TRUE ); } + +JSBool forceGC( JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval ) +{ + double time = get_time(); + JS_GC( cx ); + time = get_time() - time; + g_Console->InsertMessage( L"Garbage collection completed in: %f", time ); + *rval = JSVAL_TRUE; + return( JS_TRUE ); +} + JSBool getGUIGlobal( JSContext* UNUSEDPARAM(context), JSObject* UNUSEDPARAM(globalObject), unsigned int UNUSEDPARAM(argc), jsval* UNUSEDPARAM(argv), jsval* rval ) { *rval = OBJECT_TO_JSVAL( g_GUI.GetScriptObject() ); diff --git a/source/scripting/ScriptGlue.h b/source/scripting/ScriptGlue.h index 70d3e755c8..014cfec44a 100755 --- a/source/scripting/ScriptGlue.h +++ b/source/scripting/ScriptGlue.h @@ -10,12 +10,19 @@ typedef JSBool JSFunc(JSContext* /*context*/, JSObject* /*globalObject*/, unsign JSFunc WriteLog; +// Entity JSFunc getEntityByHandle; JSFunc getEntityTemplate; +JSBool GetEntitySet( JSContext* context, JSObject* globalObject, jsval argv, jsval* vp ); + +// Timer JSFunc setTimeout; JSFunc setInterval; JSFunc cancelInterval; +// Debug +JSBool forceGC( JSContext* context, JSObject* globalObject, unsigned int argc, jsval* argv, jsval* rval ); + // Returns the sort-of-global object associated with the current GUI JSFunc getGUIGlobal; diff --git a/source/scripting/ScriptableObject.cpp b/source/scripting/ScriptableObject.cpp new file mode 100755 index 0000000000..e69de29bb2 diff --git a/source/scripting/ScriptableObject.h b/source/scripting/ScriptableObject.h new file mode 100755 index 0000000000..1203582c4f --- /dev/null +++ b/source/scripting/ScriptableObject.h @@ -0,0 +1,565 @@ +// ScriptableObject.h +// +// A quick way to add (mostly) sensibly-behaving JavaScript interfaces to native classes. +// +// Mark Thompson (mark@wildfiregames.com / mot20@cam.ac.uk) +// +// I really, really hope this is the last time I touch this code. + +#include "scripting/ScriptingHost.h" +#include "JSConversions.h" + +// The Last Redesign + +#ifndef SCRIPTABLE_INCLUDED +#define SCRIPTABLE_INCLUDED + +class IJSProperty +{ +public: + + bool m_AllowsInheritance; + bool m_Inherited; + bool m_Intrinsic; + + virtual jsval Get( JSContext* cx ) = 0; + virtual void Set( JSContext* cx, 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( IJSProperty* Copy ) = 0; + + jsval Get() { return( Get( g_ScriptingHost.GetContext() ) ); } + void Set( jsval Value ) { return( Set( g_ScriptingHost.GetContext(), Value ) ); } + + virtual ~IJSProperty() {} +}; + +class IJSObject +{ +public: + typedef STL_HASH_MAP PropertyTable; + typedef std::vector InheritorsList; + + // Used for freshen/update + typedef void (IJSObject::*NotifyFn)(); + + // 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 ); + virtual void Rebuild() = 0; + + // Check for a property + virtual IJSProperty* HasProperty( CStrW PropertyName ) = 0; + + // Add a property (inherits value from parent) + virtual void ReplicateProperty( CStrW PropertyName, jsval Value ) = 0; + + // Add a property (with immediate value) + virtual void AddProperty( CStrW PropertyName, jsval Value ) = 0; + virtual void AddProperty( CStrW PropertyName, CStrW Value ) = 0; +}; + +template class CJSObject; + +template class CJSPropertyAccessor +{ + T* m_Owner; + CStrW m_PropertyRoot; + 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 ); + + *rval = Instance->m_Owner->m_Properties[Instance->m_PropertyRoot]->Get( cx ); + + 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 ); + + JSString* str = JS_ValueToString( cx, Instance->m_Owner->m_Properties[Instance->m_PropertyRoot]->Get( cx ) ); + *rval = STRING_TO_JSVAL( str ); + + return( JS_TRUE ); + } + /* + static void JSFinalize( JSContext* cx, JSObject* obj ) + { + CJSPropertyAccessor* Instance = (CJSPropertyAccessor*)JS_GetPrivate( cx, obj ); + if( !Instance ) + return; + + if( Instance ) delete( Instance ); + } + */ + 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 JSClass CJSPropertyAccessor::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 class CJSProperty : public IJSProperty +{ + T* m_Data; + + IJSObject* m_Owner; + + // 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, IJSObject* Owner = NULL, bool AllowsInheritance = true, IJSObject::NotifyFn Update = NULL, IJSObject::NotifyFn Freshen = NULL ) + { + assert( !( !m_Owner && ( Freshen || Update ) ) ); // Bad programmer. + m_Data = Data; + m_Owner = Owner; + m_Update = Update; + m_Freshen = Freshen; + m_AllowsInheritance = AllowsInheritance; + m_Intrinsic = true; + } + jsval Get( JSContext* cx ) + { + if( m_Freshen ) (m_Owner->*m_Freshen)(); + return( ToJSVal( *m_Data ) ); + } + void ImmediateCopy( IJSProperty* Copy ) + { + *m_Data = *( ((CJSProperty*)Copy)->m_Data ); + } + void Set( JSContext* cx, jsval Value ) + { + if( m_Freshen ) (m_Owner->*m_Freshen)(); + if( ToPrimitive( cx, Value, *m_Data ) ) + if( m_Update ) (m_Owner->*m_Update)(); + } +}; + +class CJSValProperty : public IJSProperty +{ + friend class CJSObject; + + jsval m_Data; + JSObject* m_JSAccessor; + +public: + CJSValProperty( jsval Data, bool Inherited ) + { + m_Inherited = Inherited; + m_Data = Data; + m_Intrinsic = false; + m_JSAccessor = NULL; + Root(); + } + ~CJSValProperty() + { + Uproot(); + } + void Root() + { + if( JSVAL_IS_GCTHING( m_Data ) ) + JS_AddRoot( g_ScriptingHost.GetContext(), &m_Data ); + } + void Uproot() + { + if( JSVAL_IS_GCTHING( m_Data ) ) + JS_RemoveRoot( g_ScriptingHost.GetContext(), &m_Data ); + } + jsval Get( JSContext* cx ) + { + return( m_Data ); + } + void Set( JSContext* cx, jsval Value ) + { + Uproot(); + m_Data = Value; + Root(); + } + void ImmediateCopy( IJSProperty* Copy ) + { + Uproot(); + m_Data = ((CJSValProperty*)Copy)->m_Data; + Root(); + } +}; + +// Wrapper around native functions that are attached to CJSObjects + +template class CNativeFunction +{ +public: + static JSBool JSFunction( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ) + { + T* Native = ToNative( cx, obj ); + if( !Native ) + return( JS_TRUE ); + + *rval = ToJSVal( (Native->*NativeFunction)( cx, argc, argv ) ); + + return( JS_TRUE ); + } +}; + +template class CJSObject : public IJSObject +{ + friend class CNativeFunction; + JSObject* m_JS; +public: + + // JS Property access + void GetProperty( JSContext* cx, CStrW PropertyName, jsval* vp ); + void SetProperty( JSContext* cx, CStrW PropertyName, jsval* vp ) + { + IJSProperty* prop = HasProperty( PropertyName ); + + if( prop ) + { + // Already exists + prop->Set( cx, *vp ); + + if( prop->m_AllowsInheritance ) + { + // Run along and update this property in any inheritors + InheritorsList UpdateSet( m_Inheritors ); + + while( !UpdateSet.empty() ) + { + IJSObject* UpdateObj = UpdateSet.back(); + UpdateSet.pop_back(); + IJSProperty* UpdateProp = UpdateObj->m_Properties[PropertyName]; + if( UpdateProp->m_Inherited ) + { + UpdateProp->Set( cx, *vp ); + InheritorsList::iterator it2; + for( it2 = UpdateObj->m_Inheritors.begin(); it2 != UpdateObj->m_Inheritors.end(); it2++ ) + UpdateSet.push_back( *it2 ); + } + } + } + } + else + { + // Need to add it + AddProperty( PropertyName, *vp ); + } + } + + // + // Functions that must be provided to JavaScript + // + static JSBool JSGetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) + { + CJSObject* Instance = ToNative( cx, obj ); + if( !Instance ) + return( JS_TRUE ); + + CStrW PropName = g_ScriptingHost.ValueToUCString( id ); + + Instance->GetProperty( cx, PropName, vp ); + + return( JS_TRUE ); + } + static JSBool JSSetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) + { + CJSObject* Instance = ToNative( cx, obj ); + if( !Instance ) + return( JS_TRUE ); + + CStrW PropName = g_ScriptingHost.ValueToUCString( id ); + + Instance->SetProperty( cx, PropName, vp ); + + return( JS_TRUE ); + } + static void ScriptingInit( const char* ClassName, JSNative Constructor = NULL, uintN ConstructorMinArgs = 0 ) + { + JSFunctionSpec* JSI_methods = new JSFunctionSpec[ m_Methods.size() + 1 ]; + for( unsigned int 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 ); + } +private: + void CreateScriptObject() + { + assert( !m_JS ); + m_JS = JS_NewObject( g_ScriptingHost.GetContext(), &JSI_class, NULL, NULL ); + JS_AddRoot( g_ScriptingHost.GetContext(), m_JS ); + JS_SetPrivate( g_ScriptingHost.GetContext(), m_JS, this ); + } +public: + static JSClass JSI_class; + JSObject* GetScript() + { + if( !m_JS ) + CreateScriptObject(); + return( m_JS ); + } +private: + static JSPropertySpec JSI_props[]; + static std::vector m_Methods; + static void JSFinalize( JSContext* cx, JSObject* obj ); + +public: + CJSObject() + { + m_Parent = NULL; + m_JS = NULL; + } + ~CJSObject() + { + PropertyTable::iterator it; + for( it = m_Properties.begin(); it != m_Properties.end(); it++ ) + { + if( !it->second->m_Intrinsic ) + { + CJSValProperty* extProp = (CJSValProperty*)it->second; + if( extProp->m_JSAccessor ) + { + CJSPropertyAccessor< CJSObject >* accessor = (CJSPropertyAccessor< CJSObject >*)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 ); + } + } + delete( it->second ); + } + /* + ReferrersSet::iterator rit; + for( rit = m_Referring.begin(); rit != m_Referring.end(); rit++ ) + { + JSObject* ref = *rit; + if( JS_GetClass( ref ) == &JSI_class ) + { + // Reference to this object directly. + // Replace with null pointer. + + // - Make sure it refers to this object. + assert( JS_GetPrivate( g_ScriptingHost.GetContext(), ref ) == this ); + + JS_SetPrivate( g_ScriptingHost.GetContext(), ref, NULL ); + } + else + { + // Possibly just deallocate the property object? + + // - Nothing else it should be. + assert( JS_GetClass( ref ) == &CJSPropertyAccessor::JSI_Class ); + + CJSPropertyAccessor* Accessor = (CJSPropertyAccessor*)JS_GetPrivate( g_ScriptingHost.GetContext(), ref ); + + // - Make sure it refers to this object. + assert( Accessor->m_Owner == this ); + + Accessor->m_Owner = NULL; + } + } + */ + if( m_JS ) + { + JS_SetPrivate( g_ScriptingHost.GetContext(), m_JS, NULL ); + JS_RemoveRoot( g_ScriptingHost.GetContext(), m_JS ); + } + } + 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 ); + // TODO: Remove any properties we were inheriting from this parent that we didn't specify ourselves + } + 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 property in the parent + for( it = m_Parent->m_Properties.begin(); it != m_Parent->m_Properties.end(); it++ ) + { + if( !it->second->m_AllowsInheritance ) + continue; + PropertyTable::iterator cp; + // Attempt to locate it in this object + cp = m_Properties.find( it->first ); + if( cp != m_Properties.end() ) + { + if( cp->second->m_Inherited ) + cp->second->ImmediateCopy( it->second ); + } + else + m_Properties[it->first] = new CJSValProperty( it->second->Get(), true ); + } + InheritorsList::iterator c; + for( c = m_Inheritors.begin(); c != m_Inheritors.end(); c++ ) + (*c)->Rebuild(); + } + IJSProperty* HasProperty( CStrW PropertyName ) + { + PropertyTable::iterator it; + it = m_Properties.find( PropertyName ); + if( it == m_Properties.end() ) + return( NULL ); + return( it->second ); + } + + void ReplicateProperty( CStrW PropertyName, jsval Value ) + { + m_Properties[PropertyName] = new CJSValProperty( Value, true ); + // Run through our descendants to add the property to all of them that don't + // already have it. + InheritorsList::iterator it; + for( it = m_Inheritors.begin(); it != m_Inheritors.end(); it++ ) + if( !((*it)->HasProperty( PropertyName ) ) ) + (*it)->ReplicateProperty( PropertyName, Value ); + } + + void AddProperty( CStrW PropertyName, jsval Value ) + { + assert( !HasProperty( PropertyName ) ); + m_Properties[PropertyName] = new CJSValProperty( Value, false ); + + // Run through our descendants to add the property to all of them that don't + // already have it. + InheritorsList::iterator it; + for( it = m_Inheritors.begin(); it != m_Inheritors.end(); it++ ) + if( !((*it)->HasProperty( PropertyName ) ) ) + (*it)->ReplicateProperty( PropertyName, Value ); + } + + void AddProperty( CStrW PropertyName, CStrW Value ) + { + AddProperty( PropertyName, ToJSVal( Value ) ); + } + template + static void AddMethod( const char* Name, uintN MinArgs ) + { + JSFunctionSpec FnInfo = { Name, CNativeFunction::JSFunction, MinArgs, 0, 0 }; + T::m_Methods.push_back( FnInfo ); + } + template void AddProperty( CStrW PropertyName, PropType* Native, bool AllowInheritance = true, NotifyFn Update = NULL, NotifyFn Refresh = NULL ) + { + m_Properties[PropertyName] = new CJSProperty( Native, this, AllowInheritance, Update, Refresh ); + } +}; + +template JSClass CJSObject::JSI_class = { + NULL, JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, + JSGetProperty, JSSetProperty, + JS_EnumerateStub, JS_ResolveStub, + JS_ConvertStub, JS_FinalizeStub, + NULL, NULL, NULL, NULL +}; + +template JSPropertySpec CJSObject::JSI_props[] = { + { 0 }, +}; + +template std::vector CJSObject::m_Methods; + +template void CJSObject::GetProperty( JSContext* cx, CStrW PropertyName, jsval* vp ) +{ + IJSProperty* Property = HasProperty( PropertyName ); + if( Property ) + { + if( Property->m_Intrinsic ) + { + *vp = Property->Get( cx ); + } + else + { + CJSValProperty* extProp = (CJSValProperty*)Property; + if( !extProp->m_JSAccessor ) + { + extProp->m_JSAccessor = CJSPropertyAccessor< CJSObject >::CreateAccessor( cx, this, PropertyName ); + JS_AddRoot( cx, extProp->m_JSAccessor ); + } + + *vp = OBJECT_TO_JSVAL( extProp->m_JSAccessor ); + } + } +} + +#endif + + diff --git a/source/scripting/ScriptingHost.cpp b/source/scripting/ScriptingHost.cpp index 3dab520233..c4ea3deeb7 100755 --- a/source/scripting/ScriptingHost.cpp +++ b/source/scripting/ScriptingHost.cpp @@ -72,7 +72,7 @@ ScriptingHost::~ScriptingHost() { if (m_Context != NULL) { - JS_DestroyContext(m_Context); + JS_DestroyContext(m_Context); m_Context = NULL; } @@ -376,33 +376,6 @@ void ScriptingHost::ErrorReporter(JSContext * context, const char * message, JSE } } -/* -void ScriptingHost::Tick(float timeElapsed) -{ - if (timeElapsed > 0.2f) timeElapsed = 0.2f; - - for (int i = 0; i < (int)m_DelayedScripts.size(); ) - { - m_DelayedScripts[i].m_SecondsToExecution -= timeElapsed; - - if (m_DelayedScripts[i].m_SecondsToExecution <= 0.0f) - { - this->ExecuteScript(m_DelayedScripts[i].m_FunctionName); - m_DelayedScripts.erase(m_DelayedScripts.begin() + i); - } - else - { - i++; - } - } -} - -void ScriptingHost::AddDelayedScript(const std::string & functionName, float delaySeconds) -{ - m_DelayedScripts.push_back(DelayedScriptExecutor(functionName, delaySeconds)); -} -*/ - void ScriptingHost::_CollectGarbage() { JS_GC(m_Context); diff --git a/source/simulation/BaseEntity.cpp b/source/simulation/BaseEntity.cpp index 1f978b8afd..254baf9046 100755 --- a/source/simulation/BaseEntity.cpp +++ b/source/simulation/BaseEntity.cpp @@ -6,14 +6,22 @@ #include "ps/Xeromyces.h" +#include "CLogger.h" +#define LOG_CATEGORY "entity" + CBaseEntity::CBaseEntity() { - m_base = NULL; - m_base.associate( this, L"super" ); - m_name.associate( this, L"name" ); - m_speed.associate( this, L"speed" ); - m_turningRadius.associate( this, L"turningRadius" ); + AddProperty( L"tag", &m_Tag, false ); + AddProperty( L"parent", (CBaseEntity**)&m_base, false ); + AddProperty( L"actions.move.speed", &m_speed ); + AddProperty( L"actions.move.turningradius", &m_turningRadius ); + AddProperty( L"actor", &m_actorObject ); + + for( int t = 0; t < EVENT_LAST; t++ ) + AddProperty( EventNames[t], &m_EventHandlers[t] ); + m_actorObject = NULL; + m_bound_type = CBoundingObject::BOUND_NONE; m_bound_circle = NULL; m_bound_box = NULL; } @@ -26,6 +34,27 @@ CBaseEntity::~CBaseEntity() delete( m_bound_circle ); } +void CBaseEntity::loadBase() +{ + // Don't bother if we're providing a replacement. + if( m_bound_type == CBoundingObject::BOUND_NONE ) + { + if( m_base->m_bound_type == CBoundingObject::BOUND_CIRCLE ) + { + m_bound_circle = new CBoundingCircle(); + m_bound_circle->setRadius( m_base->m_bound_circle->m_radius ); + } + else if( m_base->m_bound_type == CBoundingObject::BOUND_OABB ) + { + m_bound_box = new CBoundingBox(); + m_bound_box->setDimensions( m_base->m_bound_box->getWidth(), m_base->m_bound_box->getHeight() ); + } + m_bound_type = m_base->m_bound_type; + } + + SetBase( m_base ); +} + bool CBaseEntity::loadXML( CStr filename ) { CXeromyces XeroFile; @@ -36,77 +65,193 @@ bool CBaseEntity::loadXML( CStr filename ) // Define all the elements and attributes used in the XML file #define EL(x) int el_##x = XeroFile.getElementID(#x) #define AT(x) int at_##x = XeroFile.getAttributeID(#x) + // Only the ones we can't load using normal methods. EL(entity); - EL(name); - EL(actor); - EL(speed); - EL(turningradius); - EL(size); + EL(script); EL(footprint); - EL(graphicsoffset); + EL(event); + AT(tag); + AT(parent); AT(radius); AT(width); AT(height); - AT(x); - AT(y); + AT(on); #undef AT #undef EL XMBElement Root = XeroFile.getRoot(); - assert(Root.getNodeName() == el_entity); + if( Root.getNodeName() != el_entity ) + { + LOG( ERROR, LOG_CATEGORY, "CBaseEntity::LoadXML: XML root was not \"Entity\" in file %s. Load failed.", filename.c_str() ); + return( false ); + } XMBElementList RootChildren = Root.getChildNodes(); + m_Tag = Root.getAttributes().getNamedItem( at_tag ); + + if( !m_Tag.Length() ) + { + LOG( ERROR, LOG_CATEGORY, "CBaseEntity::LoadXML: Tag attribute was not specified in file %s. Load failed.", filename.c_str() ); + return( false ); + } + + m_Base_Name = Root.getAttributes().getNamedItem( at_parent ); + for (int i = 0; i < RootChildren.Count; ++i) { XMBElement Child = RootChildren.item(i); - int ChildName = Child.getNodeName(); + unsigned int ChildName = Child.getNodeName(); - if (ChildName == el_name) + if( ChildName == el_script ) { - m_name = (CStrW)Child.getText(); - } - else if (ChildName == el_actor) - { - m_actorObject = g_ObjMan.FindObject( (CStr)Child.getText() ); - } - else if (ChildName == el_speed) - { - m_speed = CStrW(Child.getText()).ToFloat(); - } - else if (ChildName == el_turningradius) - { - m_turningRadius = CStrW(Child.getText()).ToFloat(); - } - else if (ChildName == el_size) - { - if( !m_bound_circle ) - m_bound_circle = new CBoundingCircle(); - CStrW radius (Child.getAttributes().getNamedItem(at_radius)); - m_bound_circle->setRadius( radius.ToFloat() ); - m_bound_type = CBoundingObject::BOUND_CIRCLE; + CStr Include = Child.getAttributes().getNamedItem( XeroFile.getAttributeID( "file" ) ); + + jsval dy; + + // TODO: Probably try and determine if this file has already been loaded, and skip it. + + if( Include.Length() ) + { + CVFSFile IncludeFile; + if( IncludeFile.Load( Include ) != PSRETURN_OK ) + { + LOG( WARNING, LOG_CATEGORY, "CBaseEntity::loadXML: Could not load script file %s specified in file %s; ignored.", Include.c_str(), filename.c_str() ); + } + else + JS_EvaluateScript( g_ScriptingHost.getContext(), JS_GetGlobalObject( g_ScriptingHost.GetContext() ), (const char*)IncludeFile.GetBuffer(), (int)IncludeFile.GetBufferSize(), Include, 1, &dy ); + } + + CStr Inline = Child.getText(); + + if( Inline.Length() ) + JS_EvaluateScript( g_ScriptingHost.getContext(), JS_GetGlobalObject( g_ScriptingHost.GetContext() ), Inline.c_str(), (int)Inline.Length(), filename.c_str(), Child.getLineNumber(), &dy ); + } else if (ChildName == el_footprint) { - if( !m_bound_box ) + if( Child.getAttributes().getNamedItem( at_radius ).length() ) + { + // Specifying a circular footprint + if( !m_bound_circle ) + m_bound_circle = new CBoundingCircle(); + CStrW radius (Child.getAttributes().getNamedItem(at_radius)); + m_bound_circle->setRadius( radius.ToFloat() ); + m_bound_type = CBoundingObject::BOUND_CIRCLE; + } + else + { + if( !m_bound_box ) m_bound_box = new CBoundingBox(); - CStrW width (Child.getAttributes().getNamedItem(at_width)); - CStrW height (Child.getAttributes().getNamedItem(at_height)); + CStrW width (Child.getAttributes().getNamedItem(at_width)); + CStrW height (Child.getAttributes().getNamedItem(at_height)); - m_bound_box->setDimensions( width.ToFloat(), height.ToFloat() ); - m_bound_type = CBoundingObject::BOUND_OABB; + m_bound_box->setDimensions( width.ToFloat(), height.ToFloat() ); + m_bound_type = CBoundingObject::BOUND_OABB; + } } - else if (ChildName == el_graphicsoffset) + else if( ChildName == el_event ) { - CStrW x (Child.getAttributes().getNamedItem(at_x)); - CStrW y (Child.getAttributes().getNamedItem(at_y)); + // Action...On for consistency with the GUI. + CStrW EventName = CStrW( L"on" ) + (CStrW)Child.getAttributes().getNamedItem( at_on ); - m_graphicsOffset.x = x.ToFloat(); - m_graphicsOffset.y = y.ToFloat(); + CStrW Code = (CStrW)Child.getText(); + + // Does a property with this name already exist? + + for( uint eventID = 0; eventID < EVENT_LAST; eventID++ ) + { + if( CStrW( EventNames[eventID] ) == EventName ) + { + m_EventHandlers[eventID].CompileScript( CStrW( filename ) + L"::" + EventName + L" (" + CStrW( Child.getLineNumber() ) + L")", Code ); + HasProperty( EventName )->m_Inherited = false; + break; + } + } } - } + else + { + XMLLoadProperty( XeroFile, Child, CStrW() ); + } + } return true; } + +void CBaseEntity::XMLLoadProperty( CXeromyces& XeroFile, XMBElement& Source, CStrW BasePropertyName ) +{ + // Add a property, put the node text into it. + CStrW PropertyName = BasePropertyName + CStr8( XeroFile.getElementString( Source.getNodeName() ) ); + + IJSProperty* Existing = HasProperty( PropertyName ); + if( Existing ) + { + if( !Existing->m_Intrinsic ) + LOG( WARNING, LOG_CATEGORY, "CBaseEntity::XMLAddProperty: %s already defined for %s. Property trees will be merged.", PropertyName.c_str(), m_Tag.c_str() ); + Existing->Set( ToJSVal( Source.getText() ) ); + Existing->m_Inherited = false; + } + else + { + if( !Source.getText().length() ) + { + // Arbitrarily say that if a node has no other value, define it to be 'true'. + // Appears to be the most convenient thing to do in most circumstances. + AddProperty( PropertyName, JSVAL_TRUE ); + } + else + AddProperty( PropertyName, Source.getText() ); + } + + + PropertyName += CStrW( L"." ); + + // Retrieve any attributes it has and add them as subproperties. + XMBAttributeList AttributeSet = Source.getAttributes(); + for( unsigned int AttributeID = 0; AttributeID < (unsigned int)AttributeSet.Count; AttributeID++ ) + { + XMBAttribute Attribute = AttributeSet.item( AttributeID ); + CStrW AttributeName = PropertyName + CStr8( XeroFile.getAttributeString( Attribute.Name ) ); + Existing = HasProperty( AttributeName ); + if( Existing ) + { + Existing->Set( ToJSVal( Attribute.Value ) ); + Existing->m_Inherited = false; + } + else + AddProperty( AttributeName, Attribute.Value ); + } + + // Retrieve any child nodes the property has and, similarly, add them as subproperties. + XMBElementList NodeSet = Source.getChildNodes(); + for( unsigned int NodeID = 0; NodeID < (unsigned int)NodeSet.Count; NodeID++ ) + { + XMBElement Node = NodeSet.item( NodeID ); + XMLLoadProperty( XeroFile, Node, PropertyName ); + } + +} + +/* + Scripting Interface +*/ + +// Scripting initialization + +void CBaseEntity::ScriptingInit() +{ + AddMethod( "toString", 0 ); + CJSObject::ScriptingInit( "EntityTemplate" ); +} + +// Script-bound functions + +jsval CBaseEntity::ToString( JSContext* cx, uintN argc, jsval* argv ) +{ + utf16_t buffer[256]; + swprintf( buffer, 256, L"[object EntityTemplate: %ls]", m_Tag.c_str() ); + buffer[255] = 0; + return( STRING_TO_JSVAL( JS_NewUCStringCopyZ( cx, buffer ) ) ); +} \ No newline at end of file diff --git a/source/simulation/BaseEntity.h b/source/simulation/BaseEntity.h index a7479b4d48..52f1255797 100755 --- a/source/simulation/BaseEntity.h +++ b/source/simulation/BaseEntity.h @@ -21,29 +21,48 @@ #include "CStr.h" #include "ObjectEntry.h" -#include "EntityProperties.h" +#include "scripting/ScriptableObject.h" #include "BoundingObjects.h" +#include "EventHandlers.h" +#include "ScriptObject.h" +#include "Xeromyces.h" -class CBaseEntity : public IBoundPropertyOwner +class CBaseEntity : public CJSObject { public: CBaseEntity(); ~CBaseEntity(); // Load from XML bool loadXML( CStr filename ); + // Load a tree of properties from an XML (XMB) node. + // MT: XeroFile/Source seem to me like they should be const, but the functions they require aren't const members + // and I'm reluctant to go messing round with xerophilic fungi I don't understand. + void XMLLoadProperty( CXeromyces& XeroFile, XMBElement& Source, CStrW BasePropertyName ); // Base stats + CBaseEntity* m_base; + CStrW m_Base_Name; // <- We don't guarantee the order XML files will be loaded in, so we'll store the name of the + // parent entity referenced, then, after all files are loaded, attempt to match names to objects. + CObjectEntry* m_actorObject; - CBoundObjectProperty m_name; + CStrW m_Tag; CBoundingCircle* m_bound_circle; CBoundingBox* m_bound_box; CBoundingObject::EBoundingType m_bound_type; - CVector2D m_graphicsOffset; - CBoundProperty m_speed; - CBoundProperty m_turningRadius; + float m_speed; + float m_turningRadius; + CScriptObject m_EventHandlers[EVENT_LAST]; + + void loadBase(); + + // Script-bound functions + + jsval ToString( JSContext* cx, uintN argc, jsval* argv ); + + static void ScriptingInit(); }; #endif diff --git a/source/simulation/BaseEntityCollection.cpp b/source/simulation/BaseEntityCollection.cpp index 6b95782b0e..22a8589879 100755 --- a/source/simulation/BaseEntityCollection.cpp +++ b/source/simulation/BaseEntityCollection.cpp @@ -53,7 +53,26 @@ void CBaseEntityCollection::loadTemplates() vfs_close_dir( maindir ); } else - LOG(ERROR, LOG_CATEGORY, "CBaseEntityCollection::loadTemplates(): Unable to open directory entities/"); + LOG(ERROR, LOG_CATEGORY, "CBaseEntityCollection::loadTemplates(): Unable to open directory entities/"); + + // Fix up parent links in the templates. + + std::vector::iterator it; + for( it = m_templates.begin(); it != m_templates.end(); it++ ) + { + if( !( (*it)->m_Base_Name.Length() ) ) + continue; + + CBaseEntity* Base = getTemplate( (*it)->m_Base_Name ); + if( Base ) + { + (*it)->m_base = Base; + (*it)->loadBase(); + } + else + LOG( WARNING, LOG_CATEGORY, "Parent template %s does not exist in template %s", CStr8( (*it)->m_Base_Name ).c_str(), CStr8( (*it)->m_Tag ).c_str() ); + } + } void CBaseEntityCollection::addTemplate( CBaseEntity* temp ) @@ -64,7 +83,7 @@ void CBaseEntityCollection::addTemplate( CBaseEntity* temp ) CBaseEntity* CBaseEntityCollection::getTemplate( CStrW name ) { for( u16 t = 0; t < m_templates.size(); t++ ) - if( m_templates[t]->m_name == name ) return( m_templates[t] ); + if( m_templates[t]->m_Tag == name ) return( m_templates[t] ); return( NULL ); } diff --git a/source/simulation/BoundingObjects.h b/source/simulation/BoundingObjects.h index b8007b6e40..09114bbdba 100755 --- a/source/simulation/BoundingObjects.h +++ b/source/simulation/BoundingObjects.h @@ -21,6 +21,7 @@ public: CBoundingObject() {} enum EBoundingType { + BOUND_NONE, BOUND_CIRCLE, BOUND_OABB }; diff --git a/source/simulation/Entity.cpp b/source/simulation/Entity.cpp index 403d981a5a..b6477fbbb4 100755 --- a/source/simulation/Entity.cpp +++ b/source/simulation/Entity.cpp @@ -16,6 +16,8 @@ #include "Game.h" +#include "scripting/JSInterface_Vector3D.h" + CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation ) { m_position = position; @@ -24,49 +26,48 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation ) m_ahead.x = sin( m_orientation ); m_ahead.y = cos( m_orientation ); - m_base.associate( this, L"template", ( void( IBoundPropertyOwner::* )() )&CEntity::loadBase ); - m_name.associate( this, L"name" ); - m_speed.associate( this, L"speed" ); - m_selected.associate( this, L"selected", ( void( IBoundPropertyOwner::* )() )&CEntity::checkSelection ); - m_grouped.associate( this, L"group", ( void( IBoundPropertyOwner::* )() )&CEntity::checkGroup ); - m_extant_mirror.associate( this, L"extant", ( void( IBoundPropertyOwner::* )() )&CEntity::checkExtant ); - m_turningRadius.associate( this, L"turningRadius" ); - m_graphics_position.associate( this, L"position", ( void( IBoundPropertyOwner::* )() )&CEntity::teleport ); - m_graphics_orientation.associate( this, L"orientation", ( void( IBoundPropertyOwner::* )() )&CEntity::reorient ); + AddProperty( L"template", (CBaseEntity**)&m_base, false, (NotifyFn)loadBase ); + AddProperty( L"actions.move.speed", &m_speed ); + AddProperty( L"selected", &m_selected, false, (NotifyFn)checkSelection ); + AddProperty( L"group", &m_grouped, false, (NotifyFn)checkGroup ); + AddProperty( L"extant", &m_extant, false, (NotifyFn)checkExtant ); + AddProperty( L"actions.move.turningradius", &m_turningRadius ); + AddProperty( L"position", &m_graphics_position, false, (NotifyFn)teleport ); + AddProperty( L"orientation", &m_graphics_orientation, false, (NotifyFn)reorient ); + + for( int t = 0; t < EVENT_LAST; t++ ) + AddProperty( EventNames[t], &m_EventHandlers[t] ); + // Set our parent unit and build us an actor. m_actor = NULL; m_bounds = NULL; + m_moving = false; m_base = base; loadBase(); - - // Nasty hackish correction for models that are off-centre. - // Bad artist. No cookie. - - m_position.X += m_graphicsOffset.x; - m_position.Z += m_graphicsOffset.y; + if( m_bounds ) m_bounds->setPosition( m_position.X, m_position.Z ); m_position_previous = m_position; m_orientation_previous = m_orientation; + m_graphics_position = m_position; + m_graphics_orientation = m_orientation; + m_extant = true; m_extant_mirror = true; m_selected = false; + m_grouped = -1; } CEntity::~CEntity() { - for( size_t i = 0; i < m_base->m_inheritors.size(); i++ ) - if( m_base->m_inheritors[i] == this ) - m_base->m_inheritors.erase( m_base->m_inheritors.begin() + i ); - if( m_actor ) { g_UnitMan.RemoveUnit( m_actor ); @@ -95,10 +96,7 @@ void CEntity::loadBase() // Set up our instance data - m_base->m_inheritors.push_back( this ); - rebuild(); - - m_graphicsOffset = m_base->m_graphicsOffset; + SetBase( m_base ); if( m_base->m_bound_type == CBoundingObject::BOUND_CIRCLE ) { @@ -108,14 +106,11 @@ void CEntity::loadBase() { m_bounds = new CBoundingBox( m_position.X, m_position.Z, m_ahead, m_base->m_bound_box ); } - - - } void CEntity::kill() { - g_Selection.removeAll( this ); + g_Selection.removeAll( me ); if( m_bounds ) delete( m_bounds ); m_bounds = NULL; @@ -133,11 +128,6 @@ void CEntity::kill() me = HEntity(); // will deallocate the entity, assuming nobody else has a reference to it } -bool isWaypoint( CEntity* e ) -{ - return( e->m_base->m_name == CStrW( L"House" ) ); -} - void CEntity::updateActorTransforms() { CMatrix3D m; @@ -145,9 +135,9 @@ void CEntity::updateActorTransforms() float s = sin( m_graphics_orientation ); float c = cos( m_graphics_orientation ); - m._11 = -c; m._12 = 0.0f; m._13 = -s; m._14 = m_graphics_position.X - m_graphicsOffset.x; + m._11 = -c; m._12 = 0.0f; m._13 = -s; m._14 = m_graphics_position.X; m._21 = 0.0f; m._22 = 1.0f; m._23 = 0.0f; m._24 = m_graphics_position.Y; - m._31 = s; m._32 = 0.0f; m._33 = -c; m._34 = m_graphics_position.Z - m_graphicsOffset.y; + m._31 = s; m._32 = 0.0f; m._33 = -c; m._34 = m_graphics_position.Z; m._41 = 0.0f; m._42 = 0.0f; m._43 = 0.0f; m._44 = 1.0f; m_actor->GetModel()->SetTransform( m ); @@ -199,9 +189,11 @@ void CEntity::dispatch( const CMessage* msg ) switch( msg->type ) { case CMessage::EMSG_TICK: + m_EventHandlers[EVENT_TICK].Run( GetScript() ); break; case CMessage::EMSG_INIT: - if( m_base->m_name == CStrW( L"Prometheus Dude" ) ) + m_EventHandlers[EVENT_INITIALIZE].Run( GetScript() ); + if( m_base->m_Tag == CStrW( L"Prometheus Dude" ) ) { if( getCollisionObject( this ) ) { @@ -209,6 +201,7 @@ void CEntity::dispatch( const CMessage* msg ) kill(); return; } + /* std::vector* waypoints = g_EntityManager.matches( isWaypoint ); while( !waypoints->empty() ) { @@ -224,6 +217,7 @@ void CEntity::dispatch( const CMessage* msg ) waypoints->erase( it ); } delete( waypoints ); + */ } break; case CMessage::EMSG_ORDER: @@ -295,21 +289,21 @@ void CEntity::checkSelection() { if( m_selected ) { - if( !g_Selection.isSelected( this ) ) - g_Selection.addSelection( this ); + if( !g_Selection.isSelected( me ) ) + g_Selection.addSelection( me ); } else { - if( g_Selection.isSelected( this ) ) - g_Selection.removeSelection( this ); + if( g_Selection.isSelected( me ) ) + g_Selection.removeSelection( me ); } } void CEntity::checkGroup() { - g_Selection.changeGroup( this, -1 ); // Ungroup + g_Selection.changeGroup( me, -1 ); // Ungroup if( ( m_grouped >= 0 ) && ( m_grouped < MAX_GROUPS ) ) - g_Selection.changeGroup( this, m_grouped ); + g_Selection.changeGroup( me, m_grouped ); } void CEntity::checkExtant() @@ -505,4 +499,139 @@ void CEntity::renderSelectionOutline( float alpha ) glEnd(); } +/* + + Scripting interface + +*/ + +// Scripting initialization + +void CEntity::ScriptingInit() +{ + AddMethod( "toString", 0 ); + AddMethod( "order", 1 ); + AddMethod( "orderQueued", 1 ); + CJSObject::ScriptingInit( "Entity", Construct, 2 ); +} + +// Script constructor + +JSBool CEntity::Construct( JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval ) +{ + assert( argc >= 2 ); + + CBaseEntity* baseEntity = NULL; + CVector3D position; + float orientation = 0.0f; + + JSObject* jsBaseEntity = JSVAL_TO_OBJECT( argv[0] ); + CStrW templateName; + + if( !JSVAL_IS_OBJECT( argv[0] ) || !( baseEntity = ToNative( cx, jsBaseEntity ) ) ) + { + try + { + templateName = g_ScriptingHost.ValueToUCString( argv[0] ); + } + catch( PSERROR_Scripting_ConversionFailed ) + { + *rval = JSVAL_NULL; + JS_ReportError( cx, "Invalid template identifier" ); + return( JS_TRUE ); + } + baseEntity = g_EntityTemplateCollection.getTemplate( templateName ); + } + + if( !baseEntity ) + { + *rval = JSVAL_NULL; + JS_ReportError( cx, "No such template: %ls", CStr8(templateName).c_str() ); + return( JS_TRUE ); + } + + JSI_Vector3D::Vector3D_Info* jsVector3D = NULL; + if( JSVAL_IS_OBJECT( argv[1] ) && ( jsVector3D = (JSI_Vector3D::Vector3D_Info*)JS_GetInstancePrivate( cx, JSVAL_TO_OBJECT( argv[1] ), &JSI_Vector3D::JSI_class, NULL ) ) ) + { + position = *( jsVector3D->vector ); + } + if( argc >= 3 ) + { + try + { + orientation = (float)g_ScriptingHost.ValueToDouble( argv[2] ); + } + catch( PSERROR_Scripting_ConversionFailed ) + { + // TODO: Net-safe random for this parameter. + orientation = 0.0f; + } + } + + HEntity handle = g_EntityManager.create( baseEntity, position, orientation ); + + CMessage message( CMessage::EMSG_INIT ); + handle->dispatch( &message ); + + *rval = ToJSVal( *handle ); + return( JS_TRUE ); +} + +// Script-bound methods + +jsval CEntity::ToString( JSContext* cx, uintN argc, jsval* argv ) +{ + utf16_t buffer[256]; + swprintf( buffer, 256, L"[object Entity: %ls]", m_base->m_Tag.c_str() ); + buffer[255] = 0; + return( STRING_TO_JSVAL( JS_NewUCStringCopyZ( cx, buffer ) ) ); +} + +bool CEntity::Order( JSContext* cx, uintN argc, jsval* argv, bool Queued ) +{ + // This needs to be sorted (uses Scheduler rather than network messaging) + assert( argc >= 1 ); + + int orderCode; + + try + { + orderCode = g_ScriptingHost.ValueToInt( argv[0] ); + } + catch( PSERROR_Scripting_ConversionFailed ) + { + JS_ReportError( cx, "Invalid order type" ); + return( false ); + } + + CEntityOrder newOrder; + + (int&)newOrder.m_type = orderCode; + + switch( orderCode ) + { + case CEntityOrder::ORDER_GOTO: + case CEntityOrder::ORDER_PATROL: + if( argc < 3 ) + { + JS_ReportError( cx, "Too few parameters" ); + return( false ); + } + try + { + newOrder.m_data[0].location.x = g_ScriptingHost.ValueToDouble( argv[1] ); + newOrder.m_data[0].location.y = g_ScriptingHost.ValueToDouble( argv[2] ); + } + catch( PSERROR_Scripting_ConversionFailed ) + { + JS_ReportError( cx, "Invalid location" ); + return( false ); + } + g_Scheduler.pushFrame( ORDER_DELAY, me, new CMessageOrder( newOrder, Queued ) ); + return( true ); + default: + JS_ReportError( cx, "Invalid order type" ); + return( false ); + } +} diff --git a/source/simulation/Entity.h b/source/simulation/Entity.h index 34668887ff..a08ae2cb1a 100755 --- a/source/simulation/Entity.h +++ b/source/simulation/Entity.h @@ -32,7 +32,7 @@ #define ENTITY_INCLUDED #include -#include "EntityProperties.h" +#include "scripting/ScriptableObject.h" #include "BaseEntity.h" #include "Vector2D.h" @@ -43,29 +43,29 @@ #include "EntityOrders.h" #include "EntityHandles.h" #include "EntityMessage.h" +#include "EventHandlers.h" class CEntityManager; -class CEntity : public IBoundPropertyOwner +class CEntity : public CJSObject { friend class CEntityManager; public: // Intrinsic properties - CBoundObjectProperty m_name; - CBoundProperty m_speed; - CBoundProperty m_turningRadius; - CBoundProperty m_selected; - CBoundProperty m_grouped; + CBaseEntity* m_base; + + float m_speed; + float m_turningRadius; + bool m_selected; + i32 m_grouped; bool m_extant; // Don't want JS to have direct write-access to these. (Things that should be done might not be) - CBoundProperty m_extant_mirror; // plus this way limits the number of nasty semantics to work around. + bool m_extant_mirror; // plus this way limits the number of nasty semantics to work around. //-- Interpolated property CVector3D m_position; CVector3D m_position_previous; - CBoundObjectProperty m_graphics_position; - - CVector2D m_graphicsOffset; + CVector3D m_graphics_position; CBoundingObject* m_bounds; float m_targetorientation; @@ -74,7 +74,10 @@ public: //-- Interpolated property float m_orientation; float m_orientation_previous; - CBoundProperty m_graphics_orientation; + float m_graphics_orientation; + + //-- Scripts + CScriptObject m_EventHandlers[EVENT_LAST]; CUnit* m_actor; bool m_moving; @@ -118,6 +121,25 @@ public: void clearOrders(); void pushOrder( CEntityOrder& order ); + + // Script constructor + + static JSBool Construct( JSContext* cx, JSObject* obj, unsigned int argc, jsval* argv, jsval* rval ); + + // Script-bound functions + + jsval ToString( JSContext* cx, uintN argc, jsval* argv ); + bool Order( JSContext* cx, uintN argc, jsval* argv, bool Queued ); + inline bool OrderSingle( JSContext* cx, uintN argc, jsval* argv ) + { + return( Order( cx, argc, argv, false ) ); + } + inline bool OrderQueued( JSContext* cx, uintN argc, jsval* argv ) + { + return( Order( cx, argc, argv, true ) ); + } + + static void ScriptingInit(); }; // General entity globals diff --git a/source/simulation/EntityHandles.cpp b/source/simulation/EntityHandles.cpp index 6869d096a3..8a70bb6546 100755 --- a/source/simulation/EntityHandles.cpp +++ b/source/simulation/EntityHandles.cpp @@ -72,7 +72,9 @@ CEntity* HEntity::operator->() const HEntity::operator CEntity*() const { - assert( m_handle != INVALID_HANDLE ); + if( m_handle == INVALID_HANDLE ) + return( NULL ); + assert( g_EntityManager.m_entities[m_handle].m_refcount ); return( g_EntityManager.m_entities[m_handle].m_entity ); } diff --git a/source/simulation/EntityHandles.h b/source/simulation/EntityHandles.h index 15f8cefccd..761492d2b1 100755 --- a/source/simulation/EntityHandles.h +++ b/source/simulation/EntityHandles.h @@ -52,6 +52,7 @@ public: HEntity( const HEntity& copy ); void operator=( const HEntity& copy ); bool operator==( const HEntity& test ) const; + bool operator!=( const HEntity& test ) const { return( !operator==( test ) ); } operator bool() const { return( m_handle != INVALID_HANDLE ); } operator CEntity*() const; ~HEntity(); diff --git a/source/simulation/EntityManager.cpp b/source/simulation/EntityManager.cpp index fd091b8aad..c638a857ed 100755 --- a/source/simulation/EntityManager.cpp +++ b/source/simulation/EntityManager.cpp @@ -93,6 +93,9 @@ void CEntityManager::updateAll( size_t timestep ) delete( *it ); m_reaper.clear(); + CMessage Tick_msg( CMessage::EMSG_TICK ); + dispatchAll( &Tick_msg ); + for( int i = 0; i < MAX_HANDLES; i++ ) if( m_entities[i].m_refcount && m_entities[i].m_entity->m_extant ) m_entities[i].m_entity->update( timestep ); diff --git a/source/simulation/EntityProperties.cpp b/source/simulation/EntityProperties.cpp index f5d49da501..f6fbaa3fac 100755 --- a/source/simulation/EntityProperties.cpp +++ b/source/simulation/EntityProperties.cpp @@ -1,5 +1,6 @@ #include "precompiled.h" +/* #include "EntityProperties.h" #include "BaseEntityCollection.h" #include "scripting/JSInterface_BaseEntity.h" @@ -105,7 +106,7 @@ void CBoundObjectProperty::set( const jsval value ) { try { - *this = g_ScriptingHost.ValueToUCString( value ); + m_String = g_ScriptingHost.ValueToUCString( value ); m_inherited = false; } catch( ... ) @@ -153,11 +154,38 @@ bool CBoundObjectProperty::rebuild( IBoundProperty* parent, bool trig return( false ); } +//-- + +void CBoundObjectProperty::set( const jsval value ) +{ + switch( JS_TypeOfValue( g_ScriptingHost.GetContext(), value ) ) + { + case JSTYPE_STRING: + CompileScript( L"", g_ScriptingHost.ValueToUCString( value ) ); + m_inherited = false; + break; + case JSTYPE_FUNCTION: + Function = JS_ValueToFunction( g_ScriptingHost.GetContext(), value ); + m_inherited = false; + break; + } +} + +jsval CBoundObjectProperty::tojsval() +{ + if( Function ) + return( OBJECT_TO_JSVAL( JS_GetFunctionObject( Function ) ) ); + return( JSVAL_NULL ); +} + +//-- + void CBoundProperty::set( const jsval value ) { JSObject* baseEntity = JSVAL_TO_OBJECT( value ); CBaseEntity* base = NULL; - if( JSVAL_IS_OBJECT( value ) && ( base = (CBaseEntity*)JS_GetInstancePrivate( g_ScriptingHost.getContext(), baseEntity, &JSI_BaseEntity::JSI_class, NULL ) ) ) + + if( JSVAL_IS_OBJECT( value ) && ( base = ToNative( g_ScriptingHost.GetContext(), baseEntity ) ) ) { m_data = base; } @@ -167,50 +195,10 @@ void CBoundProperty::set( const jsval value ) jsval CBoundProperty::tojsval() { - JSObject* baseEntity = JS_NewObject( g_ScriptingHost.getContext(), &JSI_BaseEntity::JSI_class, NULL, NULL ); - JS_SetPrivate( g_ScriptingHost.getContext(), baseEntity, m_data ); + JSObject* baseEntity = m_data->GetScript(); return( OBJECT_TO_JSVAL( baseEntity ) ); } -void IBoundPropertyOwner::rebuild( CStrW propertyName ) -{ - IBoundProperty* thisProperty = m_properties[propertyName]; - IBoundProperty* baseProperty = NULL; - if( m_base ) - { - if( m_base->m_properties.find( propertyName ) != m_base->m_properties.end() ) - baseProperty = m_base->m_properties[propertyName]; - } - if( thisProperty->rebuild( baseProperty ) ) - { - std::vector::iterator it; - for( it = m_inheritors.begin(); it != m_inheritors.end(); it++ ) - (*it)->rebuild( propertyName ); - } -} -void IBoundPropertyOwner::rebuild() -{ - STL_HASH_MAP::iterator property; - if( m_base ) - { - for( property = m_properties.begin(); property != m_properties.end(); property++ ) - { - IBoundProperty* baseProperty = NULL; - if( m_base->m_properties.find( property->first ) != m_base->m_properties.end() ) - baseProperty = m_base->m_properties[property->first]; - (property->second)->rebuild( baseProperty, false ); - } - } - else - { - for( property = m_properties.begin(); property != m_properties.end(); property++ ) - (property->second)->rebuild( NULL, false ); - } - - std::vector::iterator it; - for( it = m_inheritors.begin(); it != m_inheritors.end(); it++ ) - (*it)->rebuild(); - -} +*/ \ No newline at end of file diff --git a/source/simulation/EntityProperties.h b/source/simulation/EntityProperties.h index 8c48d2851b..410e372921 100755 --- a/source/simulation/EntityProperties.h +++ b/source/simulation/EntityProperties.h @@ -11,35 +11,23 @@ // Mark Thompson mot20@cam.ac.uk / mark@wildfiregames.com +/* #ifndef ENTITY_PROPERTIES_INCLUDED #define ENTITY_PROPERTIES_INCLUDED #include "CStr.h" #include "Vector3D.h" -#include "scripting/ScriptingHost.h" +#include "ScriptObject.h" + #include "scripting/JSInterface_Entity.h" #include "scripting/JSInterface_Vector3D.h" #ifndef __GNUC__ -# include -# if( defined( _MSC_VER ) && ( _MSC_VER >= 1300 ) ) -# define STL_HASH_MAP stdext::hash_map -# else -# define STL_HASH_MAP std::hash_map -# endif //( defined( _MSC_VER ) && ( _MSC_VER >= 1300 ) ) - -#else // #ifndef __GNUC__ - -# include -# define STL_HASH_MAP __gnu_cxx::hash_map - -#endif class IBoundPropertyOwner; class CBaseEntity; -class CBoundPropertyModifier; // Property interface @@ -55,6 +43,7 @@ public: virtual bool rebuild( IBoundProperty* parent, bool triggerFn = true ) = 0; // Returns true if the rebuild changed the value of this property. void associate( IBoundPropertyOwner* owner, const CStrW& name ); void associate( IBoundPropertyOwner* owner, const CStrW& name, void (IBoundPropertyOwner::*updateFn)() ); + virtual ~IBoundProperty() {} }; // Specialize at least: @@ -66,7 +55,6 @@ template class CBoundProperty : public IBoundProperty { T m_data; bool m_inherited; - void (IBoundPropertyOwner::*m_updateFn)(); public: CBoundProperty() { m_inherited = true; } @@ -94,19 +82,40 @@ public: template class CBoundObjectProperty : public IBoundProperty, public T { bool m_inherited; - void (IBoundPropertyOwner::*m_updateFn)(); public: CBoundObjectProperty() { m_inherited = true; } CBoundObjectProperty( const T& copy ) : T( copy ) { m_inherited = false; } + T& operator=( const T& copy ) + { + IBoundPropertyOwner* sv_owner = m_owner; + void (IBoundPropertyOwner::*sv_updateFn)() = m_updateFn; + + (T&)*this = copy; + + m_owner = sv_owner; + m_updateFn = sv_updateFn; + m_inherited = false; + + return( *this ); + } void set( const jsval value ); jsval tojsval(); bool rebuild( IBoundProperty* parent, bool triggerFn = true ) { if( m_inherited && parent ) { + // Save some properties so they won't be overwritten + IBoundPropertyOwner* sv_owner = m_owner; + void (IBoundPropertyOwner::*sv_updateFn)() = m_updateFn; + *this = *( (CBoundObjectProperty*)parent ); + + m_owner = sv_owner; + m_updateFn = sv_updateFn; + m_inherited = true; + if( triggerFn && m_updateFn ) (m_owner->*m_updateFn)(); } @@ -119,7 +128,6 @@ public: template<> class CBoundProperty : public IBoundProperty { CBaseEntity* m_data; - void (IBoundPropertyOwner::*m_updateFn)(); public: CBoundProperty() { m_data = NULL; } @@ -129,16 +137,10 @@ public: operator CBaseEntity*() const { return( m_data ); } CBaseEntity*& operator=( CBaseEntity* copy ) { return( m_data = copy ); } -// Standard pointerish things + // Standard pointerish things CBaseEntity& operator*() { return( *m_data ); } CBaseEntity* operator->() { return( m_data ); } - - /* - CBoundProperty( uintptr_t ptr ) { m_data = (CBaseEntity*)ptr; } - CBaseEntity*& operator=( uintptr_t ptr ) { m_data = (CBaseEntity*)ptr; } - operator uintptr_t() { return( (uintptr_t)m_data ); } - */ void set( const jsval value ); jsval tojsval(); @@ -148,17 +150,38 @@ public: } }; -// e.g. Entities and their templates. - -class IBoundPropertyOwner +// A jsval property +template<> class CBoundProperty : public IBoundProperty { + jsval m_data; + bool m_inherited; public: - CBoundProperty m_base; - STL_HASH_MAP m_properties; - std::vector m_inheritors; - void rebuild( CStrW propName ); // Recursively rebuild just the named property over the inheritance tree. - void rebuild(); // Recursively rebuild everything over the inheritance tree. + CBoundProperty() { m_data = JSVAL_NULL; m_inherited = true; RootJSVal(); } + CBoundProperty( const jsval value ) { set( value ); m_inherited = false; RootJSVal(); } + CBoundProperty( CStrW value ) + { + m_data = STRING_TO_JSVAL( JS_NewUCStringCopyZ( g_ScriptingHost.getContext(), value ) ); + RootJSVal(); + } + ~CBoundProperty() + { + UprootJSVal(); + } + void set( const jsval value ) { UprootJSVal(); m_data = value; m_inherited = false; RootJSVal(); } + jsval tojsval() { return( m_data ); } + bool rebuild( IBoundProperty* parent, bool triggerFn = true ) + { + if( m_inherited && parent ) + { + UprootJSVal(); + m_data = ( (CBoundProperty*)parent )->m_data; + RootJSVal(); + } + return( !m_inherited ); + } + void RootJSVal() { if( JSVAL_IS_GCTHING( m_data ) ) JS_AddRoot( g_ScriptingHost.GetContext(), &m_data ); } + void UprootJSVal() { if( JSVAL_IS_GCTHING( m_data ) ) JS_RemoveRoot( g_ScriptingHost.GetContext(), &m_data ); } }; #endif - +*/ diff --git a/source/simulation/Scheduler.h b/source/simulation/Scheduler.h index 352029bb26..72e8248d72 100755 --- a/source/simulation/Scheduler.h +++ b/source/simulation/Scheduler.h @@ -82,4 +82,6 @@ struct CScheduler : public Singleton #define g_Scheduler CScheduler::GetSingleton() +extern const int ORDER_DELAY; + #endif diff --git a/source/simulation/scripting/JSInterface_BaseEntity.cpp b/source/simulation/scripting/JSInterface_BaseEntity.cpp index 41aa859213..c145af0f80 100755 --- a/source/simulation/scripting/JSInterface_BaseEntity.cpp +++ b/source/simulation/scripting/JSInterface_BaseEntity.cpp @@ -3,72 +3,3 @@ #include "JSInterface_BaseEntity.h" #include "BaseEntity.h" #include "EntityHandles.h" - -JSClass JSI_BaseEntity::JSI_class = { - "EntityTemplate", JSCLASS_HAS_PRIVATE, - JS_PropertyStub, JS_PropertyStub, - JSI_BaseEntity::getProperty, JSI_BaseEntity::setProperty, - JS_EnumerateStub, JS_ResolveStub, - JS_ConvertStub, JS_FinalizeStub, - NULL, NULL, NULL, NULL -}; - -JSPropertySpec JSI_BaseEntity::JSI_props[] = -{ - { 0 } -}; - -JSFunctionSpec JSI_BaseEntity::JSI_methods[] = -{ - { "toString", JSI_BaseEntity::toString, 0, 0, 0 }, - { 0 } -}; - -JSBool JSI_BaseEntity::getProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) -{ - CBaseEntity* e = (CBaseEntity*)JS_GetPrivate( cx, obj ); - CStrW propName = g_ScriptingHost.ValueToUCString( id ); - - if( e->m_properties.find( propName ) != e->m_properties.end() ) - { - *vp = e->m_properties[propName]->tojsval(); - return( JS_TRUE ); - } - else - JS_ReportError( cx, "No such property on %ls: %ls", (const wchar_t*)e->m_name, (const wchar_t*)propName ); - - return( JS_TRUE ); -} - -JSBool JSI_BaseEntity::setProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) -{ - CBaseEntity* e = (CBaseEntity*)JS_GetPrivate( cx, obj ); - CStrW propName = g_ScriptingHost.ValueToUCString( id ); - - if( e->m_properties.find( propName ) != e->m_properties.end() ) - { - e->m_properties[propName]->fromjsval( *vp ); - e->rebuild( propName ); - return( JS_TRUE ); - } - else - JS_ReportError( cx, "No such property on %ls: %ls", (const wchar_t*)e->m_name, (const wchar_t*)propName ); - - return( JS_TRUE ); -} - -void JSI_BaseEntity::init() -{ - g_ScriptingHost.DefineCustomObjectType( &JSI_class, NULL, 0, JSI_props, JSI_methods, NULL, NULL ); -} - -JSBool JSI_BaseEntity::toString( JSContext* cx, JSObject* obj, uintN UNUSEDPARAM(argc), jsval* UNUSEDPARAM(argv), jsval* rval ) -{ - CBaseEntity* e = (CBaseEntity*)JS_GetPrivate( cx, obj ); - - char buffer[256]; - snprintf( buffer, 256, "[object EntityTemplate: %ls]", (const wchar_t*)e->m_name ); - buffer[255] = 0; - *rval = STRING_TO_JSVAL( JS_NewStringCopyZ( cx, buffer ) ); - return( JS_TRUE ); -} diff --git a/source/simulation/scripting/JSInterface_BaseEntity.h b/source/simulation/scripting/JSInterface_BaseEntity.h index f6f4a2379d..b8e6f00356 100755 --- a/source/simulation/scripting/JSInterface_BaseEntity.h +++ b/source/simulation/scripting/JSInterface_BaseEntity.h @@ -11,6 +11,7 @@ #ifndef JSI_BASEENTITY_INCLUDED #define JSI_BASEENTITY_INCLUDED +/* namespace JSI_BaseEntity { JSBool toString( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ); @@ -24,5 +25,6 @@ namespace JSI_BaseEntity void finalize( JSContext* cx, JSObject* obj ); void init(); } +*/ #endif diff --git a/source/simulation/scripting/JSInterface_Entity.cpp b/source/simulation/scripting/JSInterface_Entity.cpp index db57baadab..1274ca0461 100755 --- a/source/simulation/scripting/JSInterface_Entity.cpp +++ b/source/simulation/scripting/JSInterface_Entity.cpp @@ -8,7 +8,9 @@ #include "EntityManager.h" #include "BaseEntityCollection.h" #include "CConsole.h" +#include "Scheduler.h" +/* JSClass JSI_Entity::JSI_class = { "Entity", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, @@ -26,6 +28,8 @@ JSPropertySpec JSI_Entity::JSI_props[] = JSFunctionSpec JSI_Entity::JSI_methods[] = { { "toString", JSI_Entity::toString, 0, 0, 0 }, + { "order", JSI_Entity::orderSingle, 1, 0, 0 }, + { "orderQueued", JSI_Entity::orderQueued, 1, 0, 0 }, { 0 } }; @@ -45,8 +49,18 @@ JSBool JSI_Entity::getProperty( JSContext* cx, JSObject* obj, jsval id, jsval* v return( JS_TRUE ); } else - JS_ReportError( cx, "No such property on %ls: %ls", (const wchar_t*)((*e)->m_name), (const wchar_t*)propName ); - return( JS_TRUE ); + { + // Don't show the message if we're using a function defined by the engine. + JSFunctionSpec* fnSpec = JSI_Entity::JSI_methods; + while( fnSpec->name ) + { + if( CStr8( propName ) == CStr8( fnSpec->name ) ) + return( JS_TRUE ); + fnSpec++; + } + JS_ReportError( cx, "No such property on %s: %s", CStr8((*e)->m_name).c_str(), CStr8(propName).c_str() ); + return( JS_TRUE ); + } } JSBool JSI_Entity::setProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) @@ -58,10 +72,11 @@ JSBool JSI_Entity::setProperty( JSContext* cx, JSObject* obj, jsval id, jsval* v { (*e)->m_properties[propName]->fromjsval( *vp ); (*e)->rebuild( propName ); - return( JS_TRUE ); } else - JS_ReportError( cx, "No such property on %ls: %ls", (const wchar_t*)((*e)->m_name), (const wchar_t*)propName ); + { + (*e)->addProperty( propName, *vp ); + } return( JS_TRUE ); } @@ -74,7 +89,7 @@ JSBool JSI_Entity::construct( JSContext* cx, JSObject* obj, unsigned int argc, j JSObject* jsBaseEntity = JSVAL_TO_OBJECT( argv[0] ); CStrW templateName; - if( !JSVAL_IS_OBJECT( argv[0] ) || !( baseEntity = (CBaseEntity*)JS_GetInstancePrivate( cx, jsBaseEntity, &JSI_BaseEntity::JSI_class, NULL ) ) ) + if( !JSVAL_IS_OBJECT( argv[0] ) || !( baseEntity = CBaseEntity::GetNative( cx, jsBaseEntity ) ) ) { try { @@ -91,7 +106,7 @@ JSBool JSI_Entity::construct( JSContext* cx, JSObject* obj, unsigned int argc, j if( !baseEntity ) { *rval = JSVAL_NULL; - JS_ReportError( cx, "No such template: %ls", (const wchar_t*)templateName ); + JS_ReportError( cx, "No such template: %ls", CStr8(templateName).c_str() ); return( JS_TRUE ); } JSI_Vector3D::Vector3D_Info* jsVector3D = NULL; @@ -123,6 +138,7 @@ JSBool JSI_Entity::construct( JSContext* cx, JSObject* obj, unsigned int argc, j void JSI_Entity::finalize( JSContext* cx, JSObject* obj ) { + JSClass* DebugInfo = JS_GetClass( obj ); delete( (HEntity*)JS_GetPrivate( cx, obj ) ); } @@ -143,3 +159,70 @@ JSBool JSI_Entity::toString( JSContext* cx, JSObject* obj, uintN argc, jsval* ar *rval = STRING_TO_JSVAL( JS_NewUCStringCopyZ( cx, utfbuf ) ); return( JS_TRUE ); } + +JSBool JSI_Entity::orderSingle( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ) +{ + return( order( cx, obj, argc, argv, rval, false ) ); +} + +JSBool JSI_Entity::orderQueued( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ) +{ + return( order( cx, obj, argc, argv, rval, true ) ); +} + +JSBool JSI_Entity::order( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval, bool queueOrder ) +{ + HEntity* e = (HEntity*)JS_GetPrivate( cx, obj ); + + // This needs to be sorted (uses Scheduler rather than network messaging) + assert( argc >= 1 ); + + int orderCode; + + try + { + orderCode = g_ScriptingHost.ValueToInt( argv[0] ); + } + catch( PSERROR_Scripting_ConversionFailed ) + { + *rval = JSVAL_FALSE; + JS_ReportError( cx, "Invalid order type" ); + return( JS_TRUE ); + } + + CEntityOrder newOrder; + + (int&)newOrder.m_type = orderCode; + + switch( orderCode ) + { + case CEntityOrder::ORDER_GOTO: + case CEntityOrder::ORDER_PATROL: + if( argc < 3 ) + { + *rval = JSVAL_FALSE; + JS_ReportError( cx, "Too few parameters" ); + return( JS_TRUE ); + } + try + { + newOrder.m_data[0].location.x = g_ScriptingHost.ValueToDouble( argv[1] ); + newOrder.m_data[0].location.y = g_ScriptingHost.ValueToDouble( argv[2] ); + } + catch( PSERROR_Scripting_ConversionFailed ) + { + *rval = JSVAL_FALSE; + JS_ReportError( cx, "Invalid location" ); + return( JS_TRUE ); + } + g_Scheduler.pushFrame( ORDER_DELAY, (*e)->me, new CMessageOrder( newOrder, queueOrder ) ); + *rval = JSVAL_TRUE; + return( JS_TRUE ); + default: + *rval = JSVAL_FALSE; + JS_ReportError( cx, "Invalid order type" ); + return( JS_TRUE ); + } +} + +*/ \ No newline at end of file diff --git a/source/simulation/scripting/JSInterface_Entity.h b/source/simulation/scripting/JSInterface_Entity.h index c16c86ac38..3c17f06fee 100755 --- a/source/simulation/scripting/JSInterface_Entity.h +++ b/source/simulation/scripting/JSInterface_Entity.h @@ -11,9 +11,13 @@ #ifndef JSI_ENTITY_INCLUDED #define JSI_ENTITY_INCLUDED +/* namespace JSI_Entity { JSBool toString( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ); + JSBool order( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval, bool queueOrder ); + JSBool orderSingle( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ); + JSBool orderQueued( JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval ); extern JSClass JSI_class; extern JSPropertySpec JSI_props[]; extern JSFunctionSpec JSI_methods[]; @@ -23,5 +27,6 @@ namespace JSI_Entity void finalize( JSContext* cx, JSObject* obj ); void init(); } +*/ #endif