Files
sfall/sfall/Modules/Perks.cpp
T

1127 lines
27 KiB
C++

/*
* sfall
* Copyright (C) 2008, 2009, 2010, 2012 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 <vector>
#include "..\main.h"
#include "..\FalloutEngine\Fallout2.h"
#include "LoadGameHook.h"
#include "Perks.h"
namespace sfall
{
using namespace fo;
static char Name[64 * PERK_count];
static char Desc[1024 * PERK_count];
static char tName[64 * TRAIT_count];
static char tDesc[1024 * TRAIT_count];
static char perksFile[MAX_PATH];
static BYTE disableTraits[TRAIT_count];
#define check_trait(a) !disableTraits[a] && (var::pc_trait[0] == a || var::pc_trait[1] == a)
static DWORD addPerkMode = 2;
//static const PerkStruct BlankPerk={ &Name[PERK_count*64], &Desc[PERK_count*1024], 0x48, 1, 1, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0 };
static PerkInfo perks[PERK_count];
static TraitInfo traits[TRAIT_count];
struct FakePerk {
int Level;
int Image;
char Name[64];
char Desc[1024];
};
std::vector<FakePerk> fakeTraits;
std::vector<FakePerk> fakePerks;
std::vector<FakePerk> fakeSelectablePerks;
static DWORD RemoveTraitID;
static DWORD RemovePerkID;
static DWORD RemoveSelectableID;
static DWORD TraitSkillBonuses[TRAIT_count*18];
static DWORD TraitStatBonuses[TRAIT_count*(STAT_max_derived+1)];
static DWORD IgnoringDefaultPerks = 0;
static char PerkBoxTitle[64];
static DWORD PerkFreqOverride = 0;
static const DWORD GainStatPerks[7][2] = {
{0x4AF122, 0xC9}, // Strength // mov ecx, ecx
{0x4AF184, 0xC9}, // Perception
{0x4AF19F, 0x90}, // Endurance // nop
{0x4AF1C0, 0xC9}, // Charisma
{0x4AF217, 0xC9}, // Intelligance
{0x4AF232, 0x90}, // Agility
{0x4AF24D, 0x90}, // Luck
};
void _stdcall SetPerkFreq(int i) {
PerkFreqOverride = i;
}
static DWORD _stdcall IsTraitDisabled(int id) {
return disableTraits[id];
}
static void __declspec(naked) LevelUpHook() {
__asm {
push ecx;
push ebx;
mov ecx, PerkFreqOverride;
test ecx, ecx;
jnz afterskilled;
mov eax, TRAIT_skilled;
call fo::funcoffs::trait_level_; //Check if the player has the skilled trait
test eax, eax;
jz notskilled;
push TRAIT_skilled;
call IsTraitDisabled;
test eax, eax;
jnz notskilled;
mov ecx, 4;
jmp afterskilled;
notskilled:
mov ecx, 3;
afterskilled:
mov eax, ds:[FO_VAR_Level_]; //Get players level
inc eax;
xor edx, edx;
div ecx;
test edx, edx;
jnz end;
inc byte ptr ds:[FO_VAR_free_perk]; //Increment the number of perks owed
end:
pop ebx;
pop ecx;
mov edx, ds:[FO_VAR_Level_];
retn;
}
}
static void __declspec(naked) GetPerkBoxTitleHook() {
__asm {
lea eax, PerkBoxTitle;
ret;
}
}
void _stdcall IgnoreDefaultPerks() {
IgnoringDefaultPerks = 1;
}
void _stdcall RestoreDefaultPerks() {
IgnoringDefaultPerks = 0;
}
void _stdcall SetPerkboxTitle(char* name) {
if (name[0] == '\0') {
SafeWrite32(0x43C77D, 0x488CB);
} else {
strcpy_s(PerkBoxTitle, name);
HookCall(0x43C77C, GetPerkBoxTitleHook);
}
}
void _stdcall SetSelectablePerk(char* name, int level, int image, char* desc) {
if (level < 0 || level > 1) return;
if (level == 0) {
for (DWORD i = 0; i < fakeSelectablePerks.size(); i++) {
if (!strcmp(name, fakeSelectablePerks[i].Name)) {
fakeSelectablePerks.erase(fakeSelectablePerks.begin() + i);
return;
}
}
} else {
for (DWORD i = 0; i < fakeSelectablePerks.size(); i++) {
if (!strcmp(name, fakeSelectablePerks[i].Name)) {
fakeSelectablePerks[i].Level = level;
fakeSelectablePerks[i].Image = image;
strcpy_s(fakeSelectablePerks[i].Desc, desc);
return;
}
}
FakePerk fp;
memset(&fp, 0, sizeof(FakePerk));
fp.Level = level;
fp.Image = image;
strcpy_s(fp.Name, name);
strcpy_s(fp.Desc, desc);
fakeSelectablePerks.push_back(fp);
}
}
void _stdcall SetFakePerk(char* name, int level, int image, char* desc) {
if (level < 0 || level > 100) return;
if (level == 0) {
for (DWORD i = 0; i < fakePerks.size(); i++) {
if (!strcmp(name, fakePerks[i].Name)) {
fakePerks.erase(fakePerks.begin() + i);
return;
}
}
} else {
for (DWORD i = 0; i < fakePerks.size(); i++) {
if (!strcmp(name, fakePerks[i].Name)) {
fakePerks[i].Level = level;
fakePerks[i].Image = image;
strcpy_s(fakePerks[i].Desc, desc);
return;
}
}
FakePerk fp;
memset(&fp, 0, sizeof(FakePerk));
fp.Level = level;
fp.Image = image;
strcpy_s(fp.Name, name);
strcpy_s(fp.Desc, desc);
fakePerks.push_back(fp);
}
}
void _stdcall SetFakeTrait(char* name, int level, int image, char* desc) {
if (level < 0 || level > 1) return;
if (level == 0) {
for (DWORD i = 0; i < fakeTraits.size(); i++) {
if (!strcmp(name, fakeTraits[i].Name)) {
fakeTraits.erase(fakeTraits.begin() + i);
return;
}
}
} else {
for (DWORD i = 0; i < fakeTraits.size(); i++) {
if (!strcmp(name, fakeTraits[i].Name)) {
fakeTraits[i].Level = level;
fakeTraits[i].Image = image;
strcpy_s(fakeTraits[i].Desc, desc);
return;
}
}
FakePerk fp;
memset(&fp, 0, sizeof(FakePerk));
fp.Level = level;
fp.Image = image;
strcpy_s(fp.Name, name);
strcpy_s(fp.Desc, desc);
fakeTraits.push_back(fp);
}
}
static DWORD _stdcall HaveFakeTraits2() {
return fakeTraits.size();
}
static void __declspec(naked) HaveFakeTraits() {
__asm {
push ebx;
push ecx;
push edx;
call HaveFakeTraits2;
pop edx;
pop ecx;
pop ebx;
ret;
}
}
static DWORD _stdcall HaveFakePerks2() {
return fakePerks.size();
}
static void __declspec(naked) HaveFakePerks() {
__asm {
push ebx;
push ecx;
push edx;
call HaveFakePerks2;
pop edx;
pop ecx;
pop ebx;
ret;
}
}
static FakePerk* _stdcall GetFakePerk2(int id) {
return &fakePerks[id-PERK_count];
}
static void __declspec(naked) GetFakePerk() {
__asm {
mov eax, [esp+4];
push ebx;
push ecx;
push edx;
push eax;
call GetFakePerk2;
pop edx;
pop ecx;
pop ebx;
ret 4;
}
}
static FakePerk* _stdcall GetFakeSPerk2(int id) {
return &fakeSelectablePerks[id-PERK_count];
}
static void __declspec(naked) GetFakeSPerk() {
__asm {
mov eax, [esp+4];
push ebx;
push ecx;
push edx;
push eax;
call GetFakeSPerk2;
pop edx;
pop ecx;
pop ebx;
ret 4;
}
}
static DWORD _stdcall GetFakeSPerkLevel2(int id) {
char* c = fakeSelectablePerks[id - PERK_count].Name;
for (DWORD i = 0; i < fakePerks.size(); i++) {
if (!strcmp(c, fakePerks[i].Name)) return fakePerks[i].Level;
}
return 0;
}
static void __declspec(naked) GetFakeSPerkLevel() {
__asm {
mov eax, [esp+4];
push ebx;
push ecx;
push edx;
push eax;
call GetFakeSPerkLevel2;
pop edx;
pop ecx;
pop ebx;
ret 4;
}
}
static DWORD _stdcall HandleFakeTraits(int i2) {
for (DWORD i = 0; i < fakeTraits.size(); i++) {
DWORD a = (DWORD)fakeTraits[i].Name;
__asm {
mov eax, a;
call fo::funcoffs::folder_print_line_;
mov a, eax;
}
if (a && !i2) {
i2 = 1;
var::folder_card_fid = fakeTraits[i].Image;
var::folder_card_title = (DWORD)fakeTraits[i].Name;
var::folder_card_title2 = 0;
var::folder_card_desc = (DWORD)fakeTraits[i].Desc;
}
}
return i2;
}
static void __declspec(naked) PlayerHasPerkHook() {
__asm {
push ecx;
call HandleFakeTraits;
mov ecx, eax;
xor ebx, ebx;
oloop:
mov eax, ds:[FO_VAR_obj_dude];
mov edx, ebx;
call fo::funcoffs::perk_level_;
test eax, eax;
jnz win;
inc ebx;
cmp ebx, PERK_count;
jl oloop;
call HaveFakePerks;
test eax, eax;
jnz win;
push 0x434446;
retn;
win:
push 0x43438A;
retn;
}
}
static void __declspec(naked) PlayerHasTraitHook() {
__asm {
call HaveFakeTraits;
test eax, eax;
jz end;
push 0x43425B;
retn;
end:
jmp PlayerHasPerkHook;
}
}
static void __declspec(naked) GetPerkLevelHook() {
__asm {
cmp edx, PERK_count;
jl end;
push edx;
call GetFakePerk;
mov eax, ds:[eax];
ret;
end:
jmp fo::funcoffs::perk_level_;
}
}
static void __declspec(naked) GetPerkImageHook() {
__asm {
cmp eax, PERK_count;
jl end;
push eax;
call GetFakePerk;
mov eax, ds:[eax+4];
ret;
end:
jmp fo::funcoffs::perk_skilldex_fid_;
}
}
static void __declspec(naked) GetPerkNameHook() {
__asm {
cmp eax, PERK_count;
jl end;
push eax;
call GetFakePerk;
lea eax, ds:[eax+8];
ret;
end:
jmp fo::funcoffs::perk_name_;
}
}
static void __declspec(naked) GetPerkDescHook() {
__asm {
cmp eax, PERK_count;
jl end;
push eax;
call GetFakePerk;
lea eax, ds:[eax+72];
ret;
end:
jmp fo::funcoffs::perk_description_
}
}
static void __declspec(naked) EndPerkLoopHook() {
__asm {
call HaveFakePerks;
add eax, PERK_count;
cmp ebx, eax;
jl end;
push 0x434446;
retn;
end:
push 0x4343A5;
retn;
}
}
static DWORD _stdcall HandleExtraSelectablePerks(DWORD offset, DWORD* data) {
for (DWORD i = 0; i < fakeSelectablePerks.size(); i++) {
//*(WORD*)(_name_sort_list + (offset+i)*8)=(WORD)(PERK_count+i);
data[offset + i] = PERK_count + i;
}
return offset + fakeSelectablePerks.size();
}
static void __declspec(naked) GetAvailablePerksHook() {
__asm {
push ecx;
push ebx;
push edx;
mov ebx, IgnoringDefaultPerks;
test ebx, ebx;
jnz skipdefaults;
call fo::funcoffs::perk_make_list_;
jmp next;
skipdefaults:
xor eax, eax;
next:
push eax;
call HandleExtraSelectablePerks;
pop ebx;
pop ecx;
ret;
}
}
static void __declspec(naked) GetPerkSLevelHook() {
__asm {
cmp edx, PERK_count;
jl end;
push edx;
call GetFakeSPerkLevel;
ret;
end:
jmp fo::funcoffs::perk_level_;
}
}
static void __declspec(naked) GetPerkSImageHook() {
__asm {
cmp eax, PERK_count;
jl end;
push eax;
call GetFakeSPerk;
mov eax, ds:[eax+4];
ret;
end:
jmp fo::funcoffs::perk_skilldex_fid_;
}
}
static void __declspec(naked) GetPerkSNameHook() {
__asm {
cmp eax, PERK_count;
jl end;
push eax;
call GetFakeSPerk;
lea eax, ds:[eax+8];
ret;
end:
jmp fo::funcoffs::perk_name_;
}
}
static void __declspec(naked) GetPerkSDescHook() {
__asm {
cmp eax, PERK_count;
jl end;
push eax;
call GetFakeSPerk;
lea eax, ds:[eax+72];
ret;
end:
jmp fo::funcoffs::perk_description_;
}
}
static void _stdcall AddFakePerk(DWORD perkID) {
perkID -= PERK_count;
if (addPerkMode & 1) {
bool matched = false;
for (DWORD d = 0; d < fakeTraits.size(); d++) {
if (!strcmp(fakeTraits[d].Name, fakeSelectablePerks[perkID].Name)) {
matched = true;
}
}
if (!matched) {
RemoveTraitID = fakeTraits.size();
fakeTraits.push_back(fakeSelectablePerks[perkID]);
}
}
if (addPerkMode & 2) {
bool matched = false;
for (DWORD d = 0; d < fakePerks.size(); d++) {
if (!strcmp(fakePerks[d].Name, fakeSelectablePerks[perkID].Name)) {
RemovePerkID = d;
fakePerks[d].Level++;
matched = true;
}
}
if (!matched) {
RemovePerkID = fakePerks.size();
fakePerks.push_back(fakeSelectablePerks[perkID]);
}
}
if (addPerkMode & 4) {
RemoveSelectableID = perkID;
//fakeSelectablePerks.remove_at(perkID);
}
}
static void __declspec(naked) AddPerkHook() {
__asm {
cmp edx, PERK_count;
jl end;
push ecx;
push ebx;
push edx;
call AddFakePerk;
pop ebx;
pop ecx;
xor eax, eax;
ret;
end:
push edx;
call fo::funcoffs::perk_add_;
pop edx;
test eax, eax;
jnz end2;
cmp edx, 84;
jl end2;
cmp edx, 90;
jg end2;
inc ds:[edx*4 + (FO_VAR_pc_proto + 0x24 - (PERK_gain_strength_perk)*4)]; // base_stat_srength
end2:
retn;
}
}
static void __declspec(naked) HeaveHoHook() {
__asm {
xor edx, edx;
mov eax, ecx;
call fo::funcoffs::stat_level_;
lea ebx, [0+eax*4];
sub ebx, eax;
cmp ebx, esi;
jle lower;
mov ebx, esi;
lower:
mov eax, ecx;
mov edx, PERK_heave_ho;
call fo::funcoffs::perk_level_;
lea ecx, [0+eax*8];
sub ecx, eax;
sub ecx, eax;
mov eax, ecx;
add eax, ebx;
push 0x478AFC;
retn;
}
}
void _stdcall ApplyHeaveHoFix() {
MakeJump(0x478AC4, HeaveHoHook);
perks[PERK_heave_ho].strengthMin = 0;
}
static void PerkSetup() {
//Character screen
HookCall(0x434256, PlayerHasTraitHook);
SafeWrite8(0x43436B, 0xE9);
HookCall(0x43436B, PlayerHasPerkHook);
HookCall(0x4343AC, GetPerkLevelHook);
HookCall(0x4343C1, GetPerkNameHook);
HookCall(0x4343DF, GetPerkNameHook);
HookCall(0x43440D, GetPerkImageHook);
HookCall(0x43441B, GetPerkNameHook);
HookCall(0x434432, GetPerkDescHook);
SafeWrite8(0x43443D, 0xE9);
HookCall(0x43443D, EndPerkLoopHook);
//GetPlayerAvailablePerks
HookCall(0x43D127, GetAvailablePerksHook);
HookCall(0x43D17D, GetPerkSNameHook);
HookCall(0x43D25E, GetPerkSLevelHook);
HookCall(0x43D275, GetPerkSLevelHook);
//ShowPerkBox
HookCall(0x43C82E, GetPerkSLevelHook);
HookCall(0x43C85B, GetPerkSLevelHook);
HookCall(0x43C888, GetPerkSDescHook);
HookCall(0x43C8A6, GetPerkSNameHook);
HookCall(0x43C8D1, GetPerkSDescHook);
HookCall(0x43C8EF, GetPerkSNameHook);
HookCall(0x43C90F, GetPerkSImageHook);
HookCall(0x43C952, AddPerkHook);
//PerkboxSwitchPerk
HookCall(0x43C3F1, GetPerkSLevelHook);
HookCall(0x43C41E, GetPerkSLevelHook);
HookCall(0x43C44B, GetPerkSDescHook);
HookCall(0x43C469, GetPerkSNameHook);
HookCall(0x43C494, GetPerkSDescHook);
HookCall(0x43C4B2, GetPerkSNameHook);
HookCall(0x43C4D2, GetPerkSImageHook);
memset(Name, 0, sizeof(Name));
memset(Desc, 0, sizeof(Desc));
memcpy(perks, var::perk_data, sizeof(PerkInfo) * PERK_count);
SafeWrite32(0x496669, (DWORD)perks);
SafeWrite32(0x496837, (DWORD)perks);
SafeWrite32(0x496BAD, (DWORD)perks);
SafeWrite32(0x496C41, (DWORD)perks);
SafeWrite32(0x496D25, (DWORD)perks);
SafeWrite32(0x496696, (DWORD)perks + 4);
SafeWrite32(0x496BD1, (DWORD)perks + 4);
SafeWrite32(0x496BF5, (DWORD)perks + 8);
SafeWrite32(0x496AD4, (DWORD)perks + 12);
if (strlen(perksFile)) {
char num[4];
for (int i = 0; i < PERK_count; i++) {
_itoa_s(i, num, 10);
if (GetPrivateProfileString(num, "Name", "", &Name[i * 64], 63, perksFile)) {
perks[i].name = &Name[i * 64];
}
if (GetPrivateProfileString(num, "Desc", "", &Desc[i * 1024], 1023, perksFile)) {
perks[i].description = &Desc[i * 1024];
}
int value;
value = GetPrivateProfileInt(num, "Image", -99999, perksFile);
if (value != -99999) perks[i].image = value;
value = GetPrivateProfileInt(num, "Ranks", -99999, perksFile);
if (value != -99999) perks[i].ranks = value;
value = GetPrivateProfileInt(num, "Level", -99999, perksFile);
if (value != -99999) perks[i].levelMin = value;
value = GetPrivateProfileInt(num, "Stat", -99999, perksFile);
if (value != -99999) perks[i].stat = value;
value = GetPrivateProfileInt(num, "StatMag", -99999, perksFile);
if (value != -99999) perks[i].statMod = value;
value = GetPrivateProfileInt(num, "Skill1", -99999, perksFile);
if (value != -99999) perks[i].skill1 = value;
value = GetPrivateProfileInt(num, "Skill1Mag", -99999, perksFile);
if (value != -99999) perks[i].skill1Min = value;
value = GetPrivateProfileInt(num, "Type", -99999, perksFile);
if (value != -99999) perks[i].skillOperator = value;
value = GetPrivateProfileInt(num, "Skill2", -99999, perksFile);
if (value != -99999) perks[i].skill2 = value;
value = GetPrivateProfileInt(num, "Skill2Mag", -99999, perksFile);
if (value != -99999) perks[i].skill2Min = value;
value = GetPrivateProfileInt(num, "STR", -99999, perksFile);
if (value != -99999) perks[i].strengthMin = value;
value = GetPrivateProfileInt(num, "PER", -99999, perksFile);
if (value != -99999) perks[i].perceptionMin = value;
value = GetPrivateProfileInt(num, "END", -99999, perksFile);
if (value != -99999) perks[i].enduranceMin = value;
value = GetPrivateProfileInt(num, "CHR", -99999, perksFile);
if (value != -99999) perks[i].charismaMin = value;
value = GetPrivateProfileInt(num, "INT", -99999, perksFile);
if (value != -99999) perks[i].intelligenceMin = value;
value = GetPrivateProfileInt(num, "AGL", -99999, perksFile);
if (value != -99999) perks[i].agilityMin = value;
value = GetPrivateProfileInt(num, "LCK", -99999, perksFile);
if (value != -99999) perks[i].luckMin = value;
}
}
for (int i = 0; i < PERK_count; i++) {
if (perks[i].name != &Name[64 * i]) {
strcpy_s(&Name[64 * i], 64, perks[i].name);
perks[i].name = &Name[64 * i];
}
if (perks[i].description&&perks[i].description != &Desc[1024 * i]) {
strcpy_s(&Desc[1024 * i], 1024, perks[i].description);
perks[i].description = &Desc[1024 * i];
}
}
//perk_owed hooks
MakeCall(0x4AFB2F, LevelUpHook);//replaces 'mov edx, ds:[PlayerLevel]
SafeWrite8(0x4AFB34, 0x90);
SafeWrite8(0x43C2EC, 0xEB); //skip the block of code which checks if the player has gained a perk (now handled in level up code)
SafeWrite8(0x43C362, 0xE8); //call sub_43401C, dec byte ptr ds:[0x570A29]
SafeWrite32(0x43C363, 0x43410C - 0x43C367); //replaces call sub_43401C, mov byte ptr ds:[0x570A29], 0
SafeWrite8(0x43C367, 0x3E);
SafeWrite16(0x43C368, 0x0DFE);
SafeWrite32(0x43C36A, 0x00570A29);
SafeWrite8(0x43C36e, 0x90);
}
static int _stdcall stat_get_base_direct(DWORD statID) {
DWORD result;
__asm {
mov edx, statID;
mov eax, dword ptr ds:[FO_VAR_obj_dude];
call fo::funcoffs::stat_get_base_direct_;
mov result, eax;
}
return result;
}
static int _stdcall trait_adjust_stat_override(DWORD statID) {
if (statID > STAT_max_derived) return 0;
int result = 0;
if (var::pc_trait[0] != -1) result += TraitStatBonuses[statID*TRAIT_count + var::pc_trait[0]];
if (var::pc_trait[1] != -1) result += TraitStatBonuses[statID*TRAIT_count + var::pc_trait[1]];
switch (statID) {
case STAT_st:
if (check_trait(TRAIT_gifted)) result++;
if (check_trait(TRAIT_bruiser)) result += 2;
break;
case STAT_pe:
if (check_trait(TRAIT_gifted)) result++;
break;
case STAT_en:
if (check_trait(TRAIT_gifted)) result++;
break;
case STAT_ch:
if (check_trait(TRAIT_gifted)) result++;
break;
case STAT_iq:
if (check_trait(TRAIT_gifted)) result++;
break;
case STAT_ag:
if (check_trait(TRAIT_gifted)) result++;
if (check_trait(TRAIT_small_frame)) result++;
break;
case STAT_lu:
if (check_trait(TRAIT_gifted)) result++;
break;
case STAT_max_move_points:
if (check_trait(TRAIT_bruiser)) result -= 2;
break;
case STAT_ac:
if (check_trait(TRAIT_kamikaze)) return -stat_get_base_direct(STAT_ac);
break;
case STAT_melee_dmg:
if (check_trait(TRAIT_heavy_handed)) result += 4;
break;
case STAT_carry_amt:
if (check_trait(TRAIT_small_frame)) {
int str = stat_get_base_direct(STAT_st);
result -= str * 10;
}
break;
case STAT_sequence:
if (check_trait(TRAIT_kamikaze)) result += 5;
break;
case STAT_heal_rate:
if (check_trait(TRAIT_fast_metabolism)) result += 2;
break;
case STAT_crit_chance:
if (check_trait(TRAIT_finesse)) result += 10;
break;
case STAT_better_crit:
if (check_trait(TRAIT_heavy_handed)) result -= 30;
break;
case STAT_rad_resist:
if (check_trait(TRAIT_fast_metabolism)) return -stat_get_base_direct(STAT_rad_resist);
break;
case STAT_poison_resist:
if (check_trait(TRAIT_fast_metabolism)) return -stat_get_base_direct(STAT_poison_resist);
break;
}
return result;
}
static void __declspec(naked) TraitAdjustStatHook() {
__asm {
push edx;
push ecx;
push eax;
call trait_adjust_stat_override;
pop ecx;
pop edx;
retn;
}
}
static int _stdcall trait_adjust_skill_override(DWORD skillID) {
int result = 0;
if (var::pc_trait[0] != -1) result += TraitSkillBonuses[skillID*TRAIT_count + var::pc_trait[0]];
if (var::pc_trait[1] != -1) result += TraitSkillBonuses[skillID*TRAIT_count + var::pc_trait[1]];
if (check_trait(TRAIT_gifted)) result -= 10;
if (check_trait(TRAIT_good_natured)) {
if (skillID <= SKILL_THROWING) result -= 10;
else if (skillID == SKILL_FIRST_AID || skillID == SKILL_DOCTOR || skillID == SKILL_CONVERSANT || skillID == SKILL_BARTER) result += 15;
}
return result;
}
static void __declspec(naked) TraitAdjustSkillHook() {
__asm {
push edx;
push ecx;
push eax;
call trait_adjust_skill_override;
pop ecx;
pop edx;
retn;
}
}
static void __declspec(naked) BlockedTrait() {
__asm {
xor eax, eax;
retn;
}
}
static void TraitSetup() {
MakeJump(0x4B3C7C, TraitAdjustStatHook);
MakeJump(0x4B40FC, TraitAdjustSkillHook);
memset(tName, 0, sizeof(tName));
memset(tDesc, 0, sizeof(tDesc));
memcpy(traits, var::trait_data, sizeof(TraitInfo) * TRAIT_count);
memset(TraitStatBonuses, 0, sizeof(TraitStatBonuses));
memset(TraitSkillBonuses, 0, sizeof(TraitSkillBonuses));
SafeWrite32(0x4B3A81, (DWORD)traits);
SafeWrite32(0x4B3B80, (DWORD)traits);
SafeWrite32(0x4B3AAE, (DWORD)traits + 4);
SafeWrite32(0x4B3BA0, (DWORD)traits + 4);
SafeWrite32(0x4B3BC0, (DWORD)traits + 8);
if (strlen(perksFile)) {
char num[5], buf[512];
num[0] = 't';
char* num2 = &num[1];
for (int i = 0; i < TRAIT_count; i++) {
_itoa_s(i, num2, 4, 10);
if (GetPrivateProfileString(num, "Name", "", &tName[i * 64], 63, perksFile)) {
traits[i].name = &tName[i * 64];
}
if (GetPrivateProfileString(num, "Desc", "", &tDesc[i * 1024], 1023, perksFile)) {
traits[i].description = &tDesc[i * 1024];
}
int value;
value = GetPrivateProfileInt(num, "Image", -99999, perksFile);
if (value != -99999) traits[i].image = value;
if (GetPrivateProfileStringA(num, "StatMod", "", buf, 512, perksFile) > 0) {
char *stat, *mod;
stat = strtok(buf, "|");
mod = strtok(0, "|");
while (stat&&mod) {
int _stat = atoi(stat), _mod = atoi(mod);
if (_stat >= 0 && _stat <= STAT_max_derived) TraitStatBonuses[_stat*TRAIT_count + i] = _mod;
stat = strtok(0, "|");
mod = strtok(0, "|");
}
}
if (GetPrivateProfileStringA(num, "SkillMod", "", buf, 512, perksFile) > 0) {
char *stat, *mod;
stat = strtok(buf, "|");
mod = strtok(0, "|");
while (stat&&mod) {
int _stat = atoi(stat), _mod = atoi(mod);
if (_stat >= 0 && _stat < 18) TraitSkillBonuses[_stat*TRAIT_count + i] = _mod;
stat = strtok(0, "|");
mod = strtok(0, "|");
}
}
if (GetPrivateProfileInt(num, "NoHardcode", 0, perksFile)) {
disableTraits[i] = 1;
switch (i) {
case 3:
HookCall(0x4245E0, &BlockedTrait);
break;
case 4:
HookCall(0x4248F9, &BlockedTrait);
break;
case 7:
HookCall(0x478C8A, &BlockedTrait); //fast shot
HookCall(0x478E70, &BlockedTrait);
break;
case 8:
HookCall(0x410707, &BlockedTrait);
break;
case 9:
HookCall(0x42389F, &BlockedTrait);
break;
case 11:
HookCall(0x47A0CD, &BlockedTrait);
HookCall(0x47A51A, &BlockedTrait);
break;
case 12:
HookCall(0x479BE1, &BlockedTrait);
HookCall(0x47A0DD, &BlockedTrait);
break;
case 14:
HookCall(0x43C295, &BlockedTrait);
HookCall(0x43C2F3, &BlockedTrait);
break;
case 15:
HookCall(0x43C2A4, &BlockedTrait);
break;
}
}
}
}
for (int i = 0; i < TRAIT_count; i++) {
if (traits[i].name != &tName[64 * i]) {
strcpy_s(&tName[64 * i], 64, traits[i].name);
traits[i].name = &tName[64 * i];
}
if (traits[i].description&&traits[i].description != &tDesc[1024 * i]) {
strcpy_s(&tDesc[1024 * i], 1024, traits[i].description);
traits[i].description = &tDesc[1024 * i];
}
}
}
static __declspec(naked) void PerkInitWrapper() {
__asm {
call fo::funcoffs::perk_init_;
pushad;
call PerkSetup;
popad;
retn;
}
}
static __declspec(naked) void TraitInitWrapper() {
__asm {
call fo::funcoffs::trait_init_;
pushad;
call TraitSetup;
popad;
retn;
}
}
void _stdcall SetPerkValue(int id, int value, DWORD offset) {
if (id < 0 || id >= PERK_count) return;
*(DWORD*)((DWORD)(&perks[id]) + offset) = value;
}
void _stdcall SetPerkName(int id, char* value) {
if (id < 0 || id >= PERK_count) return;
strcpy_s(&Name[id * 64], 64, value);
}
void _stdcall SetPerkDesc(int id, char* value) {
if (id < 0 || id >= PERK_count) return;
strcpy_s(&Desc[id * 1024], 1024, value);
perks[id].description = &Desc[1024 * id];
}
void PerksReset() {
fakeTraits.clear();
fakePerks.clear();
fakeSelectablePerks.clear();
SafeWrite32(0x43C77D, 0x488CB);
IgnoringDefaultPerks = 0;
addPerkMode = 2;
SafeWrite8(0x478AC4, 0xBA);
SafeWrite32(0x478AC5, 0x23);
PerkFreqOverride = 0;
}
void Perks::save(HANDLE file) {
DWORD count;
DWORD unused;
count = fakeTraits.size();
WriteFile(file, &count, 4, &unused, 0);
for (DWORD i = 0; i < count; i++) {
WriteFile(file, &fakeTraits[i], sizeof(FakePerk), &unused, 0);
}
count = fakePerks.size();
WriteFile(file, &count, 4, &unused, 0);
for (DWORD i = 0; i < count; i++) {
WriteFile(file, &fakePerks[i], sizeof(FakePerk), &unused, 0);
}
count = fakeSelectablePerks.size();
WriteFile(file, &count, 4, &unused, 0);
for (DWORD i = 0; i < count; i++) {
WriteFile(file, &fakeSelectablePerks[i], sizeof(FakePerk), &unused, 0);
}
}
bool Perks::load(HANDLE file) {
DWORD count;
DWORD size;
ReadFile(file, &count, 4, &size, 0);
if (size != 4) return false;
for (DWORD i = 0; i < count; i++) {
FakePerk fp;
ReadFile(file, &fp, sizeof(FakePerk), &size, 0);
fakeTraits.push_back(fp);
}
ReadFile(file, &count, 4, &size, 0);
if (size != 4) return false;
for (DWORD i = 0; i < count; i++) {
FakePerk fp;
ReadFile(file, &fp, sizeof(FakePerk), &size, 0);
fakePerks.push_back(fp);
}
ReadFile(file, &count, 4, &size, 0);
if (size != 4) return false;
for (DWORD i = 0; i < count; i++) {
FakePerk fp;
ReadFile(file, &fp, sizeof(FakePerk), &size, 0);
fakeSelectablePerks.push_back(fp);
}
return true;
}
void _stdcall AddPerkMode(DWORD mode) {
addPerkMode = mode;
}
DWORD _stdcall HasFakePerk(char* name) {
for (DWORD i = 0; i < fakePerks.size(); i++) {
if (!strcmp(name, fakePerks[i].Name)) {
return fakePerks[i].Level;
}
}
return 0;
}
DWORD _stdcall HasFakeTrait(char* name) {
for (DWORD i = 0; i < fakeTraits.size(); i++) {
if (!strcmp(name, fakeTraits[i].Name)) {
return 1;
}
}
return 0;
}
void _stdcall ClearSelectablePerks() {
fakeSelectablePerks.clear();
addPerkMode = 2;
SafeWrite32(0x43C77D, 0x488CB);
IgnoringDefaultPerks = 0;
}
void PerksEnterCharScreen() {
RemoveTraitID = -1;
RemovePerkID = -1;
RemoveSelectableID = -1;
}
void PerksCancelCharScreen() {
if (RemoveTraitID != -1) {
fakeTraits.erase(fakeTraits.begin() + RemoveTraitID);
}
if (RemovePerkID != -1) {
if (!--fakePerks[RemovePerkID].Level) fakePerks.erase(fakePerks.begin() + RemovePerkID);
}
}
void PerksAcceptCharScreen() {
if (RemoveSelectableID != -1) fakeSelectablePerks.erase(fakeSelectablePerks.begin() + RemoveSelectableID);
}
void Perks::init() {
for (int i = STAT_st; i <= STAT_lu; i++) SafeWrite8(GainStatPerks[i][0], (BYTE)GainStatPerks[i][1]);
HookCall(0x442729, &PerkInitWrapper);
if (GetConfigString("Misc", "PerksFile", "", &perksFile[2], MAX_PATH)) {
perksFile[0] = '.';
perksFile[1] = '\\';
HookCall(0x44272E, &TraitInitWrapper);
} else perksFile[0] = 0;
LoadGameHook::OnGameReset() += PerksReset;
}
}