Adjusted how the fix for create_object_sid works

Added CreateObjectSidFix option.
This commit is contained in:
NovaRain
2021-05-02 11:59:51 +08:00
parent afc0cdd4c8
commit 81046daa9d
11 changed files with 74 additions and 26 deletions
+4
View File
@@ -604,6 +604,10 @@ StartGDialogFix=0
;num_attacks - the number of free action points on the first turn only
AttackComplexFix=0
;Set to 1 to enable the create_object_sid script function to run the 'start' script procedure when creating an object
;It is recommended to enable this option only if you are developing a new project (TC), otherwise it may affect some existing game scripts
CreateObjectSidFix=0
;Set to 1 to fix the issue with the division operator treating negative integers as unsigned
;Note: To perform the unsigned integer division, use the new 'div' operator
DivisionOperatorFix=1
+23 -4
View File
@@ -1804,10 +1804,12 @@ static void __declspec(naked) op_obj_can_hear_obj_hack() {
}
}
// correct signed division by 4
static void __declspec(naked) ai_best_weapon_hack() {
__asm {
sar edx, 31;
sub eax, edx
add edx, 3;
test eax, eax;
cmovs eax, edx;
sar eax, 1;
retn;
}
@@ -2974,6 +2976,8 @@ skip:
}
}
static bool createObjectSidStartFix = false;
static void __declspec(naked) op_create_object_sid_hack() {
static const char* proDbgMsg = "\nError: attempt to create object with PID of %d: %s!";
using Scripts::start;
@@ -2981,8 +2985,22 @@ static void __declspec(naked) op_create_object_sid_hack() {
mov ebx, [esp + 0x50 - 0x20 + 4]; // createObj
test ebx, ebx;
jz noObject;
mov ecx, [ebx + scriptId];
cmp ecx, -1;
jne init;
mov edx, ebx;
mov eax, esi;
retn;
init:
cmp createObjectSidStartFix, 0;
jne runStart;
call InitScript;
mov edx, ebx;
mov eax, esi;
retn;
runStart:
mov edx, start; // procedure
mov eax, [ebx + scriptId];
mov eax, ecx;
call exec_script_proc_;
end:
mov edx, ebx;
@@ -2990,7 +3008,7 @@ end:
retn;
noObject:
push [esi];
push [esp + 0x50 - 0x34 + 2*4]; // object pid
push [esp + 0x50 - 0x34 + 8]; // object pid
push proDbgMsg;
call debug_printf_;
add esp, 3*4;
@@ -3789,6 +3807,7 @@ void BugFixes_Init()
HookCall(0x4B6C13, checkAllRegions_hook);
// Fix for the script attached to an object not being initialized properly upon object creation
createObjectSidStartFix = (GetConfigInt("Misc", "CreateObjectSidFix", 0) != 0);
MakeCall(0x4551C0, op_create_object_sid_hack, 1);
// Fix the error handling in create_object_sid function to prevent a crash when the proto is missing
SafeWrite8(0x45507B, 0x51); // jz 0x4550CD
+2 -2
View File
@@ -155,9 +155,9 @@ void __declspec(naked) fo_interpretError(const char* fmt, ...) {
__asm jmp interpretError_;
}
long __fastcall fo_tile_num(long x, long y) {
long __stdcall fo_tile_num(long x, long y) {
__asm push ebx; // don't delete (bug in tile_num_)
WRAP_WATCOM_FCALL2(tile_num_, x, y)
WRAP_WATCOM_CALL2(tile_num_, x, y)
__asm pop ebx;
}
+1 -1
View File
@@ -393,7 +393,7 @@ void __stdcall fo_interpretReturnValue(TProgram* scriptPtr, DWORD val, DWORD val
// USE WITH CAUTION
void __declspec() fo_interpretError(const char* fmt, ...);
long __fastcall fo_tile_num(long x, long y);
long __stdcall fo_tile_num(long x, long y);
TGameObj* __fastcall obj_blocking_at_wrapper(TGameObj* obj, DWORD tile, DWORD elevation, void* func);
+2
View File
@@ -500,6 +500,8 @@ FUNC(report_explosion_, 0x413144)
FUNC(reset_box_bar_win_, 0x4614A0)
FUNC(roll_random_, 0x4A30C0)
FUNC(runProgram_, 0x46E154) // eax - programPtr, called once for each program after first loaded - hooks program to game and UI events
FUNC(scr_build_lookup_table_, 0x4A49D0)
FUNC(scr_exec_map_enter_scripts_, 0x4A67DC)
FUNC(scr_exec_map_exit_scripts_, 0x4A69A0)
FUNC(scr_exec_map_update_scripts_, 0x4A67E4)
FUNC(scr_find_first_at_, 0x4A6524) // eax - elevation, returns spatial scriptID
+3
View File
@@ -17,6 +17,8 @@
/*
For functions that have 3 or more arguments, it is preferable to use the fastcall calling convention
because the compiler builds the better/optimized code when calling the engine functions
NOTES: be careful when using fastcall for engine functions inside C++ loops, as VS2010 might generate unexpected glithes
*/
WRAP_WATCOM_FFUNC4(long, _word_wrap, const char*, text, int, maxWidth, DWORD*, buf, BYTE*, count)
WRAP_WATCOM_FFUNC3(long, ai_have_ammo, TGameObj*, critter, TGameObj*, item, TGameObj**, outAmmo)
@@ -179,6 +181,7 @@ WRAP_WATCOM_FUNC3(long, register_object_turn_towards, TGameObj*, object, long, t
WRAP_WATCOM_FUNC2(long, roll_random, long, minValue, long, maxValue)
WRAP_WATCOM_FUNC1(long*, runProgram, TProgram*, progPtr)
WRAP_WATCOM_FUNC1(long, selectWindowID, long, sWinID)
WRAP_WATCOM_FUNC1(void, scr_build_lookup_table, TScript*, script)
WRAP_WATCOM_FUNC1(TScript*, scr_find_first_at, long, elevation)
WRAP_WATCOM_FUNC0(TScript*, scr_find_next_at)
WRAP_WATCOM_FUNC1(TGameObj*, scr_find_obj_from_program, TProgram*, program)
+1 -1
View File
@@ -372,7 +372,7 @@ static void __stdcall GameInitialized(int initResult) { // OnAfterGameInit
}
static void __stdcall GameExit() { // OnGameExit
if (femaleMsgs > 1) RestoreCutsState();
if (femaleMsgs > 1) PlayerGenderCutsRestore();
}
static void __stdcall GameClose() { // OnBeforeGameClose
+11 -14
View File
@@ -33,6 +33,15 @@ static bool cutsPatch = false;
static std::vector<int> savPrototypes;
void PlayerGenderCutsRestore() {
if (cutsPatch) { // restore
SafeWrite32(0x43FA9F, FO_VAR_aTextSCuts);
SafeWrite32(0x44EB5B, FO_VAR_aTextSCutsS);
SafeWrite32(0x48152E, FO_VAR_aTextSCutsSS);
cutsPatch = false;
}
}
void CheckPlayerGender() {
isFemale = HeroIsFemale();
@@ -43,24 +52,12 @@ void CheckPlayerGender() {
SafeWrite32(0x43FA9F, (DWORD)cutsEndGameFemale);
SafeWrite32(0x44EB5B, (DWORD)cutsSubFemale);
SafeWrite32(0x48152E, (DWORD)cutsDeathFemale);
} else if (cutsPatch) {
SafeWrite32(0x43FA9F, FO_VAR_aTextSCuts);
SafeWrite32(0x44EB5B, FO_VAR_aTextSCutsS);
SafeWrite32(0x48152E, FO_VAR_aTextSCutsSS);
cutsPatch = false;
} else {
PlayerGenderCutsRestore();
}
}
}
void RestoreCutsState() {
if (cutsPatch) { // restore
SafeWrite32(0x43FA9F, FO_VAR_aTextSCuts);
SafeWrite32(0x44EB5B, FO_VAR_aTextSCutsS);
SafeWrite32(0x48152E, FO_VAR_aTextSCutsSS);
cutsPatch = false;
}
}
static const DWORD scr_get_dialog_msg_file_Back = 0x4A6BD2;
static void __declspec(naked) scr_get_dialog_msg_file_hack1() {
+1 -1
View File
@@ -27,4 +27,4 @@ void LoadOrder_art_get_name_hack();
extern long femaleMsgs;
void CheckPlayerGender();
void RestoreCutsState();
void PlayerGenderCutsRestore();
+21 -1
View File
@@ -995,9 +995,9 @@ void InitScriptProgram(sScriptProgram &prog, const char* fileName) {
TProgram* scriptPtr = fo_loadProgram(fileName);
if (scriptPtr) {
const char** procTable = ptr_procTableStrs;
prog.ptr = scriptPtr;
// fill lookup table
const char** procTable = ptr_procTableStrs;
for (int i = 0; i < Scripts::count; ++i) {
prog.procLookup[i] = fo_interpretFindProcedure(prog.ptr, procTable[i]);
}
@@ -1043,6 +1043,26 @@ bool __stdcall IsGameScript(const char* filename) {
return false; // script name was not found in scripts.lst
}
// loads and initializes script file (for normal game scripts)
long __fastcall InitScript(long sid) {
TScript* scriptPtr;
if (fo_scr_ptr(sid, &scriptPtr) == -1) return -1;
scriptPtr->program = fo_loadProgram((*ptr_scriptListInfo)[scriptPtr->scriptIdx & 0xFFFFFF].fileName);
if (!scriptPtr->program) return -1;
if (scriptPtr->program->flags & 0x124) return 0;
// fill lookup table
fo_scr_build_lookup_table(scriptPtr);
scriptPtr->flags |= 4 | 1; // init | loaded
scriptPtr->action = Scripts::no_p_proc;
scriptPtr->scriptOverrides = 0;
fo_runProgram(scriptPtr->program);
return 0;
}
static void LoadGlobalScriptsList() {
dlogr("Running global scripts...", DL_SCRIPT);
+5 -2
View File
@@ -65,8 +65,6 @@ long __stdcall SetGlobalVar(const char* var, int val);
long __stdcall GetGlobalVar(const char* var);
int __stdcall ScriptHasLoaded(TProgram* script);
// loads script from .int file into a sScriptProgram struct, filling script pointer and proc lookup table
// prog - reference to program structure
// fileName - the script file name without extension
@@ -93,6 +91,11 @@ void __stdcall AddTimerEventScripts(TProgram* script, long time, long param);
void __stdcall RemoveTimerEventScripts(TProgram* script, long param);
void __stdcall RemoveTimerEventScripts(TProgram* script);
int __stdcall ScriptHasLoaded(TProgram* script);
// loads and initializes script file (for normal game scripts)
long __fastcall InitScript(long sid);
// variables
extern DWORD availableGlobalScriptTypes;
extern bool alwaysFindScripts;