mirror of
https://github.com/sfall-team/sfall.git
synced 2026-07-27 16:52:34 -07:00
Backported game hooks from 4.0.x
* COMBATTURN is not implemented. Updated documents.
This commit is contained in:
@@ -146,7 +146,7 @@
|
||||
//#define RMOBJ_AI_USE_DRUG_ON_2 4360176 // same as RMOBJ_AI_USE_DRUG_ON (obsolete, use only for sfall before 4.3.1/3.8.31)
|
||||
#define RMOBJ_BARTER_ARMOR 4675656 // removing armor from NPC's slot before entering the barter screen
|
||||
#define RMOBJ_BARTER_WEAPON 4675722 // removing weapon from NPC's slot before entering the barter screen
|
||||
#define RMOBJ_INVEN_DROP_CAPS 4667295 // if money/caps are dropped manually by the player from the inventory screen
|
||||
#define RMOBJ_INVEN_DROP_CAPS 4667295 // if multiple money/caps are dropped manually by the player from the inventory screen
|
||||
#define RMOBJ_DROP_INTO_CONTAINER 4678833 // when dropping items into a container item (bag/backpack)
|
||||
// old defines
|
||||
#define RMOBJ_RM_MULT_OBJS RMOBJ_ITEM_REMOVED_MULTI
|
||||
|
||||
@@ -52,6 +52,10 @@
|
||||
#define HOOK_INVENTORYMOVE (24)
|
||||
#define HOOK_INVENWIELD (25)
|
||||
#define HOOK_ADJUSTFID (26)
|
||||
//#define HOOK_COMBATTURN (27) // unimplemented
|
||||
#define HOOK_CARTRAVEL (28)
|
||||
#define HOOK_SETGLOBALVAR (29)
|
||||
#define HOOK_RESTTIMER (30)
|
||||
#define HOOK_GAMEMODECHANGE (31)
|
||||
|
||||
// Valid arguments to list_begin
|
||||
|
||||
@@ -582,6 +582,48 @@ int ret0 - overrides the calculated FID with provided value
|
||||
|
||||
-------------------------------------------
|
||||
|
||||
#### `HOOK_CARTRAVEL (hs_cartravel.int)`
|
||||
|
||||
Runs continuously during worldmap travel on car.
|
||||
|
||||
```
|
||||
int arg0 - vanilla car speed (between 3 and 8 "steps")
|
||||
int arg1 - vanilla fuel consumption (100 and below)
|
||||
|
||||
int ret0 - car speed override (pass -1 if you just want to override fuel consumption)
|
||||
int ret1 - fuel consumption override
|
||||
```
|
||||
|
||||
-------------------------------------------
|
||||
|
||||
#### `HOOK_SETGLOBALVAR (hs_setglobalvar.int)`
|
||||
|
||||
Runs when setting the value of a global variable.
|
||||
|
||||
```
|
||||
int arg0 - the index number of the global variable being set
|
||||
int arg1 - the set value of the global variable
|
||||
|
||||
int ret0 - overrides the value of the global variable
|
||||
```
|
||||
|
||||
-------------------------------------------
|
||||
|
||||
#### `HOOK_RESTTIMER (hs_resttimer.int)`
|
||||
|
||||
Runs continuously while the player is resting (using pipboy alarm clock).
|
||||
|
||||
```
|
||||
int arg0 - the game time in ticks
|
||||
int arg1 - event type: 1 - when the resting ends normally, -1 - when pressing ESC to cancel the timer, 0 - otherwise
|
||||
int arg2 - the hour part of the length of resting time
|
||||
int arg3 - the minute part of the length of resting time
|
||||
|
||||
int ret0 - pass 1 to interrupt the resting, pass 0 to continue the rest if it was interrupted by pressing ESC key
|
||||
```
|
||||
|
||||
-------------------------------------------
|
||||
|
||||
#### `HOOK_GAMEMODECHANGE (hs_gamemodechange.int)`
|
||||
|
||||
Runs once every time when the game mode was changed, like opening/closing the inventory, character screen, pipboy, etc.
|
||||
|
||||
@@ -732,6 +732,7 @@ FUNC(windowOutput_, 0x4B80A4)
|
||||
FUNC(windowShow_, 0x4B7648)
|
||||
FUNC(windowWidth_, 0x4B7734)
|
||||
FUNC(windowWrapLineWithSpacing_, 0x4B8854)
|
||||
FUNC(wmCarUseGas_, 0x4C4DA4)
|
||||
FUNC(wmDrawCursorStopped_, 0x4C41EC)
|
||||
FUNC(wmEvalTileNumForPlacement_, 0x4C1A64)
|
||||
FUNC(wmFindCurSubTileFromPos_, 0x4C0C00)
|
||||
|
||||
@@ -58,8 +58,6 @@ WRAP_WATCOM_FFUNC4(void, register_object_call, long*, target, long*, source, voi
|
||||
WRAP_WATCOM_FFUNC3(long, scr_get_local_var, long, sid, long, varId, long*, value)
|
||||
WRAP_WATCOM_FFUNC3(long, scr_set_local_var, long, sid, long, varId, long, value)
|
||||
WRAP_WATCOM_FFUNC6(long, text_object_create, fo::GameObject*, object, const char*, text, long, font, long, colorText, long, colorOutline, fo::BoundRect*, rect)
|
||||
WRAP_WATCOM_FFUNC3(long, tile_coord, long, tile, long*, outX, long*, outY) // the fourth argument of the function is not used
|
||||
WRAP_WATCOM_FFUNC3(long, tile_num_in_direction, long, tile, long, rotation, long, distance)
|
||||
WRAP_WATCOM_FFUNC8(void, trans_cscale, void*, fromBuff, long, width, long, height, long, fromPitch, void*, toBuff, long, toWidth, long, toHeight, long, toPitch)
|
||||
WRAP_WATCOM_FFUNC3(void, win_clip, fo::Window*, window, fo::RectList**, rects, void*, buffer)
|
||||
WRAP_WATCOM_FFUNC6(void, win_print, long, winID, const char*, text, long, textWidth, long, xPos, long, yPos, long, colorFlags)
|
||||
@@ -99,6 +97,7 @@ WRAP_WATCOM_FUNC2(void, executeProcedure, fo::Program*, sptr, long, procNum)
|
||||
WRAP_WATCOM_FUNC1(long, folder_print_line, const char*, text)
|
||||
WRAP_WATCOM_FUNC1(const char*, findCurrentProc, fo::Program*, program) // Returns the name of current procedure by program pointer
|
||||
WRAP_WATCOM_FUNC1(long, FMtext_width, const char*, text)
|
||||
WRAP_WATCOM_FUNC1(long, game_get_global_var, long, globalVar)
|
||||
WRAP_WATCOM_FUNC0(long, get_input)
|
||||
// Searches for message ID in given message file and places result in result argument
|
||||
WRAP_WATCOM_FUNC3(const char*, getmsg, const fo::MessageList*, fileAddr, fo::MessageNode*, result, long, messageId)
|
||||
@@ -222,9 +221,11 @@ WRAP_WATCOM_FUNC2(void, skill_set_tags, long*, tags, long, num)
|
||||
WRAP_WATCOM_FUNC2(long, stat_level, fo::GameObject*, critter, long, statId)
|
||||
WRAP_WATCOM_FUNC1(void, stat_pc_add_experience, long, amount) // Adds experience points to PC
|
||||
WRAP_WATCOM_FUNC1(long, text_font, long, fontNum)
|
||||
WRAP_WATCOM_FUNC3(long, tile_coord, long, tile, long*, outX, long*, outY) // the fourth argument of the function is not used
|
||||
WRAP_WATCOM_FUNC2(long, tile_dist, long, scrTile, long, dstTile)
|
||||
WRAP_WATCOM_FUNC2(long, tile_dir, long, scrTile, long, dstTile)
|
||||
WRAP_WATCOM_FUNC2(long, tile_idistance, long, sourceTile, long, targetTile)
|
||||
WRAP_WATCOM_FUNC3(long, tile_num_in_direction, long, tile, long, rotation, long, distance)
|
||||
WRAP_WATCOM_FUNC1(long, tile_on_edge, long, tile)
|
||||
WRAP_WATCOM_FUNC0(void, tile_refresh_display) // Redraws the whole screen
|
||||
WRAP_WATCOM_FUNC2(void, tile_refresh_rect, fo::BoundRect*, boundRect, long, elevation) // Redraws the given rectangle on screen
|
||||
@@ -237,7 +238,9 @@ WRAP_WATCOM_FUNC1(BYTE*, win_get_buf, DWORD, winRef)
|
||||
WRAP_WATCOM_FUNC1(void, win_hide, DWORD, winRef)
|
||||
WRAP_WATCOM_FUNC1(void, win_show, DWORD, winRef)
|
||||
WRAP_WATCOM_FUNC0(long, windowWidth)
|
||||
WRAP_WATCOM_FUNC1(void, wmCarUseGas, long, gasAmount)
|
||||
WRAP_WATCOM_FUNC1(long, wmEvalTileNumForPlacement, long, tile)
|
||||
WRAP_WATCOM_FUNC0(void, wmPartyWalkingStep)
|
||||
WRAP_WATCOM_FUNC1(void, wmRefreshInterfaceOverlay, long, isRedraw)
|
||||
|
||||
/* Database functions */
|
||||
|
||||
@@ -90,9 +90,9 @@ static HooksInjectInfo injectHooks[] = {
|
||||
{HOOK_INVENWIELD, Inject_InvenWieldHook, false},
|
||||
{HOOK_ADJUSTFID, nullptr, true}, // always embedded to the engine
|
||||
{-1, nullptr, false}, // dummy
|
||||
{-1, nullptr, false}, // dummy
|
||||
{-1, nullptr, false}, // dummy
|
||||
{-1, nullptr, false}, // dummy
|
||||
{HOOK_CARTRAVEL, Inject_CarTravelHook, false},
|
||||
{HOOK_SETGLOBALVAR, Inject_SetGlobalVarHook, false},
|
||||
{HOOK_RESTTIMER, Inject_RestTimerHook, false},
|
||||
{HOOK_GAMEMODECHANGE, nullptr, true}, // always embedded to the engine
|
||||
};
|
||||
|
||||
|
||||
@@ -51,8 +51,12 @@ enum HookType
|
||||
HOOK_WITHINPERCEPTION = 23,
|
||||
HOOK_INVENTORYMOVE = 24,
|
||||
HOOK_INVENWIELD = 25,
|
||||
HOOK_ADJUSTFID = 26, // 4.x backport
|
||||
HOOK_GAMEMODECHANGE = 31, // 4.x backport
|
||||
HOOK_ADJUSTFID = 26,
|
||||
HOOK_COMBATTURN = 27, // unimplemented
|
||||
HOOK_CARTRAVEL = 28,
|
||||
HOOK_SETGLOBALVAR = 29,
|
||||
HOOK_RESTTIMER = 30,
|
||||
HOOK_GAMEMODECHANGE = 31,
|
||||
HOOK_COUNT
|
||||
};
|
||||
|
||||
|
||||
@@ -62,6 +62,20 @@ void __stdcall HookCommon::SetHSReturn(DWORD value) {
|
||||
}
|
||||
}
|
||||
|
||||
// List of hooks that are not allowed to be called recursively
|
||||
static bool CheckRecursiveHooks(DWORD hook) {
|
||||
if (hook == currentRunHook) {
|
||||
switch (hook) {
|
||||
case HOOK_SETGLOBALVAR:
|
||||
// case HOOK_SETLIGHTING:
|
||||
return true;
|
||||
default:
|
||||
if (isDebug) fo::func::debug_printf("\nWARNING: A recursive hook with ID %d was running.", hook);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void __stdcall BeginHook() {
|
||||
if (callDepth && callDepth <= maxDepth) {
|
||||
// save all values of the current hook if another hook was called during the execution of the current hook
|
||||
@@ -97,10 +111,12 @@ static void __stdcall RunSpecificHookScript(HookScript *hook) {
|
||||
void __stdcall RunHookScript(DWORD hook) {
|
||||
cRet = 0;
|
||||
if (!hooks[hook].empty()) {
|
||||
if (callDepth > 8) {
|
||||
fo::func::debug_printf("\n[SFALL] The hook ID: %d cannot be executed.", hook);
|
||||
dlog_f("The hook %d cannot be executed due to exceeding depth limit\n", DL_MAIN, hook);
|
||||
return;
|
||||
if (callDepth > 1) {
|
||||
if (CheckRecursiveHooks(hook) || callDepth > 8) {
|
||||
fo::func::debug_printf("\n[SFALL] The hook ID: %d cannot be executed.", hook);
|
||||
dlog_f("The hook %d cannot be executed due to exceeding depth limit or disallowed recursive calls\n", DL_MAIN, hook);
|
||||
return;
|
||||
}
|
||||
}
|
||||
currentRunHook = hook;
|
||||
size_t hooksCount = hooks[hook].size();
|
||||
|
||||
@@ -226,6 +226,167 @@ static void __declspec(naked) PerceptionSearchTargetHook() {
|
||||
}
|
||||
}
|
||||
|
||||
static const long maxGasAmount = 80000;
|
||||
|
||||
static void CarTravelHook_Script() {
|
||||
BeginHook();
|
||||
argCount = 2;
|
||||
|
||||
// calculate vanilla speed
|
||||
int carSpeed = 3;
|
||||
if (fo::func::game_get_global_var(fo::GVAR_CAR_BLOWER)) {
|
||||
carSpeed += 1;
|
||||
}
|
||||
if (fo::func::game_get_global_var(fo::GVAR_NEW_RENO_CAR_UPGRADE)) {
|
||||
carSpeed += 1;
|
||||
}
|
||||
if (fo::func::game_get_global_var(fo::GVAR_NEW_RENO_SUPER_CAR)) {
|
||||
carSpeed += 3;
|
||||
}
|
||||
// calculate vanilla fuel consumption
|
||||
int originalGas = *fo::ptr::carGasAmount;
|
||||
*fo::ptr::carGasAmount = maxGasAmount;
|
||||
fo::func::wmCarUseGas(100);
|
||||
int consumption = maxGasAmount - *fo::ptr::carGasAmount;
|
||||
|
||||
// run script
|
||||
args[0] = carSpeed;
|
||||
args[1] = consumption;
|
||||
RunHookScript(HOOK_CARTRAVEL);
|
||||
|
||||
// override travel speed
|
||||
if (cRet > 0 && static_cast<long>(rets[0]) >= 0) {
|
||||
carSpeed = rets[0];
|
||||
}
|
||||
// move car on map
|
||||
for (int i = 0; i < carSpeed; ++i) {
|
||||
fo::func::wmPartyWalkingStep();
|
||||
}
|
||||
|
||||
// override fuel consumption
|
||||
if (cRet > 1) {
|
||||
consumption = static_cast<long>(rets[1]);
|
||||
}
|
||||
// consume fuel
|
||||
*fo::ptr::carGasAmount = max(0, originalGas - consumption); // Note: the reverse breaks the result in VS2010
|
||||
|
||||
EndHook();
|
||||
}
|
||||
|
||||
static void __declspec(naked) CarTravelHack() {
|
||||
static const DWORD CarTravelHack_back = 0x4BFF43;
|
||||
__asm {
|
||||
pushad;
|
||||
call CarTravelHook_Script;
|
||||
popad;
|
||||
jmp CarTravelHack_back;
|
||||
}
|
||||
}
|
||||
|
||||
static long __fastcall GlobalVarHook_Script(register DWORD number, register int value) {
|
||||
int old = (*fo::ptr::game_global_vars)[number];
|
||||
|
||||
if (IsGameLoaded() && HookScripts::HookHasScript(HOOK_SETGLOBALVAR)) { // IsGameLoaded - don't execute hook until loading sfall scripts
|
||||
BeginHook();
|
||||
argCount = 2;
|
||||
args[0] = number;
|
||||
args[1] = value;
|
||||
|
||||
RunHookScript(HOOK_SETGLOBALVAR);
|
||||
if (cRet > 0) value = rets[0];
|
||||
EndHook();
|
||||
}
|
||||
if (number == fo::GVAR_PLAYER_REPUTATION && displayKarmaChanges) {
|
||||
int diff = value - old;
|
||||
if (diff != 0) Karma::DisplayKarma(diff);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static void __declspec(naked) SetGlobalVarHook() {
|
||||
__asm {
|
||||
push eax;
|
||||
push ecx;
|
||||
push edx;
|
||||
mov ecx, eax; // number
|
||||
call GlobalVarHook_Script; // edx - value
|
||||
pop edx;
|
||||
pop ecx;
|
||||
cmp eax, edx; // if return value != set value
|
||||
cmovne edx, eax;
|
||||
pop eax;
|
||||
jmp fo::funcoffs::game_set_global_var_;
|
||||
}
|
||||
}
|
||||
|
||||
static int restTicks;
|
||||
|
||||
static long __fastcall RestTimerHook_Script(DWORD hours, DWORD minutes, DWORD gameTime, DWORD addrHook) {
|
||||
addrHook -= 5;
|
||||
|
||||
BeginHook();
|
||||
argCount = 4;
|
||||
|
||||
args[0] = gameTime;
|
||||
args[2] = hours;
|
||||
args[3] = minutes;
|
||||
|
||||
if (addrHook == 0x499CA1 || addrHook == 0x499B63) {
|
||||
args[0] = restTicks;
|
||||
args[1] = -1;
|
||||
} else {
|
||||
restTicks = args[0];
|
||||
args[1] = (addrHook == 0x499DF2 || (args[2] == 0 && addrHook == 0x499BE0)) ? 1 : 0;
|
||||
}
|
||||
RunHookScript(HOOK_RESTTIMER);
|
||||
|
||||
long result = -1;
|
||||
if (cRet > 0) {
|
||||
result = (rets[0] != 0) ? 1 : 0;
|
||||
}
|
||||
EndHook();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void __declspec(naked) RestTimerLoopHook() {
|
||||
__asm {
|
||||
pushadc;
|
||||
mov edx, [esp + 16 + 0x44]; // minutes_
|
||||
mov ecx, [esp + 16 + 0x40]; // hours_
|
||||
push [esp + 12]; // addrHook
|
||||
push eax; // gameTime
|
||||
call RestTimerHook_Script;
|
||||
pop ecx;
|
||||
pop edx;
|
||||
test eax, eax; // result >= 0
|
||||
cmovge edi, eax; // return 1 to interrupt resting
|
||||
pop eax;
|
||||
jmp fo::funcoffs::set_game_time_;
|
||||
}
|
||||
}
|
||||
|
||||
static void __declspec(naked) RestTimerEscapeHook() {
|
||||
__asm {
|
||||
mov edi, 1; // engine code
|
||||
cmp eax, 0x1B; // ESC ASCII code
|
||||
jnz skip;
|
||||
pushadc;
|
||||
mov edx, [esp + 16 + 0x44]; // minutes_
|
||||
mov ecx, [esp + 16 + 0x40]; // hours_
|
||||
push [esp + 12]; // addrHook
|
||||
push eax; // gameTime
|
||||
call RestTimerHook_Script;
|
||||
pop ecx;
|
||||
pop edx;
|
||||
test eax, eax; // result >= 0
|
||||
cmovge edi, eax; // return 0 for cancel ESC key
|
||||
pop eax;
|
||||
skip:
|
||||
retn;
|
||||
}
|
||||
}
|
||||
|
||||
void Inject_BarterPriceHook() {
|
||||
const DWORD barterPriceHkAddr[] = {
|
||||
0x474D4C, // barter_attempt_transaction_ (offers button)
|
||||
@@ -261,11 +422,30 @@ void Inject_WithinPerceptionHook() {
|
||||
HookCall(0x458403, PerceptionRangeHearHook);
|
||||
}
|
||||
|
||||
void Inject_CarTravelHook() {
|
||||
MakeJump(0x4BFEF1, CarTravelHack);
|
||||
BlockCall(0x4BFF6E); // vanilla wmCarUseGas(100) call
|
||||
}
|
||||
|
||||
void Inject_SetGlobalVarHook() {
|
||||
HookCall(0x455A6D, SetGlobalVarHook);
|
||||
}
|
||||
|
||||
void Inject_RestTimerHook() {
|
||||
const DWORD restTimerLoopHkAddr[] = {0x499B4B, 0x499BE0, 0x499D2C, 0x499DF2};
|
||||
HookCalls(RestTimerLoopHook, restTimerLoopHkAddr);
|
||||
const DWORD restTimerEscHkAddr[] = {0x499B63, 0x499CA1};
|
||||
MakeCalls(RestTimerEscapeHook, restTimerEscHkAddr);
|
||||
}
|
||||
|
||||
void InitMiscHookScripts() {
|
||||
HookScripts::LoadHookScript("hs_barterprice", HOOK_BARTERPRICE);
|
||||
HookScripts::LoadHookScript("hs_useskill", HOOK_USESKILL);
|
||||
HookScripts::LoadHookScript("hs_steal", HOOK_STEAL);
|
||||
HookScripts::LoadHookScript("hs_withinperception", HOOK_WITHINPERCEPTION);
|
||||
HookScripts::LoadHookScript("hs_cartravel", HOOK_CARTRAVEL);
|
||||
HookScripts::LoadHookScript("hs_setglobalvar", HOOK_SETGLOBALVAR);
|
||||
HookScripts::LoadHookScript("hs_resttimer", HOOK_RESTTIMER);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@ void Inject_BarterPriceHook();
|
||||
void Inject_UseSkillHook();
|
||||
void Inject_StealCheckHook();
|
||||
void Inject_WithinPerceptionHook();
|
||||
void Inject_CarTravelHook();
|
||||
void Inject_SetGlobalVarHook();
|
||||
void Inject_RestTimerHook();
|
||||
|
||||
long PerceptionRangeHook_Invoke(fo::GameObject* watcher, fo::GameObject* target, long type, long result);
|
||||
|
||||
|
||||
+7
-20
@@ -23,6 +23,8 @@
|
||||
#include "..\FalloutEngine\Fallout2.h"
|
||||
#include "..\Translate.h"
|
||||
|
||||
#include "HookScripts.h"
|
||||
|
||||
#include "Karma.h"
|
||||
|
||||
namespace sfall
|
||||
@@ -37,6 +39,7 @@ static std::vector<KarmaFrmSetting> karmaFrms;
|
||||
|
||||
static char karmaGainMsg[128];
|
||||
static char karmaLossMsg[128];
|
||||
bool displayKarmaChanges;
|
||||
|
||||
static DWORD __stdcall DrawCard() {
|
||||
int reputation = (*fo::ptr::game_global_vars)[fo::GVAR_PLAYER_REPUTATION];
|
||||
@@ -64,7 +67,7 @@ skip:
|
||||
}
|
||||
}
|
||||
|
||||
static void __stdcall SetKarma(int value) {
|
||||
void Karma::DisplayKarma(int value) {
|
||||
char buf[128];
|
||||
if (value > 0) {
|
||||
sprintf_s(buf, karmaGainMsg, value);
|
||||
@@ -74,29 +77,13 @@ static void __stdcall SetKarma(int value) {
|
||||
fo::func::display_print(buf);
|
||||
}
|
||||
|
||||
static void __declspec(naked) SetGlobalVarWrapper() {
|
||||
__asm {
|
||||
test eax, eax; // Gvar number
|
||||
jnz end;
|
||||
pushadc;
|
||||
call fo::funcoffs::game_get_global_var_;
|
||||
sub edx, eax; // value -= old
|
||||
jz skip;
|
||||
push edx;
|
||||
call SetKarma;
|
||||
skip:
|
||||
popadc;
|
||||
end:
|
||||
jmp fo::funcoffs::game_set_global_var_;
|
||||
}
|
||||
}
|
||||
|
||||
static void ApplyDisplayKarmaChangesPatch() {
|
||||
if (IniReader::GetConfigInt("Misc", "DisplayKarmaChanges", 0)) {
|
||||
displayKarmaChanges = IniReader::GetConfigInt("Misc", "DisplayKarmaChanges", 0) != 0;
|
||||
if (displayKarmaChanges) {
|
||||
dlog("Applying display karma changes patch.", DL_INIT);
|
||||
Translate::Get("sfall", "KarmaGain", "You gained %d karma.", karmaGainMsg);
|
||||
Translate::Get("sfall", "KarmaLoss", "You lost %d karma.", karmaLossMsg);
|
||||
HookCall(0x455A6D, SetGlobalVarWrapper);
|
||||
HookScripts::InjectingHook(HOOK_SETGLOBALVAR);
|
||||
dlogr(" Done", DL_INIT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,10 @@ class Karma {
|
||||
public:
|
||||
static const char* name() { return "Karma"; }
|
||||
static void init();
|
||||
|
||||
static void DisplayKarma(int value);
|
||||
};
|
||||
|
||||
extern bool displayKarmaChanges;
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user