/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /*********************************************************************************************** *** Confidential - Westwood Studios *** *********************************************************************************************** * * * Project Name : Commando * * * * $Archive:: /Commando/Code/Combat/scripts.cpp $* * * * $Author:: Greg_h $* * * * $Modtime:: 7/09/02 9:19a $* * * * $Revision:: 53 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "scripts.h" #include "debug.h" #include "scriptcommands.h" #include "physicalgameobj.h" #include "wwstring.h" #include "combat.h" #include "wwprofile.h" #include "ffactorylist.h" #include "rawfile.h" #include "gametype.h" #include #include ScriptCommands* EngineCommands = NULL; #if 1 #define SCRIPT_PROFILE_START( x ) WWProfileManager::Profile_Start( "Scripts" ); #define SCRIPT_PROFILE_STOP( x ) WWProfileManager::Profile_Stop( ); #else #define SCRIPT_PROFILE_START( x ) #define SCRIPT_PROFILE_STOP( x ) #endif /* ** */ HINSTANCE hDLL = NULL; LPFN_CREATE_SCRIPT ScriptManager::ScriptCreateFunct = NULL; LPFN_DESTROY_SCRIPT ScriptManager::ScriptDestroyFunct = NULL; SimpleDynVecClass ScriptManager::ActiveScriptList; SimpleDynVecClass ScriptManager::PendingDestroyList; bool ScriptManager::EnableScriptCreation = true; /* ** */ void ScriptManager::Init(void) { hDLL = NULL; EngineCommands = Get_Script_Commands(); #ifdef PARAM_EDITING_ON // Editor build Load_Scripts("SCRIPTS.DLL"); #else #ifdef WWDEBUG // DEBUG and PROFILE if ( DebugManager::Load_Debug_Scripts() ) { Load_Scripts("SCRIPTSD.DLL"); // DEBUG } else { #ifdef NDEBUG // PROFILE Load_Scripts("SCRIPTSP.DLL"); // PROFILE #else Load_Scripts("SCRIPTSD.DLL"); // DEBUG #endif } #else Load_Scripts("SCRIPTS.DLL"); // RELEASE #endif #endif } /* ** */ void ScriptManager::Shutdown(void) { // Release scripts while (ActiveScriptList.Count()) { ScriptClass* script = ActiveScriptList[0]; assert(script != NULL); assert(ScriptDestroyFunct != NULL); ScriptDestroyFunct(script); ActiveScriptList.Delete(0); } if (hDLL != NULL) { FreeLibrary(hDLL); hDLL = NULL; } } void ScriptManager::Destroy_Pending(void) { // Destroy all the scripts in the pending destroy list. while (PendingDestroyList.Count()) { ScriptClass* script = PendingDestroyList[0]; assert(script != NULL); // If the script has an owner then it must be detached before it // can be destroyed. ScriptableGameObj* object = script->Owner(); if (object != NULL) { object->Remove_Observer(script); } // Destroy the script assert(ScriptDestroyFunct != NULL); ScriptDestroyFunct(script); PendingDestroyList.Delete(0); } } /* ** */ void ScriptManager::Load_Scripts(const char* dll_filename) { Debug_Say(("Script Manager Loading Script File %s\n", dll_filename)); // If we're in multiplay and not the server, just bail if (!IS_SOLOPLAY && CombatManager::I_Am_Only_Client()) { return; } #ifndef PARAM_EDITING_ON // Only do this in the *game* // Check if we have a mod, if so, un-pack the scripts from the PKG (if present) FileFactoryClass * mod_pkg = FileFactoryListClass::Get_Instance()->Peek_Temp_FileFactory(); if (mod_pkg != NULL) { FileClass * scripts_dll = mod_pkg->Get_File( dll_filename ); if ((scripts_dll != NULL) && (scripts_dll->Is_Available())) { const char * _TMP_SCRIPTS_DLL_FILENAME = "_MOD_SCRIPTS.DLL"; scripts_dll->Open(FileClass::READ); RawFileClass unpacked_scripts(_TMP_SCRIPTS_DLL_FILENAME); if (unpacked_scripts.Create()) { unpacked_scripts.Open(FileClass::WRITE); // Copy the dll from the PKG (mix) file into our temporary _scripts directory static char buffer[16000]; int scripts_size = scripts_dll->Size(); int cur_pos = 0; while (cur_pos < scripts_size) { int read_count = WWMath::Min(scripts_size - cur_pos,sizeof(buffer)); scripts_dll->Read(buffer,read_count); unpacked_scripts.Write(buffer,read_count); cur_pos += read_count; } // change 'dll_filename' so that we load the newly created dll if (cur_pos == scripts_size) { dll_filename = _TMP_SCRIPTS_DLL_FILENAME; } unpacked_scripts.Close(); } scripts_dll->Close(); mod_pkg->Return_File(scripts_dll); } } #endif hDLL = LoadLibrary(dll_filename); if (hDLL == NULL) { Debug_Say(("Cound not load DLL file %s\n", dll_filename)); return; } // Get create script function ScriptCreateFunct = (LPFN_CREATE_SCRIPT)GetProcAddress(hDLL, LPSTR_CREATE_SCRIPT); assert(ScriptCreateFunct != NULL); if (!ScriptCreateFunct) { Debug_Say(("Cound not find Create_Script\n")); } // Get destroy script function ScriptDestroyFunct = (LPFN_DESTROY_SCRIPT)GetProcAddress(hDLL, LPSTR_DESTROY_SCRIPT); assert(ScriptDestroyFunct != NULL); if (!ScriptDestroyFunct) { Debug_Say(("Cound not find Destroy_Script\n")); } // Initialize request script destroy function LPFN_SET_REQUEST_DESTROY_FUNC set_request_destroy_func = (LPFN_SET_REQUEST_DESTROY_FUNC)GetProcAddress(hDLL, LPSTR_SET_REQUEST_DESTROY_FUNC); assert(set_request_destroy_func != NULL); if (set_request_destroy_func != NULL) { set_request_destroy_func(Request_Destroy_Script); } else { Debug_Say(("Cound not find Set_Request_Destroy_Func\n")); } // Initialize script commands if not being run from the editor if (CombatManager::Are_Observers_Active()) { LPFN_SET_SCRIPT_COMMANDS set_commands_func = (LPFN_SET_SCRIPT_COMMANDS)GetProcAddress(hDLL, LPSTR_SET_SCRIPT_COMMANDS); assert(set_commands_func != NULL); if (set_commands_func != NULL) { ScriptCommandsClass commands; commands.Commands = EngineCommands; bool success = set_commands_func(&commands); if (!success) { Debug_Say(("Failed to set script commands!\n")); // This should keep us from going to scripts! ScriptCreateFunct = NULL; } } else { Debug_Say(("Cound not find Set_Script_Commands\n")); } } } /* ** */ ScriptClass* ScriptManager::Create_Script(const char* script_name) { ScriptClass* script = NULL; if (EnableScriptCreation && ScriptCreateFunct != NULL) { script = ScriptCreateFunct(script_name); if (script != NULL) { script->Set_ID( GameObjObserverManager::Get_Next_Observer_ID() ); ActiveScriptList.Add(script); } } return script; } /* ** */ void ScriptManager::Request_Destroy_Script(ScriptClass* script) { ActiveScriptList.Delete(script); // Do not add the script to the destroy list if it is already there. for (int index = 0; index < PendingDestroyList.Count(); index++) { if (PendingDestroyList[index] == script) { return; } } PendingDestroyList.Add(script); } /* ** Script Manager Save and Load */ enum { CHUNKID_SCRIPT_ENTRY = 131001134, CHUNKID_SCRIPT_HEADER, CHUNKID_SCRIPT_DATA, MICROCHUNKID_NAME = 1, // Denzil 3/31/00 - This information is now saved by the script. #if(0) MICROCHUNKID_PARAM_COUNT, #endif MICROCHUNKID_PARAM, MICROCHUNKID_GAME_OBJ_OBSERVER_PTR, MICROCHUNKID_OWNER_PTR, MICROCHUNKID_ID, }; /* ** */ bool ScriptManager::Save(ChunkSaveClass& csave) { for (int index = 0; index < ActiveScriptList.Count(); index++) { ScriptClass* script = ActiveScriptList[ index ]; csave.Begin_Chunk( CHUNKID_SCRIPT_ENTRY ); csave.Begin_Chunk( CHUNKID_SCRIPT_HEADER ); StringClass name = script->Get_Name(); // Debug_Say(("Saving script '%s'\n", name)); WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_NAME, name ); char paramString[256]; script->Get_Parameters_String(paramString, sizeof(paramString)); // Debug_Say(("\tParameters: '%s'\n", paramString)); WRITE_MICRO_CHUNK_STRING(csave, MICROCHUNKID_PARAM, paramString); GameObjObserverClass* game_obj_observer_ptr = (GameObjObserverClass*)script; WRITE_MICRO_CHUNK( csave, MICROCHUNKID_GAME_OBJ_OBSERVER_PTR, game_obj_observer_ptr ); ScriptableGameObj* owner_ptr = *(script->Get_Owner_Ptr()); // Debug_Say(("\tObjectPtr: '%p'\n", *owner_ptr)); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_OWNER_PTR, owner_ptr ); int id = script->Get_ID(); WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ID, id ); // Debug_Say(( "Saved Script ID %d\n", id )); csave.End_Chunk(); // If data is not saved, script will be re-created if (CombatManager::Are_Observers_Active()) { csave.Begin_Chunk(CHUNKID_SCRIPT_DATA); ScriptSaver saver(csave); script->Save(saver); csave.End_Chunk(); } csave.End_Chunk(); } return true; } bool ScriptManager::Load( ChunkLoadClass & cload ) { WWASSERT( ActiveScriptList.Count() == 0 ); while (cload.Open_Chunk()) { GameObjObserverClass * game_obj_observer_ptr = NULL; PhysicalGameObj * owner_ptr = NULL; WWASSERT( cload.Cur_Chunk_ID() == CHUNKID_SCRIPT_ENTRY ); ScriptClass *script = NULL; // Load header cload.Open_Chunk(); WWASSERT( cload.Cur_Chunk_ID() == CHUNKID_SCRIPT_HEADER ); int obs_id = -1; // int param_index = 0; while (cload.Open_Micro_Chunk()) { int id = cload.Cur_Micro_Chunk_ID(); switch( id ) { case MICROCHUNKID_NAME: { StringClass name; LOAD_MICRO_CHUNK_WWSTRING( cload, name ); WWASSERT( script == NULL ); script = Create_Script( name ); if ( script == NULL ) { Debug_Say(( "Script %s not found \n", name )); } // A Missing script is not fatal // WWASSERT( script != NULL ); break; } case MICROCHUNKID_PARAM: { if ( script != NULL ) { StringClass param; LOAD_MICRO_CHUNK_WWSTRING( cload, param ); script->Set_Parameters_String(param); } break; } READ_MICRO_CHUNK( cload, MICROCHUNKID_GAME_OBJ_OBSERVER_PTR, game_obj_observer_ptr ); READ_MICRO_CHUNK( cload, MICROCHUNKID_OWNER_PTR, owner_ptr ); READ_MICRO_CHUNK( cload, MICROCHUNKID_ID, obs_id ); default: Debug_Say(( "Unrecognized ScriptCollection Header chunkID\n" )); break; } cload.Close_Micro_Chunk(); } cload.Close_Chunk(); if ( script != NULL ) { if ( obs_id != -1 ) { script->Set_ID( obs_id ); // Debug_Say(( "Loaded Script ID %d\n", obs_id )); } // If there is data, load if ( cload.Open_Chunk() ) { WWASSERT( cload.Cur_Chunk_ID() == CHUNKID_SCRIPT_DATA ); ScriptLoader loader( cload ); script->Load( loader ); cload.Close_Chunk(); } WWASSERT( game_obj_observer_ptr != NULL ); if ( game_obj_observer_ptr != NULL ) { SaveLoadSystemClass::Register_Pointer(game_obj_observer_ptr, (GameObjObserverClass *)script); } // set the owner, and request remap *(script->Get_Owner_Ptr()) = owner_ptr; REQUEST_POINTER_REMAP( (void **)script->Get_Owner_Ptr() ); } else { SaveLoadSystemClass::Register_Pointer(game_obj_observer_ptr, (GameObjObserverClass *)NULL); } cload.Close_Chunk(); } return true; }