Added support for extra perks to has_fake_perk/get_perk_available script functions.

Fixed broken get_perk_available script function.
Some code corrections for Perks.cpp.
This commit is contained in:
NovaRain
2019-03-11 10:07:44 +08:00
parent 967e642908
commit 0f036b26ae
8 changed files with 65 additions and 134 deletions
+1 -1
View File
@@ -46,7 +46,7 @@ set_pickpocket_max and set_hit_chance_max effect all critters rather than just t
set_fake_trait and set_fake_perk can be used to add additional traits and perks to the character screen. They will be saved correctly when the player saves and reloads games, but by themselves they will have no further effect on the character. For perks, the allowed range for levels is between 0 and 100; setting the level to 0 removes that perk. For traits, the level must be 0 or 1. The image is a numeric id that corresponds to an entry in skilldex.lst. The name is limited to 64 characters and the description to 1024 characters by sfall, but internal Fallout limits may be lower. set_fake_trait and set_fake_perk can be used to add additional traits and perks to the character screen. They will be saved correctly when the player saves and reloads games, but by themselves they will have no further effect on the character. For perks, the allowed range for levels is between 0 and 100; setting the level to 0 removes that perk. For traits, the level must be 0 or 1. The image is a numeric id that corresponds to an entry in skilldex.lst. The name is limited to 64 characters and the description to 1024 characters by sfall, but internal Fallout limits may be lower.
has_fake_trait and has_fake_perk return the number of levels the player has of the perks/traits with the given name. has_fake_trait and has_fake_perk return the number of levels the player has of the perks/traits with the given name or ID of extra perk.
perk_add_mode, set_selectable_perk, set_perkbox_title, hide_real_perks, show_real_perks and clear_selectable_perks control the behaviour of the select a perk box. set_selectable_perk can be used to add additional items by setting the 'active' parameter to 1, and to remove them again by setting it to 0. set_perkbox_title can be used to change the title of the box, or by using "" it will be set back to the default. hide and show_real_perks can be used to prevent the dialog from displaying any of the original 119 perks. perk_add_mode modifies what happens when a fake perk is selected from the perks dialog. It is treated as a set of flags - if bit 1 is set then it is added to the players traits, if bit 2 is set it is added to the players perks, and if bit 3 is set it is removed from the list of selectable perks. The default is 0x2. clear_selectable_perks restores the dialog to its default state. perk_add_mode, set_selectable_perk, set_perkbox_title, hide_real_perks, show_real_perks and clear_selectable_perks control the behaviour of the select a perk box. set_selectable_perk can be used to add additional items by setting the 'active' parameter to 1, and to remove them again by setting it to 0. set_perkbox_title can be used to change the title of the box, or by using "" it will be set back to the default. hide and show_real_perks can be used to prevent the dialog from displaying any of the original 119 perks. perk_add_mode modifies what happens when a fake perk is selected from the perks dialog. It is treated as a set of flags - if bit 1 is set then it is added to the players traits, if bit 2 is set it is added to the players perks, and if bit 3 is set it is removed from the list of selectable perks. The default is 0x2. clear_selectable_perks restores the dialog to its default state.
+1 -1
View File
@@ -185,7 +185,7 @@
0x81be - void set_perkbox_title(string title) 0x81be - void set_perkbox_title(string title)
0x81bf - void hide_real_perks() 0x81bf - void hide_real_perks()
0x81c0 - void show_real_perks() 0x81c0 - void show_real_perks()
0x81c1 - int has_fake_perk(string name) 0x81c1 - int has_fake_perk(string name/int extraPerkID)
0x81c2 - int has_fake_trait(string name) 0x81c2 - int has_fake_trait(string name)
0x81c3 - void perk_add_mode(int type) 0x81c3 - void perk_add_mode(int type)
0x81c4 - void clear_selectable_perks() 0x81c4 - void clear_selectable_perks()
+2
View File
@@ -238,6 +238,7 @@ long Drugs::GetDrugGvar(long n) {
long Drugs::SetDrugNumEffect(long pid, long effect) { long Drugs::SetDrugNumEffect(long pid, long effect) {
for (int i = 0; i < drugsCount; i++) { for (int i = 0; i < drugsCount; i++) {
if (drugs[i].drugPid == pid) { if (drugs[i].drugPid == pid) {
if (effect < 0) effect = 0;
int set = -1; int set = -1;
CheckEngineNumEffects(set, pid); CheckEngineNumEffects(set, pid);
if (set != -1) fo::var::drugInfoList[set].numEffects = effect; if (set != -1) fo::var::drugInfoList[set].numEffects = effect;
@@ -252,6 +253,7 @@ long Drugs::SetDrugNumEffect(long pid, long effect) {
long Drugs::SetDrugAddictTimeOff(long pid, long time) { long Drugs::SetDrugAddictTimeOff(long pid, long time) {
for (int i = 0; i < drugsCount; i++) { for (int i = 0; i < drugsCount; i++) {
if (drugs[i].drugPid == pid) { if (drugs[i].drugPid == pid) {
if (time < 0) time = 0;
drugs[i].addictTimeOff = time; drugs[i].addictTimeOff = time;
drugsReset = true; drugsReset = true;
return 0; return 0;
+39 -42
View File
@@ -32,7 +32,7 @@ constexpr int maxNameLen = 64; // don't change size
constexpr int maxDescLen = 512; // don't change size constexpr int maxDescLen = 512; // don't change size
const int descLen = 256; // maximum text length for interface const int descLen = 256; // maximum text length for interface
static char perksFile[MAX_PATH]; static char perksFile[MAX_PATH] = {0};
static char Name[maxNameLen * PERK_count] = {0}; static char Name[maxNameLen * PERK_count] = {0};
static char Desc[descLen * PERK_count] = {0}; static char Desc[descLen * PERK_count] = {0};
@@ -263,10 +263,6 @@ void _stdcall SetFakeTrait(char* name, int active, int image, char* desc) {
} }
} }
static DWORD _stdcall HaveFakeTraits() {
return fakeTraits.size();
}
static DWORD _stdcall HaveFakePerks() { static DWORD _stdcall HaveFakePerks() {
return fakePerks.size(); return fakePerks.size();
} }
@@ -308,7 +304,8 @@ static long __fastcall GetFakeSelectPerkImage(int id) {
static FakePerk* __fastcall GetFakeSelectPerk(int id) { static FakePerk* __fastcall GetFakeSelectPerk(int id) {
if (id < startFakeID) { if (id < startFakeID) {
int i = PerkSearchID(id); int i = PerkSearchID(id);
return (i != -1) ? (FakePerk*)&extPerks[i] : 0; // Warning: if the id is not found, an exception will occur for the game! if (i < 0) i = 0; // if id is not found
return (FakePerk*)&extPerks[i];
} }
return &fakeSelectablePerks[id - startFakeID]; return &fakeSelectablePerks[id - startFakeID];
} }
@@ -330,38 +327,32 @@ static long __fastcall PlayerHasPerk(int* isSelectPtr) {
*isSelectPtr = HandleFakeTraits(*isSelectPtr); *isSelectPtr = HandleFakeTraits(*isSelectPtr);
for (int i = 0; i < PERK_count; i++) { for (int i = 0; i < PERK_count; i++) {
if (fo::func::perk_level(fo::var::obj_dude, i)) return 1; if (fo::func::perk_level(fo::var::obj_dude, i)) return 0x43438A; // print perks
} }
return !fakePerks.empty(); return (!fakePerks.empty()) ? 0x43438A : 0x434446; // skip print perks
}
static DWORD __fastcall HaveFakeTraits(int* isSelectPtr) {
return (fakeTraits.empty()) ? PlayerHasPerk(isSelectPtr) : 0x43425B;
} }
static void __declspec(naked) PlayerHasPerkHack() { static void __declspec(naked) PlayerHasPerkHack() {
__asm { __asm {
push ecx; // value push ecx; // isSelect
mov ecx, esp; // ptr to value mov ecx, esp; // ptr to isSelect
call PlayerHasPerk; call PlayerHasPerk;
pop ecx; // value from HandleFakeTraits pop ecx; // value from HandleFakeTraits
test eax, eax;
jnz win;
mov eax, 0x434446;
jmp eax;
win:
mov eax, 0x43438A;
jmp eax; jmp eax;
} }
} }
static void __declspec(naked) PlayerHasTraitHook() { static void __declspec(naked) PlayerHasTraitHook() {
__asm { __asm {
push ecx; push ecx; // isSelect
mov ecx, esp; // ptr to isSelect
call HaveFakeTraits; call HaveFakeTraits;
pop ecx; pop ecx; // value from HandleFakeTraits
test eax, eax;
jz end;
mov eax, 0x43425B;
jmp eax; jmp eax;
end:
jmp PlayerHasPerkHack;
} }
} }
@@ -424,6 +415,8 @@ fake:
} }
// Search all available perks for the player to display them in the character screen // Search all available perks for the player to display them in the character screen
static const DWORD EndPerkLoopExit = 0x434446;
static const DWORD EndPerkLoopCont = 0x4343A5;
static void __declspec(naked) EndPerkLoopHack() { static void __declspec(naked) EndPerkLoopHack() {
__asm { __asm {
jl cLoop; // if ebx < 119 jl cLoop; // if ebx < 119
@@ -433,11 +426,9 @@ static void __declspec(naked) EndPerkLoopHack() {
add eax, PERK_count; // total = perks count + vanilla count add eax, PERK_count; // total = perks count + vanilla count
cmp ebx, eax; // if perkId < total then continue cmp ebx, eax; // if perkId < total then continue
jl cLoop; jl cLoop;
mov eax, 0x434446; // exit loop jmp EndPerkLoopExit; // exit loop
jmp eax;
cLoop: cLoop:
mov eax, 0x4343A5; // continue loop jmp EndPerkLoopCont; // continue loop
jmp eax;
} }
} }
@@ -446,16 +437,16 @@ cLoop:
static DWORD _stdcall HandleExtraSelectablePerks(DWORD available, DWORD* data) { static DWORD _stdcall HandleExtraSelectablePerks(DWORD available, DWORD* data) {
size_t count = extPerks.size(); size_t count = extPerks.size();
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
if (available >= 119) break; // exit loop if the buffer is full if (available >= 119) break; // exit if the buffer is overfull
if (fo::func::perk_can_add(fo::var::obj_dude, extPerks[i].id)) data[available++] = extPerks[i].id; if (fo::func::perk_can_add(fo::var::obj_dude, extPerks[i].id)) data[available++] = extPerks[i].id;
} }
count = fakeSelectablePerks.size(); count = fakeSelectablePerks.size();
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
if (available >= 119) break; if (available >= 119) break;
// for fake perks, their ID must begin the number 256 // for fake perks, their ID should start from 256
data[available++] = startFakeID + i; //*(WORD*)(_name_sort_list + (offset+i)*8)=(WORD)(PERK_count+i); data[available++] = startFakeID + i; //*(WORD*)(_name_sort_list + (offset+i)*8)=(WORD)(PERK_count+i);
} }
return available; // total number of perks available the selection return available; // total number of perks available for selection
} }
static void __declspec(naked) GetAvailablePerksHook() { static void __declspec(naked) GetAvailablePerksHook() {
@@ -543,7 +534,11 @@ fake:
static void PerkSkillMod(fo::GameObject* critter, long skill, long mod, long type) { static void PerkSkillMod(fo::GameObject* critter, long skill, long mod, long type) {
if (mod == 0 || skill >= fo::SKILL_count) return; if (mod == 0 || skill >= fo::SKILL_count) return;
if (fo::func::skill_is_tagged(skill)) mod *= 2; if (mod < 0) {
type = !type;
mod = -mod;
}
if (fo::func::skill_is_tagged(skill)) mod /= 2;
do { do {
if (type) { if (type) {
fo::func::skill_inc_point_force(critter, skill); fo::func::skill_inc_point_force(critter, skill);
@@ -598,7 +593,7 @@ static void _stdcall AddFakePerk(DWORD perkID) {
fo::func::perk_add_effect(fo::var::obj_dude, perkID); fo::func::perk_add_effect(fo::var::obj_dude, perkID);
return; return;
} }
// for fake perk behavior // behavior for fake perk/trait
perkID -= startFakeID; perkID -= startFakeID;
if (addPerkMode & 1) { // add perk to trait if (addPerkMode & 1) { // add perk to trait
count = fakeTraits.size(); count = fakeTraits.size();
@@ -1286,16 +1281,22 @@ void _stdcall AddPerkMode(DWORD mode) {
addPerkMode = mode; addPerkMode = mode;
} }
DWORD _stdcall HasFakePerk(char* name) { DWORD HasFakePerk(const char* name, long id) {
if (id < PERK_count && name[0] == 0) return 0;
for (DWORD i = 0; i < fakePerks.size(); i++) { for (DWORD i = 0; i < fakePerks.size(); i++) {
if (!strcmp(name, fakePerks[i].Name)) { if (id) {
return fakePerks[i].Level; // current perk level if (fakePerks[i].id == id) return fakePerks[i].Level;
} else {
if (!strcmp(name, fakePerks[i].Name)) {
return fakePerks[i].Level; // current perk level
}
} }
} }
return 0; return 0;
} }
DWORD _stdcall HasFakeTrait(char* name) { DWORD _stdcall HasFakeTrait(const char* name) {
if (name[0] == 0) return 0;
for (DWORD i = 0; i < fakeTraits.size(); i++) { for (DWORD i = 0; i < fakeTraits.size(); i++) {
if (!strcmp(name, fakeTraits[i].Name)) { if (!strcmp(name, fakeTraits[i].Name)) {
return 1; return 1;
@@ -1326,11 +1327,7 @@ void PerksCancelCharScreen() {
if (RemovePerkID.size() > 1) RemovePerkID.sort(); // sorting to correctly remove from the end if (RemovePerkID.size() > 1) RemovePerkID.sort(); // sorting to correctly remove from the end
while (!RemovePerkID.empty()) { while (!RemovePerkID.empty()) {
int index = RemovePerkID.back(); int index = RemovePerkID.back();
if (!--fakePerks[index].Level) { if (!--fakePerks[index].Level) fakePerks.erase(fakePerks.begin() + index);
//int perkID = fakePerks[index].id;
//if (perkID != -1) fo::func::perk_remove_effect(fo::var::obj_dude, perkID);
fakePerks.erase(fakePerks.begin() + index);
}
RemovePerkID.pop_back(); RemovePerkID.pop_back();
} }
} }
@@ -1355,7 +1352,7 @@ void Perks::init() {
perksFile[0] = '.'; perksFile[0] = '.';
perksFile[1] = '\\'; perksFile[1] = '\\';
HookCall(0x44272E, TraitInitWrapper); // game_init_ HookCall(0x44272E, TraitInitWrapper); // game_init_
} //else perksFile[0] = 0; }
LoadGameHook::OnGameReset() += PerksReset; LoadGameHook::OnGameReset() += PerksReset;
} }
+2 -2
View File
@@ -50,8 +50,8 @@ void _stdcall IgnoreDefaultPerks();
void _stdcall RestoreDefaultPerks(); void _stdcall RestoreDefaultPerks();
void _stdcall AddPerkMode(DWORD mode); void _stdcall AddPerkMode(DWORD mode);
DWORD _stdcall HasFakePerk(char* name); DWORD HasFakePerk(const char* name, long id);
DWORD _stdcall HasFakeTrait(char* name); DWORD _stdcall HasFakeTrait(const char* name);
void _stdcall ClearSelectablePerks(); void _stdcall ClearSelectablePerks();
void _stdcall SetPerkFreq(int i); void _stdcall SetPerkFreq(int i);
+15 -84
View File
@@ -19,6 +19,7 @@
#include "..\..\..\FalloutEngine\Fallout2.h" #include "..\..\..\FalloutEngine\Fallout2.h"
#include "..\..\Perks.h" #include "..\..\Perks.h"
#include "..\..\ScriptExtender.h" #include "..\..\ScriptExtender.h"
#include "..\OpcodeContext.h"
#include "Perks.h" #include "Perks.h"
@@ -56,9 +57,9 @@ void __declspec(naked) op_set_perk_owed() {
cmp eax, 250; cmp eax, 250;
jg end; jg end;
mov byte ptr ds : [FO_VAR_free_perk], al mov byte ptr ds : [FO_VAR_free_perk], al
end : end:
popad popad
retn; retn;
} }
} }
@@ -76,38 +77,16 @@ void __declspec(naked) op_set_perk_freq() {
call SetPerkFreq; call SetPerkFreq;
end: end:
popad popad
retn; retn;
} }
} }
void __declspec(naked) op_get_perk_available() { void sf_get_perk_available(OpcodeContext& ctx) {
__asm { int result = 0, perkId = ctx.arg(0).rawValue();
pushad; if (perkId >= 0 && perkId < 256) { // start fake id
mov ecx, eax; result = fo::func::perk_can_add(fo::var::obj_dude, perkId);
call fo::funcoffs::interpretPopShort_;
mov edx, eax;
mov eax, ecx;
call fo::funcoffs::interpretPopLong_;
cmp dx, VAR_TYPE_INT;
jnz fail;
cmp eax, PERK_count;
jge fail;
mov edx, eax;
mov eax, ds:[FO_VAR_obj_dude];
call fo::funcoffs::perk_make_list_;
mov edx, eax;
jmp end;
fail:
xor edx, edx;
end:
mov eax, ecx;
call fo::funcoffs::interpretPushLong_;
mov edx, VAR_TYPE_INT;
mov eax, ecx;
call fo::funcoffs::interpretPushShort_;
popad;
retn;
} }
ctx.setReturn(result);
} }
void __declspec(naked) op_set_perk_name() { void __declspec(naked) op_set_perk_name() {
@@ -461,77 +440,33 @@ end:
void __declspec(naked) op_hide_real_perks() { void __declspec(naked) op_hide_real_perks() {
__asm { __asm {
push ebx;
push ecx; push ecx;
push edx;
call IgnoreDefaultPerks; call IgnoreDefaultPerks;
pop edx; pop ecx;
pop ecx;
pop ebx;
retn; retn;
} }
} }
void __declspec(naked) op_show_real_perks() { void __declspec(naked) op_show_real_perks() {
__asm { __asm {
push ebx;
push ecx; push ecx;
push edx;
call RestoreDefaultPerks; call RestoreDefaultPerks;
pop edx; pop ecx;
pop ecx;
pop ebx;
retn; retn;
} }
} }
void __declspec(naked) op_clear_selectable_perks() { void __declspec(naked) op_clear_selectable_perks() {
__asm { __asm {
push ebx;
push ecx; push ecx;
push edx;
call ClearSelectablePerks; call ClearSelectablePerks;
pop edx; pop ecx;
pop ecx;
pop ebx;
retn; retn;
} }
} }
void __declspec(naked) op_has_fake_perk() { void sf_has_fake_perk(OpcodeContext& ctx) {
__asm { ctx.setReturn(HasFakePerk(ctx.arg(0).asString(), ctx.arg(0).asInt()));
push ebx;
push ecx;
push edx;
push edi;
mov edi, eax;
call fo::funcoffs::interpretPopShort_;
mov edx, eax;
mov eax, edi;
call fo::funcoffs::interpretPopLong_;
cmp dx, VAR_TYPE_STR2;
jz next;
cmp dx, VAR_TYPE_STR;
jnz end;
next:
mov ebx, eax;
mov eax, edi;
call fo::funcoffs::interpretGetString_;
push eax;
call HasFakePerk;
end:
mov edx, eax;
mov eax, edi;
call fo::funcoffs::interpretPushLong_;
mov eax, edi;
mov edx, VAR_TYPE_INT;
call fo::funcoffs::interpretPushShort_;
pop edi;
pop edx;
pop ecx;
pop ebx;
retn;
}
} }
void __declspec(naked) op_has_fake_trait() { void __declspec(naked) op_has_fake_trait() {
@@ -645,13 +580,9 @@ end:
void __declspec(naked) op_apply_heaveho_fix() { void __declspec(naked) op_apply_heaveho_fix() {
__asm { __asm {
push ebx;
push ecx; push ecx;
push edx;
call ApplyHeaveHoFix; call ApplyHeaveHoFix;
pop edx; pop ecx;
pop ecx;
pop ebx;
retn; retn;
} }
} }
+2 -2
View File
@@ -29,7 +29,7 @@ void __declspec() op_set_perk_owed();
void __declspec() op_set_perk_freq(); void __declspec() op_set_perk_freq();
void __declspec() op_get_perk_available(); void sf_get_perk_available(OpcodeContext&);
void __declspec() op_set_perk_name(); void __declspec() op_set_perk_name();
@@ -51,7 +51,7 @@ void __declspec() op_show_real_perks();
void __declspec() op_clear_selectable_perks(); void __declspec() op_clear_selectable_perks();
void __declspec() op_has_fake_perk(); void sf_has_fake_perk(OpcodeContext&);
void __declspec() op_has_fake_trait(); void __declspec() op_has_fake_trait();
+3 -2
View File
@@ -65,11 +65,14 @@ static SfallOpcodeInfo opcodeInfoArray[] = {
{0x163, "get_year", sf_get_year, 0, true}, {0x163, "get_year", sf_get_year, 0, true},
{0x16c, "key_pressed", sf_key_pressed, 1, true, {ARG_INT}}, {0x16c, "key_pressed", sf_key_pressed, 1, true, {ARG_INT}},
{0x190, "get_perk_available", sf_get_perk_available, 1, true, {ARG_INT}},
{0x19d, "set_sfall_global", sf_set_sfall_global, 2, false, {ARG_INTSTR, ARG_NUMBER}}, {0x19d, "set_sfall_global", sf_set_sfall_global, 2, false, {ARG_INTSTR, ARG_NUMBER}},
{0x19e, "get_sfall_global_int", sf_get_sfall_global_int, 1, true, {ARG_INTSTR}}, {0x19e, "get_sfall_global_int", sf_get_sfall_global_int, 1, true, {ARG_INTSTR}},
{0x19f, "get_sfall_global_float", sf_get_sfall_global_float, 1, true, {ARG_INTSTR}}, {0x19f, "get_sfall_global_float", sf_get_sfall_global_float, 1, true, {ARG_INTSTR}},
{0x1a5, "inc_npc_level", sf_inc_npc_level, 1, false, {ARG_INTSTR}}, {0x1a5, "inc_npc_level", sf_inc_npc_level, 1, false, {ARG_INTSTR}},
{0x1c1, "has_fake_perk", sf_has_fake_perk, 1, true, {ARG_INTSTR}},
{0x1dc, "show_iface_tag", sf_show_iface_tag, 1, false, {ARG_INT}}, {0x1dc, "show_iface_tag", sf_show_iface_tag, 1, false, {ARG_INT}},
{0x1dd, "hide_iface_tag", sf_hide_iface_tag, 1, false, {ARG_INT}}, {0x1dd, "hide_iface_tag", sf_hide_iface_tag, 1, false, {ARG_INT}},
{0x1de, "is_iface_tag_active", sf_is_iface_tag_active, 1, true, {ARG_INT}}, {0x1de, "is_iface_tag_active", sf_is_iface_tag_active, 1, true, {ARG_INT}},
@@ -286,7 +289,6 @@ void InitNewOpcodes() {
} }
opcodes[0x18e] = op_get_perk_owed; opcodes[0x18e] = op_get_perk_owed;
opcodes[0x18f] = op_set_perk_owed; opcodes[0x18f] = op_set_perk_owed;
opcodes[0x190] = op_get_perk_available;
opcodes[0x191] = op_get_critter_current_ap; opcodes[0x191] = op_get_critter_current_ap;
opcodes[0x192] = op_set_critter_current_ap; opcodes[0x192] = op_set_critter_current_ap;
opcodes[0x193] = op_active_hand; opcodes[0x193] = op_active_hand;
@@ -331,7 +333,6 @@ void InitNewOpcodes() {
opcodes[0x1be] = op_set_perkbox_title; opcodes[0x1be] = op_set_perkbox_title;
opcodes[0x1bf] = op_hide_real_perks; opcodes[0x1bf] = op_hide_real_perks;
opcodes[0x1c0] = op_show_real_perks; opcodes[0x1c0] = op_show_real_perks;
opcodes[0x1c1] = op_has_fake_perk;
opcodes[0x1c2] = op_has_fake_trait; opcodes[0x1c2] = op_has_fake_trait;
opcodes[0x1c3] = op_perk_add_mode; opcodes[0x1c3] = op_perk_add_mode;
opcodes[0x1c4] = op_clear_selectable_perks; opcodes[0x1c4] = op_clear_selectable_perks;