mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
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:
@@ -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
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
+89
-21
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>();
|
||||
|
||||
Reference in New Issue
Block a user