This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/Combat/scripts.cpp

462 lines
12 KiB
C++
Raw Permalink Normal View History

/*
** 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 <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** 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 <stdio.h>
#include <win.h>
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<ScriptClass *> ScriptManager::ActiveScriptList;
SimpleDynVecClass<ScriptClass *> 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;
}