[Paintball]: Added module

This commit is contained in:
JerryALT 2024-03-29 19:44:14 +03:00
parent 9ae08588fa
commit 81f6cfa9a6
10 changed files with 319 additions and 0 deletions

View File

@ -64,6 +64,7 @@ namespace Components
Register(new GUI());
Register(new Markdown());
Register(new Changelog());
Register(new Paintball());
Pregame = false;
// Make sure preDestroy is called when the game shuts down

View File

@ -82,3 +82,4 @@ namespace Components
#include "Modules/GUI/GUI.hpp"
#include "Modules/GUI/Markdown.hpp"
#include "Modules/Changelog.hpp"
#include "Modules/Paintball.hpp"

View File

@ -34,6 +34,7 @@ namespace Dvars
// Player settings dvars
Game::dvar_s* p_allowFire = nullptr;
Game::dvar_s* player_sprintUnlimited = nullptr;
Game::dvar_s* paintball_mode = nullptr;
Game::dvar_s* external_console = nullptr;

View File

@ -34,6 +34,7 @@ namespace Dvars
// Player settings dvars
extern Game::dvar_s* p_allowFire;
extern Game::dvar_s* player_sprintUnlimited;
extern Game::dvar_s* paintball_mode;
extern Game::dvar_s* external_console;

View File

@ -6,6 +6,8 @@ namespace Components
Utils::ConcurrentList::Container<Events::Callback> Events::ShutdownSystemTasks_;
Utils::ConcurrentList::Container<Events::ScriptShutdownCallback> Events::ScriptsShutdownTasks_;
Utils::ConcurrentList::Container<Events::Callback> Events::OnMapLoadTasks_;
Utils::ConcurrentList::Container<Events::Callback> Events::RegisterGraphic_;
Utils::ConcurrentList::Container<Events::Callback> Events::RegisterSound_;
void Events::OnDvarInit(const std::function<void()>& callback)
{
@ -39,6 +41,22 @@ namespace Components
});
}
void Events::RegisterGraphic(const std::function<void()>& callback)
{
RegisterGraphic_.access([&callback](Callback& tasks)
{
tasks.emplace_back(callback);
});
}
void Events::RegisterSound(const std::function<void()>& callback)
{
RegisterSound_.access([&callback](Callback& tasks)
{
tasks.emplace_back(callback);
});
}
void Events::Com_InitDvars_stub()
{
DvarInitTasks_.access([](Callback& tasks)
@ -107,6 +125,32 @@ namespace Components
}
}
void Events::CG_InitStub(const char* mapname)
{
RegisterGraphic_.access([](Callback& tasks)
{
for (const auto& func : tasks)
{
func();
}
});
Utils::Hook::Call<void(const char*)>(0x41FE10)(mapname); // CG_RegisterGraphics
}
void Events::SV_SpawnServerStub()
{
RegisterSound_.access([](Callback& tasks)
{
for (const auto& func : tasks)
{
func();
}
});
Utils::Hook::Call<void()>(0x41F8F0)(); //CG_RegisterSounds
}
Events::Events()
{
Utils::Hook(0x534B15, Com_InitDvars_stub, HOOK_CALL).install()->quick();
@ -118,6 +162,10 @@ namespace Components
Utils::Hook(0x5C657C, SV_ShutdownGameVM_Hk, HOOK_CALL).install()->quick();
Utils::Hook(0x5C7748, Scr_OnMapLoadStub, HOOK_CALL).install()->quick();
// For custom object 'cgMediaMod'
Utils::Hook(0x421011, CG_InitStub, HOOK_CALL).install()->quick();
Utils::Hook(0x5C7638, SV_SpawnServerStub, HOOK_CALL).install()->quick();
}
Events::~Events()

View File

@ -15,13 +15,19 @@ namespace Components
static void OnScriptsShutdown(const std::function<void(int clearScripts)>& callback);
static void OnVMShutdown(const std::function<void()>& callback);
static void OnMapLoad(const std::function<void()>& callback);
static void RegisterGraphic(const std::function<void()>& callback);
static void RegisterSound(const std::function<void()>& callback);
private:
static Utils::ConcurrentList::Container<Callback> DvarInitTasks_;
static Utils::ConcurrentList::Container<ScriptShutdownCallback> ScriptsShutdownTasks_;
static Utils::ConcurrentList::Container<Callback> ShutdownSystemTasks_;
static Utils::ConcurrentList::Container<Callback> OnMapLoadTasks_;
static Utils::ConcurrentList::Container<Callback> RegisterGraphic_;
static Utils::ConcurrentList::Container<Callback> RegisterSound_;
static void Com_InitDvars_stub();
static void CG_InitStub(const char* mapname);
static void SV_SpawnServerStub();
static void Scr_OnMapLoadStub(int channel, const char* message, const char* format);
static void Scr_ShutdownSystem_Hk(unsigned char sys);
static void SV_ShutdownGameVM_Hk();

View File

@ -0,0 +1,217 @@
#include "STDInc.hpp"
#include <random>
namespace Components
{
bool Paintball::PB_Enabled()
{
return !Game::HasLoadedMod() && Dvars::paintball_mode->current.enabled;
}
const Game::FxEffectDef* Paintball::GetEffect()
{
const std::vector<const Game::FxEffectDef*> effects = {
Game::cgMediaMod.fxPaintBallGreen,
Game::cgMediaMod.fxPaintBallOrange,
Game::cgMediaMod.fxPaintBallBlue,
Game::cgMediaMod.fxPaintBallRed,
Game::cgMediaMod.fxPaintBallYellow
};
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> distr(0, effects.size() - 1);
int randomIndex = distr(gen);
return effects[randomIndex];
}
const Game::FxEffectDef* Paintball::GetExplosiveEffect()
{
const std::vector<const Game::FxEffectDef*> effects = {
Game::cgMediaMod.fxPaintBallExpGreen,
Game::cgMediaMod.fxPaintBallExpOrange
};
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> distr(0, effects.size() - 1);
int randomIndex = distr(gen);
return effects[randomIndex];
}
void Paintball::FireSound(Game::snd_alias_list_t* aliasList/*eax*/, int localClientNum, float* origin)
{
Game::snd_alias_list_t* soundAlias;
if (PB_Enabled())
{
std::string soundAliasName = aliasList->aliasName;
if (soundAliasName.ends_with("_plr"))
soundAlias = Game::cgMediaMod.paintBallFirePlayer;
else
soundAlias = Game::cgMediaMod.paintBallFireNPC;
}
else
soundAlias = aliasList;
Game::CG_PlayClientSoundAlias(soundAlias, localClientNum, origin);
}
void Paintball::ImpactSound(Game::snd_alias_list_t* aliasList/*eax*/, int localClientNum, float* origin)
{
Game::snd_alias_list_t* soundAlias;
if (PB_Enabled())
{
// Apply effect for bullets only!
std::string soundAliasName = aliasList->aliasName;
if (soundAliasName.starts_with("bullet_"))
soundAlias = Game::cgMediaMod.paintBallImpact;
else
soundAlias = aliasList;
}
else
soundAlias = aliasList;
Game::CG_PlayClientSoundAlias(soundAlias, localClientNum, origin);
}
void Paintball::ImpactEffect(const Game::FxEffectDef* fxDef)
{
const Game::FxEffectDef* effect = PB_Enabled() ? GetEffect() : fxDef;
unsigned int weaponIndex = Game::ps->weapon;
Game::CG_PlayBoltedEffect(Game::cgs->localClientNum,
Game::scr_const->tag_knife_fx,
weaponIndex + 2176,
effect);
}
void Paintball::ImpactMark(int axis/*edx*/, int markEntnum/*ecx*/, const Game::FxEffectDef* fxDef, int msecBegin, const float* origin)
{
const Game::FxEffectDef* effect = PB_Enabled() ? GetEffect() : fxDef;
Game::FX_SpawnOrientedEffect(axis, markEntnum, effect, msecBegin, origin);
}
void Paintball::ImpactExplosion(const float* origin/*edx*/, int axis/*ecx*/, const Game::FxEffectDef* fxDef, int time)
{
const Game::FxEffectDef* effectExplosive = PB_Enabled() ? GetExplosiveEffect() : fxDef;
Game::DynEntCl_PlayEventFx(origin, axis, effectExplosive, time);
}
void __declspec(naked) Paintball::CG_PlayClientSoundAliasStub1()
{
const static uint32_t retn_addr = 0x434776;
__asm
{
push eax;
call FireSound;
add esp, 4;
jmp retn_addr;
}
}
void __declspec(naked) Paintball::CG_PlayClientSoundAliasStub2()
{
const static uint32_t retn_addr = 0x43536B;
__asm
{
push eax;
call ImpactSound;
add esp, 4;
jmp retn_addr;
}
}
void __declspec(naked) Paintball::CG_PlayBoltedEffectStub()
{
const static uint32_t retn_addr = 0x435510;
__asm
{
call ImpactEffect
jmp retn_addr;
}
}
void __declspec(naked) Paintball::FX_CreateImpactMarkStub()
{
const static uint32_t retn_addr = 0x435404;
__asm
{
//mov ecx, [esp + 4Ch];
push ecx;
//lea edx, [esp + 20h];
push edx;
call ImpactMark
add esp, 0x8;
jmp retn_addr;
}
}
//void DynEntCl_PlayEventFx(const float* origin/*edx*/, int axis/*ecx*/, const Game::FxEffectDef* def, int time)
void __declspec(naked) Paintball::DynEntCl_PlayEventFxStub()
{
const static uint32_t retn_addr = 0x419A0F;
__asm
{
push ecx;
push edx;
call ImpactExplosion;
add esp, 0x8;
jmp retn_addr;
}
}
Paintball::Paintball()
{
Events::OnDvarInit([]
{
Dvars::paintball_mode = Dvars::Register::Dvar_RegisterBool("paintball_mode", "Enable/Disable the paintball cheat", false, Game::saved_flag);
});
Events::RegisterGraphic([]
{
if(Game::HasLoadedMod())
return;
// Bullets
Game::cgMediaMod.fxPaintBallGreen = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_FX, "impacts/flesh_hit_pball_green").fx;
Game::cgMediaMod.fxPaintBallOrange = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_FX, "impacts/flesh_hit_pball_orange").fx;
Game::cgMediaMod.fxPaintBallBlue = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_FX, "impacts/flesh_hit_pball_blue").fx;
Game::cgMediaMod.fxPaintBallRed = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_FX, "impacts/flesh_hit_pball_red").fx;
Game::cgMediaMod.fxPaintBallYellow = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_FX, "impacts/flesh_hit_pball_yellow").fx;
// Explosion
Game::cgMediaMod.fxPaintBallExpGreen = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_FX, "explosions/exp_paintball_green").fx;
Game::cgMediaMod.fxPaintBallExpOrange = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_FX, "explosions/exp_paintball_orange").fx;
});
Events::RegisterSound([]
{
if(Game::HasLoadedMod())
return;
Game::cgMediaMod.paintBallFirePlayer = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_SOUND, "paintball_fire_plr").sound;
Game::cgMediaMod.paintBallFireNPC = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_SOUND, "paintball_fire_npc").sound;
Game::cgMediaMod.paintBallImpact = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_SOUND, "paintball_impact").sound;
});
// Hook the soundalias in 'CG_FireWeapon'
Utils::Hook(0x434771, CG_PlayClientSoundAliasStub1, HOOK_JUMP).install()->quick();
// Hook the impact sound effect in 'CG_BulletHitEvent_Internal'
Utils::Hook(0x435366, CG_PlayClientSoundAliasStub2, HOOK_JUMP).install()->quick();
// Hook the impact in 'CG_EntityEvent'
Utils::Hook(0x43550B, CG_PlayBoltedEffectStub, HOOK_JUMP).install()->quick();
// Hook the impact in 'FX_SpawnOrientedEffect'
Utils::Hook(0x4353FF, FX_CreateImpactMarkStub, HOOK_JUMP).install()->quick();
// Hook the explosion impact for grenade launchers, grenades, c4 and claymore in 'CG_EntityEvent'
Utils::Hook(0x419A0A, DynEntCl_PlayEventFxStub, HOOK_JUMP).install()->quick();
}
Paintball::~Paintball()
{
}
}

View File

@ -0,0 +1,26 @@
#pragma once
namespace Components
{
class Paintball : public Component
{
public:
Paintball();
~Paintball();
private:
static bool PB_Enabled();
static const Game::FxEffectDef* GetEffect();
static const Game::FxEffectDef* GetExplosiveEffect();
static void FireSound(Game::snd_alias_list_t* aliasList/*eax*/, int localClientNum, float* origin);
static void ImpactSound(Game::snd_alias_list_t* aliasList/*eax*/, int localClientNum, float* origin);
static void ImpactEffect(const Game::FxEffectDef* fxDef);
static void ImpactMark(int axis/*edx*/, int markEntnum/*ecx*/, const Game::FxEffectDef* fxDef, int msecBegin, const float* origin);
static void ImpactExplosion(const float* origin/*edx*/, int axis/*ecx*/, const Game::FxEffectDef* fxDef, int time);
static void CG_PlayClientSoundAliasStub1();
static void CG_PlayClientSoundAliasStub2();
static void CG_PlayBoltedEffectStub();
static void FX_CreateImpactMarkStub();
static void DynEntCl_PlayEventFxStub();
};
}

View File

@ -2,6 +2,7 @@
namespace Game
{
Game::cgMedia_t_mod cgMediaMod = {};
Game::gui_t gui = {};
bool HasLoadedMod()

View File

@ -9,6 +9,23 @@
namespace Game
{
// IW3SP-MOD cgMedia
struct cgMedia_t_mod
{
// Paintball assets
const FxEffectDef* fxPaintBallGreen;
const FxEffectDef* fxPaintBallOrange;
const FxEffectDef* fxPaintBallBlue;
const FxEffectDef* fxPaintBallRed;
const FxEffectDef* fxPaintBallYellow;
const FxEffectDef* fxPaintBallExpGreen;
const FxEffectDef* fxPaintBallExpOrange;
snd_alias_list_t* paintBallFirePlayer;
snd_alias_list_t* paintBallFireNPC;
snd_alias_list_t* paintBallImpact;
};
extern Game::cgMedia_t_mod cgMediaMod;
extern Game::gui_t gui;
bool HasLoadedMod();