scripts.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** Confidential - Westwood Studios ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : Commando *
  23. * *
  24. * $Archive:: /Commando/Code/Combat/scripts.cpp $*
  25. * *
  26. * $Author:: Greg_h $*
  27. * *
  28. * $Modtime:: 7/09/02 9:19a $*
  29. * *
  30. * $Revision:: 53 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "scripts.h"
  36. #include "debug.h"
  37. #include "scriptcommands.h"
  38. #include "physicalgameobj.h"
  39. #include "wwstring.h"
  40. #include "combat.h"
  41. #include "wwprofile.h"
  42. #include "ffactorylist.h"
  43. #include "rawfile.h"
  44. #include "gametype.h"
  45. #include <stdio.h>
  46. #include <win.h>
  47. ScriptCommands* EngineCommands = NULL;
  48. #if 1
  49. #define SCRIPT_PROFILE_START( x ) WWProfileManager::Profile_Start( "Scripts" );
  50. #define SCRIPT_PROFILE_STOP( x ) WWProfileManager::Profile_Stop( );
  51. #else
  52. #define SCRIPT_PROFILE_START( x )
  53. #define SCRIPT_PROFILE_STOP( x )
  54. #endif
  55. /*
  56. **
  57. */
  58. HINSTANCE hDLL = NULL;
  59. LPFN_CREATE_SCRIPT ScriptManager::ScriptCreateFunct = NULL;
  60. LPFN_DESTROY_SCRIPT ScriptManager::ScriptDestroyFunct = NULL;
  61. SimpleDynVecClass<ScriptClass *> ScriptManager::ActiveScriptList;
  62. SimpleDynVecClass<ScriptClass *> ScriptManager::PendingDestroyList;
  63. bool ScriptManager::EnableScriptCreation = true;
  64. /*
  65. **
  66. */
  67. void ScriptManager::Init(void)
  68. {
  69. hDLL = NULL;
  70. EngineCommands = Get_Script_Commands();
  71. #ifdef PARAM_EDITING_ON // Editor build
  72. Load_Scripts("SCRIPTS.DLL");
  73. #else
  74. #ifdef WWDEBUG // DEBUG and PROFILE
  75. if ( DebugManager::Load_Debug_Scripts() ) {
  76. Load_Scripts("SCRIPTSD.DLL"); // DEBUG
  77. } else {
  78. #ifdef NDEBUG // PROFILE
  79. Load_Scripts("SCRIPTSP.DLL"); // PROFILE
  80. #else
  81. Load_Scripts("SCRIPTSD.DLL"); // DEBUG
  82. #endif
  83. }
  84. #else
  85. Load_Scripts("SCRIPTS.DLL"); // RELEASE
  86. #endif
  87. #endif
  88. }
  89. /*
  90. **
  91. */
  92. void ScriptManager::Shutdown(void)
  93. {
  94. // Release scripts
  95. while (ActiveScriptList.Count()) {
  96. ScriptClass* script = ActiveScriptList[0];
  97. assert(script != NULL);
  98. assert(ScriptDestroyFunct != NULL);
  99. ScriptDestroyFunct(script);
  100. ActiveScriptList.Delete(0);
  101. }
  102. if (hDLL != NULL) {
  103. FreeLibrary(hDLL);
  104. hDLL = NULL;
  105. }
  106. }
  107. void ScriptManager::Destroy_Pending(void)
  108. {
  109. // Destroy all the scripts in the pending destroy list.
  110. while (PendingDestroyList.Count()) {
  111. ScriptClass* script = PendingDestroyList[0];
  112. assert(script != NULL);
  113. // If the script has an owner then it must be detached before it
  114. // can be destroyed.
  115. ScriptableGameObj* object = script->Owner();
  116. if (object != NULL) {
  117. object->Remove_Observer(script);
  118. }
  119. // Destroy the script
  120. assert(ScriptDestroyFunct != NULL);
  121. ScriptDestroyFunct(script);
  122. PendingDestroyList.Delete(0);
  123. }
  124. }
  125. /*
  126. **
  127. */
  128. void ScriptManager::Load_Scripts(const char* dll_filename)
  129. {
  130. Debug_Say(("Script Manager Loading Script File %s\n", dll_filename));
  131. // If we're in multiplay and not the server, just bail
  132. if (!IS_SOLOPLAY && CombatManager::I_Am_Only_Client())
  133. {
  134. return;
  135. }
  136. #ifndef PARAM_EDITING_ON // Only do this in the *game*
  137. // Check if we have a mod, if so, un-pack the scripts from the PKG (if present)
  138. FileFactoryClass * mod_pkg = FileFactoryListClass::Get_Instance()->Peek_Temp_FileFactory();
  139. if (mod_pkg != NULL) {
  140. FileClass * scripts_dll = mod_pkg->Get_File( dll_filename );
  141. if ((scripts_dll != NULL) && (scripts_dll->Is_Available())) {
  142. const char * _TMP_SCRIPTS_DLL_FILENAME = "_MOD_SCRIPTS.DLL";
  143. scripts_dll->Open(FileClass::READ);
  144. RawFileClass unpacked_scripts(_TMP_SCRIPTS_DLL_FILENAME);
  145. if (unpacked_scripts.Create()) {
  146. unpacked_scripts.Open(FileClass::WRITE);
  147. // Copy the dll from the PKG (mix) file into our temporary _scripts directory
  148. static char buffer[16000];
  149. int scripts_size = scripts_dll->Size();
  150. int cur_pos = 0;
  151. while (cur_pos < scripts_size) {
  152. int read_count = WWMath::Min(scripts_size - cur_pos,sizeof(buffer));
  153. scripts_dll->Read(buffer,read_count);
  154. unpacked_scripts.Write(buffer,read_count);
  155. cur_pos += read_count;
  156. }
  157. // change 'dll_filename' so that we load the newly created dll
  158. if (cur_pos == scripts_size) {
  159. dll_filename = _TMP_SCRIPTS_DLL_FILENAME;
  160. }
  161. unpacked_scripts.Close();
  162. }
  163. scripts_dll->Close();
  164. mod_pkg->Return_File(scripts_dll);
  165. }
  166. }
  167. #endif
  168. hDLL = LoadLibrary(dll_filename);
  169. if (hDLL == NULL) {
  170. Debug_Say(("Cound not load DLL file %s\n", dll_filename));
  171. return;
  172. }
  173. // Get create script function
  174. ScriptCreateFunct = (LPFN_CREATE_SCRIPT)GetProcAddress(hDLL, LPSTR_CREATE_SCRIPT);
  175. assert(ScriptCreateFunct != NULL);
  176. if (!ScriptCreateFunct) {
  177. Debug_Say(("Cound not find Create_Script\n"));
  178. }
  179. // Get destroy script function
  180. ScriptDestroyFunct = (LPFN_DESTROY_SCRIPT)GetProcAddress(hDLL, LPSTR_DESTROY_SCRIPT);
  181. assert(ScriptDestroyFunct != NULL);
  182. if (!ScriptDestroyFunct) {
  183. Debug_Say(("Cound not find Destroy_Script\n"));
  184. }
  185. // Initialize request script destroy function
  186. LPFN_SET_REQUEST_DESTROY_FUNC set_request_destroy_func =
  187. (LPFN_SET_REQUEST_DESTROY_FUNC)GetProcAddress(hDLL, LPSTR_SET_REQUEST_DESTROY_FUNC);
  188. assert(set_request_destroy_func != NULL);
  189. if (set_request_destroy_func != NULL) {
  190. set_request_destroy_func(Request_Destroy_Script);
  191. } else {
  192. Debug_Say(("Cound not find Set_Request_Destroy_Func\n"));
  193. }
  194. // Initialize script commands if not being run from the editor
  195. if (CombatManager::Are_Observers_Active()) {
  196. LPFN_SET_SCRIPT_COMMANDS set_commands_func =
  197. (LPFN_SET_SCRIPT_COMMANDS)GetProcAddress(hDLL, LPSTR_SET_SCRIPT_COMMANDS);
  198. assert(set_commands_func != NULL);
  199. if (set_commands_func != NULL) {
  200. ScriptCommandsClass commands;
  201. commands.Commands = EngineCommands;
  202. bool success = set_commands_func(&commands);
  203. if (!success) {
  204. Debug_Say(("Failed to set script commands!\n"));
  205. // This should keep us from going to scripts!
  206. ScriptCreateFunct = NULL;
  207. }
  208. } else {
  209. Debug_Say(("Cound not find Set_Script_Commands\n"));
  210. }
  211. }
  212. }
  213. /*
  214. **
  215. */
  216. ScriptClass* ScriptManager::Create_Script(const char* script_name)
  217. {
  218. ScriptClass* script = NULL;
  219. if (EnableScriptCreation && ScriptCreateFunct != NULL) {
  220. script = ScriptCreateFunct(script_name);
  221. if (script != NULL) {
  222. script->Set_ID( GameObjObserverManager::Get_Next_Observer_ID() );
  223. ActiveScriptList.Add(script);
  224. }
  225. }
  226. return script;
  227. }
  228. /*
  229. **
  230. */
  231. void ScriptManager::Request_Destroy_Script(ScriptClass* script)
  232. {
  233. ActiveScriptList.Delete(script);
  234. // Do not add the script to the destroy list if it is already there.
  235. for (int index = 0; index < PendingDestroyList.Count(); index++) {
  236. if (PendingDestroyList[index] == script) {
  237. return;
  238. }
  239. }
  240. PendingDestroyList.Add(script);
  241. }
  242. /*
  243. ** Script Manager Save and Load
  244. */
  245. enum {
  246. CHUNKID_SCRIPT_ENTRY = 131001134,
  247. CHUNKID_SCRIPT_HEADER,
  248. CHUNKID_SCRIPT_DATA,
  249. MICROCHUNKID_NAME = 1,
  250. // Denzil 3/31/00 - This information is now saved by the script.
  251. #if(0)
  252. MICROCHUNKID_PARAM_COUNT,
  253. #endif
  254. MICROCHUNKID_PARAM,
  255. MICROCHUNKID_GAME_OBJ_OBSERVER_PTR,
  256. MICROCHUNKID_OWNER_PTR,
  257. MICROCHUNKID_ID,
  258. };
  259. /*
  260. **
  261. */
  262. bool ScriptManager::Save(ChunkSaveClass& csave)
  263. {
  264. for (int index = 0; index < ActiveScriptList.Count(); index++) {
  265. ScriptClass* script = ActiveScriptList[ index ];
  266. csave.Begin_Chunk( CHUNKID_SCRIPT_ENTRY );
  267. csave.Begin_Chunk( CHUNKID_SCRIPT_HEADER );
  268. StringClass name = script->Get_Name();
  269. // Debug_Say(("Saving script '%s'\n", name));
  270. WRITE_MICRO_CHUNK_WWSTRING( csave, MICROCHUNKID_NAME, name );
  271. char paramString[256];
  272. script->Get_Parameters_String(paramString, sizeof(paramString));
  273. // Debug_Say(("\tParameters: '%s'\n", paramString));
  274. WRITE_MICRO_CHUNK_STRING(csave, MICROCHUNKID_PARAM, paramString);
  275. GameObjObserverClass* game_obj_observer_ptr = (GameObjObserverClass*)script;
  276. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_GAME_OBJ_OBSERVER_PTR, game_obj_observer_ptr );
  277. ScriptableGameObj* owner_ptr = *(script->Get_Owner_Ptr());
  278. // Debug_Say(("\tObjectPtr: '%p'\n", *owner_ptr));
  279. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_OWNER_PTR, owner_ptr );
  280. int id = script->Get_ID();
  281. WRITE_MICRO_CHUNK( csave, MICROCHUNKID_ID, id );
  282. // Debug_Say(( "Saved Script ID %d\n", id ));
  283. csave.End_Chunk();
  284. // If data is not saved, script will be re-created
  285. if (CombatManager::Are_Observers_Active()) {
  286. csave.Begin_Chunk(CHUNKID_SCRIPT_DATA);
  287. ScriptSaver saver(csave);
  288. script->Save(saver);
  289. csave.End_Chunk();
  290. }
  291. csave.End_Chunk();
  292. }
  293. return true;
  294. }
  295. bool ScriptManager::Load( ChunkLoadClass & cload )
  296. {
  297. WWASSERT( ActiveScriptList.Count() == 0 );
  298. while (cload.Open_Chunk()) {
  299. GameObjObserverClass * game_obj_observer_ptr = NULL;
  300. PhysicalGameObj * owner_ptr = NULL;
  301. WWASSERT( cload.Cur_Chunk_ID() == CHUNKID_SCRIPT_ENTRY );
  302. ScriptClass *script = NULL;
  303. // Load header
  304. cload.Open_Chunk();
  305. WWASSERT( cload.Cur_Chunk_ID() == CHUNKID_SCRIPT_HEADER );
  306. int obs_id = -1;
  307. // int param_index = 0;
  308. while (cload.Open_Micro_Chunk()) {
  309. int id = cload.Cur_Micro_Chunk_ID();
  310. switch( id ) {
  311. case MICROCHUNKID_NAME:
  312. {
  313. StringClass name;
  314. LOAD_MICRO_CHUNK_WWSTRING( cload, name );
  315. WWASSERT( script == NULL );
  316. script = Create_Script( name );
  317. if ( script == NULL ) {
  318. Debug_Say(( "Script %s not found \n", name ));
  319. }
  320. // A Missing script is not fatal
  321. // WWASSERT( script != NULL );
  322. break;
  323. }
  324. case MICROCHUNKID_PARAM:
  325. {
  326. if ( script != NULL ) {
  327. StringClass param;
  328. LOAD_MICRO_CHUNK_WWSTRING( cload, param );
  329. script->Set_Parameters_String(param);
  330. }
  331. break;
  332. }
  333. READ_MICRO_CHUNK( cload, MICROCHUNKID_GAME_OBJ_OBSERVER_PTR, game_obj_observer_ptr );
  334. READ_MICRO_CHUNK( cload, MICROCHUNKID_OWNER_PTR, owner_ptr );
  335. READ_MICRO_CHUNK( cload, MICROCHUNKID_ID, obs_id );
  336. default:
  337. Debug_Say(( "Unrecognized ScriptCollection Header chunkID\n" ));
  338. break;
  339. }
  340. cload.Close_Micro_Chunk();
  341. }
  342. cload.Close_Chunk();
  343. if ( script != NULL ) {
  344. if ( obs_id != -1 ) {
  345. script->Set_ID( obs_id );
  346. // Debug_Say(( "Loaded Script ID %d\n", obs_id ));
  347. }
  348. // If there is data, load
  349. if ( cload.Open_Chunk() ) {
  350. WWASSERT( cload.Cur_Chunk_ID() == CHUNKID_SCRIPT_DATA );
  351. ScriptLoader loader( cload );
  352. script->Load( loader );
  353. cload.Close_Chunk();
  354. }
  355. WWASSERT( game_obj_observer_ptr != NULL );
  356. if ( game_obj_observer_ptr != NULL ) {
  357. SaveLoadSystemClass::Register_Pointer(game_obj_observer_ptr, (GameObjObserverClass *)script);
  358. }
  359. // set the owner, and request remap
  360. *(script->Get_Owner_Ptr()) = owner_ptr;
  361. REQUEST_POINTER_REMAP( (void **)script->Get_Owner_Ptr() );
  362. } else {
  363. SaveLoadSystemClass::Register_Pointer(game_obj_observer_ptr, (GameObjObserverClass *)NULL);
  364. }
  365. cload.Close_Chunk();
  366. }
  367. return true;
  368. }