Files
sfall/sfall/ScriptExtender.cpp
T
NovaRain 50c91db5ea Moved part of AnimationsAtOnceInit code from main.cpp to AnimationsAtOnceLimit.cpp.
Changed the if condition for ProcessorIdle to make sure out-of-bounds values not resulting in unexpected but valid values.
2017-03-21 21:05:44 +08:00

1893 lines
48 KiB
C++

/*
* sfall
* Copyright (C) 2008-2016 The sfall team
*
* 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/>.
*/
#include "main.h"
#include <cassert>
#include <hash_map>
#include <set>
#include <string>
#include "Arrays.h"
#include "BarBoxes.h"
#include "Console.h"
#include "Define.h"
#include "Explosions.h"
#include "FalloutEngine.h"
#include "HookScripts.h"
#include "input.h"
#include "LoadGameHook.h"
#include "Logging.h"
#include "ScriptExtender.h"
#include "version.h"
#if (_MSC_VER < 1600)
#include "Cpp11_emu.h"
#endif
void _stdcall HandleMapUpdateForScripts(DWORD procId);
// variables for new opcodes
#define OP_MAX_ARGUMENTS (10)
// masks for argument validation
#define DATATYPE_MASK_INT (1 << DATATYPE_INT)
#define DATATYPE_MASK_FLOAT (1 << DATATYPE_FLOAT)
#define DATATYPE_MASK_STR (1 << DATATYPE_STR)
#define DATATYPE_MASK_NOT_NULL (0x00010000)
#define DATATYPE_MASK_VALID_OBJ (DATATYPE_MASK_INT | DATATYPE_MASK_NOT_NULL)
struct SfallOpcodeMetadata {
// opcode handler, will be used as key
void (*handler)();
// opcode name, only used for logging
const char* name;
// argument validation masks
int argTypeMasks[OP_MAX_ARGUMENTS];
};
typedef std::tr1::unordered_map<void(*)(), const SfallOpcodeMetadata*> OpcodeMetaTableType;
static OpcodeMetaTableType opcodeMetaTable;
class ScriptValue {
public:
ScriptValue(SfallDataType type, DWORD value) {
_val.dw = value;
_type = type;
}
ScriptValue() {
_val.dw = 0;
_type = DATATYPE_NONE;
}
ScriptValue(const char* strval) {
_val.str = strval;
_type = DATATYPE_STR;
}
ScriptValue(int val) {
_val.i = val;
_type = DATATYPE_INT;
}
ScriptValue(float strval) {
_val.f = strval;
_type = DATATYPE_FLOAT;
}
ScriptValue(TGameObj* obj) {
_val.gObj = obj;
_type = DATATYPE_INT;
}
bool isInt() const {
return _type == DATATYPE_INT;
}
bool isFloat() const {
return _type == DATATYPE_FLOAT;
}
bool isString() const {
return _type == DATATYPE_STR;
}
DWORD rawValue() const {
return _val.dw;
}
int asInt() const {
switch (_type) {
case DATATYPE_FLOAT:
return static_cast<int>(_val.f);
case DATATYPE_INT:
return _val.i;
default:
return 0;
}
}
float asFloat() const {
switch (_type) {
case DATATYPE_FLOAT:
return _val.f;
case DATATYPE_INT:
return static_cast<float>(_val.i);
default:
return 0.0;
}
}
const char* asString() const {
return (_type == DATATYPE_STR)
? _val.str
: "";
}
TGameObj* asObject() const {
return (_type == DATATYPE_INT)
? _val.gObj
: nullptr;
}
SfallDataType type() const {
return _type;
}
private:
union Value {
DWORD dw;
int i;
float f;
const char* str;
TGameObj* gObj;
} _val;
SfallDataType _type; // TODO: replace with enum class
} ;
typedef struct {
union Value {
DWORD dw;
int i;
float f;
const char* str;
TGameObj* gObj;
} val;
DWORD type; // TODO: replace with enum class
} ScriptValue1;
class OpcodeHandler {
public:
OpcodeHandler() {
_argShift = 0;
_args.reserve(OP_MAX_ARGUMENTS);
}
// number of arguments, possibly reduced by argShift
int numArgs() const {
return _args.size() - _argShift;
}
// returns current argument shift, default is 0
int argShift() const {
return _argShift;
}
// sets shift value for arguments
// for example if shift value is 2, then subsequent calls to arg(i) will return arg(i+2) instead, etc.
void setArgShift(int shift) {
assert(shift >= 0);
_argShift = shift;
}
// returns argument with given index, possible shifted by argShift
const ScriptValue& arg(int index) const {
return _args.at(index + _argShift);
}
// current return value
const ScriptValue& returnValue() const {
return _ret;
}
// current script program
TProgram* program() const {
return _program;
}
// set return value for current opcode
void setReturn(DWORD value, SfallDataType type) {
_ret = ScriptValue(type, value);
}
// set return value for current opcode
void setReturn(const ScriptValue& val) {
_ret = val;
}
// resets the state of handler for new opcode invocation
void resetState(TProgram* program, int argNum) {
_program = program;
// reset return value
_ret = ScriptValue();
// reset argument list
_args.resize(argNum);
// reset arg shift
_argShift = 0;
}
// writes error message to debug.log along with the name of script & procedure
void printOpcodeError(const char* fmt, ...) const {
assert(_program != nullptr);
va_list args;
va_start(args, fmt);
char msg[1024];
vsnprintf_s(msg, sizeof(msg), _TRUNCATE, fmt, args);
va_end(args);
const char* procName = FindCurrentProc(_program);
DebugPrintf("\nOPCODE ERROR: %s\n Current script: %s, procedure %s.", msg, _program->fileName, procName);
}
// Validate opcode arguments against type masks
// if validation pass, returns true, otherwise writes error to debug.log and returns false
bool validateArguments(const int argTypeMasks[], int argCount, const char* opcodeName) const {
for (int i = 0; i < argCount; i++) {
int typeMask = argTypeMasks[i];
const ScriptValue &argI = arg(i);
if (typeMask != 0 && ((1 << argI.type()) & typeMask) == 0) {
printOpcodeError(
"%s() - argument #%d has invalid type: %s.",
opcodeName,
i,
GetSfallTypeName(argI.type()));
return false;
} else if ((typeMask & DATATYPE_MASK_NOT_NULL) && argI.rawValue() == 0) {
printOpcodeError(
"%s() - argument #%d is null.",
opcodeName,
i);
return false;
}
}
return true;
}
// Handle opcodes
// scriptPtr - pointer to script program (from the engine)
// func - opcode handler
// hasReturn - true if opcode has return value (is expression)
void __thiscall handleOpcode(TProgram* program, void(*func)(), int argNum, bool hasReturn) {
assert(argNum < OP_MAX_ARGUMENTS);
// reset state after previous
resetState(program, argNum);
// process arguments on stack (reverse order)
for (int i = argNum - 1; i >= 0; i--) {
// get argument from stack
DWORD rawValueType = InterpretPopShort(program);
DWORD rawValue = InterpretPopLong(program);
SfallDataType type = static_cast<SfallDataType>(getSfallTypeByScriptType(rawValueType));
// retrieve string argument
if (type == DATATYPE_STR) {
_args.at(i) = InterpretGetString(program, rawValue, rawValueType);
} else {
_args.at(i) = ScriptValue(type, rawValue);
}
}
// flag that arguments passed are valid
bool argumentsValid = true;
// check if metadata is available
OpcodeMetaTableType::iterator it = opcodeMetaTable.find(func);
if (it != opcodeMetaTable.end()) {
const SfallOpcodeMetadata* meta = it->second;
// automatically validate argument types
argumentsValid = validateArguments(meta->argTypeMasks, argNum, meta->name);
}
// call opcode handler if arguments are valid (or no automatic validation was done)
if (argumentsValid) {
func();
}
// process return value
if (hasReturn) {
if (_ret.type() == DATATYPE_NONE) {
// if no value was set in handler, force return 0 to avoid stack error
_ret = ScriptValue(0);
}
DWORD rawResult = _ret.rawValue();
if (_ret.type() == DATATYPE_STR) {
rawResult = InterpretAddString(program, _ret.asString());
}
InterpretPushLong(program, rawResult);
InterpretPushShort(program, getScriptTypeBySfallType(_ret.type()));
}
}
private:
TProgram* _program;
int _argShift;
std::vector<ScriptValue> _args;
ScriptValue _ret;
};
static OpcodeHandler opHandler;
#include "ScriptOps\AsmMacros.h"
#include "ScriptOps\ScriptArrays.hpp"
#include "ScriptOps\ScriptUtils.hpp"
#include "ScriptOps\WorldmapOps.hpp"
#include "ScriptOps\PerksOp.hpp"
#include "ScriptOps\MemoryOp.hpp"
#include "ScriptOps\StatsOp.hpp"
#include "ScriptOps\InterfaceOp.hpp"
#include "ScriptOps\GraphicsOp.hpp"
#include "ScriptOps\FileSystemOps.hpp"
#include "ScriptOps\ObjectsOps.hpp"
#include "ScriptOps\AnimOps.hpp"
#include "ScriptOps\MiscOps.hpp"
#include "ScriptOps\MetaruleOp.hpp"
/*
Array for opcodes metadata.
This is completely optional, added for convenience only.
By adding opcode to this array, Sfall will automatically validate it's arguments using provided info.
On fail, errors will be printed to debug.log and opcode will not be executed.
If you don't include opcode in this array, you should take care of all argument validation inside handler itself.
*/
static const SfallOpcodeMetadata opcodeMetaArray[] = {
{sf_test, "validate_test", {DATATYPE_MASK_INT, DATATYPE_MASK_INT | DATATYPE_MASK_FLOAT, DATATYPE_MASK_STR, DATATYPE_NONE}},
{sf_spatial_radius, "spatial_radius", {DATATYPE_MASK_VALID_OBJ}},
{sf_critter_inven_obj2, "critter_inven_obj2", {DATATYPE_MASK_VALID_OBJ, DATATYPE_MASK_INT}},
//{op_message_str_game, {}}
};
static void InitOpcodeMetaTable() {
int length = sizeof(opcodeMetaArray) / sizeof(SfallOpcodeMetadata);
for (int i = 0; i < length; ++i) {
opcodeMetaTable[opcodeMetaArray[i].handler] = &opcodeMetaArray[i];
}
}
typedef void (_stdcall *regOpcodeProc)(WORD opcode,void* ptr);
static DWORD highlightingToggled=0;
static DWORD MotionSensorMode;
static BYTE toggleHighlightsKey;
static DWORD HighlightContainers;
static DWORD Color_Containers;
static int idle;
static char HighlightFail1[128];
static char HighlightFail2[128];
struct sGlobalScript {
sScriptProgram prog;
DWORD count;
DWORD repeat;
DWORD mode; //0 - local map loop, 1 - input loop, 2 - world map loop, 3 - local and world map loops
sGlobalScript() {}
sGlobalScript(sScriptProgram script) {
prog=script;
count=0;
repeat=0;
mode=0;
}
};
struct sExportedVar {
DWORD type; // in scripting engine terms, eg. VAR_TYPE_*
DWORD val;
sExportedVar() : val(0), type(VAR_TYPE_INT) {}
};
static std::vector<DWORD> checkedScripts;
static std::vector<sGlobalScript> globalScripts;
// a map of all sfall programs (global and hook scripts) by thier scriptPtr
typedef stdext::hash_map<DWORD, sScriptProgram> SfallProgsMap;
static SfallProgsMap sfallProgsMap;
// a map scriptPtr => self_obj to override self_obj for all script types using set_self
stdext::hash_map<DWORD, TGameObj*> selfOverrideMap;
typedef std::tr1::unordered_map<std::string, sExportedVar> ExportedVarsMap;
static ExportedVarsMap globalExportedVars;
DWORD isGlobalScriptLoading = 0;
stdext::hash_map<__int64, int> globalVars;
typedef stdext::hash_map<__int64, int> :: iterator glob_itr;
typedef stdext::hash_map<__int64, int> :: const_iterator glob_citr;
typedef std::pair<__int64, int> glob_pair;
static void* opcodes[0x300];
DWORD AddUnarmedStatToGetYear=0;
DWORD AvailableGlobalScriptTypes=0;
bool isGameLoading;
TScript OverrideScriptStruct;
//eax contains the script pointer, edx contains the opcode*4
//To read a value, mov the script pointer to eax, call interpretPopShort_, eax now contains the value type
//mov the script pointer to eax, call interpretPopLong_, eax now contains the value
//To return a value, move it to edx, mov the script pointer to eax, call interpretPushLong_
//mov the value type to edx, mov the script pointer to eax, call interpretPushShort_
static void _stdcall SetGlobalScriptRepeat2(DWORD script, DWORD frames) {
for(DWORD d=0;d<globalScripts.size();d++) {
if(globalScripts[d].prog.ptr==script) {
if(frames==0xFFFFFFFF) globalScripts[d].mode=!globalScripts[d].mode;
else globalScripts[d].repeat=frames;
break;
}
}
}
static void __declspec(naked) SetGlobalScriptRepeat() {
__asm {
push ebx;
push ecx;
push edx;
mov ecx, eax;
call interpretPopShort_;
mov edx, eax;
mov eax, ecx;
call interpretPopLong_;
cmp dx, 0xC001;
jnz end;
push eax;
push ecx;
call SetGlobalScriptRepeat2;
end:
pop edx;
pop ecx;
pop ebx;
retn;
}
}
static void _stdcall SetGlobalScriptType2(DWORD script, DWORD type) {
if(type>3) return;
for(DWORD d=0;d<globalScripts.size();d++) {
if(globalScripts[d].prog.ptr==script) {
globalScripts[d].mode=type;
break;
}
}
}
static void __declspec(naked) SetGlobalScriptType() {
__asm {
push ebx;
push ecx;
push edx;
mov ecx, eax;
call interpretPopShort_;
mov edx, eax;
mov eax, ecx;
call interpretPopLong_;
cmp dx, 0xC001;
jnz end;
push eax;
push ecx;
call SetGlobalScriptType2;
end:
pop edx;
pop ecx;
pop ebx;
retn;
}
}
static void __declspec(naked) GetGlobalScriptTypes() {
__asm {
push ebx;
push ecx;
push edx;
mov edx, AvailableGlobalScriptTypes;
mov ecx, eax;
call interpretPushLong_;
mov edx, 0xc001;
mov eax, ecx;
call interpretPushShort_;
pop edx;
pop ecx;
pop ebx;
retn;
}
}
static void SetGlobalVarInternal(__int64 var, int val) {
glob_itr itr=globalVars.find(var);
if (itr==globalVars.end()) globalVars.insert(glob_pair(var, val));
else {
if (val == 0) globalVars.erase(itr); // applies for both float 0.0 and integer 0
else itr->second=val;
}
}
static void _stdcall SetGlobalVar2(const char* var, int val) {
if(strlen(var)!=8) return;
SetGlobalVarInternal(*(__int64*)var, val);
}
static void _stdcall SetGlobalVar2Int(DWORD var, int val) {
SetGlobalVarInternal(var, val);
}
static void __declspec(naked) SetGlobalVar() {
__asm {
push ebx;
push ecx;
push edx;
push edi;
push esi;
mov edi, eax;
call interpretPopShort_;
mov eax, edi;
call interpretPopLong_;
mov esi, eax;
mov eax, edi;
call interpretPopShort_;
mov edx, eax;
mov eax, edi;
call interpretPopLong_;
cmp dx, 0x9001;
jz next;
cmp dx, 0x9801;
jz next;
cmp dx, 0xc001;
jnz end;
push esi;
push eax;
call SetGlobalVar2Int;
jmp end;
next:
mov ebx, eax;
mov eax, edi;
call interpretGetString_;
push esi;
push eax;
call SetGlobalVar2;
end:
pop esi;
pop edi;
pop edx;
pop ecx;
pop ebx;
retn;
}
}
static DWORD GetGlobalVarInternal(__int64 val) {
glob_citr itr=globalVars.find(val);
if(itr==globalVars.end()) return 0;
else return itr->second;
}
static DWORD _stdcall GetGlobalVar2(const char* var) {
if(strlen(var)!=8) return 0;
return GetGlobalVarInternal(*(__int64*)var);
}
static DWORD _stdcall GetGlobalVar2Int(DWORD var) {
return GetGlobalVarInternal(var);
}
static void __declspec(naked) GetGlobalVarInt() {
__asm {
push ebx;
push ecx;
push edx;
push edi;
push esi;
xor edx, edx;
mov edi, eax;
call interpretPopShort_;
mov esi, eax;
mov eax, edi;
call interpretPopLong_;
cmp si, 0x9001;
jz next;
cmp si, 0x9801;
jz next;
cmp si, 0xc001;
jnz end;
push eax;
call GetGlobalVar2Int;
mov edx, eax;
jmp end;
next:
mov edx, esi;
mov ebx, eax;
mov eax, edi;
call interpretGetString_;
push eax;
call GetGlobalVar2;
mov edx, eax;
end:
mov eax, edi;
call interpretPushLong_;
mov edx, 0xc001;
mov eax, edi;
call interpretPushShort_;
pop esi;
pop edi;
pop edx;
pop ecx;
pop ebx;
retn;
}
}
static void __declspec(naked) GetGlobalVarFloat() {
__asm {
push ebx;
push ecx;
push edx;
push edi;
push esi;
xor edx, edx;
mov edi, eax;
call interpretPopShort_;
mov esi, eax;
mov eax, edi;
call interpretPopLong_;
cmp si, 0x9001;
jz next;
cmp si, 0x9801;
jz next;
cmp si, 0xc001;
jnz end;
push eax;
call GetGlobalVar2Int;
mov edx, eax;
jmp end;
next:
mov edx, esi;
mov ebx, eax;
mov eax, edi;
call interpretGetString_;
push eax;
call GetGlobalVar2;
mov edx, eax;
end:
mov eax, edi;
call interpretPushLong_;
mov edx, 0xa001;
mov eax, edi;
call interpretPushShort_;
pop esi;
pop edi;
pop edx;
pop ecx;
pop ebx;
retn;
}
}
static void __declspec(naked) GetSfallArg() {
__asm {
pushad;
push eax;
call GetHSArg;
pop ecx;
mov edx, eax;
mov eax, ecx;
call interpretPushLong_;
mov eax, ecx;
mov edx, 0xc001;
call interpretPushShort_;
popad;
retn;
}
}
static DWORD _stdcall GetSfallArgs2() {
DWORD argCount=GetHSArgCount();
DWORD id=TempArray(argCount, 4);
DWORD* args = GetHSArgs();
for(DWORD i=0;i<argCount;i++) arrays[id].val[i].set(*(long*)&args[i]);
return id;
}
static void __declspec(naked) GetSfallArgs() {
__asm {
pushad;
push eax;
call GetSfallArgs2;
pop ecx;
mov edx, eax;
mov eax, ecx;
call interpretPushLong_;
mov eax, ecx;
mov edx, 0xc001;
call interpretPushShort_;
popad;
retn;
}
}
static void __declspec(naked) SetSfallArg() {
__asm {
pushad;
mov ecx, eax;
call interpretPopShort_;
mov edi, eax;
mov eax, ecx;
call interpretPopLong_;
mov edx, eax;
mov eax, ecx;
call interpretPopShort_;
mov esi, eax;
mov eax, ecx;
call interpretPopLong_;
cmp di, 0xc001;
jnz end;
cmp si, 0xc001;
jnz end;
push edx;
push eax;
call SetHSArg;
end:
popad;
retn;
}
}
static void __declspec(naked) SetSfallReturn() {
__asm {
push ebx;
push ecx;
push edx;
mov ecx, eax;
call interpretPopShort_;
mov edx, eax;
mov eax, ecx;
call interpretPopLong_;
cmp dx, 0xc001;
jnz end;
push eax;
call SetHSReturn;
end:
pop edx;
pop ecx;
pop ebx;
retn;
}
}
static void __declspec(naked) InitHook() {
__asm {
push ecx;
push edx;
mov ecx, eax;
mov edx, InitingHookScripts;
call interpretPushLong_;
mov eax, ecx;
mov edx, 0xc001;
call interpretPushShort_;
pop edx;
pop ecx;
retn;
}
}
static void _stdcall set_self2(DWORD script, TGameObj* obj) {
if (obj) {
selfOverrideMap[script] = obj;
} else {
stdext::hash_map<DWORD, TGameObj*>::iterator it = selfOverrideMap.find(script);
if (it != selfOverrideMap.end())
selfOverrideMap.erase(it);
}
}
static void __declspec(naked) set_self() {
__asm {
pushad;
mov ebp, eax;
call interpretPopShort_;
mov edi, eax;
mov eax, ebp;
call interpretPopLong_;
cmp di, 0xc001;
jnz end;
push eax;
push ebp;
call set_self2;
end:
popad;
retn;
}
}
static void __declspec(naked) register_hook() {
__asm {
pushad;
mov ebp, eax;
call interpretPopShort_;
mov edi, eax;
mov eax, ebp;
call interpretPopLong_;
cmp di, 0xc001;
jnz end;
push -1;
push eax;
push ebp;
call RegisterHook;
end:
popad;
retn;
}
}
static void __declspec(naked) register_hook_proc() {
_OP_BEGIN(ebp)
_GET_ARG_R32(ebp, ecx, esi)
_GET_ARG_R32(ebp, ebx, edi)
_CHECK_ARG_INT(cx, fail)
_CHECK_ARG_INT(bx, fail)
__asm {
push esi;
push edi;
push ebp;
call RegisterHook;
fail:
}
_OP_END
}
static void __declspec(naked) sfall_ver_major() {
_OP_BEGIN(ebp)
__asm {
mov eax, VERSION_MAJOR;
}
_RET_VAL_INT(ebp)
_OP_END
}
static void __declspec(naked) sfall_ver_minor() {
_OP_BEGIN(ebp)
__asm {
mov eax, VERSION_MINOR;
}
_RET_VAL_INT(ebp)
_OP_END
}
static void __declspec(naked) sfall_ver_build() {
_OP_BEGIN(ebp)
__asm {
mov eax, VERSION_BUILD;
}
_RET_VAL_INT(ebp)
_OP_END
}
//END OF SCRIPT FUNCTIONS
static const DWORD scr_find_sid_from_program=scr_find_sid_from_program_ + 5;
static const DWORD scr_ptr=scr_ptr_ + 5;
static const DWORD scr_find_obj_from_program=scr_find_obj_from_program_ + 7;
DWORD _stdcall FindSidHook2(DWORD script) {
stdext::hash_map<DWORD, TGameObj*>::iterator overrideIt = selfOverrideMap.find(script);
if (overrideIt != selfOverrideMap.end()) {
DWORD scriptId = overrideIt->second->scriptID;
if (scriptId != -1) {
selfOverrideMap.erase(overrideIt);
return scriptId; // returns the real scriptId of object if it is scripted
}
OverrideScriptStruct.self_obj = overrideIt->second;
OverrideScriptStruct.target_obj = overrideIt->second;
selfOverrideMap.erase(overrideIt); // this reverts self_obj back to original value for next function calls
return -2; // override struct
}
// this will allow to use functions like roll_vs_skill, etc without calling set_self (they don't really need self object)
if (sfallProgsMap.find(script) != sfallProgsMap.end()) {
OverrideScriptStruct.target_obj = OverrideScriptStruct.self_obj = 0;
return -2; // override struct
}
return -1; // change nothing
}
static void __declspec(naked) FindSidHook() {
__asm {
sub esp, 4;
pushad;
push eax;
call FindSidHook2;
mov [esp+32], eax; // to save value after popad
popad;
add esp, 4;
cmp [esp-4], -1;
jz end;
cmp [esp-4], -2;
jz override_script;
mov eax, [esp-4];
retn;
override_script:
test edx, edx;
jz end;
lea eax, OverrideScriptStruct;
mov [edx], eax;
mov eax, -2;
retn;
end:
push ebx;
push ecx;
push edx;
push esi;
push ebp;
jmp scr_find_sid_from_program;
}
}
static void __declspec(naked) ScrPtrHook() {
__asm {
cmp eax, -2;
jnz end;
xor eax, eax;
retn;
end:
push ebx;
push ecx;
push esi;
push edi;
push ebp;
jmp scr_ptr;
}
}
static void __declspec(naked) MainGameLoopHook() {
__asm {
push ebx;
push ecx;
push edx;
call get_input_
push eax;
call RunGlobalScripts1;
pop eax;
pop edx;
pop ecx;
pop ebx;
retn;
}
}
static void __declspec(naked) CombatLoopHook() {
__asm {
pushad;
call RunGlobalScripts1;
popad;
jmp get_input_
}
}
static void __declspec(naked) AfterCombatAttackHook() {
__asm {
pushad;
call AfterAttackCleanup;
popad;
mov eax, 1;
push 0x4230DA;
retn;
}
}
static void __declspec(naked) ExecMapScriptsHook() {
__asm {
sub esp, 32;
mov [esp+12], eax;
pushad;
push eax; // int procId
call HandleMapUpdateForScripts;
popad;
push 0x4A67F9; // jump back
retn;
}
}
static DWORD __stdcall GetGlobalExportedVarPtr(const char* name) {
std::string str(name);
ExportedVarsMap::iterator it = globalExportedVars.find(str);
//dlog_f("\n Trying to find exported var %s... ", DL_MAIN, name);
if (it != globalExportedVars.end()) {
sExportedVar *ptr = &it->second;
return (DWORD)ptr;
}
return 0;
}
static DWORD __stdcall CreateGlobalExportedVar(DWORD scr, const char* name) {
//dlog_f("\nTrying to export variable %s (%d)\n", DL_MAIN, name, isGlobalScriptLoading);
std::string str(name);
globalExportedVars[str] = sExportedVar(); // add new
return 1;
}
/*
when fetching/storing into exported variables, first search in our own hash table instead, then (if not found), resume normal search
reason for this: game frees all scripts and exported variables from memory when you exit map
so if you create exported variable in sfall global script, it will work until you exit map, after that you will get crashes
with this you can still use variables exported from global scripts even between map changes (per global scripts logic)
*/
static void __declspec(naked) Export_FetchOrStore_FindVar_Hook() {
__asm {
push eax;
push edx;
push ecx;
push ebx;
push edx // char* varName
call GetGlobalExportedVarPtr;
test eax, eax
jz proceedNormal;
pop ebx;
pop ecx;
pop edx;
pop edx;
sub eax, 0x24; // adjust pointer for the code
retn;
proceedNormal:
pop ebx;
pop ecx;
pop edx;
pop eax;
call findVar_
retn
}
}
static const DWORD Export_Export_FindVar_back1 = 0x4414CD;
static const DWORD Export_Export_FindVar_back2 = 0x4414AC;
static void __declspec(naked) Export_Export_FindVar_Hook() {
__asm {
pushad;
mov eax, isGlobalScriptLoading;
test eax, eax;
jz proceedNormal;
push edx; // char* - var name
push ebp; // script ptr
call CreateGlobalExportedVar;
popad;
jmp Export_Export_FindVar_back2; // if sfall exported var, jump to the end of function
proceedNormal:
popad;
call findVar_; // else - proceed normal
jmp Export_Export_FindVar_back1;
}
}
// this hook prevents sfall scripts from being removed after switching to another map, since normal script engine re-loads completely
static void _stdcall FreeProgramHook2(DWORD progPtr) {
if (isGameLoading || (sfallProgsMap.find(progPtr) == sfallProgsMap.end())) { // only delete non-sfall scripts or when actually loading the game
__asm {
mov eax, progPtr;
call interpretFreeProgram_;
}
}
}
static void __declspec(naked) FreeProgramHook() {
__asm {
pushad;
push eax;
call FreeProgramHook2;
popad;
retn;
}
}
static void __declspec(naked) obj_outline_all_items_on() {
__asm {
pushad
mov eax, dword ptr ds:[_map_elevation]
call obj_find_first_at_
loopObject:
test eax, eax
jz end
cmp eax, ds:[_outlined_object]
je nextObject
xchg ecx, eax
mov eax, [ecx+0x20]
and eax, 0xF000000
sar eax, 0x18
test eax, eax // This object is an item?
jnz nextObject // No
cmp dword ptr [ecx+0x7C], eax // Owned by someone?
jnz nextObject // Yes
test dword ptr [ecx+0x74], eax // Already outlined?
jnz nextObject // Yes
mov edx, 0x10 // yellow
test byte ptr [ecx+0x25], dl // NoHighlight_ flag is set (is this a container)?
jz NoHighlight // No
cmp HighlightContainers, eax // Highlight containers?
je nextObject // No
mov edx, Color_Containers // NR: should be set to yellow or purple later
NoHighlight:
mov [ecx+0x74], edx
nextObject:
call obj_find_next_at_
jmp loopObject
end:
call tile_refresh_display_
popad
retn
}
}
static void __declspec(naked) obj_outline_all_items_off() {
__asm {
pushad
mov eax, dword ptr ds:[_map_elevation]
call obj_find_first_at_
loopObject:
test eax, eax
jz end
cmp eax, ds:[_outlined_object]
je nextObject
xchg ecx, eax
mov eax, [ecx+0x20]
and eax, 0xF000000
sar eax, 0x18
test eax, eax // Is this an item?
jnz nextObject // No
cmp dword ptr [ecx+0x7C], eax // Owned by someone?
jnz nextObject // Yes
mov dword ptr [ecx+0x74], eax
nextObject:
call obj_find_next_at_
jmp loopObject
end:
call tile_refresh_display_
popad
retn
}
}
static void __declspec(naked) obj_remove_outline_hook() {
__asm {
call obj_remove_outline_
test eax, eax
jnz end
cmp highlightingToggled, 1
jne end
mov ds:[_outlined_object], eax
call obj_outline_all_items_on
end:
retn
}
}
void ScriptExtenderSetup() {
bool AllowUnsafeScripting = IsDebug
&& GetPrivateProfileIntA("Debugging", "AllowUnsafeScripting", 0, ".\\ddraw.ini") != 0;
toggleHighlightsKey = GetPrivateProfileIntA("Input", "ToggleItemHighlightsKey", 0, ini);
if (toggleHighlightsKey) {
MotionSensorMode = GetPrivateProfileIntA("Misc", "MotionScannerFlags", 1, ini);
HighlightContainers = GetPrivateProfileIntA("Input", "HighlightContainers", 0, ini);
switch (HighlightContainers) {
case 1:
Color_Containers = 0x10; // yellow
break;
case 2:
Color_Containers = 0x40; // purple
break;
}
//HookCall(0x44B9BA, &gmouse_bk_process_hook);
HookCall(0x44BD1C, &obj_remove_outline_hook);
HookCall(0x44E559, &obj_remove_outline_hook);
}
GetPrivateProfileStringA("Sfall", "HighlightFail1", "You aren't carrying a motion sensor.", HighlightFail1, 128, translationIni);
GetPrivateProfileStringA("Sfall", "HighlightFail2", "Your motion sensor is out of charge.", HighlightFail2, 128, translationIni);
idle = GetPrivateProfileIntA("Misc", "ProcessorIdle", -1, ini);
if (idle > -1 && idle <= 127) {
SafeWrite32(_idle_func, (DWORD)Sleep);
SafeWrite8(0x4C9F12, 0x6A); // push
SafeWrite8(0x4C9F13, idle);
}
modifiedIni = GetPrivateProfileIntA("Main", "ModifiedIni", 0, ini);
dlogr("Adding additional opcodes", DL_SCRIPT);
if(AllowUnsafeScripting) dlogr(" Unsafe opcodes enabled.", DL_SCRIPT);
else dlogr(" Unsafe opcodes disabled.", DL_SCRIPT);
arraysBehavior = GetPrivateProfileIntA("Misc", "arraysBehavior", 1, ini);
if (arraysBehavior > 0) {
arraysBehavior = 1; // only 1 and 0 allowed at this time
dlogr("New arrays behavior enabled.", DL_SCRIPT);
} else dlogr("Arrays in backward-compatiblity mode.", DL_SCRIPT);
SafeWrite32(0x46E370, 0x300); //Maximum number of allowed opcodes
SafeWrite32(0x46ce34, (DWORD)opcodes); //cmp check to make sure opcode exists
SafeWrite32(0x46ce6c, (DWORD)opcodes); //call that actually jumps to the opcode
SafeWrite32(0x46e390, (DWORD)opcodes); //mov that writes to the opcode
HookCall(0x480E7B, MainGameLoopHook); //hook the main game loop
HookCall(0x422845, CombatLoopHook); //hook the combat loop
MakeCall(0x4A390C, &FindSidHook, true);
MakeCall(0x4A5E34, &ScrPtrHook, true);
memset(&OverrideScriptStruct, 0, sizeof(TScript));
MakeCall(0x4230D5, &AfterCombatAttackHook, true);
MakeCall(0x4A67F2, &ExecMapScriptsHook, true);
// this patch makes it possible to export variables from sfall global scripts
MakeCall(0x4414C8, &Export_Export_FindVar_Hook, true);
HookCall(0x441285, &Export_FetchOrStore_FindVar_Hook); // store
HookCall(0x4413D9, &Export_FetchOrStore_FindVar_Hook); // fetch
// fix vanilla negate operator on float values
MakeCall(0x46AB63, &NegateFixHook, true);
// fix incorrect int-to-float conversion
// op_mult:
SafeWrite16(0x46A3F4, 0x04DB); // replace operator to "fild 32bit"
SafeWrite16(0x46A3A8, 0x04DB);
// op_div:
SafeWrite16(0x46A566, 0x04DB);
SafeWrite16(0x46A4E7, 0x04DB);
HookCall(0x46E141, FreeProgramHook);
if(AllowUnsafeScripting) {
opcodes[0x156]=ReadByte;
opcodes[0x157]=ReadShort;
opcodes[0x158]=ReadInt;
opcodes[0x159]=ReadString;
}
opcodes[0x15a]=SetPCBaseStat;
opcodes[0x15b]=SetPCExtraStat;
opcodes[0x15c]=GetPCBaseStat;
opcodes[0x15d]=GetPCExtraStat;
opcodes[0x15e]=SetCritterBaseStat;
opcodes[0x15f]=SetCritterExtraStat;
opcodes[0x160]=GetCritterBaseStat;
opcodes[0x161]=GetCritterExtraStat;
opcodes[0x162]=funcTapKey;
opcodes[0x163]=GetYear;
opcodes[0x164]=GameLoaded;
opcodes[0x165]=GraphicsFuncsAvailable;
opcodes[0x166]=funcLoadShader;
opcodes[0x167]=funcFreeShader;
opcodes[0x168]=funcActivateShader;
opcodes[0x169]=funcDeactivateShader;
opcodes[0x16a]=SetGlobalScriptRepeat;
opcodes[0x16b]=InputFuncsAvailable;
opcodes[0x16c]=KeyPressed;
opcodes[0x16d]=funcSetShaderInt;
opcodes[0x16e]=funcSetShaderFloat;
opcodes[0x16f]=funcSetShaderVector;
opcodes[0x170]=funcInWorldMap;
opcodes[0x171]=ForceEncounter;
opcodes[0x172]=SetWorldMapPos;
opcodes[0x173]=GetWorldMapXPos;
opcodes[0x174]=GetWorldMapYPos;
opcodes[0x175]=SetDMModel;
opcodes[0x176]=SetDFModel;
opcodes[0x177]=SetMoviePath;
for(int i=0x178;i<0x189;i++)
opcodes[i]=funcSetPerkValue;
opcodes[0x189]=funcSetPerkName;
opcodes[0x18a]=funcSetPerkDesc;
opcodes[0x18b]=SetPipBoyAvailable;
if(UsingExtraKillTypes()) {
opcodes[0x18c]=GetKillCounter2;
opcodes[0x18d]=ModKillCounter2;
} else {
opcodes[0x18c]=GetKillCounter;
opcodes[0x18d]=ModKillCounter;
}
opcodes[0x18e]=GetPerkOwed;
opcodes[0x18f]=SetPerkOwed;
opcodes[0x190]=GetPerkAvailable;
opcodes[0x191]=GetCritterAP;
opcodes[0x192]=SetCritterAP;
opcodes[0x193]=GetActiveHand;
opcodes[0x194]=ToggleActiveHand;
opcodes[0x195]=SetWeaponKnockback;
opcodes[0x196]=SetTargetKnockback;
opcodes[0x197]=SetAttackerKnockback;
opcodes[0x198]=RemoveWeaponKnockback;
opcodes[0x199]=RemoveTargetKnockback;
opcodes[0x19a]=RemoveAttackerKnockback;
opcodes[0x19b]=SetGlobalScriptType;
opcodes[0x19c]=GetGlobalScriptTypes;
opcodes[0x19d]=SetGlobalVar;
opcodes[0x19e]=GetGlobalVarInt;
opcodes[0x19f]=GetGlobalVarFloat;
opcodes[0x1a0]=fSetPickpocketMax;
opcodes[0x1a1]=fSetHitChanceMax;
opcodes[0x1a2]=fSetSkillMax;
opcodes[0x1a3]=EaxAvailable;
//opcodes[0x1a4]=SetEaxEnvironmentFunc;
opcodes[0x1a5]=IncNPCLevel;
opcodes[0x1a6]=GetViewportX;
opcodes[0x1a7]=GetViewportY;
opcodes[0x1a8]=SetViewportX;
opcodes[0x1a9]=SetViewportY;
opcodes[0x1aa]=SetXpMod;
opcodes[0x1ab]=SetPerkLevelMod;
opcodes[0x1ac]=GetIniSetting;
opcodes[0x1ad]=funcGetShaderVersion;
opcodes[0x1ae]=funcSetShaderMode;
opcodes[0x1af]=GetGameMode;
opcodes[0x1b0]=funcForceGraphicsRefresh;
opcodes[0x1b1]=funcGetShaderTexture;
opcodes[0x1b2]=funcSetShaderTexture;
opcodes[0x1b3]=funcGetTickCount;
opcodes[0x1b4]=SetStatMax;
opcodes[0x1b5]=SetStatMin;
opcodes[0x1b6]=SetCarTown;
opcodes[0x1b7]=fSetPCStatMax;
opcodes[0x1b8]=fSetPCStatMin;
opcodes[0x1b9]=fSetNPCStatMax;
opcodes[0x1ba]=fSetNPCStatMin;
opcodes[0x1bb]=fSetFakePerk;
opcodes[0x1bc]=fSetFakeTrait;
opcodes[0x1bd]=fSetSelectablePerk;
opcodes[0x1be]=fSetPerkboxTitle;
opcodes[0x1bf]=fIgnoreDefaultPerks;
opcodes[0x1c0]=fRestoreDefaultPerks;
opcodes[0x1c1]=fHasFakePerk;
opcodes[0x1c2]=fHasFakeTrait;
opcodes[0x1c3]=fAddPerkMode;
opcodes[0x1c4]=fClearSelectablePerks;
opcodes[0x1c5]=SetCritterHitChance;
opcodes[0x1c6]=SetBaseHitChance;
opcodes[0x1c7]=SetCritterSkillMod;
opcodes[0x1c8]=SetBaseSkillMod;
opcodes[0x1c9]=SetCritterPickpocket;
opcodes[0x1ca]=SetBasePickpocket;
opcodes[0x1cb]=SetPyromaniacMod;
opcodes[0x1cc]=fApplyHeaveHoFix;
opcodes[0x1cd]=SetSwiftLearnerMod;
opcodes[0x1ce]=SetLevelHPMod;
if(AllowUnsafeScripting) {
opcodes[0x1cf]=WriteByte;
opcodes[0x1d0]=WriteShort;
opcodes[0x1d1]=WriteInt;
for(int i=0x1d2;i<0x1dc;i++)
opcodes[i]=CallOffset;
}
opcodes[0x1dc]=ShowIfaceTag;
opcodes[0x1dd]=HideIfaceTag;
opcodes[0x1de]=IsIfaceTagActive;
opcodes[0x1df]=GetBodypartHitModifier;
opcodes[0x1e0]=SetBodypartHitModifier;
opcodes[0x1e1]=funcSetCriticalTable;
opcodes[0x1e2]=funcGetCriticalTable;
opcodes[0x1e3]=funcResetCriticalTable;
opcodes[0x1e4]=GetSfallArg;
opcodes[0x1e5]=SetSfallReturn;
opcodes[0x1e6]=SetApAcBonus;
opcodes[0x1e7]=GetApAcBonus;
opcodes[0x1e8]=SetApAcEBonus;
opcodes[0x1e9]=GetApAcEBonus;
opcodes[0x1ea]=InitHook;
opcodes[0x1eb]=GetIniString;
opcodes[0x1ec]=funcSqrt;
opcodes[0x1ed]=funcAbs;
opcodes[0x1ee]=funcSin;
opcodes[0x1ef]=funcCos;
opcodes[0x1f0]=funcTan;
opcodes[0x1f1]=funcATan;
opcodes[0x1f2]=SetPalette;
opcodes[0x1f3]=RemoveScript;
opcodes[0x1f4]=SetScript;
opcodes[0x1f5]=GetScript;
opcodes[0x1f6]=NBCreateChar;
opcodes[0x1f7]=fs_create;
opcodes[0x1f8]=fs_copy;
opcodes[0x1f9]=fs_find;
opcodes[0x1fa]=fs_write_byte;
opcodes[0x1fb]=fs_write_short;
opcodes[0x1fc]=fs_write_int;
opcodes[0x1fd]=fs_write_int;
opcodes[0x1fe]=fs_write_string;
opcodes[0x1ff]=fs_delete;
opcodes[0x200]=fs_size;
opcodes[0x201]=fs_pos;
opcodes[0x202]=fs_seek;
opcodes[0x203]=fs_resize;
opcodes[0x204]=get_proto_data;
opcodes[0x205]=set_proto_data;
opcodes[0x206]=set_self;
opcodes[0x207]=register_hook;
opcodes[0x208]=fs_write_bstring;
opcodes[0x209]=fs_read_byte;
opcodes[0x20a]=fs_read_short;
opcodes[0x20b]=fs_read_int;
opcodes[0x20c]=fs_read_float;
opcodes[0x20d]=list_begin;
opcodes[0x20e]=list_next;
opcodes[0x20f]=list_end;
opcodes[0x210]=sfall_ver_major;
opcodes[0x211]=sfall_ver_minor;
opcodes[0x212]=sfall_ver_build;
opcodes[0x213]=funcHeroSelectWin;
opcodes[0x214]=funcSetHeroRace;
opcodes[0x215]=funcSetHeroStyle;
opcodes[0x216]=set_critter_burst_disable;
opcodes[0x217]=get_weapon_ammo_pid;
opcodes[0x218]=set_weapon_ammo_pid;
opcodes[0x219]=get_weapon_ammo_count;
opcodes[0x21a]=set_weapon_ammo_count;
if(AllowUnsafeScripting) {
opcodes[0x21b]=WriteString;
}
opcodes[0x21c]=get_mouse_x;
opcodes[0x21d]=get_mouse_y;
opcodes[0x21e]=get_mouse_buttons;
opcodes[0x21f]=get_window_under_mouse;
opcodes[0x220]=get_screen_width;
opcodes[0x221]=get_screen_height;
opcodes[0x222]=stop_game;
opcodes[0x223]=resume_game;
opcodes[0x224]=create_message_window;
opcodes[0x225]=remove_trait;
opcodes[0x226]=get_light_level;
opcodes[0x227]=refresh_pc_art;
opcodes[0x228]=get_attack_type;
opcodes[0x229]=ForceEncounterWithFlags;
opcodes[0x22a]=set_map_time_multi;
opcodes[0x22b]=play_sfall_sound;
opcodes[0x22c]=stop_sfall_sound;
opcodes[0x22d]=create_array;
opcodes[0x22e]=set_array;
opcodes[0x22f]=get_array;
opcodes[0x230]=free_array;
opcodes[0x231]=len_array;
opcodes[0x232]=resize_array;
opcodes[0x233]=temp_array;
opcodes[0x234]=fix_array;
opcodes[0x235]=string_split;
opcodes[0x236]=list_as_array;
opcodes[0x237]=str_to_int;
opcodes[0x238]=str_to_flt;
opcodes[0x239]=scan_array;
opcodes[0x23a]=get_tile_pid;
opcodes[0x23b]=modified_ini;
opcodes[0x23c]=GetSfallArgs;
opcodes[0x23d]=SetSfallArg;
opcodes[0x23e]=force_aimed_shots;
opcodes[0x23f]=disable_aimed_shots;
opcodes[0x240]=mark_movie_played;
opcodes[0x241]=get_npc_level;
opcodes[0x242]=set_critter_skill_points;
opcodes[0x243]=get_critter_skill_points;
opcodes[0x244]=set_available_skill_points;
opcodes[0x245]=get_available_skill_points;
opcodes[0x246]=mod_skill_points_per_level;
opcodes[0x247]=set_perk_freq;
opcodes[0x248]=get_last_attacker;
opcodes[0x249]=get_last_target;
opcodes[0x24a]=block_combat;
opcodes[0x24b]=tile_under_cursor;
opcodes[0x24c]=gdialog_get_barter_mod;
opcodes[0x24d]=set_inven_ap_cost;
opcodes[0x24e]=op_substr;
opcodes[0x24f]=op_strlen;
opcodes[0x250]=op_sprintf;
opcodes[0x251]=op_ord;
// opcodes[0x252]= RESERVED
opcodes[0x253]=op_typeof;
opcodes[0x254]=op_save_array;
opcodes[0x255]=op_load_array;
opcodes[0x256]=op_get_array_key;
opcodes[0x257]=op_stack_array;
// opcodes[0x258]= RESERVED for arrays
// opcodes[0x259]= RESERVED for arrays
opcodes[0x25a]=op_reg_anim_destroy;
opcodes[0x25b]=op_reg_anim_animate_and_hide;
opcodes[0x25c]=op_reg_anim_combat_check;
opcodes[0x25d]=op_reg_anim_light;
opcodes[0x25e]=op_reg_anim_change_fid;
opcodes[0x25f]=op_reg_anim_take_out;
opcodes[0x260]=op_reg_anim_turn_towards;
opcodes[0x261]=op_explosions_metarule;
opcodes[0x262]=register_hook_proc;
opcodes[0x263]=funcPow;
opcodes[0x264]=funcLog;
opcodes[0x265]=funcExp;
opcodes[0x266]=funcCeil;
opcodes[0x267]=funcRound;
// opcodes[0x268]= RESERVED
// opcodes[0x269]= RESERVED
// opcodes[0x26a]=op_game_ui_redraw;
opcodes[0x26b]=op_message_str_game;
opcodes[0x26c]=op_sneak_success;
opcodes[0x26d]=op_tile_light;
opcodes[0x26e]=op_make_straight_path;
opcodes[0x26f]=op_obj_blocking_at;
opcodes[0x270]=op_tile_get_objects;
opcodes[0x271]=op_get_party_members;
opcodes[0x272]=op_make_path;
opcodes[0x273]=op_create_spatial;
opcodes[0x274]=op_art_exists;
opcodes[0x275]=op_obj_is_carrying_obj;
// universal opcodes
opcodes[0x276]=op_sfall_metarule0;
opcodes[0x277]=op_sfall_metarule1;
opcodes[0x278]=op_sfall_metarule2;
opcodes[0x279]=op_sfall_metarule3;
opcodes[0x27a]=op_sfall_metarule4;
opcodes[0x27b]=op_sfall_metarule5;
opcodes[0x27c]=op_sfall_metarule6; // if you need more arguments - use arrays
InitOpcodeMetaTable();
InitMetaruleTable();
}
DWORD GetScriptProcByName(DWORD scriptPtr, const char* procName) {
DWORD result;
__asm {
mov edx, procName;
mov eax, scriptPtr;
call interpretFindProcedure_;
mov result, eax;
}
return result;
}
// loads script from .int file into a sScriptProgram struct, filling script pointer and proc lookup table
void LoadScriptProgram(sScriptProgram &prog, const char* fileName) {
DWORD scriptPtr;
__asm {
mov eax, fileName;
call loadProgram_;
mov scriptPtr, eax;
}
if (scriptPtr) {
const char** procTable = (const char **)_procTableStrs;
prog.ptr = scriptPtr;
// fill lookup table
for (int i=0; i<=SCRIPT_PROC_MAX; i++) {
prog.procLookup[i] = GetScriptProcByName(prog.ptr, procTable[i]);
}
prog.initialized = 0;
} else {
prog.ptr = NULL;
}
}
void InitScriptProgram(sScriptProgram &prog) {
DWORD progPtr = prog.ptr;
if (prog.initialized == 0) {
__asm {
mov eax, progPtr;
call runProgram_;
mov edx, -1;
mov eax, progPtr;
call interpret_;
}
prog.initialized = 1;
}
}
void AddProgramToMap(sScriptProgram &prog) {
sfallProgsMap[prog.ptr] = prog;
}
sScriptProgram* GetGlobalScriptProgram(DWORD scriptPtr) {
for (std::vector<sGlobalScript>::iterator it = globalScripts.begin(); it != globalScripts.end(); it++) {
if (it->prog.ptr == scriptPtr) return &it->prog;
}
return NULL;
}
bool _stdcall isGameScript(const char* filename) {
for (DWORD i = 0; i < *(DWORD*)_maxScriptNum; i++) {
if (strcmp(filename, (char*)(*(DWORD*)_scriptListInfo + i*20)) == 0) return true;
}
return false;
}
// this runs after the game was loaded/started
void LoadGlobalScripts() {
isGameLoading = false;
HookScriptInit();
dlogr("Loading global scripts", DL_SCRIPT|DL_INIT);
char* name = "scripts\\gl*.int";
DWORD count, *filenames;
__asm {
xor ecx, ecx
xor ebx, ebx
lea edx, filenames
mov eax, name
call db_get_file_list_
mov count, eax
}
// TODO: refactor script programs
sScriptProgram prog;
for (DWORD i = 0; i < count; i++) {
name = _strlwr((char*)filenames[i]);
name[strlen(name) - 4] = 0;
if (!isGameScript(name)) {
dlog(">", DL_SCRIPT);
dlog(name, DL_SCRIPT);
isGlobalScriptLoading = 1;
LoadScriptProgram(prog, name);
if (prog.ptr) {
dlogr(" Done", DL_SCRIPT);
DWORD idx;
sGlobalScript gscript = sGlobalScript(prog);
idx = globalScripts.size();
globalScripts.push_back(gscript);
AddProgramToMap(prog);
// initialize script (start proc will be executed for the first time) -- this needs to be after script is added to "globalScripts" array
InitScriptProgram(prog);
} else dlogr(" Error!", DL_SCRIPT);
isGlobalScriptLoading = 0;
}
}
__asm {
xor edx, edx
lea eax, filenames
call db_free_file_list_
}
dlogr("Finished loading global scripts.", DL_SCRIPT|DL_INIT);
//ButtonsReload();
}
static DWORD _stdcall ScriptHasLoaded(DWORD script) {
for(DWORD d=0;d<checkedScripts.size();d++) {
if(checkedScripts[d]==script) return 0;
}
checkedScripts.push_back(script);
return 1;
}
// this runs before actually loading/starting the game
void ClearGlobalScripts() {
isGameLoading = true;
checkedScripts.clear();
sfallProgsMap.clear();
globalScripts.clear();
selfOverrideMap.clear();
globalExportedVars.clear();
HookScriptClear();
//Reset some settable game values back to the defaults
//Pyromaniac bonus
SafeWrite8(0x424AB6, 5);
//xp mod
SafeWrite8(0x004AFAB8, 0x53);
SafeWrite32(0x004AFAB9, 0x55575651);
//Perk level mod
SafeWrite32(0x00496880, 0x00019078);
//HP bonus
SafeWrite8(0x4AFBC1, 2);
//Stat ranges
StatsReset();
//Bodypart hit chances
*((DWORD*)0x510954) = GetPrivateProfileIntA("Misc", "BodyHit_Head", 0xFFFFFFD8, ini);
*((DWORD*)0x510958) = GetPrivateProfileIntA("Misc", "BodyHit_Left_Arm", 0xFFFFFFE2, ini);
*((DWORD*)0x51095C) = GetPrivateProfileIntA("Misc", "BodyHit_Right_Arm", 0xFFFFFFE2, ini);
*((DWORD*)0x510960) = GetPrivateProfileIntA("Misc", "BodyHit_Torso_Uncalled", 0x00000000, ini);
*((DWORD*)0x510964) = GetPrivateProfileIntA("Misc", "BodyHit_Right_Leg", 0xFFFFFFEC, ini);
*((DWORD*)0x510968) = GetPrivateProfileIntA("Misc", "BodyHit_Left_Leg", 0xFFFFFFEC, ini);
*((DWORD*)0x51096C) = GetPrivateProfileIntA("Misc", "BodyHit_Eyes", 0xFFFFFFC4, ini);
*((DWORD*)0x510970) = GetPrivateProfileIntA("Misc", "BodyHit_Groin", 0xFFFFFFE2, ini);
*((DWORD*)0x510974) = GetPrivateProfileIntA("Misc", "BodyHit_Torso_Uncalled", 0x00000000, ini);
//skillpoints per level mod
SafeWrite8(0x43C27a, 5);
}
void RunScriptProcByNum(DWORD sptr, DWORD procNum) {
__asm {
mov edx, procNum;
mov eax, sptr;
call executeProcedure_
}
}
void RunScriptProc(sScriptProgram* prog, const char* procName) {
DWORD sptr=prog->ptr;
DWORD procNum = GetScriptProcByName(sptr, procName);
if (procNum != -1) {
RunScriptProcByNum(sptr, procNum);
}
}
void RunScriptProc(sScriptProgram* prog, DWORD procId) {
if (procId > 0 && procId <= SCRIPT_PROC_MAX) {
DWORD sptr = prog->ptr;
DWORD procNum = prog->procLookup[procId];
if (procNum != -1) {
RunScriptProcByNum(sptr, procNum);
}
}
}
static void RunScript(sGlobalScript* script) {
script->count=0;
RunScriptProc(&script->prog, start); // run "start"
}
/**
Do some clearing after each frame:
- delete all temp arrays
- reset reg_anim_* combatstate checks
*/
static void ResetStateAfterFrame() {
if (tempArrays.size()) {
for (std::set<DWORD>::iterator it = tempArrays.begin(); it != tempArrays.end(); ++it)
FreeArray(*it);
tempArrays.clear();
}
RegAnimCombatCheck(1);
}
/**
Do some cleaning after each combat attack action
*/
void AfterAttackCleanup() {
ResetExplosionSettings();
}
static void RunGlobalScripts1() {
if (idle > -1 && idle <= 127) Sleep(idle);
if (toggleHighlightsKey) {
//0x48C294 to toggle
if (KeyDown(toggleHighlightsKey)) {
if (!highlightingToggled) {
if (MotionSensorMode & 4) {
DWORD scanner;
__asm {
mov eax, ds:[_obj_dude];
mov edx, PID_MOTION_SENSOR;
call inven_pid_is_carried_ptr_;
mov scanner, eax;
}
if (scanner) {
if (!(MotionSensorMode & 2)) {
__asm {
mov eax, scanner;
call item_m_dec_charges_; //Returns -1 if the item has no charges
call intface_redraw_;
inc eax;
mov highlightingToggled, eax;
}
if (!highlightingToggled) DisplayConsoleMessage(HighlightFail2);
} else highlightingToggled = 1;
} else {
DisplayConsoleMessage(HighlightFail1);
}
} else highlightingToggled = 1;
if (highlightingToggled) obj_outline_all_items_on();
else highlightingToggled = 2;
}
} else if (highlightingToggled) {
if (highlightingToggled == 1) obj_outline_all_items_off();
highlightingToggled = 0;
}
}
for (DWORD d = 0; d < globalScripts.size(); d++) {
if (!globalScripts[d].repeat || (globalScripts[d].mode != 0 && globalScripts[d].mode != 3)) continue;
if (++globalScripts[d].count >= globalScripts[d].repeat) {
RunScript(&globalScripts[d]);
}
}
ResetStateAfterFrame();
}
void RunGlobalScripts2() {
if (idle > -1 && idle <= 127) Sleep(idle);
for (DWORD d = 0; d < globalScripts.size(); d++) {
if (!globalScripts[d].repeat || globalScripts[d].mode != 1) continue;
if (++globalScripts[d].count >= globalScripts[d].repeat) {
RunScript(&globalScripts[d]);
}
}
ResetStateAfterFrame();
}
void RunGlobalScripts3() {
if (idle > -1 && idle <= 127) Sleep(idle);
for (DWORD d = 0; d < globalScripts.size(); d++) {
if (!globalScripts[d].repeat || (globalScripts[d].mode != 2 && globalScripts[d].mode != 3)) continue;
if (++globalScripts[d].count >= globalScripts[d].repeat) {
RunScript(&globalScripts[d]);
}
}
ResetStateAfterFrame();
}
void _stdcall HandleMapUpdateForScripts(DWORD procId) {
if (procId == map_enter_p_proc) {
// map changed, all game objects were destroyed and scripts detached, need to re-insert global scripts into the game
for (SfallProgsMap::iterator it = sfallProgsMap.begin(); it != sfallProgsMap.end(); it++) {
DWORD progPtr = it->second.ptr;
__asm {
mov eax, progPtr;
call runProgram_;
}
}
}
RunGlobalScriptsAtProc(procId); // gl* scripts of types 0 and 3
RunHookScriptsAtProc(procId); // all hs_ scripts
}
// run all global scripts of types 0 and 3 at specific procedure (if exist)
void _stdcall RunGlobalScriptsAtProc(DWORD procId) {
for (DWORD d = 0; d < globalScripts.size(); d++) {
if (globalScripts[d].mode != 0 && globalScripts[d].mode != 3) continue;
RunScriptProc(&globalScripts[d].prog, procId);
}
}
void LoadGlobals(HANDLE h) {
DWORD count, unused;
ReadFile(h, &count, 4, &unused, 0);
if(unused!=4) return;
sGlobalVar var;
for(DWORD i=0;i<count;i++) {
ReadFile(h, &var, sizeof(sGlobalVar), &unused, 0);
globalVars.insert(glob_pair(var.id, var.val));
}
}
void SaveGlobals(HANDLE h) {
DWORD count, unused;
count=globalVars.size();
WriteFile(h, &count, 4, &unused, 0);
sGlobalVar var;
glob_citr itr=globalVars.begin();
while(itr!=globalVars.end()) {
var.id=itr->first;
var.val=itr->second;
WriteFile(h, &var, sizeof(sGlobalVar), &unused, 0);
itr++;
}
}
void ClearGlobals() {
globalVars.clear();
for (array_itr it = arrays.begin(); it != arrays.end(); ++it)
it->second.clear();
arrays.clear();
savedArrays.clear();
}
int GetNumGlobals() { return globalVars.size(); }
void GetGlobals(sGlobalVar* globals) {
glob_citr itr=globalVars.begin();
int i=0;
while(itr!=globalVars.end()) {
globals[i].id=itr->first;
globals[i++].val=itr->second;
itr++;
}
}
void SetGlobals(sGlobalVar* globals) {
glob_itr itr=globalVars.begin();
int i=0;
while(itr!=globalVars.end()) {
itr->second=globals[i++].val;
itr++;
}
}
//fuctions to load and save appearance globals
void SetAppearanceGlobals(int race, int style) {
SetGlobalVar2("HAp_Race", race);
SetGlobalVar2("HApStyle", style);
}
void GetAppearanceGlobals(int *race, int *style) {
*race=GetGlobalVar2("HAp_Race");
*style=GetGlobalVar2("HApStyle");
}