Added displaying the NPC's addictions in the character screen when controlled by the player (from Mr.Stalin)

Added "npc_engine_level_up" script function.
Moved the code of set_map_time_multi to Worldmap handler, and NPCAutoLevel to PartyControl module.
Removed validate_test example function from release builds.
This commit is contained in:
NovaRain
2019-02-27 12:06:06 +08:00
parent aa36d54b27
commit f84578e84b
17 changed files with 177 additions and 109 deletions
+1
View File
@@ -270,6 +270,7 @@
#define item_weight(obj) sfall_func1("item_weight", obj)
#define lock_is_jammed(obj) sfall_func1("lock_is_jammed", obj)
#define loot_obj sfall_func0("loot_obj")
#define npc_engine_level_up(toggle) sfall_func1("npc_engine_level_up", toggle)
#define obj_under_cursor(crSwitch, inclDude) sfall_func2("obj_under_cursor", crSwitch, inclDude)
#define outlined_object sfall_func0("outlined_object")
#define real_dude_obj sfall_func0("real_dude_obj")
@@ -569,6 +569,10 @@ Some utility/math functions are available:
> void sfall_func0("art_cache_clear")
- clears the cache of FRM image files loaded into memory
> void sfall_func1("npc_engine_level_up", bool toggle)
- enables/disables the engine function that increases the level of party members in the player leveling process
- if the engine function is disabled, the process of leveling up party members should be performed by script functions
------------------------
------ MORE INFO -------
------------------------
+34 -24
View File
@@ -19,6 +19,7 @@
#include <cassert>
#include "Functions.h"
#include "FunctionOffsets.h"
#include "Structs.h"
#include "Variables.h"
#include "VariableOffsets.h"
@@ -107,20 +108,29 @@ bool HeroIsFemale() {
return (fo::func::stat_level(fo::var::obj_dude, fo::Stat::STAT_gender) == fo::Gender::GENDER_FEMALE);
}
long CheckAddictByPid(fo::GameObject* critter, long pid) {
__asm {
mov eax, pid;
mov esi, critter;
call fo::funcoffs::item_d_check_addict_;
}
/* keyword 'return' is not needed, the compiler will do everything correctly */
}
//---------------------------------------------------------
//print text to surface
void PrintText(char *DisplayText, BYTE ColourIndex, DWORD Xpos, DWORD Ypos, DWORD TxtWidth, DWORD ToWidth, BYTE *ToSurface) {
DWORD posOffset = Ypos * ToWidth + Xpos;
__asm {
xor eax, eax
mov al, ColourIndex
push eax
mov edx, DisplayText
mov ebx, TxtWidth
mov ecx, ToWidth
mov eax, ToSurface
add eax, posOffset
call dword ptr ds:[FO_VAR_text_to_buf]
xor eax, eax;
mov al, ColourIndex;
push eax;
mov edx, DisplayText;
mov ebx, TxtWidth;
mov ecx, ToWidth;
mov eax, ToSurface;
add eax, posOffset;
call dword ptr ds:[FO_VAR_text_to_buf];
}
}
@@ -129,8 +139,8 @@ void PrintText(char *DisplayText, BYTE ColourIndex, DWORD Xpos, DWORD Ypos, DWOR
DWORD GetTextHeight() {
DWORD TxtHeight;
__asm {
call dword ptr ds:[FO_VAR_text_height] //get text height
mov TxtHeight, eax
call dword ptr ds:[FO_VAR_text_height]; //get text height
mov TxtHeight, eax;
}
return TxtHeight;
}
@@ -140,9 +150,9 @@ DWORD GetTextHeight() {
DWORD GetTextWidth(char *TextMsg) {
DWORD TxtWidth;
__asm {
mov eax, TextMsg
call dword ptr ds:[FO_VAR_text_width] //get text width
mov TxtWidth, eax
mov eax, TextMsg;
call dword ptr ds:[FO_VAR_text_width]; //get text width
mov TxtWidth, eax;
}
return TxtWidth;
}
@@ -152,9 +162,9 @@ DWORD GetTextWidth(char *TextMsg) {
DWORD GetCharWidth(char CharVal) {
DWORD charWidth;
__asm {
mov al, CharVal
call dword ptr ds:[FO_VAR_text_char_width]
mov charWidth, eax
mov al, CharVal;
call dword ptr ds:[FO_VAR_text_char_width];
mov charWidth, eax;
}
return charWidth;
}
@@ -164,9 +174,9 @@ DWORD GetCharWidth(char CharVal) {
DWORD GetMaxTextWidth(char *TextMsg) {
DWORD msgWidth;
__asm {
mov eax, TextMsg
call dword ptr ds:[FO_VAR_text_mono_width]
mov msgWidth, eax
mov eax, TextMsg;
call dword ptr ds:[FO_VAR_text_mono_width];
mov msgWidth, eax;
}
return msgWidth;
}
@@ -176,8 +186,8 @@ DWORD GetMaxTextWidth(char *TextMsg) {
DWORD GetCharGapWidth() {
DWORD gapWidth;
__asm {
call dword ptr ds:[FO_VAR_text_spacing]
mov gapWidth, eax
call dword ptr ds:[FO_VAR_text_spacing];
mov gapWidth, eax;
}
return gapWidth;
}
@@ -187,8 +197,8 @@ DWORD GetCharGapWidth() {
DWORD GetMaxCharWidth() {
DWORD charWidth = 0;
__asm {
call dword ptr ds:[FO_VAR_text_max]
mov charWidth, eax
call dword ptr ds:[FO_VAR_text_max];
mov charWidth, eax;
}
return charWidth;
}
+2
View File
@@ -60,6 +60,8 @@ GameObject* GetActiveItem();
bool HeroIsFemale();
long CheckAddictByPid(fo::GameObject* critter, long pid);
// 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
+7
View File
@@ -709,4 +709,11 @@ struct Queue {
Queue* next;
};
#pragma pack(1)
struct DrugInfoList {
DWORD itemPid;
long addictGvar;
long numEffects;
};
}
+1 -1
View File
@@ -39,7 +39,7 @@ VAR_(dialogue_switch_mode, DWORD)
VAR_(dialog_target, DWORD)
VAR_(dialog_target_is_party, DWORD)
VAR_(dropped_explosive, DWORD)
VAR_(drugInfoList, DWORD)
VARA(drugInfoList, DrugInfoList, 9)
VAR_(edit_win, DWORD)
VAR_(Educated, DWORD)
VAR_(elevation, DWORD)
-12
View File
@@ -39,8 +39,6 @@ static char versionString[65];
static int* scriptDialog = nullptr;
bool npcAutoLevelEnabled;
static const DWORD PutAwayWeapon[] = {
0x411EA2, // action_climb_ladder_
0x412046, // action_use_an_item_on_object_
@@ -467,15 +465,6 @@ static const DWORD EncounterTableSize[] = {
0x4C0815, 0x4C0D4A, 0x4C0FD4,
};
void NpcAutoLevelPatch() {
npcAutoLevelEnabled = GetConfigInt("Misc", "NPCAutoLevel", 0) != 0;
if (npcAutoLevelEnabled) {
dlog("Applying NPC autolevel patch.", DL_INIT);
SafeWrite8(0x495CFB, 0xEB); // jmps 0x495D28 (skip random check)
dlogr(" Done", DL_INIT);
}
}
void AdditionalWeaponAnimsPatch() {
if (GetConfigInt("Misc", "AdditionalWeaponAnims", 0)) {
dlog("Applying additional weapon animations patch.", DL_INIT);
@@ -924,7 +913,6 @@ void MiscPatches::init() {
LoadGameHook::OnBeforeGameStart() += BodypartHitChances; // set on start & load
CombatProcFix();
NpcAutoLevelPatch();
DialogueFix();
AdditionalWeaponAnimsPatch();
MultiPatchesPatch();
-4
View File
@@ -35,9 +35,5 @@ struct CodeData {
BYTE db = 0x90;
};
extern bool npcAutoLevelEnabled;
void _stdcall SetMapMulti(float d);
}
+41 -4
View File
@@ -30,6 +30,9 @@
namespace sfall
{
bool npcAutoLevelEnabled;
bool npcEngineLevelUp = true;
bool isControllingNPC = false;
bool skipCounterAnim = false;
@@ -54,8 +57,7 @@ static struct DudeState {
DWORD itemCurrentItem;
fo::ItemButtonItem itemButtonItems[2];
long perkLevelDataList[fo::PERK_count];
//DWORD drug_gvar[6];
//DWORD jet_gvar;
long addictGvar[8];
long tag_skill[4];
//DWORD bbox_sneak;
} realDude;
@@ -78,6 +80,10 @@ static void SaveRealDudeState() {
realDude.sneak_working = fo::var::sneak_working;
fo::SkillGetTags(realDude.tag_skill, 4);
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 (skipCounterAnim) SafeWriteBatch<BYTE>(0, {0x422BDE, 0x4229EC}); // no animate
}
@@ -121,6 +127,17 @@ static void SetCurrentDude(fo::GameObject* npc) {
fo::var::itemCurrentItem = 1;
}
long alcoholAddict = 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;
}
// switch main dude_obj pointers - this should be done last!
fo::var::obj_dude = npc;
fo::var::inven_dude = npc;
@@ -160,6 +177,10 @@ static void RestoreRealDudeState() {
fo::func::stat_pc_add_experience(delayedExperience);
}
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 (skipCounterAnim) SafeWriteBatch<BYTE>(1, {0x422BDE, 0x4229EC}); // restore
fo::func::intface_redraw();
@@ -279,12 +300,28 @@ static void __declspec(naked) gdControlUpdateInfo_hook() {
}
}
void PartyControl::init() {
LoadGameHook::OnGameReset() += PartyControlReset;
static void NpcAutoLevelPatch() {
npcAutoLevelEnabled = GetConfigInt("Misc", "NPCAutoLevel", 0) != 0;
if (npcAutoLevelEnabled) {
dlog("Applying NPC autolevel patch.", DL_INIT);
SafeWrite8(0x495CFB, 0xEB); // jmps 0x495D28 (skip random check)
dlogr(" Done", DL_INIT);
}
}
void PartyControl::init() {
LoadGameHook::OnGameReset() += []() {
PartyControlReset();
if (!npcEngineLevelUp) {
npcEngineLevelUp = true;
SafeWrite16(0x4AFC1C, 0x840F);
}
};
HookCall(0x454218, stat_pc_add_experience_hook); // call inside op_give_exp_points_hook
HookCalls(pc_flag_toggle_hook, { 0x4124F1, 0x41279A });
NpcAutoLevelPatch();
skipCounterAnim = (GetConfigInt("Misc", "SpeedInterfaceCounterAnims", 0) == 3);
// display party member's current level & AC & addict flag
+2
View File
@@ -39,5 +39,7 @@ public:
};
extern bool isControllingNPC;
extern bool npcAutoLevelEnabled;
extern bool npcEngineLevelUp;
}
+33 -29
View File
@@ -110,6 +110,7 @@ static const SfallMetarule metarules[] = {
{"item_weight", sf_item_weight, 1, 1, {ARG_OBJECT}},
{"lock_is_jammed", sf_lock_is_jammed, 1, 1, {ARG_OBJECT}},
{"loot_obj", sf_get_loot_object, 0, 0},
{"npc_engine_level_up", sf_npc_engine_level_up, 1, 1, {ARG_ANY}},
{"obj_under_cursor", sf_get_obj_under_cursor, 2, 2, {ARG_INT, ARG_INT}},
{"outlined_object", sf_outlined_object, 0, 0},
{"real_dude_obj", sf_real_dude_obj, 0, 0},
@@ -129,38 +130,11 @@ static const SfallMetarule metarules[] = {
{"spatial_radius", sf_spatial_radius, 1, 1, {ARG_OBJECT}},
{"tile_refresh_display", sf_tile_refresh_display, 0, 0},
{"unjam_lock", sf_unjam_lock, 1, 1, {ARG_OBJECT}},
#ifndef NDEBUG
{"validate_test", sf_test, 2, 5, {ARG_INT, ARG_NUMBER, ARG_STRING, ARG_OBJECT, ARG_ANY}},
#endif
};
static std::string sf_test_stringBuf;
void sf_test(OpcodeContext& ctx) {
std::ostringstream sstream;
sstream << "sfall_funcX(\"test\"";
for (int i = 0; i < ctx.numArgs(); i++) {
const ScriptValue &arg = ctx.arg(i);
sstream << ", ";
switch (arg.type()) {
case DataType::INT:
sstream << arg.asInt();
break;
case DataType::FLOAT:
sstream << arg.asFloat();
break;
case DataType::STR:
sstream << '"' << arg.asString() << '"';
break;
default:
sstream << "???";
break;
}
}
sstream << ")";
sf_test_stringBuf = sstream.str();
ctx.setReturn(sf_test_stringBuf.c_str());
}
// returns current contents of metarule table
static void sf_get_metarule_table(OpcodeContext& ctx) {
DWORD arrId = TempArray(metaruleTable.size(), 0);
@@ -219,5 +193,35 @@ void HandleMetarule(OpcodeContext& ctx) {
}
}
#ifndef NDEBUG
static std::string sf_test_stringBuf;
void sf_test(OpcodeContext& ctx) {
std::ostringstream sstream;
sstream << "sfall_funcX(\"test\"";
for (int i = 0; i < ctx.numArgs(); i++) {
const ScriptValue &arg = ctx.arg(i);
sstream << ", ";
switch (arg.type()) {
case DataType::INT:
sstream << arg.asInt();
break;
case DataType::FLOAT:
sstream << arg.asFloat();
break;
case DataType::STR:
sstream << '"' << arg.asString() << '"';
break;
default:
sstream << "???";
break;
}
}
sstream << ")";
sf_test_stringBuf = sstream.str();
ctx.setReturn(sf_test_stringBuf.c_str());
}
#endif
}
}
@@ -31,7 +31,9 @@ namespace script
{
// Example handler. Feel free to add handlers in other files.
#ifndef NDEBUG
void sf_test(OpcodeContext&);
#endif
// returns current contents of metarule table
void sf_get_metarule_table(OpcodeContext&);
+12 -31
View File
@@ -25,9 +25,10 @@
#include "..\..\HeroAppearance.h"
#include "..\..\Inventory.h"
#include "..\..\KillCounter.h"
#include "..\..\MiscPatches.h"
//#include "..\..\MiscPatches.h"
#include "..\..\Movies.h"
#include "..\..\Objects.h"
#include "..\..\PartyControl.h"
#include "..\..\PlayerModel.h"
#include "..\..\ScriptExtender.h"
#include "..\..\Stats.h"
@@ -201,36 +202,6 @@ void __declspec(naked) op_game_loaded() {
}
}
void __declspec(naked) op_set_map_time_multi() {
__asm {
push ebx;
push ecx;
push edx;
mov ecx, eax;
call fo::funcoffs::interpretPopShort_;
mov edx, eax;
mov eax, ecx;
call fo::funcoffs::interpretPopLong_;
cmp dx, VAR_TYPE_FLOAT;
jz paramWasFloat;
cmp dx, VAR_TYPE_INT;
jnz fail;
push eax;
fild dword ptr [esp];
fstp dword ptr [esp];
jmp end;
paramWasFloat:
push eax;
end:
call SetMapMulti;
fail:
pop edx;
pop ecx;
pop ebx;
retn;
}
}
void __declspec(naked) op_set_pipboy_available() {
__asm {
push ebx;
@@ -1646,5 +1617,15 @@ void sf_get_ini_section(OpcodeContext& ctx) {
ctx.setReturn(arrayId);
}
void sf_npc_engine_level_up(OpcodeContext& ctx) {
if (ctx.arg(0).asBool()) {
if (!npcEngineLevelUp) SafeWrite16(0x4AFC1C, 0x840F); // enable
npcEngineLevelUp = true;
} else {
if (npcEngineLevelUp) SafeWrite16(0x4AFC1C, 0xE990);
npcEngineLevelUp = false;
}
}
}
}
+2 -2
View File
@@ -41,8 +41,6 @@ void sf_get_year(OpcodeContext&);
void __declspec() op_game_loaded();
void __declspec() op_set_map_time_multi();
void __declspec() op_set_pipboy_available();
// Kill counters
@@ -165,5 +163,7 @@ void sf_get_ini_sections(OpcodeContext&);
void sf_get_ini_section(OpcodeContext&);
void sf_npc_engine_level_up(OpcodeContext&);
}
}
@@ -227,6 +227,36 @@ end:
}
}
void __declspec(naked) op_set_map_time_multi() {
__asm {
push ebx;
push ecx;
push edx;
mov ecx, eax;
call fo::funcoffs::interpretPopShort_;
mov edx, eax;
mov eax, ecx;
call fo::funcoffs::interpretPopLong_;
cmp dx, VAR_TYPE_FLOAT;
jz paramWasFloat;
cmp dx, VAR_TYPE_INT;
jnz fail;
push eax;
fild dword ptr [esp];
fstp dword ptr [esp];
jmp end;
paramWasFloat:
push eax;
end:
call SetMapMulti;
fail:
pop edx;
pop ecx;
pop ebx;
retn;
}
}
void sf_set_car_intface_art(OpcodeContext& ctx) {
Worldmap::SetCarInterfaceArt(ctx.arg(0).asInt());
}
@@ -40,6 +40,8 @@ void __declspec() op_get_world_map_y_pos();
void __declspec() op_set_world_map_pos();
void __declspec() op_set_map_time_multi();
void sf_set_car_intface_art(OpcodeContext&);
void sf_set_map_enter_position(OpcodeContext&);
+2
View File
@@ -44,4 +44,6 @@ public:
static void SetAddedYears(DWORD years);
};
void _stdcall SetMapMulti(float d);
}