diff --git a/artifacts/scripting/headers/sfall.h b/artifacts/scripting/headers/sfall.h
index 1ee667d9..802cea30 100644
--- a/artifacts/scripting/headers/sfall.h
+++ b/artifacts/scripting/headers/sfall.h
@@ -69,6 +69,7 @@
#define HOOK_STDPROCEDURE_END (41)
#define HOOK_TARGETOBJECT (42)
#define HOOK_ENCOUNTER (43)
+#define HOOK_ADJUSTPOISON (44)
//Valid arguments to list_begin
#define LIST_CRITTERS (0)
diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt
index 712f7912..d41419da 100644
--- a/artifacts/scripting/hookscripts.txt
+++ b/artifacts/scripting/hookscripts.txt
@@ -368,7 +368,7 @@ int ret0 - overrides the pressed key (a new key DX scancode or 0 for no over
HOOK_MOUSECLICK (hs_mouseclick.int)
-Runs once every time when a mouse button was pressed or release.
+Runs once every time when a mouse button was pressed or released.
int arg0 - event type: 1 - pressed, 0 - released
int arg1 - button number (0 - left, 1 - right, up to 7)
@@ -693,3 +693,17 @@ int arg2 - 1 when the encounter occurs is a special encounter, 0 otherwise
int ret0 - overrides the map ID, or pass -1 for event type 0 to cancel the encounter and continue traveling
int ret1 - pass 1 to cancel the encounter and load the specified map from the ret1 (only for event type 0)
+
+-------------------------------------------
+
+HOOK_ADJUSTPOISON (hs_adjustpoison.int)
+
+Runs when a critter's poison level is changed, or when the player takes damage from the poison.
+
+Critter arg0 - the critter
+int arg1 - the amount of poison being added/removed
+int arg2 - the damage value at the time of applying the poison effect
+ (damage from the poison effect is implemented only for the player character; for other critters, this value will always be 0)
+
+int ret0 - the new amount of poison being added/removed
+int ret1 - the new damage value, only negative values are allowed (will only be valid at the time of taking damage from the poison)
diff --git a/sfall/FalloutEngine/Functions_def.h b/sfall/FalloutEngine/Functions_def.h
index 2d0242ad..c7c82da7 100644
--- a/sfall/FalloutEngine/Functions_def.h
+++ b/sfall/FalloutEngine/Functions_def.h
@@ -205,6 +205,7 @@ WRAP_WATCOM_FUNC0(void, process_bk)
WRAP_WATCOM_FUNC0(void, proto_dude_update_gender)
// Places pointer to a prototype structure into ptrPtr and returns 0 on success or -1 on failure
WRAP_WATCOM_FUNC2(long, proto_ptr, long, pid, Proto**, ptrPtr)
+WRAP_WATCOM_FUNC2(void, queue_clear_type, long, qType, void*, func)
WRAP_WATCOM_FUNC2(void*, queue_find_first, GameObject*, object, long, qType)
WRAP_WATCOM_FUNC2(void*, queue_find_next, GameObject*, object, long, qType)
WRAP_WATCOM_FUNC2(void, queue_remove_this, GameObject*, object, long, qType)
diff --git a/sfall/Modules/BugFixes.h b/sfall/Modules/BugFixes.h
index 859a5a52..88010834 100644
--- a/sfall/Modules/BugFixes.h
+++ b/sfall/Modules/BugFixes.h
@@ -16,6 +16,6 @@ public:
extern int tagSkill4LevelBase;
-extern void ResetBodyState();
+void ResetBodyState();
}
diff --git a/sfall/Modules/CritterPoison.cpp b/sfall/Modules/CritterPoison.cpp
new file mode 100644
index 00000000..b3a72a9d
--- /dev/null
+++ b/sfall/Modules/CritterPoison.cpp
@@ -0,0 +1,122 @@
+/*
+ * sfall
+ * Copyright (C) 2008-2021 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 .
+ */
+
+#include "..\main.h"
+#include "..\FalloutEngine\Fallout2.h"
+#include "PartyControl.h"
+
+#include "CritterPoison.h"
+
+namespace sfall
+{
+
+//static long adjustPoison = -2;
+
+long CritterPoison::adjustPoisonHP_Default = -1;
+long CritterPoison::adjustPoisonHP; // default or returned value from HOOK_ADJUSTPOISON
+
+void CritterPoison::SetDefaultAdjustPoisonHP(long value) {
+ adjustPoisonHP_Default = adjustPoisonHP = value;
+}
+
+void __fastcall sf_critter_adjust_poison(fo::GameObject* critter, long amount) {
+ if (amount == 0) return;
+ if (amount > 0) {
+ amount -= fo::func::stat_level(critter, fo::STAT_poison_resist) * amount / 100;
+ } else if (critter->critter.poison == 0) {
+ return;
+ }
+ critter->critter.poison += amount;
+ if (critter->critter.poison < 0) {
+ critter->critter.poison = 0; // level can't be negative
+ } else {
+ // set uID for saving queue
+ //Objects::SetObjectUniqueID(critter);
+ //fo::func::queue_add(10 * (505 - 5 * critter->critter.poison), critter, nullptr, fo::QueueType::poison_event);
+ }
+}
+
+static void __declspec(naked) critter_adjust_poison_hack() {
+ __asm {
+ mov edx, esi;
+ mov ecx, edi;
+ jmp sf_critter_adjust_poison;
+ }
+}
+
+void __declspec(naked) critter_check_poison_hack() {
+ __asm {
+ mov eax, CritterPoison::adjustPoisonHP_Default;
+ mov edx, CritterPoison::adjustPoisonHP;
+ mov CritterPoison::adjustPoisonHP, eax;
+ retn;
+ }
+}
+
+void __fastcall critter_check_poison_fix() {
+ if (PartyControl::IsNpcControlled()) {
+ // since another critter is being controlled, we can't apply the poison effect to it
+ // instead, we add the "poison" event to dude again, which will be triggered when dude returns to the player's control
+ fo::func::queue_clear_type(fo::QueueType::poison_event, nullptr);
+ fo::GameObject* dude = PartyControl::RealDudeObject();
+ fo::func::queue_add(10, dude, nullptr, fo::QueueType::poison_event);
+ }
+}
+
+static void __declspec(naked) critter_check_poison_hack_fix() {
+ using namespace fo;
+ using namespace Fields;
+ __asm {
+ mov ecx, [eax + protoId]; // critter.pid
+ cmp ecx, PID_Player;
+ jnz notDude;
+ retn;
+notDude:
+ call critter_check_poison_fix;
+ or al, 1; // unset ZF (exit from func)
+ retn;
+ }
+}
+
+void __declspec(naked) critter_adjust_poison_hack_fix() { // can also be called from HOOK_ADJUSTPOISON
+ using namespace fo;
+ using namespace Fields;
+ __asm {
+ mov edx, ds:[FO_VAR_obj_dude];
+ mov ebx, [eax + protoId]; // critter.pid
+ mov ecx, PID_Player;
+ retn;
+ }
+}
+
+void CritterPoison::init() {
+ // Allow changing the poison level for critters
+ MakeCall(0x42D226, critter_adjust_poison_hack);
+ SafeWrite8(0x42D22C, 0xDA); // jmp 0x42D30A
+
+ // Adjust poison damage
+ SetDefaultAdjustPoisonHP(*(DWORD*)0x42D332);
+ MakeCall(0x42D331, critter_check_poison_hack);
+
+ // Fix/tweak for party control
+ MakeCall(0x42D31F, critter_check_poison_hack_fix, 1);
+ MakeCall(0x42D21C, critter_adjust_poison_hack_fix, 1);
+ SafeWrite8(0x42D223, 0xCB); // cmp eax, edx > cmp ebx, ecx
+}
+
+}
diff --git a/sfall/Modules/CritterPoison.h b/sfall/Modules/CritterPoison.h
new file mode 100644
index 00000000..c0899055
--- /dev/null
+++ b/sfall/Modules/CritterPoison.h
@@ -0,0 +1,39 @@
+/*
+ * sfall
+ * Copyright (C) 2008-2021 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 .
+ */
+
+#pragma once
+
+#include "Module.h"
+
+namespace sfall
+{
+
+class CritterPoison : public Module {
+public:
+ const char* name() { return "CritterPoison"; }
+ void init();
+
+ static long adjustPoisonHP_Default;
+ static long adjustPoisonHP; // temp value from HOOK_ADJUSTPOISON
+
+ static void SetDefaultAdjustPoisonHP(long value);
+};
+
+void critter_adjust_poison_hack_fix();
+
+}
diff --git a/sfall/Modules/HookScripts.cpp b/sfall/Modules/HookScripts.cpp
index 685eb7ea..de56cd4f 100644
--- a/sfall/Modules/HookScripts.cpp
+++ b/sfall/Modules/HookScripts.cpp
@@ -103,6 +103,7 @@ static HooksInjectInfo injectHooks[] = {
{HOOK_STDPROCEDURE_END, Inject_ScriptProcedureHook2, false},
{HOOK_TARGETOBJECT, Inject_TargetObjectHook, false},
{HOOK_ENCOUNTER, Inject_EncounterHook, false},
+ {HOOK_ADJUSTPOISON, Inject_AdjustPoisonHook, false},
};
void HookScripts::InjectingHook(int hookId) {
diff --git a/sfall/Modules/HookScripts.h b/sfall/Modules/HookScripts.h
index d411e083..30fbad0b 100644
--- a/sfall/Modules/HookScripts.h
+++ b/sfall/Modules/HookScripts.h
@@ -70,6 +70,7 @@ enum HookType
HOOK_STDPROCEDURE_END = 41,
HOOK_TARGETOBJECT = 42,
HOOK_ENCOUNTER = 43,
+ HOOK_ADJUSTPOISON = 44,
HOOK_COUNT
};
diff --git a/sfall/Modules/HookScripts/ObjectHs.cpp b/sfall/Modules/HookScripts/ObjectHs.cpp
index d599612c..27f7c681 100644
--- a/sfall/Modules/HookScripts/ObjectHs.cpp
+++ b/sfall/Modules/HookScripts/ObjectHs.cpp
@@ -1,6 +1,7 @@
#include "..\..\FalloutEngine\Fallout2.h"
#include "..\..\SafeWrite.h"
#include "..\HookScripts.h"
+#include "..\CritterPoison.h"
#include "Common.h"
#include "ObjectHs.h"
@@ -302,6 +303,36 @@ skip:
}
}
+static DWORD __fastcall AdjustPoison_Script(DWORD critter, long amount, DWORD addr) {
+ BeginHook();
+ argCount = 3;
+
+ bool checkPoison = (addr == (0x42D32C + 5)); // from critter_check_poison_
+
+ args[0] = critter;
+ args[1] = amount;
+ args[2] = (checkPoison) ? CritterPoison::adjustPoisonHP_Default : 0;
+
+ RunHookScript(HOOK_ADJUSTPOISON);
+
+ if (cRet > 0) amount = rets[0];
+ if (cRet > 1 && checkPoison && (long)rets[1] < 0) CritterPoison::adjustPoisonHP = rets[1];
+
+ EndHook();
+ return amount;
+}
+
+static void __declspec(naked) critter_adjust_poison_hack() {
+ __asm {
+ push [esp + 0x24 + 4]; // called from
+ mov ecx, eax; // critter
+ call AdjustPoison_Script; // edx - amount
+ mov esi, eax; // old/new amount
+ mov eax, edi; // restore eax value
+ jmp critter_adjust_poison_hack_fix; // in CritterPoison
+ }
+}
+
void Inject_UseObjOnHook() {
HookCalls(UseObjOnHook, { 0x49C606, 0x473619 });
@@ -310,7 +341,7 @@ void Inject_UseObjOnHook() {
0x4285DF, // ai_check_drugs
0x4286F8, // ai_check_drugs
0x4287F8, // ai_check_drugs
- 0x473573 // inven_action_cursor
+ 0x473573 // inven_action_cursor
});
}
@@ -339,6 +370,10 @@ void Inject_ScriptProcedureHook2() {
HookCall(0x4A49A7, After_ScriptStdProcedureHook);
}
+void Inject_AdjustPoisonHook() {
+ MakeCall(0x42D21C, critter_adjust_poison_hack, 1);
+}
+
void InitObjectHookScripts() {
HookScripts::LoadHookScript("hs_useobjon", HOOK_USEOBJON);
HookScripts::LoadHookScript("hs_useobj", HOOK_USEOBJ);
@@ -347,6 +382,7 @@ void InitObjectHookScripts() {
HookScripts::LoadHookScript("hs_setlighting", HOOK_SETLIGHTING);
HookScripts::LoadHookScript("hs_stdprocedure", HOOK_STDPROCEDURE); // combo hook
HookScripts::LoadHookScript("hs_stdprocedure", HOOK_STDPROCEDURE_END);
+ HookScripts::LoadHookScript("hs_adjustpoison", HOOK_ADJUSTPOISON);
}
}
diff --git a/sfall/Modules/HookScripts/ObjectHs.h b/sfall/Modules/HookScripts/ObjectHs.h
index 832f00df..3a05f626 100644
--- a/sfall/Modules/HookScripts/ObjectHs.h
+++ b/sfall/Modules/HookScripts/ObjectHs.h
@@ -11,4 +11,5 @@ namespace sfall
void Inject_SetLightingHook();
void Inject_ScriptProcedureHook();
void Inject_ScriptProcedureHook2();
+ void Inject_AdjustPoisonHook();
}
diff --git a/sfall/Modules/Sound.cpp b/sfall/Modules/Sound.cpp
index 95a1a3fe..e9481f76 100644
--- a/sfall/Modules/Sound.cpp
+++ b/sfall/Modules/Sound.cpp
@@ -916,10 +916,7 @@ static void __fastcall SetPanPosition(fo::ACMSoundData* sound) {
if (distance > 5) {
long direction = fo::func::tile_dir(fo::var::obj_dude->tile, relativeObject->tile);
bool isRightSide = (direction <= 2);
-
- long panValue = (distance - 5) * 100;
- if (panValue > 10000) panValue = 10000;
-
+ long panValue = (distance < 55) ? (distance - 5) * 200 : 10000;
sound->soundBuffer->SetPan((isRightSide) ? panValue : -panValue); // left mute 10000 ... -10000 right mute
}
relativeObject = nullptr; // just in case
diff --git a/sfall/Modules/Stats.cpp b/sfall/Modules/Stats.cpp
index d135ab8f..f51179a5 100644
--- a/sfall/Modules/Stats.cpp
+++ b/sfall/Modules/Stats.cpp
@@ -26,17 +26,6 @@
namespace sfall
{
-void __fastcall sf_critter_adjust_poison(fo::GameObject* critter, long amount) {
- if (amount == 0) return;
- if (amount > 0) {
- amount -= fo::func::stat_level(critter, fo::STAT_poison_resist) * amount / 100;
- } else if (critter->critter.poison == 0) {
- return;
- }
- critter->critter.poison += amount;
- if (critter->critter.poison < 0) critter->critter.poison = 0; // level can't be negative
-}
-
static DWORD statMaximumsPC[fo::STAT_max_stat];
static DWORD statMinimumsPC[fo::STAT_max_stat];
static DWORD statMaximumsNPC[fo::STAT_max_stat];
@@ -220,14 +209,6 @@ allow:
}
}
-static void __declspec(naked) critter_adjust_poison_hack() {
- __asm {
- mov edx, esi;
- mov ecx, edi;
- jmp sf_critter_adjust_poison;
- }
-}
-
static void StatsReset() {
for (size_t i = 0; i < fo::STAT_max_stat; i++) {
statMaximumsPC[i] = statMaximumsNPC[i] = fo::var::stat_data[i].maxValue;
@@ -262,9 +243,6 @@ void Stats::init() {
// Allow set_critter_stat function to change STAT_unused and STAT_dmg_* stats for the player
MakeCall(0x4AF54E, stat_set_base_hack_allow);
MakeCall(0x455D65, op_set_critter_stat_hack); // STAT_unused for other critters
- // Allow changing the poison level for critters
- MakeCall(0x42D226, critter_adjust_poison_hack);
- SafeWrite8(0x42D22C, 0xDA); // jmp 0x42D30A
auto xpTableList = GetConfigList("Misc", "XPTable", "", 2048);
size_t numLevels = xpTableList.size();
diff --git a/sfall/ddraw.vcxproj b/sfall/ddraw.vcxproj
index 9a245402..3c301a23 100644
--- a/sfall/ddraw.vcxproj
+++ b/sfall/ddraw.vcxproj
@@ -291,6 +291,7 @@
+
@@ -388,6 +389,7 @@
+
diff --git a/sfall/ddraw.vcxproj.filters b/sfall/ddraw.vcxproj.filters
index 7034ad7c..7f60f541 100644
--- a/sfall/ddraw.vcxproj.filters
+++ b/sfall/ddraw.vcxproj.filters
@@ -304,6 +304,9 @@
Modules\SubModules
+
+ Modules
+
@@ -562,6 +565,9 @@
Modules\SubModules
+
+ Modules
+
diff --git a/sfall/main.cpp b/sfall/main.cpp
index 615559a4..250ae616 100644
--- a/sfall/main.cpp
+++ b/sfall/main.cpp
@@ -38,6 +38,7 @@
#include "Modules\Credits.h"
#include "Modules\Criticals.h"
#include "Modules\CritterStats.h"
+#include "Modules\CritterPoison.h"
#include "Modules\DamageMod.h"
#include "Modules\DebugEditor.h"
#include "Modules\Drugs.h"
@@ -185,6 +186,7 @@ static void InitModules() {
manager.add();
manager.add();
manager.add();
+ manager.add();
manager.add();
manager.add();
manager.add();
diff --git a/sfall/version.h b/sfall/version.h
index e936c480..6bce6f1f 100644
--- a/sfall/version.h
+++ b/sfall/version.h
@@ -20,7 +20,7 @@
#define TARGETVERSION "Fallout 2 v1.02 US"
-#define LEGAL_COPYRIGHT "Copyright (C) 2006-2020, sfall team"
+#define LEGAL_COPYRIGHT "Copyright (C) 2006-2021, sfall team"
#define VERSION_MAJOR 4
#define VERSION_MINOR 2