diff --git a/source/graphics/MapReader.cpp b/source/graphics/MapReader.cpp index 0f1206c8dd..edaa5bd0a9 100644 --- a/source/graphics/MapReader.cpp +++ b/source/graphics/MapReader.cpp @@ -474,7 +474,7 @@ int CXMLReader::ReadEntities(XMBElement parent, double end_time) debug_warn("Invalid map XML data"); } - CBaseEntity* base = g_EntityTemplateCollection.getTemplate(TemplateName); + CBaseEntity* base = g_EntityTemplateCollection.getTemplate( TemplateName, g_Game->GetPlayer(PlayerID) ); if (! base) LOG(ERROR, LOG_CATEGORY, "Failed to load entity template '%ls'", TemplateName.c_str()); else diff --git a/source/scripting/ScriptGlue.cpp b/source/scripting/ScriptGlue.cpp index 1b9c4bf100..793e7be256 100644 --- a/source/scripting/ScriptGlue.cpp +++ b/source/scripting/ScriptGlue.cpp @@ -169,20 +169,28 @@ JSBool getEntityByHandle( JSContext* cx, JSObject*, uint argc, jsval* argv, jsva // returns: entity template object JSBool getEntityTemplate( JSContext* cx, JSObject*, uint argc, jsval* argv, jsval* rval ) { - REQUIRE_PARAMS(1, getEntityTemplate); + REQUIRE_MIN_PARAMS(1, getEntityTemplate); + REQUIRE_MAX_PARAMS(2, getEntityTemplate); *rval = JSVAL_NULL; CStrW templateName; + CPlayer* player = 0; + try { templateName = g_ScriptingHost.ValueToUCString( argv[0] ); + if( argc == 2 ) + { + player = ToNative( argv[1] ); + } } catch( PSERROR_Scripting_ConversionFailed ) { JS_ReportError( cx, "Invalid template identifier" ); return( JS_TRUE ); } - CBaseEntity* v = g_EntityTemplateCollection.getTemplate( templateName ); + + CBaseEntity* v = g_EntityTemplateCollection.getTemplate( templateName, player ); if( !v ) { JS_ReportError( cx, "No such template: %s", CStr8(templateName).c_str() ); diff --git a/source/scripting/ScriptableComplex.h b/source/scripting/ScriptableComplex.h index 248182b1dc..45c66f48ce 100644 --- a/source/scripting/ScriptableComplex.h +++ b/source/scripting/ScriptableComplex.h @@ -99,7 +99,7 @@ public: // Add a property (with immediate value) virtual void AddProperty( CStrW PropertyName, jsval Value ) = 0; virtual void AddProperty( CStrW PropertyName, CStrW Value ) = 0; - + inline IJSComplex() {} }; @@ -117,6 +117,7 @@ public: m_Owner = Owner; m_PropertyRoot = PropertyRoot; } + static JSObject* CreateAccessor( JSContext* cx, T* Owner, CStrW PropertyRoot ) { JSObject* Accessor = JS_NewObject( cx, &JSI_Class, NULL, NULL ); @@ -124,6 +125,7 @@ public: return( Accessor ); } + static JSBool JSGetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) { CJSComplexPropertyAccessor* Instance = (CJSComplexPropertyAccessor*)JS_GetPrivate( cx, obj ); @@ -135,6 +137,7 @@ public: return( JS_TRUE ); } + static JSBool JSSetProperty( JSContext* cx, JSObject* obj, jsval id, jsval* vp ) { CJSComplexPropertyAccessor* Instance = (CJSComplexPropertyAccessor*)JS_GetPrivate( cx, obj ); @@ -146,6 +149,7 @@ public: return( JS_TRUE ); } + static JSBool JSEnumerate( JSContext* cx, JSObject* obj, JSIterateOp enum_op, jsval* statep, jsid *idp ) { IJSComplex::IteratorState* it; @@ -204,6 +208,7 @@ public: } return( JS_FALSE ); } + static JSBool JSPrimitive( JSContext* cx, JSObject* obj, uintN UNUSED(argc), jsval* UNUSED(argv), jsval* rval ) { CJSComplexPropertyAccessor* Instance = (CJSComplexPropertyAccessor*)JS_GetPrivate( cx, obj ); @@ -227,6 +232,7 @@ public: return( JS_TRUE ); } + static JSBool JSToString( JSContext* cx, JSObject* obj, uintN UNUSED(argc), jsval* UNUSED(argv), jsval* rval ) { CJSComplexPropertyAccessor* Instance = (CJSComplexPropertyAccessor*)JS_GetPrivate( cx, obj ); @@ -253,7 +259,9 @@ public: return( JS_TRUE ); } + static JSClass JSI_Class; + static void ScriptingInit() { JSFunctionSpec JSI_methods[] = { { "valueOf", JSPrimitive, 0, 0, 0 }, { "toString", JSToString, 0, 0, 0 }, { 0 } }; @@ -698,12 +706,15 @@ public: JSI_methods[MethodID] = m_Methods[MethodID]; JSFunctionSpec watchAll = { "watchAll", SetWatchAll, 1, 0, 0 }; - JSI_methods[MethodID] = watchAll; + JSI_methods[MethodID + 0] = watchAll; + JSFunctionSpec unwatchAll = { "unwatchAll", UnWatchAll, 1, 0, 0 }; JSI_methods[MethodID + 1] = unwatchAll; + JSI_methods[MethodID + 2].name = 0; JSI_class.name = ClassName; + g_ScriptingHost.DefineCustomObjectType( &JSI_class, Constructor, ConstructorMinArgs, JSI_props, JSI_methods, NULL, NULL ); delete[]( JSI_methods ); diff --git a/source/simulation/BaseEntity.cpp b/source/simulation/BaseEntity.cpp index d82352f4b1..aa798dacdb 100644 --- a/source/simulation/BaseEntity.cpp +++ b/source/simulation/BaseEntity.cpp @@ -1,8 +1,10 @@ #include "precompiled.h" #include "BaseEntity.h" +#include "BaseEntityCollection.h" #include "ObjectManager.h" #include "CStr.h" +#include "Player.h" #include "ps/XML/Xeromyces.h" @@ -11,12 +13,14 @@ STL_HASH_SET CBaseEntity::scriptsLoaded; -CBaseEntity::CBaseEntity() +CBaseEntity::CBaseEntity( CPlayer* player ) { + m_player = player; m_base = NULL; AddProperty( L"tag", &m_Tag, false ); AddProperty( L"parent", &m_base, false ); + AddProperty( L"unmodified", &m_unmodified, false ); AddProperty( L"actions.move.speed_curr", &m_speed ); AddProperty( L"actions.move.turningradius", &m_turningRadius ); AddProperty( L"actions.move.run.speed", &m_runSpeed ); @@ -348,6 +352,16 @@ bool CBaseEntity::loadXML( CStr filename ) } } + + if( m_player == 0 ) + { + m_unmodified = this; + } + else + { + m_unmodified = g_EntityTemplateCollection.getTemplate( m_Tag, 0 ); + } + return true; } @@ -432,7 +446,10 @@ JSObject* CBaseEntity::GetScriptExecContext( IEventTarget* target ) jsval CBaseEntity::ToString( JSContext* cx, uintN UNUSED(argc), jsval* UNUSED(argv) ) { wchar_t buffer[256]; - swprintf( buffer, 256, L"[object EntityTemplate: %ls]", m_Tag.c_str() ); + if( m_player == 0 ) + swprintf( buffer, 256, L"[object EntityTemplate: %ls base]", m_Tag.c_str() ); + else + swprintf( buffer, 256, L"[object EntityTemplate: %ls for player %d]", m_Tag.c_str(), m_player->GetPlayerID() ); buffer[255] = 0; utf16string str16(buffer, buffer+wcslen(buffer)); return( STRING_TO_JSVAL( JS_NewUCStringCopyZ( cx, str16.c_str() ) ) ); diff --git a/source/simulation/BaseEntity.h b/source/simulation/BaseEntity.h index d56925c24a..5dc046c518 100644 --- a/source/simulation/BaseEntity.h +++ b/source/simulation/BaseEntity.h @@ -27,10 +27,15 @@ #include "ScriptObject.h" #include "XML/Xeromyces.h" +class CPlayer; + class CBaseEntity : public CJSComplex, public IEventTarget { public: - CBaseEntity(); + CPlayer* m_player; // Which player this template is for, or null for the no-player template + // used to read unmodified values for upgrades and such. + + CBaseEntity( CPlayer* player ); ~CBaseEntity(); // Load from XML bool loadXML( CStr filename ); @@ -38,11 +43,13 @@ public: void XMLLoadProperty( const CXeromyces& XeroFile, const XMBElement& Source, CStrW BasePropertyName ); // Base stats - CBaseEntity* m_base; CStrW m_corpse; bool m_extant; + // The unmodified, no-player version of this template + CBaseEntity* m_unmodified; + // The class types this entity has SClassSet m_classes; diff --git a/source/simulation/BaseEntityCollection.cpp b/source/simulation/BaseEntityCollection.cpp index 7028e6de4c..5fc2f68efc 100644 --- a/source/simulation/BaseEntityCollection.cpp +++ b/source/simulation/BaseEntityCollection.cpp @@ -5,10 +5,10 @@ #include "Model.h" #include "CLogger.h" #include "VFSUtil.h" +#include "Player.h" #define LOG_CATEGORY "entity" - void CBaseEntityCollection::LoadFile( const char* path ) { // Build the entity name -> filename mapping. This is done so that @@ -35,11 +35,14 @@ int CBaseEntityCollection::loadTemplates() return 0; } -CBaseEntity* CBaseEntityCollection::getTemplate( CStrW name ) +CBaseEntity* CBaseEntityCollection::getTemplate( CStrW name, CPlayer* player ) { + // Find player ID + int id = ( player == 0 ? NULL_PLAYER : player->GetPlayerID() ); + // Check whether this template has already been loaded - templateMap::iterator it = m_templates.find( name ); - if( it != m_templates.end() ) + templateMap::iterator it = m_templates[id].find( name ); + if( it != m_templates[id].end() ) return( it->second ); // Find the filename corresponding to this template @@ -50,7 +53,7 @@ CBaseEntity* CBaseEntityCollection::getTemplate( CStrW name ) CStr path( filename_it->second ); // Try to load to the entity - CBaseEntity* newTemplate = new CBaseEntity(); + CBaseEntity* newTemplate = new CBaseEntity( player ); if( !newTemplate->loadXML( path ) ) { LOG(ERROR, LOG_CATEGORY, "CBaseEntityCollection::loadTemplates(): Couldn't load template \"%s\"", path.c_str()); @@ -58,12 +61,12 @@ CBaseEntity* CBaseEntityCollection::getTemplate( CStrW name ) } LOG(NORMAL, LOG_CATEGORY, "CBaseEntityCollection::loadTemplates(): Loaded template \"%s\"", path.c_str()); - m_templates[name] = newTemplate; + m_templates[id][name] = newTemplate; // Load the entity's parent, if it has one if( newTemplate->m_Base_Name.Length() ) { - CBaseEntity* base = getTemplate( newTemplate->m_Base_Name ); + CBaseEntity* base = getTemplate( newTemplate->m_Base_Name, player ); if( base ) { newTemplate->m_base = base; @@ -89,6 +92,7 @@ void CBaseEntityCollection::getBaseEntityNames( std::vector& names ) CBaseEntityCollection::~CBaseEntityCollection() { - for( templateMap::iterator it = m_templates.begin(); it != m_templates.end(); ++it ) - delete( it->second ); + for( int id = 0; id < PS_MAX_PLAYERS + 2; id++ ) + for( templateMap::iterator it = m_templates[id].begin(); it != m_templates[id].end(); ++it ) + delete( it->second ); } diff --git a/source/simulation/BaseEntityCollection.h b/source/simulation/BaseEntityCollection.h index b0e66fa15d..084c271f0f 100644 --- a/source/simulation/BaseEntityCollection.h +++ b/source/simulation/BaseEntityCollection.h @@ -24,18 +24,23 @@ #include "Singleton.h" #include "ObjectEntry.h" #include "BaseEntity.h" +#include "Game.h" #define g_EntityTemplateCollection CBaseEntityCollection::GetSingleton() +#define NULL_PLAYER (PS_MAX_PLAYERS+1) + +class CPlayer; class CBaseEntityCollection : public Singleton { - typedef std::map templateMap; - typedef std::map templateFilenameMap; - templateMap m_templates; + typedef STL_HASH_MAP templateMap; + typedef STL_HASH_MAP templateFilenameMap; + + templateMap m_templates[PS_MAX_PLAYERS + 2]; templateFilenameMap m_templateFilenames; public: ~CBaseEntityCollection(); - CBaseEntity* getTemplate( CStrW entityType ); + CBaseEntity* getTemplate( CStrW entityType, CPlayer* player = 0 ); int loadTemplates(); void LoadFile( const char* path ); diff --git a/source/simulation/Entity.cpp b/source/simulation/Entity.cpp index 95f5b72120..5ae7f1d4d5 100644 --- a/source/simulation/Entity.cpp +++ b/source/simulation/Entity.cpp @@ -39,6 +39,7 @@ CEntity::CEntity( CBaseEntity* base, CVector3D position, float orientation, cons m_orientation.Y = orientation; m_ahead.x = sin( m_orientation.Y ); m_ahead.y = cos( m_orientation.Y ); + m_player = 0; // set sane default in case someone forgets to add this to the entity's // XML file, which is prone to happen. (prevents crash below when @@ -235,6 +236,12 @@ void CEntity::loadBase() } m_actor_transform_valid = false; + + if( m_player ) + { + // Make sure the actor has the right player colour + m_actor->SetPlayerID( m_player->GetPlayerID() ); + } } void CEntity::kill() @@ -507,7 +514,7 @@ void CEntity::update( size_t timestep ) m_orderQueue.pop_front(); break; default: - debug_warn("Invalid entity order" ); + debug_warn( "Invalid entity order" ); } } diff --git a/source/simulation/EntityManager.cpp b/source/simulation/EntityManager.cpp index 060d921082..12b871cf33 100644 --- a/source/simulation/EntityManager.cpp +++ b/source/simulation/EntityManager.cpp @@ -91,15 +91,9 @@ HEntity CEntityManager::create( CBaseEntity* base, CVector3D position, float ori return( HEntity( m_nextalloc++ ) ); } -HEntity CEntityManager::create( const CStrW templateName, CVector3D position, float orientation, const std::set& actorSelections ) +HEntity CEntityManager::createFoundation( CStrW templateName, CPlayer* player, CVector3D position, float orientation ) { - CBaseEntity* templateObj = g_EntityTemplateCollection.getTemplate( templateName ); - return( create( templateObj, position, orientation, actorSelections ) ); -} - -HEntity CEntityManager::createFoundation( CStrW templateName, CVector3D position, float orientation ) -{ - CBaseEntity* base = g_EntityTemplateCollection.getTemplate( templateName ); + CBaseEntity* base = g_EntityTemplateCollection.getTemplate( templateName, player ); debug_assert( base ); if( !base ) return HEntity(); diff --git a/source/simulation/EntityManager.h b/source/simulation/EntityManager.h index f9a000aec7..7a747c3194 100644 --- a/source/simulation/EntityManager.h +++ b/source/simulation/EntityManager.h @@ -51,9 +51,8 @@ public: ~CEntityManager(); HEntity create( CBaseEntity* base, CVector3D position, float orientation, const std::set& actorSelections ); - HEntity create( CStrW templateName, CVector3D position, float orientation, const std::set& actorSelections ); - HEntity createFoundation( CStrW templateName, CVector3D position, float orientation ); + HEntity createFoundation( CStrW templateName, CPlayer* player, CVector3D position, float orientation ); HEntity* getByHandle( u16 index ); CHandle *getHandle( int index ); diff --git a/source/simulation/PathfindEngine.cpp b/source/simulation/PathfindEngine.cpp index 2a4da04cac..b0cd583382 100644 --- a/source/simulation/PathfindEngine.cpp +++ b/source/simulation/PathfindEngine.cpp @@ -32,25 +32,28 @@ void CPathfindEngine::requestLowLevelPath( HEntity entity, const CVector2D& dest if ( mLowPathfinder.findPath(source, destination, entity->m_player) ) { std::vector path = mLowPathfinder.getLastPath(); - std::vector::iterator it; - CEntityOrder node; - for( it = path.begin(); (it+1) != path.end(); it++ ) + if( path.size() > 0 ) { - if ( !contact ) + std::vector::iterator it; + CEntityOrder node; + for( it = path.begin(); (it+1) != path.end(); it++ ) { - node.m_type = CEntityOrder::ORDER_GOTO_NOPATHING; - } - else - { - // TODO: Is this right? - node.m_type = CEntityOrder::ORDER_GOTO_NOPATHING; + if ( !contact ) + { + node.m_type = CEntityOrder::ORDER_GOTO_NOPATHING; + } + else + { + // TODO: Is this right? + node.m_type = CEntityOrder::ORDER_GOTO_NOPATHING; + } + node.m_data[0].location = *it; + entity->m_orderQueue.push_back(node); } + node.m_type = CEntityOrder::ORDER_PATH_END_MARKER; node.m_data[0].location = *it; entity->m_orderQueue.push_back(node); } - node.m_type = CEntityOrder::ORDER_PATH_END_MARKER; - node.m_data[0].location = *it; - entity->m_orderQueue.push_back(node); } else { diff --git a/source/simulation/Simulation.cpp b/source/simulation/Simulation.cpp index 68e2838cef..59a33be803 100644 --- a/source/simulation/Simulation.cpp +++ b/source/simulation/Simulation.cpp @@ -335,19 +335,17 @@ uint CSimulation::TranslateMessage(CNetMessage* pMsg, uint clientMask, void* UNU { CPlaceObject *msg = (CPlaceObject *) pMsg; + // Figure out the player + CPlayer* player = 0; + if(msg->m_Entities.size() > 0) + player = msg->m_Entities[0]->GetPlayer(); + else + player = g_Game->GetLocalPlayer(); + // Create the object CVector3D pos(msg->m_X/1000.0f, msg->m_Y/1000.0f, msg->m_Z/1000.0f); - HEntity newObj = g_EntityManager.createFoundation( msg->m_Template, pos, msg->m_Angle/1000.0f ); - - if(msg->m_Entities.size() > 0) - { - newObj->SetPlayer(msg->m_Entities[0]->GetPlayer()); - } - else - { - // Object might have been placed from the console just for testing - newObj->SetPlayer(g_Game->GetLocalPlayer()); - } + HEntity newObj = g_EntityManager.createFoundation( msg->m_Template, player, pos, msg->m_Angle/1000.0f ); + newObj->SetPlayer(player); // Order all the selected units to work on the new object using the given action order.m_type = CEntityOrder::ORDER_START_CONSTRUCTION;