Compare commits

...
6 Commits
Author SHA1 Message Date
NovaRain 8b9ffd08a8 Minor edits to ddraw.ini 2019-07-18 09:27:28 +08:00
NovaRain b26208141f Changed DebugMode to not require enabling sfall debugging mode
Changed version to 3.8.19.1 as maintenance release.
2019-07-17 10:10:28 +08:00
NovaRain 147382de25 Minor code edits to LoadGameHook, PartyControl, ScriptExtender.cpp 2019-07-16 13:01:44 +08:00
NovaRain 9529208c7c Fixed the error handling on loading sfallgv.sav
* to improve backward compatibility with older saved games.
2019-07-15 20:51:29 +08:00
NovaRain fc3b7180af Backported the code of COMBATDAMAGE hook from 4.x
Moved instadeath crit fix from COMBATDAMAGE hook to BugFixes.cpp.
Completed the compute_attack struct.
Updated version number.
2019-07-15 09:59:58 +08:00
NovaRain f53aa16923 Excluded Awareness from perks resetting in PartyControl.cpp 2019-07-14 10:46:56 +08:00
11 changed files with 154 additions and 143 deletions
+7 -5
View File
@@ -1,5 +1,5 @@
;sfall configuration settings
;v3.8.19
;v3.8.19.1
[Main]
;Change to 1 if you want to use command line args to tell sfall to use another ini file.
@@ -15,7 +15,7 @@ UseCommandLine=0
NumSoundBuffers=0
;Set to 1 to allow attaching sound files to combat float messages
AllowSoundForFloats=1
AllowSoundForFloats=0
;Set to 1 to automatically search for alternative formats (mp3/wma/wav) when Fallout tries to play an acm
;Set to 2 to play alternative music files even if original acm files are not present in the music folder
@@ -173,7 +173,7 @@ FastMoveFromContainer=0
;A key to press to open a debug game editor
;Set to 0 to disable, or a DX scancode otherwise
;Requires sfall debugging mode and FalloutClient.exe from the modders pack
;Requires sfall debugging mode to be enabled and FalloutClient.exe from the modders pack
DebugEditorKey=0
;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
@@ -682,6 +682,7 @@ Enable=0
;Fallout 2 Debug Patch
;Set to 1 to send debug output to the screen, 2 to a debug.log file, or 3 to both the screen and a debug.log file
;Does not require enabling sfall debugging mode
;While you don't need to create an environment variable, you do still need to set the appropriate lines in fallout2.cfg:
;-------
;[debug]
@@ -700,12 +701,12 @@ DebugMode=0
SkipCompatModeCheck=0
;Set to 1 to skip the executable file size check
;Does not require sfall debugging mode
;Does not require enabling sfall debugging mode
SkipSizeCheck=0
;If you're testing changes to the Fallout exe, you can override the CRC that sfall looks for here
;You can use several hex values, separated by commas
;Does not require sfall debugging mode
;Does not require enabling sfall debugging mode
;ExtraCRC=0x00000000,0x00000000
;Set to 1 to stop Fallout from deleting non read-only protos at startup
@@ -716,6 +717,7 @@ DontDeleteProtos=0
AllowUnsafeScripting=0
;Set to 1 to hide error messages in debug output when a null value is passed to the function as an object
;Requires DebugMode to be enabled
HideObjIsNullMsg=0
;Set to 1 to force critters to display combat float messages
+18 -15
View File
@@ -176,12 +176,13 @@ bool LoadArrayElement(sArrayElement* el, HANDLE h)
return (el->len) ? (unused != el->len) : (unused != 4);
}
static bool LoadArraysOld(HANDLE h) {
dlogr("Loading arrays (old fmt)", DL_MAIN);
static long LoadArraysOld(HANDLE h) {
DWORD count, unused, id;
ReadFile(h, &count, 4, &unused, 0); // count of saved arrays
if (unused != 4) return true;
if (unused != 4) return -1;
if (!count) return 0;
dlogr("Loading arrays (old fmt)", DL_MAIN);
sArrayVarOld var;
sArrayVar varN;
@@ -189,7 +190,7 @@ static bool LoadArraysOld(HANDLE h) {
for (DWORD i = 0; i < count; i++) {
ReadFile(h, &id, 4, &unused, 0);
ReadFile(h, &var, 8, &unused, 0);
if (unused != 8) return true;
if (unused != 8) return -1;
var.types = new DWORD[var.len];
var.data = new char[var.len * var.datalen];
@@ -219,23 +220,25 @@ static bool LoadArraysOld(HANDLE h) {
arrays.insert(array_pair(id, varN));
savedArrays[varN.key] = id;
}
return false;
return 1;
}
bool LoadArrays(HANDLE h) {
long LoadArrays(HANDLE h) {
nextArrayID = 1;
if (LoadArraysOld(h)) return true;
dlogr("Loading arrays (new fmt)", DL_MAIN);
long result = LoadArraysOld(h);
if (result) return result;
DWORD count, unused, elCount;
ReadFile(h, &count, 4, &unused, 0); // count of saved arrays
if (unused != 4) return true;
if (unused != 4 && !result) return 1;
dlogr("Loading arrays (new fmt)", DL_MAIN);
if (unused != 4) return -1;
sArrayVar arrayVar;
for (DWORD i = 0; i < count; i++) {
if (LoadArrayElement(&arrayVar.key, h)) return true;
if (LoadArrayElement(&arrayVar.key, h)) return -1;
if (arrayVar.key.intVal == 0 || static_cast<long>(arrayVar.key.type) >= 4) { // partial compatibility with 3.4
arrayVar.key.intVal = static_cast<long>(arrayVar.key.type);
arrayVar.key.type = DATATYPE_INT;
@@ -243,12 +246,12 @@ bool LoadArrays(HANDLE h) {
ReadFile(h, &arrayVar.flags, 4, &unused, 0);
ReadFile(h, &elCount, 4, &unused, 0); // actual number of elements: keys+values
if (unused != 4) return true;
if (unused != 4) return -1;
bool isAssoc = arrayVar.isAssoc();
arrayVar.val.resize(elCount);
for (size_t j = 0; j < elCount; j++) { // normal and associative arrays stored and loaded equally
if (LoadArrayElement(&arrayVar.val[j], h)) return true;
if (LoadArrayElement(&arrayVar.val[j], h)) return -1;
if (isAssoc && (j % 2) == 0) { // only difference is that keyHash is filled with appropriate indexes
arrayVar.keyHash[arrayVar.val[j]] = j;
}
@@ -260,7 +263,7 @@ bool LoadArrays(HANDLE h) {
savedArrays[arrayVar.key] = nextArrayID++;
arrayVar.keyHash.clear();
}
return false;
return 0;
}
void SaveArrays(HANDLE h) {
+1 -1
View File
@@ -142,7 +142,7 @@ extern DWORD arraysBehavior;
// saved arrays: arrayKey => arrayId
extern ArrayKeysMap savedArrays;
bool LoadArrays(HANDLE h);
long LoadArrays(HANDLE h);
void SaveArrays(HANDLE h);
int GetNumArrays();
void GetArrays(int* arrays);
+27 -2
View File
@@ -47,7 +47,7 @@ void DrugsSaveFix(HANDLE file) {
bool DrugsLoadFix(HANDLE file) {
DWORD count, sizeRead;
ReadFile(file, &count, 4, &sizeRead, 0);
//if (sizeRead != 4) return true;
if (sizeRead != 4) return false;
for (DWORD i = 0; i < count; i++) {
DWORD pid;
ReadFile(file, &pid, 4, &sizeRead, 0);
@@ -1440,6 +1440,26 @@ end:
}
}
//zero damage insta death criticals fix (moved from compute_damage hook)
static void __fastcall InstantDeathFix(TComputeAttack &ctd) {
if (ctd.targetDamage == 0 && (ctd.targetFlags & DAM_DEAD)) {
ctd.targetDamage++; // set 1 hp damage
}
}
static const DWORD ComputeDamageRet = 0x424BA7;
static void __declspec(naked) compute_damage_hack() {
__asm {
mov ecx, esi; // ctd
call InstantDeathFix;
// overwritten engine code
add esp, 0x34;
pop ebp;
pop edi;
jmp ComputeDamageRet;
}
}
static int currDescLen = 0;
static bool showItemDescription = false;
static void __stdcall AppendText(const char* text, const char* desc) {
@@ -2224,7 +2244,7 @@ break:
void BugFixesInit()
{
#ifndef NDEBUG
if (isDebug && (GetPrivateProfileIntA("Debugging", "BugFixes", 1, ".\\ddraw.ini")) == 0)) return;
if (isDebug && (GetPrivateProfileIntA("Debugging", "BugFixes", 1, ".\\ddraw.ini") == 0)) return;
#endif
// fix vanilla negate operator on float values
@@ -2573,6 +2593,11 @@ void BugFixesInit()
// in their AI packages is set to stay_close/charge, or NPCsTryToSpendExtraAP is enabled
HookCall(0x42A1A8, ai_move_steps_closer_hook); // 0x42B24D
// Fix instant death critical
dlog("Applying instant death fix.", DL_INIT);
MakeJump(0x424BA2, compute_damage_hack);
dlogr(" Done", DL_INIT);
// Fix missing AC/DR mod stats when examining ammo in the barter screen
dlog("Applying fix for displaying ammo stats in barter screen.", DL_INIT);
MakeCall(0x49B4AD, obj_examine_func_hack_ammo0, 2);
+2 -1
View File
@@ -359,7 +359,8 @@ void DontDeleteProtosPatch() {
}
void DebugEditorInit() {
if (!isDebug) return;
DebugModePatch();
if (!isDebug) return;
DontDeleteProtosPatch();
}
+18 -11
View File
@@ -78,20 +78,27 @@ struct TGameObj
#pragma pack(push, 1)
struct TComputeAttack
{
TGameObj *attacker;
char gap_4[4];
TGameObj *weapon;
char gap_C[4];
long damageAttacker;
long flagsAttacker;
long rounds;
char gap_1C[4];
TGameObj *target;
TGameObj* attacker;
long hitMode;
TGameObj* weapon;
long field_C;
long attackerDamage;
long attackerFlags;
long numRounds;
long message;
TGameObj* target;
long targetTile;
long bodyPart;
long damageTarget;
long flagsTarget;
long targetDamage;
long targetFlags;
long knockbackValue;
TGameObj* mainTarget;
long numExtras;
TGameObj* extraTarget[6];
long extraBodyPart[6];
long extraDamage[6];
long extraFlags[6];
long extraKnockbackValue[6];
};
#pragma pack(pop)
+53 -76
View File
@@ -366,76 +366,53 @@ skip:
}
}
static void __declspec(naked) CombatDamageHook() {
// 4.x backport
static void __fastcall ComputeDamageHook_Script(TComputeAttack &ctd, DWORD rounds, DWORD multiplier) {
BeginHook();
argCount = 12;
args[0] = (DWORD)ctd.target; // Target
args[1] = (DWORD)ctd.attacker; // Attacker
args[2] = ctd.targetDamage; // amountTarget
args[3] = ctd.attackerDamage; // amountSource
args[4] = ctd.targetFlags; // flagsTarget
args[5] = ctd.attackerFlags; // flagsSource
args[6] = (DWORD)ctd.weapon;
args[7] = ctd.bodyPart;
args[8] = multiplier; // damage multiplier
args[9] = rounds; // number of rounds
args[10] = ctd.knockbackValue;
args[11] = ctd.hitMode; // attack type
RunHookScript(HOOK_COMBATDAMAGE);
if (cRet > 0) {
ctd.targetDamage = rets[0];
if (cRet > 1) {
ctd.attackerDamage = rets[1];
if (cRet > 2) {
ctd.targetFlags = rets[2]; // flagsTarget
if (cRet > 3) {
ctd.attackerFlags = rets[3]; // flagsSource
if (cRet > 4) ctd.knockbackValue = rets[4];
}
}
}
}
EndHook();
}
static void __declspec(naked) ComputeDamageHook() {
__asm {
push edx;
push ebx;
push eax;
push ecx;
push ebx; // store dmg multiplier args[8]
push edx; // store num of rounds args[9]
push eax; // store ctd
call compute_damage_;
pop edx;
//zero damage insta death criticals fix
mov ebx, [edx+0x2c];
test ebx, ebx;
jnz hookscript;
mov ebx, [edx+0x30];
test bl, 0x80;
jz hookscript;
inc dword ptr ds:[edx+0x2c];
hookscript:
hookbegin(12);
mov ebx, [edx+0x20];
mov args[0x00], ebx;
mov ebx, [edx+0x00];
mov args[0x04], ebx;
mov ebx, [edx+0x2c];
mov args[0x08], ebx;
mov ebx, [edx+0x10];
mov args[0x0c], ebx;
mov ebx, [edx+0x30];
mov args[0x10], ebx;
mov ebx, [edx+0x14];
mov args[0x14], ebx;
mov ebx, [edx+0x08];
mov args[0x18], ebx;
mov ebx, [edx+0x28];
mov args[0x1c], ebx;
pop ebx; // roll result
mov args[0x20], ebx;
pop ebx; // num rounds
mov args[0x24], ebx;
mov ebx, [edx+0x34]; // knockback value
mov args[0x28], ebx;
mov ebx, [edx+0x04]; // attack type
mov args[0x2c], ebx;
pushad;
push HOOK_COMBATDAMAGE;
call RunHookScript;
popad;
cmp cRet, 1;
jl end;
mov ebx, rets[0x00];
mov [edx+0x2c], ebx;
cmp cRet, 2;
jl end;
mov ebx, rets[0x04];
mov [edx+0x10], ebx;
cmp cRet, 3;
jl end;
mov ebx, rets[0x08];
mov [edx+0x30], ebx;
cmp cRet, 4;
jl end;
mov ebx, rets[0x0c];
mov [edx+0x14], ebx;
cmp cRet, 5;
jl end;
mov ebx, rets[0x10];
mov [edx+0x34], ebx; // knockback
end:
hookend;
pop ecx; // restore ctd (eax)
pop edx; // restore num of rounds
call ComputeDamageHook_Script; // stack - arg multiplier
pop ecx;
retn;
}
}
@@ -1486,14 +1463,14 @@ static void HookScriptInit2() {
HookCall(0x4109BF, &CalcDeathAnimHook2);
LoadHookScript("hs_combatdamage", HOOK_COMBATDAMAGE);
HookCall(0x42326C, &CombatDamageHook); // check_ranged_miss()
HookCall(0x4233E3, &CombatDamageHook); // shoot_along_path() - for extra burst targets
HookCall(0x423AB7, &CombatDamageHook); // compute_attack()
HookCall(0x423BBF, &CombatDamageHook); // compute_attack()
HookCall(0x423DE7, &CombatDamageHook); // compute_explosion_on_extras()
HookCall(0x423E69, &CombatDamageHook); // compute_explosion_on_extras()
HookCall(0x424220, &CombatDamageHook); // attack_crit_failure()
HookCall(0x4242FB, &CombatDamageHook); // attack_crit_failure()
HookCall(0x42326C, ComputeDamageHook); // check_ranged_miss()
HookCall(0x4233E3, ComputeDamageHook); // shoot_along_path() - for extra burst targets
HookCall(0x423AB7, ComputeDamageHook); // compute_attack()
HookCall(0x423BBF, ComputeDamageHook); // compute_attack()
HookCall(0x423DE7, ComputeDamageHook); // compute_explosion_on_extras()
HookCall(0x423E69, ComputeDamageHook); // compute_explosion_on_extras()
HookCall(0x424220, ComputeDamageHook); // attack_crit_failure()
HookCall(0x4242FB, ComputeDamageHook); // attack_crit_failure()
LoadHookScript("hs_ondeath", HOOK_ONDEATH);
HookCall(0x4130CC, &OnDeathHook);
+6 -4
View File
@@ -129,8 +129,8 @@ static void _stdcall SaveGame2() {
} else {
goto errorSave;
}
return;
//////////////////////////////////////////////////
errorSave:
dlog_f("ERROR creating: %s\n", DL_MAIN, buf);
@@ -198,18 +198,20 @@ static bool _stdcall LoadGame2_Before() {
if (uID > UID_START) objUniqueID = uID;
ReadFile(h, &data, 4, &size, 0);
SetAddedYears(data >> 16);
if (size != 4 || !PerksLoad(h) || LoadArrays(h)) goto errorLoad;
if (DrugsLoadFix(h)) goto errorLoad;
if (size != 4 || !PerksLoad(h)) goto errorLoad;
long result = LoadArrays(h); // 1 - old save, -1 - broken save
if (result == -1 || (!result && DrugsLoadFix(h))) goto errorLoad;
CloseHandle(h);
} else {
dlogr("Cannot open sfallgv.sav - assuming non-sfall save.", DL_MAIN);
}
return false;
//////////////////////////////////////////////////
errorLoad:
CloseHandle(h);
dlog_f("ERROR reading data: %s\n", DL_MAIN, buf);
DebugPrintf("\n[SFALL] ERROR reading data: %s", buf);
return (true & !isDebug);
}
+5 -5
View File
@@ -124,8 +124,8 @@ static void TakeControlOfNPC(TGameObj* npc) {
// reset traits
ptr_pc_traits[0] = ptr_pc_traits[1] = -1;
// reset perks
for (int i = 0; i < PERK_count; i++) {
// reset perks (except Awareness)
for (int i = 1; i < PERK_count; i++) {
(*ptr_perkLevelDataList)[i] = 0;
}
@@ -205,7 +205,7 @@ static void RestoreRealDudeState(bool redraw = true) {
IsControllingNPC = false;
real_dude = nullptr;
if (isDebug) DebugPrintf("\n[SFALL] Restore control to dude.");
if (isDebug) DebugPrintf("\n[SFALL] Restore control to dude.\n");
}
static int __stdcall CombatTurn(TGameObj* obj) {
@@ -296,14 +296,14 @@ int __stdcall PartyControl_SwitchHandHook(TGameObj* item) {
return -1;
}
long __fastcall GetRealDudePerk(TGameObj* source, long perk) {
static long __fastcall GetRealDudePerk(TGameObj* source, long perk) {
if (IsControllingNPC && source == real_dude) {
return real_perkLevelDataList[perk];
}
return PerkLevel(source, perk);
}
long __fastcall GetRealDudeTrait(TGameObj* source, long trait) {
static long __fastcall GetRealDudeTrait(TGameObj* source, long trait) {
if (IsControllingNPC && source == real_dude) {
return (trait == real_traits[0] || trait == real_traits[1]) ? 1 : 0;
}
+14 -19
View File
@@ -1342,9 +1342,6 @@ static void __declspec(naked) map_save_in_game_hook() {
}
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);
@@ -1410,17 +1407,24 @@ void ScriptExtenderSetup() {
SafeWriteBytes(0x483CB4, (BYTE*)&data, 5);
dlogr("Adding additional opcodes", DL_SCRIPT);
if (AllowUnsafeScripting) {
dlogr(" Unsafe opcodes enabled.", DL_SCRIPT);
} else {
dlogr(" Unsafe opcodes disabled.", 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
if (isDebug && (GetPrivateProfileIntA("Debugging", "AllowUnsafeScripting", 0, ".\\ddraw.ini") != 0)) {
dlogr(" Unsafe opcodes enabled.", DL_SCRIPT);
opcodes[0x1cf] = WriteByte;
opcodes[0x1d0] = WriteShort;
opcodes[0x1d1] = WriteInt;
opcodes[0x21b] = WriteString;
for (int i = 0x1d2; i < 0x1dc; i++) {
opcodes[i] = CallOffset;
}
} else {
dlogr(" Unsafe opcodes disabled.", DL_SCRIPT);
}
opcodes[0x156] = ReadByte;
opcodes[0x157] = ReadShort;
opcodes[0x158] = ReadInt;
@@ -1533,14 +1537,7 @@ void ScriptExtenderSetup() {
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;
@@ -1604,9 +1601,7 @@ void ScriptExtenderSetup() {
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;
+3 -4
View File
@@ -25,13 +25,12 @@
#define VERSION_MAJOR 3
#define VERSION_MINOR 8
#define VERSION_BUILD 19
#define VERSION_REV 0
#define VERSION_REV 1
#ifdef WIN2K
#define VERSION_STRING "3.8.19 win2k"
#define VERSION_STRING "3.8.19.1 win2k"
#else
#define VERSION_STRING "3.8.19"
#define VERSION_STRING "3.8.19.1"
#endif
//#define CHECK_VAL (4)