Added new Drugs module and DrugsFile option (from Mr.Stalin)

Fixed a bug in the party control module with the 'addict' notification box not getting hidden after the addiction ends.
Removed DrugsSaveFix option, now the list of active drug pids will be saved to sfalldb.sav.
This commit is contained in:
NovaRain
2019-03-05 12:36:38 +08:00
parent ab03186a1d
commit 3934a804f4
15 changed files with 624 additions and 44 deletions
+30
View File
@@ -0,0 +1,30 @@
; Allows you to configure advanced settings for drugs.
; Maximum 50 drugs are allowed
; Note: for vanilla drugs, you can only override the values of NumEffect and AddictTime parameters
[main]
; set to 1 to change Jet addiction to be removed after the time expires
JetWithdrawal=0
; total number of drugs in this file
Count=0
; count starts from 1
[1]
; drug item PID
PID=0
; set a limit on the count of first and second delayed effects when using the drug, i.e. if the queue has delayed effects from the drug,
; and their count is greater or equal to NumEffects, it is not possible to obtain new effects from using the drug
; this count is 4 for Buffout/Mentats/Psycho/Jet
; set -1 to leave this parameter unchanged
NumEffects=-1
; the duration of the withdrawal effects of addiction in minutes (i.e. 1440 = 24 game hours)
; set to 0 to use the default time (7 days for all drugs except Jet)
AddictTime=0
; the number of the global variable from vault13.gam responsible for displaying addiction in the character screen
; set to 0 if the addiction is not required
GvarID=0
; the index number from editor.msg to display the title of the addiction (the description uses the index number of TextID + 100)
TextID=-1
; the line number (0-indexed) of the corresponding FRM in skilldex.lst
FrmID=-1
+4 -5
View File
@@ -494,6 +494,10 @@ BoostScriptDialogLimit=0
;See the Stats.ini in the modders pack for an example file
;DerivedStats=Stats.ini
;Allows you to change some parameters for drugs and their addictions
;See the Drugs.ini in the modders pack for an example file
;DrugsFile=Drugs.ini
;These options modify the checks to see if a critter can carry an additional item, changing which items are counted towards the weight limit and adding an additional size check
;Set the mode to 0 to disable the size check, 1 to apply to the PC only, 2 to apply to the PC and party members, or 3 to apply to all critters
;Only the PC uses CritterInvSizeLimit. Other critters will use the unused stat (STAT_unused = 10) or have the size limit of 100 if the stat is not set
@@ -660,11 +664,6 @@ PartyMemberExtraInfo=0
;that overrides First Aid/Doctor functions has very limited usefulness
PartyMemberSkillFix=0
;Set to 1 to fix the active effects of drugs not being saved properly
;If you have problems loading old save games saved while the player is under drug effects, disable this option
;Set to 0 to disable
DrugsSaveFix=1
;Set to 1 to skip loading game settings from a saved game, except for the game/combat difficulty settings
;Set to 2 to also skip loading the game/combat difficulty settings
SkipLoadingGameSettings=0
+12
View File
@@ -117,6 +117,18 @@ long CheckAddictByPid(fo::GameObject* critter, long pid) {
/* keyword 'return' is not needed, the compiler will do everything correctly */
}
void ToggleNpcFlag(fo::GameObject* npc, long flag, bool set) {
Proto* protoPtr;
if (fo::func::proto_ptr(npc->protoId, &protoPtr) != -1) {
long bit = (1 << flag);
if (set) {
protoPtr->critter.critterFlags |= bit;
} else {
protoPtr->critter.critterFlags &= ~bit;
}
}
}
//---------------------------------------------------------
//print text to surface
void PrintText(char *DisplayText, BYTE ColourIndex, DWORD Xpos, DWORD Ypos, DWORD TxtWidth, DWORD ToWidth, BYTE *ToSurface) {
+2
View File
@@ -62,6 +62,8 @@ bool HeroIsFemale();
long CheckAddictByPid(fo::GameObject* critter, long pid);
void ToggleNpcFlag(fo::GameObject* npc, long flag, bool set);
// Print text to surface
void PrintText(char *displayText, BYTE colorIndex, DWORD x, DWORD y, DWORD textWidth, DWORD destWidth, BYTE *surface);
// gets the height of the currently selected font
+90 -22
View File
@@ -1,5 +1,6 @@
#include "..\main.h"
#include "..\FalloutEngine\Fallout2.h"
#include "Drugs.h"
#include "LoadGameHook.h"
#include "ScriptExtender.h"
@@ -16,13 +17,51 @@ static DWORD weightOnBody = 0;
static char textBuf[355];
std::list<int> drugsPid;
static void __fastcall DrugPidPush(int pid) {
drugsPid.push_back(pid);
}
static int __fastcall DrugPidPop() {
if (drugsPid.empty()) return 0;
int pid = drugsPid.front();
drugsPid.pop_front();
return pid;
}
void BugFixes::DrugsSaveFix(HANDLE file) {
DWORD sizeWrite, count = drugsPid.size();
WriteFile(file, &count, 4, &sizeWrite, 0);
if (!count) return;
for (auto it = drugsPid.begin(); it != drugsPid.end(); it++) {
int pid = *it;
WriteFile(file, &pid, 4, &sizeWrite, 0);
}
drugsPid.clear();
return;
}
bool BugFixes::DrugsLoadFix(HANDLE file) {
DWORD count, sizeRead;
ReadFile(file, &count, 4, &sizeRead, 0);
//if (sizeRead != 4) return true;
for (DWORD i = 0; i < count; i++) {
DWORD pid;
ReadFile(file, &pid, 4, &sizeRead, 0);
if (sizeRead != 4) return true;
drugsPid.push_back(pid);
}
return false;
}
void ResetBodyState() {
__asm mov critterBody, 0;
__asm mov sizeOnBody, 0;
__asm mov weightOnBody, 0;
}
void GameInitialization() {
static void MusicVolInitialization() {
*(DWORD*)FO_VAR_gDialogMusicVol = *(DWORD*)FO_VAR_background_volume; // fix dialog music
}
@@ -224,7 +263,7 @@ static void __declspec(naked) RemoveJetAddictFunc() {
jne end;
cmp dword ptr [edx + 0x4], PID_JET; // queue_addict.drug_pid == PID_JET?
end:
sete al; // 1 = Delete from queue, 0 = Don't touch
sete al; // 1 = Delete from queue, 0 = Don't touch
and eax, 0xFF;
retn;
}
@@ -247,12 +286,13 @@ skip:
}
}
static void __declspec(naked) item_d_load_hack() {
static void __declspec(naked) item_d_load_subfix() {
__asm {
sub esp, 4; // proto buf
mov [ebp], edi; // edi->queue_drug
mov ecx, 7;
mov esi, FO_VAR_drugInfoList + 12;
// mov [ebp], edi; // edi->queue_drug
mov ecx, 9; // vanilla count
mov esi, FO_VAR_drugInfoList;
mov ebx, 12;
loopDrug:
cmp dword ptr [esi + 8], 0; // drugInfoList.numeffects
je nextDrug;
@@ -270,20 +310,58 @@ loopDrug:
cmp eax, [edi + 0xC]; // drug.stat2 == queue_drug.stat2?
je foundPid; // Yes
nextDrug:
add esi, 12;
lea esi, [esi + ebx];
dec ecx;
jnz loopDrug;
xor ebp, ebp; // set drug_pid = 0
cmp ebx, 12;
jnz end; // failed, this drug effect was not found
// try find in new drugs
call Drugs::GetDrugCount;
test eax, eax;
jz end;
mov ebx, SIZE_S_DRUGS; // sizeof structure
mov ecx, eax;
lea edx, drugs;
mov esi, [edx];
jmp loopDrug;
foundPid:
mov eax, [esi]; // drugInfoList.pid
mov [edi], eax; // queue_drug.drug_pid
mov ebp, [esi]; // drugInfoList.pid
end:
mov [edi], ebp; // queue_drug.drug_pid
xor eax, eax;
add esp, 4;
retn;
}
}
// take the drug pid from the list after loading sfalldb.sav
static void __declspec(naked) item_d_load_hack() {
__asm {
mov [ebp], edi; // edi->queue_drug
call DrugPidPop;
test eax, eax;
jnz skip;
jmp item_d_load_subfix; // if the pid was not saved, then try to find it
skip:
mov [edi], eax; // queue_drug.drug_pid
xor eax, eax;
retn;
}
}
// add drug pid to the list to save to sfalldb.sav
static void __declspec(naked) item_d_save_hack() {
__asm {
pushadc;
mov ecx, [edx]; // drug pid
call DrugPidPush;
popadc;
mov ebx, 3;
retn;
}
}
static void __declspec(naked) queue_clear_type_mem_free_hook() {
__asm {
mov ebx, [esi]
@@ -1827,7 +1905,7 @@ void BugFixes::init()
#endif
// Missing game initialization
LoadGameHook::OnGameInit() += GameInitialization;
LoadGameHook::OnGameInit() += MusicVolInitialization;
// fix vanilla negate operator on float values
MakeCall(0x46AB68, NegateFixHack);
@@ -1887,18 +1965,8 @@ void BugFixes::init()
// Fix for gaining stats from more than two doses of a specific chem after save-load
dlog("Applying fix for save-load unlimited drug use exploit.", DL_INIT);
if (GetConfigInt("Misc", "DrugsSaveFix", 0)) {
// games saved while the player is under drug effects will be incompatible
// item_d_save_
SafeWrite8(0x47A25C, 4);
SafeWrite8(0x47A262, 0);
// item_d_load_
SafeWrite8(0x47A1F6, 4);
SafeWrite8(0x47A1FB, 0xCA); // mov edx, esp > mov edx, ecx
SafeWrite8(0x47A21B, 0x27); // jnz 0x47A243
} else {
MakeCall(0x47A243, item_d_load_hack); // partial fix without breaking save game compatibility
}
MakeCall(0x47A25B, item_d_save_hack);
MakeCall(0x47A243, item_d_load_hack);
dlogr(" Done", DL_INIT);
// Fix crash when leaving the map while waiting for someone to die of a super stimpak overdose
+3
View File
@@ -9,6 +9,9 @@ class BugFixes : public Module {
public:
const char* name() { return "BugFixes"; }
void init();
static void DrugsSaveFix(HANDLE file);
static bool DrugsLoadFix(HANDLE file);
};
extern int tagSkill4LevelBase;
+335
View File
@@ -0,0 +1,335 @@
/*
* sfall
* Copyright (C) 2008-2019 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 "..\FalloutEngine\Fallout2.h"
#include "LoadGameHook.h"
#include "Drugs.h"
namespace sfall
{
static const int drugsMax = 50;
static int drugsCount = 0;
static bool drugsReset = false; // true - need reset
long Drugs::addictionGvarCount = 0;
sDrugs *drugs = nullptr;
static long __fastcall FindDrugVar(DWORD pid) {
for (int i = 0; i < drugsCount; i++) {
if (drugs[i].drugPid == pid) {
//if (drugs[i].gvarID == 0) break;
return drugs[i].gvarID;
}
}
return -1;
}
static void __declspec(naked) pid_to_gvar_hack() {
__asm {
push ecx;
push edx;
mov ecx, eax;
call FindDrugVar;
pop edx;
pop ecx;
test eax, eax;
jg end; // if gvar > 0
mov ebx, ds:[FO_VAR_drugInfoList];
retn;
end: // force exit func
add esp, 4;
pop edx;
pop ebx;
retn;
}
}
static long __fastcall AllowUseDrug(fo::GameObject* critter, DWORD pid) {
for (int i = 0; i < drugsCount; i++) {
if (drugs[i].drugPid == pid) {
if (drugs[i].numEffects == -1) break; // for vanilla drugs
if (drugs[i].numEffects == 0) return 1;
auto queue = (fo::QueueDrug*)fo::func::queue_find_first(critter, 0);
if (!queue) return 1;
int num = 0;
while (queue->pid != pid || ++num < drugs[i].numEffects) {
queue = (fo::QueueDrug*)fo::func::queue_find_next(critter, 0);
if (!queue) return 1;
}
return 0; // not allow
}
}
return -1; // not found
}
static void __declspec(naked) drug_effect_allowed_hack() {
__asm {
push ecx;
call AllowUseDrug; // ecx - critter, edx - drug pid
pop ecx;
test eax, eax;
jge end; // if eax > -1
mov edi, ds:[FO_VAR_drugInfoList];
retn;
end: // force exit func
add esp, 4;
pop edi;
pop esi;
pop ecx;
pop ebx;
retn;
}
}
static long _stdcall FindDrugTime(DWORD pid) {
for (int i = 0; i < drugsCount; i++) {
if (drugs[i].drugPid == pid) {
if (drugs[i].addictTimeOff <= 0) break;
return drugs[i].addictTimeOff;
}
}
return 10080; // default time (7 days)
}
static void __declspec(naked) perform_withdrawal_start_hack() {
__asm {
push ecx;
push edi; // drug pid
call FindDrugTime;
pop ecx;
mov ebx, eax; // time
retn;
}
}
static long __fastcall PrintAddictionList(long isSeparator) {
long isSelect = 0;
for (int i = 0; i < drugsCount; i++) {
if (drugs[i].gvarID > 0 && fo::var::game_global_vars[drugs[i].gvarID]) {
if (!isSeparator) { // print separator line
isSeparator = 1;
const char* message = fo::GetMessageStr(&fo::var::editor_message_file, 4001);
if (fo::func::folder_print_seperator(message)) {
fo::var::folder_card_title = (DWORD)message;
fo::var::folder_card_title2 = 0;
fo::var::folder_card_desc = (DWORD)fo::GetMessageStr(&fo::var::editor_message_file, 4101);
fo::var::folder_card_fid = 53;
isSelect = 1;
}
}
int msgNum = drugs[i].msgID;
const char* message = fo::GetMessageStr(&fo::var::editor_message_file, msgNum);
if (fo::func::folder_print_line(message)) {
fo::var::folder_card_title = (DWORD)message;
fo::var::folder_card_title2 = 0;
if (msgNum > 0) msgNum += 100;
fo::var::folder_card_desc = (DWORD)fo::GetMessageStr(&fo::var::editor_message_file, msgNum);
fo::var::folder_card_fid = drugs[i].frmID;
isSelect = 1;
}
}
}
return isSelect;
}
static const DWORD list_karma_Ret = 0x43C1A3;
static void __declspec(naked) list_karma_hack() {
__asm {
mov ecx, [esp + 0x168 - 0x1C + 4];
call PrintAddictionList;
or eax, edi;
jnz end;
mov edi, 47;
retn;
end:
add esp, 4;
jmp list_karma_Ret;
}
}
static void CheckEngineNumEffects(int &set, long pid) {
switch (pid) {
case fo::PID_RADAWAY:
set = 4;
break;
case fo::PID_MENTATS:
set = 2;
break;
case fo::PID_BUFFOUT:
set = 1;
break;
case fo::PID_NUKA_COLA:
set = 0;
break;
case fo::PID_PSYCHO:
set = 3;
break;
case fo::PID_BEER:
set = 5;
break;
case fo::PID_BOOZE:
set = 6;
break;
case fo::PID_JET:
set = 7;
break;
case fo::PID_DECK_OF_TRAGIC_CARDS:
set = 8;
}
}
static void CheckValidGvarNumber() {
for (int i = 0; i < drugsCount; i++) {
if (drugs[i].gvarID > 0) {
if (drugs[i].gvarID >= (long)fo::var::num_game_global_vars) {
drugs[i].gvarID = -1;
}
}
}
}
static void ResetDrugs() {
if (!drugsReset) return;
drugsReset = false;
int set = -1;
for (int i = 0; i < drugsCount; i++) {
CheckEngineNumEffects(set, drugs[i].drugPid);
if (set != -1) {
fo::var::drugInfoList[set].numEffects = drugs[i].iniNumEffects;
set = -1;
} else {
drugs[i].numEffects = drugs[i].iniNumEffects;
}
drugs[i].addictTimeOff = drugs[i].iniAddictTimeOff;
}
}
long Drugs::GetDrugCount() {
return drugsCount;
}
long Drugs::GetDrugPid(long n) {
return drugs[n].drugPid;
}
long Drugs::GetDrugGvar(long n) {
return drugs[n].gvarID;
}
long Drugs::SetDrugNumEffect(long pid, long effect) {
for (int i = 0; i < drugsCount; i++) {
if (drugs[i].drugPid == pid) {
int set = -1;
CheckEngineNumEffects(set, pid);
if (set != -1) {
fo::var::drugInfoList[set].numEffects = effect;
} else {
drugs[i].numEffects = effect;
}
drugsReset = true;
return 0;
}
}
return -1;
}
long Drugs::SetDrugAddictTimeOff(long pid, long time) {
for (int i = 0; i < drugsCount; i++) {
if (drugs[i].drugPid == pid) {
drugs[i].addictTimeOff = time;
drugsReset = true;
return 0;
}
}
return -1;
}
void Drugs::init() {
auto drugsFile = GetConfigString("Misc", "DrugsFile", "", MAX_PATH);
if (!drugsFile.empty()) {
dlog("Applying drugs patch...", DL_INIT);
const char* iniDrugs = drugsFile.insert(0, ".\\").c_str();
if (GetPrivateProfileIntA("main", "JetWithdrawal", 0, iniDrugs) == 1) SafeWrite8(0x47A3A8, 0);
int count = GetPrivateProfileIntA("main", "Count", 0, iniDrugs);
if (count > 0) {
if (count > drugsMax) count = drugsMax;
drugs = new sDrugs[count]();
int set = -1;
char section[4];
for (int i = 1; i <= count; i++) {
_itoa_s(i, section, 10);
int pid = GetPrivateProfileIntA(section, "PID", 0, iniDrugs);
if (pid > 0) {
CheckEngineNumEffects(set, pid);
drugs[drugsCount].drugPid = pid;
drugs[drugsCount].addictTimeOff = drugs[drugsCount].iniAddictTimeOff = GetPrivateProfileIntA(section, "AddictTime", 0, iniDrugs);
long ef = GetPrivateProfileIntA(section, "NumEffects", -1, iniDrugs);
if (set != -1) {
if (ef > -1) {
drugs[i].iniNumEffects = ef;
fo::var::drugInfoList[set].numEffects = ef;
} else {
drugs[i].iniNumEffects = fo::var::drugInfoList[set].numEffects;
}
set = -1;
drugs[i].numEffects = set; // set -1, to use the value from the engine
continue;
}
drugs[drugsCount].numEffects = drugs[drugsCount].iniNumEffects = max(0, ef);
int gvar = GetPrivateProfileIntA(section, "GvarID", 0, iniDrugs);
drugs[drugsCount].gvarID = max(0, gvar); // not allow negative values
if (gvar) {
int msg = GetPrivateProfileIntA(section, "TextID", -1, iniDrugs);
drugs[drugsCount].msgID = (msg > 0) ? msg : -1;
drugs[drugsCount].frmID = GetPrivateProfileIntA(section, "FrmID", -1, iniDrugs);
addictionGvarCount++;
}
drugsCount++;
}
}
if (drugsCount) {
MakeCall(0x479EEC, drug_effect_allowed_hack, 1);
MakeCall(0x43C15C, list_karma_hack, 2);
MakeCall(0x47A5B8, pid_to_gvar_hack, 1);
MakeCall(0x47A50C, perform_withdrawal_start_hack);
SafeWrite32(0x47A523, 0x9090EBD1); // shr ebx, 1
SafeWrite8(0x47A527, 0x90);
if (addictionGvarCount) {
LoadGameHook::OnAfterGameInit() += CheckValidGvarNumber;
}
LoadGameHook::OnGameReset() += ResetDrugs;
}
}
dlog_f(" (%d/%d drugs) Done\n", DL_INIT, drugsCount, count);
}
}
void Drugs::exit() {
if (drugs) delete[] drugs;
}
}
+57
View File
@@ -0,0 +1,57 @@
/*
* sfall
* Copyright (C) 2008-2019 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/>.
*/
#pragma once
#include "Module.h"
namespace sfall
{
class Drugs : public Module {
public:
const char* name() { return "Drugs"; }
void init();
void exit() override;
static long addictionGvarCount;
static long GetDrugCount();
static long GetDrugPid(long n);
static long GetDrugGvar(long n);
static long SetDrugNumEffect(long pid, long effect);
static long SetDrugAddictTimeOff(long pid, long time);
};
#define SIZE_S_DRUGS (32)
struct sDrugs {
DWORD drugPid; // don't move
long gvarID; // don't move
long numEffects; // don't move
long addictTimeOff;
long msgID;
long frmID;
long iniNumEffects;
long iniAddictTimeOff;
};
extern sDrugs *drugs;
}
+17 -2
View File
@@ -51,6 +51,7 @@ namespace sfall
popadc }
static Delegate<> onGameInit;
static Delegate<> onAfterGameInit;
static Delegate<> onGameExit;
static Delegate<> onGameReset;
static Delegate<> onBeforeGameStart;
@@ -137,6 +138,7 @@ static void _stdcall SaveGame2() {
h = CreateFileA(buf, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
if (h != INVALID_HANDLE_VALUE) {
Worldmap::SaveData(h);
BugFixes::DrugsSaveFix(h);
CloseHandle(h);
} else {
goto errorSave;
@@ -234,6 +236,7 @@ static bool LoadGame_Before() {
h = CreateFileA(buf, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
if (h != INVALID_HANDLE_VALUE) {
if (Worldmap::LoadData(h)) goto errorLoad;
if (BugFixes::DrugsLoadFix(h)) goto errorLoad;
CloseHandle(h);
} else {
dlogr("Cannot open sfalldb.sav.", DL_MAIN);
@@ -321,10 +324,14 @@ static void __declspec(naked) main_load_new_hook() {
}
}
static void __stdcall GameInitialized() {
static void __stdcall GameInitialization() {
onGameInit.invoke();
}
static void __stdcall GameInitialized() {
onAfterGameInit.invoke();
}
static void __stdcall GameExit() {
onGameExit.invoke();
}
@@ -335,10 +342,14 @@ static void __stdcall GameClose() {
static void __declspec(naked) main_init_system_hook() {
__asm {
pushadc;
call GameInitialization;
popadc;
call fo::funcoffs::main_init_system_;
pushadc;
call GameInitialized;
popadc;
jmp fo::funcoffs::main_init_system_;
retn;
}
}
@@ -662,6 +673,10 @@ Delegate<>& LoadGameHook::OnGameInit() {
return onGameInit;
}
Delegate<>& LoadGameHook::OnAfterGameInit() {
return onAfterGameInit;
}
Delegate<>& LoadGameHook::OnGameExit() {
return onGameExit;
}
+4 -1
View File
@@ -29,9 +29,12 @@ public:
const char* name() { return "LoadGameHook"; }
void init();
// Invoked when the game has initialized (game_init_ was called).
// Invoked before game initialization (game_init_ was called).
static Delegate<>& OnGameInit();
// Invoked when the game has been initialized
static Delegate<>& OnAfterGameInit();
// Invoked when the game exits to main menu
static Delegate<>& OnGameExit();
+53 -8
View File
@@ -21,6 +21,7 @@
#include "..\main.h"
#include "..\FalloutEngine\Fallout2.h"
#include "HookScripts\InventoryHs.h"
#include "Drugs.h"
#include "HookScripts.h"
#include "LoadGameHook.h"
@@ -57,8 +58,44 @@ static struct DudeState {
long addictGvar[8];
long tag_skill[4];
//DWORD bbox_sneak;
long* extendAddictGvar = nullptr;
} realDude;
static void SaveAddictGvarState() {
int n = 0;
for (int i = 0; i < Drugs::GetDrugCount(); i++) {
long gvarID = Drugs::GetDrugGvar(i);
if (gvarID > 0) realDude.extendAddictGvar[n++] = fo::var::game_global_vars[gvarID];
}
}
static void RestoreAddictGvarState() {
int n = 0;
for (int i = 0; i < Drugs::GetDrugCount(); i++) {
long gvarID = Drugs::GetDrugGvar(i);
if (gvarID > 0) fo::var::game_global_vars[gvarID] = realDude.extendAddictGvar[n++];
}
}
static bool SetAddictGvar(fo::GameObject* npc) {
bool isAddict = false;
int count = Drugs::GetDrugCount();
for (int i = 0; i < count; i++) {
long gvarID = Drugs::GetDrugGvar(i);
if (gvarID > 0) fo::var::game_global_vars[gvarID] = 0;
}
for (int i = 0; i < count; i++) {
long pid = Drugs::GetDrugPid(i);
if (pid > 0) {
long gvarID = Drugs::GetDrugGvar(i);
if (gvarID <= 0 || !fo::CheckAddictByPid(npc, pid)) continue;
fo::var::game_global_vars[gvarID] = 1;
isAddict = true;
}
}
return isAddict;
}
// saves the state of PC before moving control to NPC
static void SaveRealDudeState() {
realDude.obj_dude = fo::var::obj_dude;
@@ -79,6 +116,7 @@ static void SaveRealDudeState() {
for (int i = 0; i < 6; i++) realDude.addictGvar[i] = fo::var::game_global_vars[fo::var::drugInfoList[i].addictGvar];
realDude.addictGvar[6] = fo::var::game_global_vars[fo::var::drugInfoList[7].addictGvar];
realDude.addictGvar[7] = fo::var::game_global_vars[fo::var::drugInfoList[8].addictGvar];
if (realDude.extendAddictGvar) SaveAddictGvarState();
if (skipCounterAnim) SafeWriteBatch<BYTE>(0, {0x422BDE, 0x4229EC}); // no animate
}
@@ -123,16 +161,15 @@ static void SetCurrentDude(fo::GameObject* npc) {
fo::var::itemCurrentItem = 1;
}
long alcoholAddict = 0;
bool isAddict = false;
for (int i = 0; i < 9; i++) fo::var::game_global_vars[fo::var::drugInfoList[i].addictGvar] = 0;
for (int i = 0; i < 9; i++) {
long result = fo::CheckAddictByPid(npc, fo::var::drugInfoList[i].itemPid);
if (i == 6) {
alcoholAddict = result;
} else if (i == 7) {
result |= alcoholAddict;
}
fo::var::game_global_vars[fo::var::drugInfoList[i].addictGvar] = result;
if (!fo::CheckAddictByPid(npc, fo::var::drugInfoList[i].itemPid)) continue;
fo::var::game_global_vars[fo::var::drugInfoList[i].addictGvar] = 1;
isAddict = true;
}
if (realDude.extendAddictGvar) isAddict |= SetAddictGvar(npc); // check new added addictions
fo::ToggleNpcFlag(npc, 4, isAddict); // for show/hide addiction box (fix bug)
// switch main dude_obj pointers - this should be done last!
fo::var::obj_dude = npc;
@@ -177,6 +214,7 @@ static void RestoreRealDudeState() {
for (int i = 0; i < 6; i++) fo::var::game_global_vars[fo::var::drugInfoList[i].addictGvar] = realDude.addictGvar[i];
fo::var::game_global_vars[fo::var::drugInfoList[7].addictGvar] = realDude.addictGvar[6];
fo::var::game_global_vars[fo::var::drugInfoList[8].addictGvar] = realDude.addictGvar[7];
if (realDude.extendAddictGvar) RestoreAddictGvarState();
if (skipCounterAnim) SafeWriteBatch<BYTE>(1, {0x422BDE, 0x4229EC}); // restore
fo::func::intface_redraw();
@@ -314,6 +352,9 @@ void PartyControl::init() {
SafeWrite16(0x4AFC1C, 0x840F);
}
};
if (Drugs::addictionGvarCount) realDude.extendAddictGvar = new long[Drugs::addictionGvarCount];
HookCall(0x454218, stat_pc_add_experience_hook); // call inside op_give_exp_points_hook
HookCalls(pc_flag_toggle_hook, { 0x4124F1, 0x41279A });
@@ -332,4 +373,8 @@ void PartyControl::init() {
}
}
void PartyControl::exit() {
if (realDude.extendAddictGvar) delete[] realDude.extendAddictGvar;
}
}
+1
View File
@@ -26,6 +26,7 @@ class PartyControl : public Module {
public:
const char* name() { return "PartyControl"; }
void init();
void exit() override;
static int __stdcall SwitchHandHook(fo::GameObject* item);
+2
View File
@@ -224,6 +224,7 @@
<ClInclude Include="FalloutEngine\Variables.h" />
<ClInclude Include="FalloutEngine\Functions.h" />
<ClInclude Include="FalloutEngine\Functions_def.h" />
<ClInclude Include="Modules\Drugs.h" />
<ClInclude Include="Modules\HookScripts\CombatHs.h" />
<ClInclude Include="Modules\HookScripts\Common.h" />
<ClInclude Include="Modules\HookScripts\DeathHs.h" />
@@ -312,6 +313,7 @@
<ClCompile Include="FalloutEngine\FunctionOffsets.cpp" />
<ClCompile Include="FalloutEngine\Variables.cpp" />
<ClCompile Include="FalloutEngine\Functions.cpp" />
<ClCompile Include="Modules\Drugs.cpp" />
<ClCompile Include="Modules\HookScripts\CombatHs.cpp" />
<ClCompile Include="Modules\HookScripts\Common.cpp" />
<ClCompile Include="Modules\HookScripts\DeathHs.cpp" />
+12 -6
View File
@@ -37,9 +37,6 @@
<ClInclude Include="Modules\BurstMods.h">
<Filter>Modules</Filter>
</ClInclude>
<ClInclude Include="Modules\Combat.h">
<Filter>Modules</Filter>
</ClInclude>
<ClInclude Include="Modules\Console.h">
<Filter>Modules</Filter>
</ClInclude>
@@ -275,6 +272,12 @@
<ClInclude Include="Modules\Objects.h">
<Filter>Modules</Filter>
</ClInclude>
<ClInclude Include="Modules\Combat.h">
<Filter>Modules</Filter>
</ClInclude>
<ClInclude Include="Modules\Drugs.h">
<Filter>Modules</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="main.cpp" />
@@ -296,9 +299,6 @@
<ClCompile Include="Modules\BurstMods.cpp">
<Filter>Modules</Filter>
</ClCompile>
<ClCompile Include="Modules\Combat.cpp">
<Filter>Modules</Filter>
</ClCompile>
<ClCompile Include="Modules\Console.cpp">
<Filter>Modules</Filter>
</ClCompile>
@@ -510,6 +510,12 @@
<ClCompile Include="Modules\Objects.cpp">
<Filter>Modules</Filter>
</ClCompile>
<ClCompile Include="Modules\Combat.cpp">
<Filter>Modules</Filter>
</ClCompile>
<ClCompile Include="Modules\Drugs.cpp">
<Filter>Modules</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="version.rc" />
+2
View File
@@ -36,6 +36,7 @@
#include "Modules\Credits.h"
#include "Modules\Criticals.h"
#include "Modules\DebugEditor.h"
#include "Modules\Drugs.h"
#include "Modules\Elevators.h"
#include "Modules\Explosions.h"
#include "Modules\FileSystem.h"
@@ -162,6 +163,7 @@ static void InitModules() {
manager.add<ExtraSaveSlots>();
manager.add<Inventory>();
manager.add<MainMenu>();
manager.add<Drugs>(); // should be loaded before PartyControl
manager.add<PartyControl>();
manager.add<BurstMods>();
manager.add<Books>();