diff --git a/artifacts/scripting/hookscripts.txt b/artifacts/scripting/hookscripts.txt index fbf30bcc..69ddba7c 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) diff --git a/sfall/FalloutFuncs_def.h b/sfall/FalloutFuncs_def.h index 36b9e96b..8b4f1bde 100644 --- a/sfall/FalloutFuncs_def.h +++ b/sfall/FalloutFuncs_def.h @@ -176,6 +176,7 @@ WRAP_WATCOM_FUNC2(long, PerkLevel, perk_level_, TGameObj*, critter, long, perkId WRAP_WATCOM_FUNC6(long, PickDeath, pick_death_, TGameObj*, attacker, TGameObj*, target, TGameObj*, weapon, long, amount, long, anim, long, hitFromBack) WRAP_WATCOM_FUNC0(void, ProcessBk, process_bk_) WRAP_WATCOM_FUNC0(void, ProtoDudeUpdateGender, proto_dude_update_gender_) +WRAP_WATCOM_FUNC2(void, QueueClearType, queue_clear_type_, long, qType, void*, func) WRAP_WATCOM_FUNC2(void*, QueueFindFirst, queue_find_first_, TGameObj*, object, long, qType) WRAP_WATCOM_FUNC2(void*, QueueFindNext, queue_find_next_, TGameObj*, object, long, qType) WRAP_WATCOM_FUNC2(void, QueueRemoveThis, queue_remove_this_, TGameObj*, object, long, qType) diff --git a/sfall/LoadGameHook.cpp b/sfall/LoadGameHook.cpp index f5a05220..fcb0d2d6 100644 --- a/sfall/LoadGameHook.cpp +++ b/sfall/LoadGameHook.cpp @@ -164,9 +164,9 @@ errorSave: static char saveFailMsg[128]; static DWORD __stdcall CombatSaveTest() { - if (!saveInCombatFix && !IsNpcControlled()) return 1; + if (!saveInCombatFix && !PartyControl_IsNpcControlled()) return 1; if (inLoop & COMBAT) { - if (saveInCombatFix == 2 || IsNpcControlled() || !(inLoop & PCOMBAT)) { + if (saveInCombatFix == 2 || PartyControl_IsNpcControlled() || !(inLoop & PCOMBAT)) { DisplayPrint(saveFailMsg); return 0; } diff --git a/sfall/PartyControl.cpp b/sfall/PartyControl.cpp index 8deba3fa..6d67a89c 100644 --- a/sfall/PartyControl.cpp +++ b/sfall/PartyControl.cpp @@ -393,11 +393,11 @@ static void PartyControlReset() { realDude.obj_dude = nullptr; } -bool IsNpcControlled() { +bool PartyControl_IsNpcControlled() { return isControllingNPC; } -TGameObj* RealDudeObject() { +TGameObj* PartyControl_RealDudeObject() { return realDude.obj_dude != nullptr ? realDude.obj_dude : *ptr_obj_dude; diff --git a/sfall/PartyControl.h b/sfall/PartyControl.h index 7c6b733c..bfab4342 100644 --- a/sfall/PartyControl.h +++ b/sfall/PartyControl.h @@ -26,7 +26,7 @@ void PartyControl_OnGameLoad(); int __fastcall PartyControl_SwitchHandHook(TGameObj* item); -bool IsNpcControlled(); +bool PartyControl_IsNpcControlled(); // Returns pointer to "real" dude, which is different from "dude_obj" when controlling another critter -TGameObj* RealDudeObject(); +TGameObj* PartyControl_RealDudeObject(); diff --git a/sfall/ScriptOps/ObjectsOps.hpp b/sfall/ScriptOps/ObjectsOps.hpp index 565d384e..5cfe3fa0 100644 --- a/sfall/ScriptOps/ObjectsOps.hpp +++ b/sfall/ScriptOps/ObjectsOps.hpp @@ -491,7 +491,7 @@ static void mf_item_weight() { } static void mf_real_dude_obj() { - opHandler.setReturn(RealDudeObject()); + opHandler.setReturn(PartyControl_RealDudeObject()); } static void mf_car_gas_amount() { diff --git a/sfall/Sound.cpp b/sfall/Sound.cpp index d6ed986c..523306d3 100644 --- a/sfall/Sound.cpp +++ b/sfall/Sound.cpp @@ -928,10 +928,7 @@ static void __fastcall SetPanPosition(ACMSoundData* sound) { if (distance > 5) { long direction = TileDir((*ptr_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/Stats.cpp b/sfall/Stats.cpp index 114dfff9..2f4b806b 100644 --- a/sfall/Stats.cpp +++ b/sfall/Stats.cpp @@ -18,20 +18,10 @@ #include "main.h" #include "FalloutEngine.h" +#include "PartyControl.h" #include "Stats.h" -void __fastcall sf_critter_adjust_poison(TGameObj* critter, long amount) { - if (amount == 0) return; - if (amount > 0) { - amount -= StatLevel(critter, 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[STAT_max_stat]; static DWORD statMinimumsPC[STAT_max_stat]; static DWORD statMaximumsNPC[STAT_max_stat]; @@ -221,6 +211,25 @@ allow: } } +//////////////////////////////// CRITTER POISON //////////////////////////////// + +void __fastcall sf_critter_adjust_poison(TGameObj* critter, long amount) { + if (amount == 0) return; + if (amount > 0) { + amount -= StatLevel(critter, 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); + //QueueAdd(10 * (505 - 5 * critter->critter.poison), critter, nullptr, poison_event); + } +} + static void __declspec(naked) critter_adjust_poison_hack() { __asm { mov edx, esi; @@ -229,6 +238,42 @@ static void __declspec(naked) critter_adjust_poison_hack() { } } +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 + QueueClearType(poison_event, nullptr); + TGameObj* dude = PartyControl_RealDudeObject(); + QueueAdd(10, dude, nullptr, poison_event); + } +} + +static void __declspec(naked) critter_check_poison_hack_fix() { + 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() { + using namespace Fields; + __asm { + mov edx, ds:[_obj_dude]; + mov ebx, [eax + protoId]; // critter.pid + mov ecx, PID_Player; + retn; + } +} + +//////////////////////////////////////////////////////////////////////////////// + static void StatsReset() { for (size_t i = 0; i < STAT_max_stat; i++) { statMaximumsPC[i] = statMaximumsNPC[i] = *(DWORD*)(_stat_data + 16 + i * 24); @@ -261,10 +306,16 @@ 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 + // 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 + std::vector xpTableList = GetConfigList("Misc", "XPTable", "", 2048); size_t numLevels = xpTableList.size(); if (numLevels > 0) { diff --git a/sfall/version.h b/sfall/version.h index 3248b9ec..b10bbf74 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 3 #define VERSION_MINOR 8