Refactor MouseControlCheat

- Split ModMenuModule::MouseControlCheat into multiple smaller subclasses called workers
This commit is contained in:
izawartka 2026-05-19 23:58:58 +02:00
parent 7d755b4314
commit f054d9c5ae
15 changed files with 775 additions and 427 deletions

View File

@ -647,7 +647,13 @@ echo Created $(ProjectDir)src\git.h with commit %GIT_HASH% (dirty=%GIT_DIRTY%)
<ClInclude Include="src\modules\modmenu\cheats\invulnerability.h" />
<ClInclude Include="src\modules\modmenu\cheats\force-controls.h" />
<ClInclude Include="src\modules\modmenu\cheats\last-car.h" />
<ClInclude Include="src\modules\modmenu\cheats\mouse-control\left-right-worker.h" />
<ClInclude Include="src\modules\modmenu\cheats\mouse-control\mouse-control-worker-registry.h" />
<ClInclude Include="src\modules\modmenu\cheats\mouse-control\mouse-control-worker.h" />
<ClInclude Include="src\modules\modmenu\cheats\mouse-control\mouse-control.h" />
<ClInclude Include="src\modules\modmenu\cheats\mouse-control\point-at-mouse-worker.h" />
<ClInclude Include="src\modules\modmenu\cheats\mouse-control\locked-mouse-worker.h" />
<ClInclude Include="src\modules\modmenu\cheats\mouse-control\attack-worker.h" />
<ClInclude Include="src\modules\modmenu\cheats\native-cheats-keeper.h" />
<ClInclude Include="src\modules\modmenu\cheats\player-appearance.h" />
<ClInclude Include="src\modules\modmenu\cheats\player-pos.h" />
@ -890,7 +896,12 @@ echo Created $(ProjectDir)src\git.h with commit %GIT_HASH% (dirty=%GIT_DIRTY%)
<ClCompile Include="src\modules\modmenu\cheats\invulnerability.cpp" />
<ClCompile Include="src\modules\modmenu\cheats\force-controls.cpp" />
<ClCompile Include="src\modules\modmenu\cheats\last-car.cpp" />
<ClCompile Include="src\modules\modmenu\cheats\mouse-control\attack-worker.cpp" />
<ClCompile Include="src\modules\modmenu\cheats\mouse-control\left-right-worker.cpp" />
<ClCompile Include="src\modules\modmenu\cheats\mouse-control\mouse-control-worker-registry.cpp" />
<ClCompile Include="src\modules\modmenu\cheats\mouse-control\mouse-control.cpp" />
<ClCompile Include="src\modules\modmenu\cheats\mouse-control\point-at-mouse-worker.cpp" />
<ClCompile Include="src\modules\modmenu\cheats\mouse-control\locked-mouse-worker.cpp" />
<ClCompile Include="src\modules\modmenu\cheats\native-cheats-keeper.cpp" />
<ClCompile Include="src\modules\modmenu\cheats\player-appearance.cpp" />
<ClCompile Include="src\modules\modmenu\cheats\player-pos.cpp" />

View File

@ -172,6 +172,11 @@
<ClCompile Include="src\modules\modmenu\quick-actions\toggle-steering-assist.cpp" />
<ClCompile Include="src\modules\modmenu\cheats\camera\freecam.cpp" />
<ClCompile Include="src\modules\modmenu\cheats\mouse-control\mouse-control.cpp" />
<ClCompile Include="src\modules\modmenu\cheats\mouse-control\locked-mouse-worker.cpp" />
<ClCompile Include="src\modules\modmenu\cheats\mouse-control\point-at-mouse-worker.cpp" />
<ClCompile Include="src\modules\modmenu\cheats\mouse-control\attack-worker.cpp" />
<ClCompile Include="src\modules\modmenu\cheats\mouse-control\left-right-worker.cpp" />
<ClCompile Include="src\modules\modmenu\cheats\mouse-control\mouse-control-worker-registry.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\converters\ammo.h" />
@ -460,6 +465,12 @@
<ClInclude Include="src\modules\modmenu\quick-actions\toggle-steering-assist.h" />
<ClInclude Include="src\modules\modmenu\cheats\camera\freecam.h" />
<ClInclude Include="src\modules\modmenu\cheats\mouse-control\mouse-control.h" />
<ClInclude Include="src\modules\modmenu\cheats\mouse-control\mouse-control-worker.h" />
<ClInclude Include="src\modules\modmenu\cheats\mouse-control\locked-mouse-worker.h" />
<ClInclude Include="src\modules\modmenu\cheats\mouse-control\point-at-mouse-worker.h" />
<ClInclude Include="src\modules\modmenu\cheats\mouse-control\attack-worker.h" />
<ClInclude Include="src\modules\modmenu\cheats\mouse-control\left-right-worker.h" />
<ClInclude Include="src\modules\modmenu\cheats\mouse-control\mouse-control-worker-registry.h" />
</ItemGroup>
<ItemGroup>
<None Include="GTA2.props" />

View File

@ -0,0 +1,81 @@
#include "attack-worker.h"
#include "mouse-control.h"
#include "../../common.h"
static constexpr size_t ATTACK_CONTROL_INDEX = Game::Utils::GetControlIndex(Game::KEYBOARD_STATE_ATTACK);
void ModMenuModule::AttackWorker::Start()
{
spdlog::debug("AttackWorker: Starting");
if (!CreateControlHandle()) return;
AddEventListener<MouseModule::MouseButtonDownEvent>(&AttackWorker::OnMouseButtonDown);
AddEventListener<MouseModule::MouseButtonUpEvent>(&AttackWorker::OnMouseButtonUp);
m_running = true;
ForceUpdate();
}
void ModMenuModule::AttackWorker::Stop()
{
spdlog::debug("AttackWorker: Stopping");
RemoveEventListener<MouseModule::MouseButtonDownEvent>();
RemoveEventListener<MouseModule::MouseButtonUpEvent>();
FreeControlHandle();
m_running = false;
}
void ModMenuModule::AttackWorker::OnMouseButtonDown(MouseModule::MouseButtonDownEvent& event)
{
if (event.GetButton() != MouseModule::MouseButton::Left) return;
ForceControlsCheat* forceControlsCheat = ForceControlsCheat::GetInstance();
forceControlsCheat->SetControlState(m_attackControlHandle, ForceControlState::ForceDown);
}
void ModMenuModule::AttackWorker::OnMouseButtonUp(MouseModule::MouseButtonUpEvent & event)
{
if (event.GetButton() != MouseModule::MouseButton::Left) return;
ForceControlsCheat* forceControlsCheat = ForceControlsCheat::GetInstance();
forceControlsCheat->SetControlState(m_attackControlHandle, ForceControlState::Unmodified);
}
bool ModMenuModule::AttackWorker::CreateControlHandle()
{
if (ForceControlsCheat::CheckUsesGamepad(ATTACK_CONTROL_INDEX)) {
spdlog::error("AttackWorker: cannot create control handle for attack control because it is used by gamepad controls");
MouseControlCheat::GetInstance()->ShowGamepadControlsToast();
return false;
}
ForceControlsCheat* forceControlsCheat = ForceControlsCheat::GetInstance();
m_attackControlHandle = forceControlsCheat->CreateControlHandle(ATTACK_CONTROL_INDEX);
if (m_attackControlHandle == -1) {
spdlog::error("AttackWorker: failed to create control handle for the control: attack");
return false;
}
return true;
}
void ModMenuModule::AttackWorker::FreeControlHandle()
{
if (m_attackControlHandle == -1) return;
ForceControlsCheat* forceControlsCheat = ForceControlsCheat::GetInstance();
forceControlsCheat->SetControlState(m_attackControlHandle, ForceControlState::Unmodified);
forceControlsCheat->FreeControlHandle(m_attackControlHandle);
m_attackControlHandle = -1;
}
void ModMenuModule::AttackWorker::ForceUpdate() const
{
MouseModule::MouseState mouseState = MouseModule::MouseManager::FetchMouseState();
bool shouldAttack = mouseState.leftButtonDown;
ForceControlsCheat* forceControlsCheat = ForceControlsCheat::GetInstance();
forceControlsCheat->SetControlState(m_attackControlHandle, shouldAttack ? ForceControlState::ForceDown : ForceControlState::Unmodified);
}

View File

@ -0,0 +1,28 @@
#pragma once
#include "mouse-control-worker.h"
#include "../force-controls.h"
namespace ModMenuModule {
class AttackWorker : public MouseControlWorker, public Core::EventListenerSupport {
public:
AttackWorker() = default;
AttackWorker(const AttackWorker&) = delete;
AttackWorker& operator=(const AttackWorker&) = delete;
void Start() override;
void Stop() override;
bool IsRunning() const override { return m_running; }
private:
void OnMouseButtonDown(MouseModule::MouseButtonDownEvent& event);
void OnMouseButtonUp(MouseModule::MouseButtonUpEvent& event);
bool CreateControlHandle();
void FreeControlHandle();
void ForceUpdate() const;
bool m_running = false;
ForceControlsCheat::ControlHandle m_attackControlHandle = -1;
};
}

View File

@ -0,0 +1,100 @@
#include "left-right-worker.h"
#include "mouse-control.h"
#include "../../common.h"
static constexpr float LEFT_RIGHT_INPUT_THRESHOLD = 0.15f;
static constexpr float LEFT_RIGHT_DELTA_DECLINE = 0.20943928f;
static constexpr size_t LEFT_CONTROL_INDEX = Game::Utils::GetControlIndex(Game::KEYBOARD_STATE_LEFT);
static constexpr size_t RIGHT_CONTROL_INDEX = Game::Utils::GetControlIndex(Game::KEYBOARD_STATE_RIGHT);
void ModMenuModule::LeftRightWorker::Start()
{
spdlog::debug("LeftRightWorker: Starting");
if (!CreateControlHandles()) return;
MouseControlCheat* mouseControlCheat = MouseControlCheat::GetInstance();
mouseControlCheat->SetDeltaX(0.0f);
m_running = true;
}
void ModMenuModule::LeftRightWorker::Stop()
{
spdlog::debug("LeftRightWorker: Stopping");
FreeControlHandles();
m_running = false;
}
void ModMenuModule::LeftRightWorker::Update()
{
MouseControlCheat* mouseControlCheat = MouseControlCheat::GetInstance();
ForceControlsCheat* forceControlsCheat = ForceControlsCheat::GetInstance();
float deltaX = mouseControlCheat->GetDeltaX();
char direction = GetDirection(deltaX);
bool shouldPressLeft = direction < 0;
bool shouldPressRight = direction > 0;
forceControlsCheat->SetControlState(m_leftControlHandle, shouldPressLeft ? ForceControlState::ForceDown : ForceControlState::ForceUp);
forceControlsCheat->SetControlState(m_rightControlHandle, shouldPressRight ? ForceControlState::ForceDown : ForceControlState::ForceUp);
deltaX -= (direction * LEFT_RIGHT_DELTA_DECLINE);
mouseControlCheat->SetDeltaX(deltaX);
}
bool ModMenuModule::LeftRightWorker::CreateControlHandles()
{
bool anyUsesGamepad = ForceControlsCheat::CheckUsesGamepad(LEFT_CONTROL_INDEX)
|| ForceControlsCheat::CheckUsesGamepad(RIGHT_CONTROL_INDEX);
if (anyUsesGamepad) {
spdlog::error("LeftRightWorker: cannot create control handle for left/right controls because they are used by gamepad controls");
MouseControlCheat::GetInstance()->ShowGamepadControlsToast();
return false;
}
ForceControlsCheat* forceControlsCheat = ForceControlsCheat::GetInstance();
m_leftControlHandle = forceControlsCheat->CreateControlHandle(LEFT_CONTROL_INDEX);
if (m_leftControlHandle == -1) {
spdlog::error("LeftRightWorker: failed to create control handle for the control: left");
return false;
}
m_rightControlHandle = forceControlsCheat->CreateControlHandle(RIGHT_CONTROL_INDEX);
if (m_rightControlHandle == -1) {
spdlog::error("LeftRightWorker: failed to create control handle for the control: right");
forceControlsCheat->FreeControlHandle(m_leftControlHandle);
m_leftControlHandle = -1;
return false;
}
return true;
}
void ModMenuModule::LeftRightWorker::FreeControlHandles()
{
ForceControlsCheat* forceControlsCheat = ForceControlsCheat::GetInstance();
if (m_leftControlHandle != -1) {
forceControlsCheat->SetControlState(m_leftControlHandle, ForceControlState::Unmodified);
forceControlsCheat->FreeControlHandle(m_leftControlHandle);
m_leftControlHandle = -1;
}
if (m_rightControlHandle != -1) {
forceControlsCheat->SetControlState(m_rightControlHandle, ForceControlState::Unmodified);
forceControlsCheat->FreeControlHandle(m_rightControlHandle);
m_rightControlHandle = -1;
}
}
char ModMenuModule::LeftRightWorker::GetDirection(float delta)
{
if (std::abs(delta) < LEFT_RIGHT_INPUT_THRESHOLD) {
return 0;
}
return delta > 0 ? 1 : -1;
}

View File

@ -0,0 +1,27 @@
#pragma once
#include "mouse-control-worker.h"
#include "../force-controls.h"
namespace ModMenuModule {
class LeftRightWorker : public MouseControlWorker, public Core::EventListenerSupport {
public:
LeftRightWorker() = default;
LeftRightWorker(const LeftRightWorker&) = delete;
LeftRightWorker& operator=(const LeftRightWorker&) = delete;
void Start() override;
void Stop() override;
bool IsRunning() const override { return m_running; }
void Update() override;
private:
bool CreateControlHandles();
void FreeControlHandles();
static char GetDirection(float deltaAngle);
bool m_running = false;
ForceControlsCheat::ControlHandle m_leftControlHandle = -1;
ForceControlsCheat::ControlHandle m_rightControlHandle = -1;
};
}

View File

@ -0,0 +1,57 @@
#include "locked-mouse-worker.h"
#include "mouse-control.h"
#include "../../common.h"
static constexpr float LOCKED_MOUSE_SMOOTING = 0.2f;
void ModMenuModule::LockedMouseWorker::Start()
{
spdlog::debug("LockedMouseWorker: Starting");
AddEventListener<MouseModule::MouseLockedMoveEvent>(&LockedMouseWorker::OnMouseLockedMove);
MouseModule::MouseManager* mouseManager = MouseModule::MouseManager::GetInstance();
mouseManager->SetLocked(true);
mouseManager->SetCursorVisibility(MouseModule::CursorVisibility::ForceInvisible);
m_running = true;
}
void ModMenuModule::LockedMouseWorker::Stop()
{
spdlog::debug("LockedMouseWorker: Stopping");
RemoveEventListener<MouseModule::MouseLockedMoveEvent>();
MouseModule::MouseManager* mouseManager = MouseModule::MouseManager::GetInstance();
mouseManager->SetLocked(false);
mouseManager->SetCursorVisibility(MouseModule::CursorVisibility::Unmodified);
m_running = false;
}
void ModMenuModule::LockedMouseWorker::Update()
{
MouseControlCheat* mouseControlCheat = MouseControlCheat::GetInstance();
float deltaX = mouseControlCheat->GetDeltaX();
float deltaY = mouseControlCheat->GetDeltaY();
deltaX *= (1.0f - LOCKED_MOUSE_SMOOTING);
deltaY *= (1.0f - LOCKED_MOUSE_SMOOTING);
mouseControlCheat->SetDeltaX(deltaX);
mouseControlCheat->SetDeltaY(deltaY);
}
void ModMenuModule::LockedMouseWorker::OnMouseLockedMove(MouseModule::MouseLockedMoveEvent& event)
{
MouseControlCheat* mouseControlCheat = MouseControlCheat::GetInstance();
float sensitivity = mouseControlCheat->GetOptions().rotateModeSensitivity;
float deltaX = mouseControlCheat->GetDeltaX();
float deltaY = mouseControlCheat->GetDeltaY();
deltaX += (event.GetDelta().x * sensitivity);
deltaY += (event.GetDelta().y * sensitivity);
mouseControlCheat->SetDeltaX(deltaX);
mouseControlCheat->SetDeltaY(deltaY);
}

View File

@ -0,0 +1,22 @@
#pragma once
#include "mouse-control-worker.h"
#include "../force-controls.h"
namespace ModMenuModule {
class LockedMouseWorker : public MouseControlWorker, public Core::EventListenerSupport {
public:
LockedMouseWorker() = default;
LockedMouseWorker(const LockedMouseWorker&) = delete;
LockedMouseWorker& operator=(const LockedMouseWorker&) = delete;
void Start() override;
void Stop() override;
bool IsRunning() const override { return m_running; }
void Update() override;
private:
void OnMouseLockedMove(MouseModule::MouseLockedMoveEvent& event);
bool m_running = false;
};
}

View File

@ -0,0 +1,54 @@
#include "mouse-control-worker.h"
#include "attack-worker.h"
#include "locked-mouse-worker.h"
#include "point-at-mouse-worker.h"
#include "left-right-worker.h"
#include "mouse-control-worker-registry.h"
namespace ModMenuModule::MouseControlWorkerRegistry {
namespace {
using WorkerFactory = std::unique_ptr<MouseControlWorker>(*)();
template<typename WorkerT>
std::unique_ptr<MouseControlWorker> CreateWorkerInstance()
{
return std::make_unique<WorkerT>();
}
const std::unordered_map<WorkerType, WorkerFactory>& GetWorkerFactories()
{
static const std::unordered_map<WorkerType, WorkerFactory> factories = {
{ GetWorkerType<AttackWorker>(), &CreateWorkerInstance<AttackWorker> },
{ GetWorkerType<LockedMouseWorker>(), &CreateWorkerInstance<LockedMouseWorker> },
{ GetWorkerType<PointAtMouseWorker>(), &CreateWorkerInstance<PointAtMouseWorker> },
{ GetWorkerType<LeftRightWorker>(), &CreateWorkerInstance<LeftRightWorker> },
};
return factories;
}
const std::unordered_map<WorkerSetType, MouseControlWorkerSetDef>& GetWorkerSetDefs()
{
static const std::unordered_map<WorkerSetType, MouseControlWorkerSetDef> workerSetDefs = {
{ WorkerSetType::RotateMode, MouseControlWorkerSetDef::Create<AttackWorker, LockedMouseWorker, LeftRightWorker>() },
{ WorkerSetType::RotateModeInCar, MouseControlWorkerSetDef::Create<AttackWorker, void, void>() },
{ WorkerSetType::PointAtMode, MouseControlWorkerSetDef::Create<AttackWorker, PointAtMouseWorker, LeftRightWorker>() },
{ WorkerSetType::PointAtModeInCar, MouseControlWorkerSetDef::Create<AttackWorker, void, void>() },
};
return workerSetDefs;
}
}
const MouseControlWorkerSetDef& GetWorkerSetDef(WorkerSetType type)
{
return GetWorkerSetDefs().at(type);
}
const std::unique_ptr<MouseControlWorker> CreateWorker(WorkerType type)
{
const auto& factories = GetWorkerFactories();
const auto factoryIt = factories.find(type);
return factoryIt != factories.end() ? factoryIt->second() : nullptr;
}
}

View File

@ -0,0 +1,45 @@
#pragma once
#include "../../common.h"
#include "mouse-control-worker.h"
namespace ModMenuModule::MouseControlWorkerRegistry {
enum class WorkerSetType {
None = 0,
RotateMode,
RotateModeInCar,
PointAtMode,
PointAtModeInCar,
};
using WorkerType = std::type_index;
template<typename WorkerT>
static inline WorkerType GetWorkerType()
{
return std::type_index(typeid(WorkerT));
}
static inline WorkerType GetWorkerType(MouseControlWorker* worker)
{
return worker ? std::type_index(typeid(*worker)) : std::type_index(typeid(void));
}
struct MouseControlWorkerSetDef {
WorkerType attackWorkerType;
WorkerType mouseWorkerType;
WorkerType resultWorkerType;
template<typename AttackWorkerT, typename MouseWorkerT, typename ResultWorkerT>
static constexpr MouseControlWorkerSetDef Create()
{
return {
GetWorkerType<AttackWorkerT>(),
GetWorkerType<MouseWorkerT>(),
GetWorkerType<ResultWorkerT>()
};
}
};
const MouseControlWorkerSetDef& GetWorkerSetDef(WorkerSetType type);
const std::unique_ptr<MouseControlWorker> CreateWorker(WorkerType type);
}

View File

@ -0,0 +1,20 @@
#pragma once
namespace ModMenuModule {
class MouseControlCheat;
class MouseControlWorker {
public:
virtual ~MouseControlWorker() = default;
virtual void Start() = 0;
virtual void Stop() = 0;
virtual bool IsRunning() const = 0;
virtual void Update() {}
protected:
MouseControlWorker() = default;
MouseControlWorker(const MouseControlWorker&) = delete;
MouseControlWorker& operator=(const MouseControlWorker&) = delete;
};
}

View File

@ -1,16 +1,10 @@
#include "mouse-control.h"
#include "../../utils/angle-utils.h"
#include "../camera/camera.h"
#include "../../toast-manager.h"
#include "../../events/cheat-options-update.h"
#include "../../cheat-registry.h"
static constexpr float ROTATE_MODE_SMOOTING = 0.2f;
static constexpr float ROTATION_INPUT_THRESHOLD = 0.15f;
static constexpr float ROTATION_SPEED = 0.20943928f;
static constexpr size_t LEFT_CONTROL_INDEX = Game::Utils::GetControlIndex(Game::KEYBOARD_STATE_LEFT);
static constexpr size_t RIGHT_CONTROL_INDEX = Game::Utils::GetControlIndex(Game::KEYBOARD_STATE_RIGHT);
static constexpr size_t ATTACK_CONTROL_INDEX = Game::Utils::GetControlIndex(Game::KEYBOARD_STATE_ATTACK);
static constexpr size_t PERSISTENCE_VERSION = 1;
ModMenuModule::MouseControlCheat* ModMenuModule::MouseControlCheat::m_instance = nullptr;
@ -47,9 +41,26 @@ void ModMenuModule::MouseControlCheat::SetOptions(const MouseControlCheatOptions
return;
}
SetOptionsInternal(options);
MouseControlCheatOptions oldOptions = m_options;
m_options = options;
Core::EventManager* eventManager = Core::EventManager::GetInstance();
CheatOptionsUpdateEvent<MouseControlCheat> event(oldOptions, m_options);
eventManager->Dispatch(event);
}
void ModMenuModule::MouseControlCheat::ShowGamepadControlsToast()
{
if (m_gamepadControlsToastShown) return;
m_gamepadControlsToastShown = true;
// the name "Controller" is purposely used instead of "Gamepad" or because that's how the preset is named in GTA2 Manager
spdlog::warn("Mouse control won't work with Controller controls. Please update your controls in GTA2 Manager");
ToastManager::GetInstance()->Show({ L"Mouse control won't work with Controller controls.", ToastType::Warning, 240 });
ToastManager::GetInstance()->Show({ L"Please update your controls in GTA2 Manager", ToastType::Warning, 240 });
}
void ModMenuModule::MouseControlCheat::OnFirstEnable()
{
LoadFromPersistence();
@ -57,182 +68,41 @@ void ModMenuModule::MouseControlCheat::OnFirstEnable()
void ModMenuModule::MouseControlCheat::OnEnable()
{
AddEventListener<GameStartEvent>(&MouseControlCheat::OnGameStart);
AddEventListener<PreGameTickEvent>(&MouseControlCheat::OnPreGameTick);
AddEventListener<GameEndEvent>(&MouseControlCheat::OnGameEnd);
AddEventListener<GamePauseEvent>(&MouseControlCheat::OnGamePause);
AddEventListener<GameUnpauseEvent>(&MouseControlCheat::OnGameUnpause);
UpdateAutoModeListeners();
UpdateAutoMode();
CreateControlHandles();
if (ForceControlsCheat::CheckGameReadyToForce()) Start();
}
void ModMenuModule::MouseControlCheat::OnDisable()
{
RemoveEventListener<GameStartEvent>();
RemoveEventListener<PreGameTickEvent>();
RemoveEventListener<GameEndEvent>();
RemoveEventListener<GamePauseEvent>();
RemoveEventListener<GameUnpauseEvent>();
RemoveAutoModeListeners();
Stop();
FreeControlHandles();
ClearWorkerSet();
SaveToPersistence();
m_gamepadControlsToastShown = false;
}
void ModMenuModule::MouseControlCheat::OnPreGameTick(PreGameTickEvent& event)
{
if (CheckShouldUseRotation()) {
StartRotation();
UpdateTargetDeltaRotation();
}
else {
StopRotation();
return;
}
char rotationDirection = GetRotationDirection(m_targetDeltaRotation);
bool shouldRotateLeft = rotationDirection == -1;
bool shouldRotateRight = rotationDirection == 1;
if (shouldRotateLeft) m_targetDeltaRotation -= ROTATION_SPEED;
else if (shouldRotateRight) m_targetDeltaRotation += ROTATION_SPEED;
ForceControlsCheat* forceControlsCheat = ForceControlsCheat::GetInstance();
forceControlsCheat->SetControlState(m_leftControlHandle, shouldRotateLeft ? ForceControlState::ForceDown : ForceControlState::ForceUp);
forceControlsCheat->SetControlState(m_rightControlHandle, shouldRotateRight ? ForceControlState::ForceDown : ForceControlState::ForceUp);
}
void ModMenuModule::MouseControlCheat::OnMouseLockedMove(MouseModule::MouseLockedMoveEvent& event)
{
int deltaX = event.GetDelta().x;
m_targetDeltaRotation -= deltaX * m_options.rotateModeSensitivity;
}
void ModMenuModule::MouseControlCheat::OnMouseMove(MouseModule::MouseMoveEvent& event)
{
MouseModule::MousePosition pos = event.GetPosition();
m_lastNormalizedPos = MouseModule::MouseManager::ToNormalizedPosition(pos);
}
void ModMenuModule::MouseControlCheat::OnMouseButtonDown(MouseModule::MouseButtonDownEvent& event)
{
if (event.GetButton() != MouseModule::MouseButton::Left) return;
ForceControlsCheat* forceControlsCheat = ForceControlsCheat::GetInstance();
forceControlsCheat->SetControlState(m_attackControlHandle, ForceControlState::ForceDown);
}
void ModMenuModule::MouseControlCheat::OnMouseButtonUp(MouseModule::MouseButtonUpEvent& event)
{
if (event.GetButton() != MouseModule::MouseButton::Left) return;
ForceControlsCheat* forceControlsCheat = ForceControlsCheat::GetInstance();
forceControlsCheat->SetControlState(m_attackControlHandle, ForceControlState::Unmodified);
}
void ModMenuModule::MouseControlCheat::OnGameStart(GameStartEvent& event)
{
if (ForceControlsCheat::CheckGameReadyToForce()) Start();
ApplyAutoMode();
UpdateWorkerSet();
SendWorkersUpdate();
}
void ModMenuModule::MouseControlCheat::OnGameEnd(GameEndEvent& event)
{
Stop();
ClearWorkerSet();
}
void ModMenuModule::MouseControlCheat::OnGamePause(GamePauseEvent& event)
{
Stop();
ClearWorkerSet();
}
void ModMenuModule::MouseControlCheat::OnGameUnpause(GameUnpauseEvent& event)
{
if (ForceControlsCheat::CheckGameReadyToForce(true)) Start();
}
void ModMenuModule::MouseControlCheat::OnCheatStateChange(CheatStateEvent& event)
{
if (event.GetCheatType() != typeid(CameraCheat)) return;
UpdateAutoMode();
}
void ModMenuModule::MouseControlCheat::OnCameraCheatOptionsUpdate(CheatOptionsUpdateEvent<CameraCheat>& event)
{
if (event.GetOldOptions().followPedRotation == event.GetNewOptions().followPedRotation) return;
UpdateAutoMode();
}
void ModMenuModule::MouseControlCheat::SetOptionsInternal(const MouseControlCheatOptions& options)
{
MouseControlCheatOptions oldOptions = m_options;
m_options = options;
bool autoModeChanged = (oldOptions.autoMode != m_options.autoMode);
if (autoModeChanged) {
if (m_options.autoMode) m_options.mode = GetAutoModeTargetMode();
if (IsEnabled()) UpdateAutoModeListeners();
}
bool modeChanged = (oldOptions.mode != m_options.mode);
if (m_started && modeChanged) UpdateMode();
Core::EventManager* eventManager = Core::EventManager::GetInstance();
CheatOptionsUpdateEvent<MouseControlCheat> event(oldOptions, m_options);
eventManager->Dispatch(event);
}
void ModMenuModule::MouseControlCheat::UpdateTargetDeltaRotation()
{
float currentRotation = GetPlayerPedRotation();
bool isPointAtMode = m_options.mode == MouseControlCheatMode::PointAt;
if (isPointAtMode) {
std::optional<float> targetRotationOpt = GetTargetRotation(m_lastNormalizedPos);
if (!targetRotationOpt.has_value()) {
m_targetDeltaRotation = 0.0f;
return;
}
m_targetDeltaRotation = Utils::Angle::GetShortestAngleDifference(currentRotation, targetRotationOpt.value());
}
else {
m_targetDeltaRotation *= (1.0f - ROTATE_MODE_SMOOTING);
}
}
void ModMenuModule::MouseControlCheat::UpdateMode()
{
bool isRotateMode = m_options.mode == MouseControlCheatMode::Rotate;
bool isPointAtMode = m_options.mode == MouseControlCheatMode::PointAt;
SetEventListener<MouseModule::MouseLockedMoveEvent>(&MouseControlCheat::OnMouseLockedMove, isRotateMode);
SetEventListener<MouseModule::MouseMoveEvent>(&MouseControlCheat::OnMouseMove, isPointAtMode);
MouseModule::MouseManager* mouseManager = MouseModule::MouseManager::GetInstance();
mouseManager->SetLocked(isRotateMode);
mouseManager->SetCursorVisibility(isRotateMode ? MouseModule::CursorVisibility::ForceInvisible : MouseModule::CursorVisibility::ForceVisible);
if (isPointAtMode) UpdateLastNormalizedPos();
}
void ModMenuModule::MouseControlCheat::UpdateLastNormalizedPos()
{
MouseModule::MousePosition currentPos = MouseModule::MouseManager::FetchMouseState().position;
m_lastNormalizedPos = MouseModule::MouseManager::ToNormalizedPosition(currentPos);
}
void ModMenuModule::MouseControlCheat::UpdateAttack() const
{
MouseModule::MouseState mouseState = MouseModule::MouseManager::FetchMouseState();
bool shouldAttack = mouseState.leftButtonDown;
ForceControlsCheat* forceControlsCheat = ForceControlsCheat::GetInstance();
forceControlsCheat->SetControlState(m_attackControlHandle, shouldAttack ? ForceControlState::ForceDown : ForceControlState::Unmodified);
}
void ModMenuModule::MouseControlCheat::UpdateAutoMode()
void ModMenuModule::MouseControlCheat::ApplyAutoMode()
{
if (!m_options.autoMode) return;
@ -240,229 +110,11 @@ void ModMenuModule::MouseControlCheat::UpdateAutoMode()
newOptions.mode = GetAutoModeTargetMode();
if (newOptions.mode != m_options.mode) {
SetOptionsInternal(newOptions);
SetOptions(newOptions);
}
}
void ModMenuModule::MouseControlCheat::UpdateAutoModeListeners()
{
SetEventListener<CheatStateEvent>(&MouseControlCheat::OnCheatStateChange, m_options.autoMode);
SetEventListener<CheatOptionsUpdateEvent<CameraCheat>>(&MouseControlCheat::OnCameraCheatOptionsUpdate, m_options.autoMode);
}
void ModMenuModule::MouseControlCheat::RemoveAutoModeListeners()
{
RemoveEventListener<CheatStateEvent>(true);
RemoveEventListener<CheatOptionsUpdateEvent<CameraCheat>>(true);
}
bool ModMenuModule::MouseControlCheat::EnsureNotGamepadControls() const
{
bool allOk = !ForceControlsCheat::CheckUsesGamepad(LEFT_CONTROL_INDEX)
&& !ForceControlsCheat::CheckUsesGamepad(RIGHT_CONTROL_INDEX)
&& !ForceControlsCheat::CheckUsesGamepad(ATTACK_CONTROL_INDEX);
if (!allOk) {
// the name "Controller" is purposely used instead of "Gamepad" or because that's how the preset is named in GTA2 Manager
spdlog::warn("Mouse control won't work with Controller controls. Please update your controls in GTA2 Manager");
ToastManager::GetInstance()->Show({ L"Mouse control won't work with Controller controls.", ToastType::Warning, 240 });
ToastManager::GetInstance()->Show({ L"Please update your controls in GTA2 Manager", ToastType::Warning, 240 });
}
return allOk;
}
bool ModMenuModule::MouseControlCheat::EnsureControlHandlesOk() const
{
if (m_controlHandlesOk) return true;
spdlog::warn("Mouse control cheat cannot start because control handles are not ok");
ToastManager::GetInstance()->Show({ L"Mouse control cheat has failed to start", ToastType::Error, 240 });
return false;
}
void ModMenuModule::MouseControlCheat::StartRotation()
{
if (m_usingRotation) return;
spdlog::debug("MouseControlCheat: Starting rotation");
m_usingRotation = true;
m_targetDeltaRotation = 0.0f;
}
void ModMenuModule::MouseControlCheat::StopRotation()
{
if (!m_usingRotation) return;
spdlog::debug("MouseControlCheat: Stopping rotation");
m_usingRotation = false;
ForceControlsCheat* forceControlsCheat = ForceControlsCheat::GetInstance();
forceControlsCheat->SetControlState(m_leftControlHandle, ForceControlState::Unmodified);
forceControlsCheat->SetControlState(m_rightControlHandle, ForceControlState::Unmodified);
}
void ModMenuModule::MouseControlCheat::Start()
{
if (m_started) return;
if (!EnsureControlHandlesOk()) return;
if (!EnsureNotGamepadControls()) return;
spdlog::debug("MouseControlCheat: Starting");
m_started = true;
AddEventListener<PreGameTickEvent>(&MouseControlCheat::OnPreGameTick);
AddEventListener<MouseModule::MouseButtonDownEvent>(&MouseControlCheat::OnMouseButtonDown);
AddEventListener<MouseModule::MouseButtonUpEvent>(&MouseControlCheat::OnMouseButtonUp);
UpdateMode();
UpdateAttack();
}
void ModMenuModule::MouseControlCheat::Stop()
{
if (!m_started) return;
spdlog::debug("MouseControlCheat: Stopping");
m_started = false;
RemoveEventListener<PreGameTickEvent>();
RemoveEventListener<MouseModule::MouseButtonDownEvent>();
RemoveEventListener<MouseModule::MouseButtonUpEvent>();
RemoveEventListener<MouseModule::MouseLockedMoveEvent>(true);
RemoveEventListener<MouseModule::MouseMoveEvent>(true);
MouseModule::MouseManager* mouseManager = MouseModule::MouseManager::GetInstance();
mouseManager->SetLocked(false);
mouseManager->SetCursorVisibility(MouseModule::CursorVisibility::Unmodified);
ForceControlsCheat* forceControlsCheat = ForceControlsCheat::GetInstance();
forceControlsCheat->SetControlState(m_attackControlHandle, ForceControlState::Unmodified);
StopRotation();
}
bool ModMenuModule::MouseControlCheat::CreateControlHandles()
{
ForceControlsCheat* forceControlsCheat = ForceControlsCheat::GetInstance();
m_leftControlHandle = forceControlsCheat->CreateControlHandle(LEFT_CONTROL_INDEX);
m_rightControlHandle = forceControlsCheat->CreateControlHandle(RIGHT_CONTROL_INDEX);
m_attackControlHandle = forceControlsCheat->CreateControlHandle(ATTACK_CONTROL_INDEX);
m_controlHandlesOk = (m_leftControlHandle != -1) && (m_rightControlHandle != -1) && (m_attackControlHandle != -1);
if(!m_controlHandlesOk) {
spdlog::error("Mouse control cheat failed to create control handles");
FreeControlHandles();
return false;
}
return true;
}
void ModMenuModule::MouseControlCheat::FreeControlHandles()
{
ForceControlsCheat* forceControlsCheat = ForceControlsCheat::GetInstance();
if (m_leftControlHandle != -1) {
forceControlsCheat->FreeControlHandle(m_leftControlHandle);
m_leftControlHandle = -1;
}
if (m_rightControlHandle != -1) {
forceControlsCheat->FreeControlHandle(m_rightControlHandle);
m_rightControlHandle = -1;
}
if (m_attackControlHandle != -1) {
forceControlsCheat->FreeControlHandle(m_attackControlHandle);
m_attackControlHandle = -1;
}
m_controlHandlesOk = false;
}
void ModMenuModule::MouseControlCheat::SaveToPersistence() const
{
PersistenceModule::PersistenceManager* persistence = PersistenceModule::PersistenceManager::GetInstance();
size_t dataSize = 1 + sizeof(MouseControlCheatOptions);
std::unique_ptr<uint8_t[]> dataPtr = std::make_unique<uint8_t[]>(dataSize);
dataPtr[0] = 1; // version
memcpy(dataPtr.get() + 1, &m_options, sizeof(MouseControlCheatOptions));
persistence->SaveRaw("Cheat_MouseControl_State", dataPtr.get(), dataSize);
}
void ModMenuModule::MouseControlCheat::LoadFromPersistence()
{
PersistenceModule::PersistenceManager* persistence = PersistenceModule::PersistenceManager::GetInstance();
std::unique_ptr<uint8_t[]> dataPtr = nullptr;
size_t dataSize = 0;
if (!persistence->LoadRaw("Cheat_MouseControl_State", dataPtr, dataSize)) return;
if (dataSize < 1) {
spdlog::error("MouseControlCheat::LoadFromPersistence: invalid data size");
return;
}
uint8_t version = dataPtr[0];
if (version != 1) {
spdlog::error("MouseControlCheat::LoadFromPersistence: unsupported version {}", version);
return;
}
if (dataSize != 1 + sizeof(MouseControlCheatOptions)) {
spdlog::error("MouseControlCheat::LoadFromPersistence: invalid data size for version {}", version);
return;
}
memcpy(&m_options, dataPtr.get() + 1, sizeof(MouseControlCheatOptions));
SetOptions(m_options);
}
bool ModMenuModule::MouseControlCheat::CheckShouldUseRotation()
{
Game::Ped* playerPed = Game::Utils::GetPlayerCurrentPed();
if (!playerPed || !playerPed->gameObject) return false;
if (playerPed->targetCarForEnter) return false;
return true;
}
char ModMenuModule::MouseControlCheat::GetRotationDirection(float deltaAngle)
{
if (std::abs(deltaAngle) < ROTATION_INPUT_THRESHOLD) {
return 0;
}
return deltaAngle > 0 ? -1 : 1;
}
float ModMenuModule::MouseControlCheat::GetPlayerPedRotation()
{
Game::Ped* playerPed = Game::Utils::GetPlayerCurrentPed();
if (!playerPed || !playerPed->gameObject) return 0; // prior verified by ShouldUseRotation to not let this happen
return Game::Utils::FromGTAAngleToRad(playerPed->gameObject->spriteRotation);
}
std::optional<float> ModMenuModule::MouseControlCheat::GetTargetRotation(MouseModule::NormalizedMousePosition normalizedPos)
{
normalizedPos.x -= 0.5f;
normalizedPos.y -= 0.5f;
normalizedPos.x *= -1.0f;
normalizedPos.x *= MouseModule::MouseManager::GetClientAreaAspectRatio();
float targetAngle;
CameraCheat* cameraCheat = CameraCheat::GetInstance();
if (cameraCheat->IsEnabled()) {
CameraCheatOptions cameraOptions = cameraCheat->GetOptions();
normalizedPos.x *= std::cos(cameraOptions.cameraTransform.horizontalAngleRad);
if (normalizedPos.x == 0.0f && normalizedPos.y == 0.0f) return std::nullopt;
targetAngle = std::atan2(normalizedPos.y, normalizedPos.x) - static_cast<float>(M_PI / 2.0f);
float cameraRotation = cameraCheat->GetOptions().cameraTransform.verticalAngleRad;
targetAngle += cameraRotation;
}
else {
targetAngle = std::atan2(normalizedPos.y, normalizedPos.x) - static_cast<float>(M_PI / 2.0f);
}
return Utils::Angle::NormalizeAngle(targetAngle);
}
ModMenuModule::MouseControlCheatMode ModMenuModule::MouseControlCheat::GetAutoModeTargetMode()
ModMenuModule::MouseControlCheatMode ModMenuModule::MouseControlCheat::GetAutoModeTargetMode() const
{
CameraCheat* cameraCheat = CameraCheat::GetInstance();
if (!cameraCheat->IsEnabled()) {
@ -477,4 +129,142 @@ ModMenuModule::MouseControlCheatMode ModMenuModule::MouseControlCheat::GetAutoMo
return MouseControlCheatMode::Rotate;
}
void ModMenuModule::MouseControlCheat::UpdateWorkerSet()
{
using namespace MouseControlWorkerRegistry;
WorkerSetType appliableWorkerSetType = GetAppliableWorkerSetType();
if (appliableWorkerSetType == m_workerSetType) return;
const MouseControlWorkerSetDef& workerSetDef = GetWorkerSetDef(appliableWorkerSetType);
UpdateWorker(m_attackWorker, workerSetDef.attackWorkerType);
UpdateWorker(m_mouseWorker, workerSetDef.mouseWorkerType);
UpdateWorker(m_resultWorker, workerSetDef.resultWorkerType);
}
void ModMenuModule::MouseControlCheat::ClearWorkerSet()
{
m_workerSetType = MouseControlWorkerRegistry::WorkerSetType::None;
RemoveWorker(m_attackWorker);
RemoveWorker(m_mouseWorker);
RemoveWorker(m_resultWorker);
}
ModMenuModule::MouseControlWorkerRegistry::WorkerSetType ModMenuModule::MouseControlCheat::GetAppliableWorkerSetType() const
{
using namespace MouseControlWorkerRegistry;
Game::Game* game = Game::Memory::GetGame();
if (!game || game->gameStatus != Game::GAME_RUN) {
return WorkerSetType::None;
}
Game::Ped* playerPed = Game::Utils::GetPlayerCurrentPed();
bool isInCar = playerPed && (playerPed->currentCar != nullptr || playerPed->targetCarForEnter != nullptr);
bool hasGameObject = playerPed && playerPed->gameObject != nullptr;
switch (m_options.mode) {
case MouseControlCheatMode::Rotate:
if (isInCar) return WorkerSetType::RotateModeInCar;
else if (hasGameObject) return WorkerSetType::RotateMode;
return WorkerSetType::None;
case MouseControlCheatMode::PointAt:
if (isInCar) return WorkerSetType::PointAtModeInCar;
else if (hasGameObject) return WorkerSetType::PointAtMode;
return WorkerSetType::None;
default:
spdlog::error("MouseControlCheat::GetAppliableWorkerSet: unknown mode {}", static_cast<int>(m_options.mode));
return WorkerSetType::None;
}
}
void ModMenuModule::MouseControlCheat::UpdateWorker(std::unique_ptr<MouseControlWorker>& worker, MouseControlWorkerRegistry::WorkerType targetType)
{
if (worker && MouseControlWorkerRegistry::GetWorkerType(worker.get()) == targetType) {
return;
}
if (worker) {
RemoveWorker(worker);
}
if (targetType != MouseControlWorkerRegistry::GetWorkerType<void>()) {
CreateWorker(worker, targetType);
}
}
void ModMenuModule::MouseControlCheat::CreateWorker(std::unique_ptr<MouseControlWorker>& worker, MouseControlWorkerRegistry::WorkerType type)
{
std::unique_ptr<MouseControlWorker> newWorker = MouseControlWorkerRegistry::CreateWorker(type);
if (!newWorker) {
spdlog::error("MouseControlCheat::CreateWorker: failed to create worker for type {}", type.name());
return;
}
newWorker->Start();
if(!newWorker->IsRunning()) {
spdlog::error("MouseControlCheat::CreateWorker: failed to start worker for type {}", type.name());
return;
}
worker = std::move(newWorker);
}
void ModMenuModule::MouseControlCheat::RemoveWorker(std::unique_ptr<MouseControlWorker>& worker)
{
if (!worker) return;
worker->Stop();
worker.reset();
}
void ModMenuModule::MouseControlCheat::SendWorkersUpdate()
{
if (m_attackWorker) m_attackWorker->Update();
if (m_mouseWorker) m_mouseWorker->Update();
if (m_resultWorker) m_resultWorker->Update();
}
void ModMenuModule::MouseControlCheat::SaveToPersistence() const
{
PersistenceModule::PersistenceManager* persistence = PersistenceModule::PersistenceManager::GetInstance();
size_t dataSize = 1 + sizeof(MouseControlCheatOptions);
std::unique_ptr<uint8_t[]> dataPtr = std::make_unique<uint8_t[]>(dataSize);
dataPtr[0] = PERSISTENCE_VERSION; // version
memcpy(dataPtr.get() + 1, &m_options, sizeof(MouseControlCheatOptions));
persistence->SaveRaw("Cheat_MouseControl_State", dataPtr.get(), dataSize);
}
void ModMenuModule::MouseControlCheat::LoadFromPersistence()
{
PersistenceModule::PersistenceManager* persistence = PersistenceModule::PersistenceManager::GetInstance();
std::unique_ptr<uint8_t[]> dataPtr = nullptr;
size_t dataSize = 0;
if (!persistence->LoadRaw("Cheat_MouseControl_State", dataPtr, dataSize)) return;
if (dataSize < 1) {
spdlog::error("MouseControlCheat::LoadFromPersistence: invalid data size");
return;
}
uint8_t version = dataPtr[0];
if (version != PERSISTENCE_VERSION) {
if (!ConvertPersistence(dataPtr, dataSize, version)) return;
}
if (dataSize != 1 + sizeof(MouseControlCheatOptions)) {
spdlog::error("MouseControlCheat::LoadFromPersistence: invalid persistence data size");
return;
}
memcpy(&m_options, dataPtr.get() + 1, sizeof(MouseControlCheatOptions));
SetOptions(m_options);
}
bool ModMenuModule::MouseControlCheat::ConvertPersistence(std::unique_ptr<uint8_t[]>& dataPtr, size_t& dataSize, uint8_t version)
{
spdlog::error("MouseControlCheat::ConvertPersistence: unsupported version {}", version);
return false;
}
REGISTER_CHEAT(MouseControlCheat)

View File

@ -3,17 +3,12 @@
#include "../../cheat-base.h"
#include "../force-controls.h"
#include "../../../../events/game-tick.h"
#include "../../../../events/game-start.h"
#include "../../../../events/game-end.h"
#include "../../../../events/game-pause.h"
#include "../../../../events/game-unpause.h"
#include "../../events/cheat-state.h"
#include "../../events/cheat-options-update.h"
#include "../../../mouse/normalized-mouse-position.h"
#include "mouse-control-worker.h"
#include "mouse-control-worker-registry.h"
namespace ModMenuModule {
class CameraCheat;
enum class MouseControlCheatMode {
Rotate = 0,
PointAt = 2,
@ -41,58 +36,47 @@ namespace ModMenuModule {
void SetOptions(const MouseControlCheatOptions& options);
MouseControlCheatOptions GetOptions() const { return m_options; }
void ShowGamepadControlsToast();
void SetDeltaX(float deltaX) { m_deltaX = deltaX; }
float GetDeltaX() const { return m_deltaX; }
void SetDeltaY(float deltaY) { m_deltaY = deltaY; }
float GetDeltaY() const { return m_deltaY; }
private:
virtual void OnFirstEnable() override;
virtual void OnEnable() override;
virtual void OnDisable() override;
void OnPreGameTick(PreGameTickEvent& event);
void OnMouseLockedMove(MouseModule::MouseLockedMoveEvent& event);
void OnMouseMove(MouseModule::MouseMoveEvent& event);
void OnMouseButtonDown(MouseModule::MouseButtonDownEvent& event);
void OnMouseButtonUp(MouseModule::MouseButtonUpEvent& event);
void OnGameStart(GameStartEvent& event);
void OnGameEnd(GameEndEvent& event);
void OnGamePause(GamePauseEvent& event);
void OnGameUnpause(GameUnpauseEvent& event);
void OnCheatStateChange(CheatStateEvent& event);
void OnCameraCheatOptionsUpdate(CheatOptionsUpdateEvent<CameraCheat>& event);
void SetOptionsInternal(const MouseControlCheatOptions& options);
void UpdateTargetDeltaRotation();
void UpdateMode();
void UpdateLastNormalizedPos();
void UpdateAttack() const;
void UpdateAutoMode();
void UpdateAutoModeListeners();
void RemoveAutoModeListeners();
bool EnsureNotGamepadControls() const;
bool EnsureControlHandlesOk() const;
void StartRotation();
void StopRotation();
void Start();
void Stop();
bool CreateControlHandles();
void FreeControlHandles();
void ApplyAutoMode();
MouseControlCheatMode GetAutoModeTargetMode() const;
void UpdateWorkerSet();
void ClearWorkerSet();
MouseControlWorkerRegistry::WorkerSetType GetAppliableWorkerSetType() const;
void UpdateWorker(std::unique_ptr<MouseControlWorker>& worker, MouseControlWorkerRegistry::WorkerType targetType);
void CreateWorker(std::unique_ptr<MouseControlWorker>& worker, MouseControlWorkerRegistry::WorkerType type);
void RemoveWorker(std::unique_ptr<MouseControlWorker>& worker);
void SendWorkersUpdate();
void SaveToPersistence() const;
void LoadFromPersistence();
static bool CheckShouldUseRotation();
static char GetRotationDirection(float deltaAngle);
static float GetPlayerPedRotation();
static std::optional<float> GetTargetRotation(MouseModule::NormalizedMousePosition normalizedPos);
static MouseControlCheatMode GetAutoModeTargetMode();
static bool ConvertPersistence(std::unique_ptr<uint8_t[]>& dataPtr, size_t& dataSize, uint8_t version);
static MouseControlCheat* m_instance;
MouseControlCheatOptions m_options;
float m_targetDeltaRotation = 0.0f;
MouseModule::NormalizedMousePosition m_lastNormalizedPos = {};
bool m_started = false;
ForceControlsCheat::ControlHandle m_leftControlHandle = -1;
ForceControlsCheat::ControlHandle m_rightControlHandle = -1;
ForceControlsCheat::ControlHandle m_attackControlHandle = -1;
bool m_controlHandlesOk = false;
bool m_usingRotation = false;
MouseControlWorkerRegistry::WorkerSetType m_workerSetType = MouseControlWorkerRegistry::WorkerSetType::None;
std::unique_ptr<MouseControlWorker> m_attackWorker;
std::unique_ptr<MouseControlWorker> m_mouseWorker;
std::unique_ptr<MouseControlWorker> m_resultWorker;
bool m_gamepadControlsToastShown = false;
float m_deltaX = 0.0f;
float m_deltaY = 0.0f;
};
}

View File

@ -0,0 +1,90 @@
#include "point-at-mouse-worker.h"
#include "mouse-control.h"
#include "../camera/camera.h"
#include "../../common.h"
#include "../../utils/angle-utils.h"
void ModMenuModule::PointAtMouseWorker::Start()
{
spdlog::debug("PointAtMouseWorker: Starting");
AddEventListener<MouseModule::MouseMoveEvent>(&PointAtMouseWorker::OnMouseMove);
MouseModule::MouseManager* mouseManager = MouseModule::MouseManager::GetInstance();
mouseManager->SetLocked(false); // ensure
mouseManager->SetCursorVisibility(MouseModule::CursorVisibility::ForceVisible);
m_running = true;
UpdateLastMousePosition();
}
void ModMenuModule::PointAtMouseWorker::Stop()
{
spdlog::debug("PointAtMouseWorker: Stopping");
RemoveEventListener<MouseModule::MouseMoveEvent>();
MouseModule::MouseManager* mouseManager = MouseModule::MouseManager::GetInstance();
mouseManager->SetCursorVisibility(MouseModule::CursorVisibility::Unmodified);
m_running = false;
}
void ModMenuModule::PointAtMouseWorker::Update()
{
float deltaRotation = 0.0f;
float currentRotation = GetPlayerPedRotation();
std::optional<float> targetRotationOpt = GetTargetRotation(m_lastMousePosition);
if (targetRotationOpt.has_value()) {
deltaRotation = Utils::Angle::GetShortestAngleDifference(currentRotation, targetRotationOpt.value());
}
MouseControlCheat* mouseControlCheat = MouseControlCheat::GetInstance();
mouseControlCheat->SetDeltaX(deltaRotation);
}
void ModMenuModule::PointAtMouseWorker::OnMouseMove(MouseModule::MouseMoveEvent& event)
{
MouseModule::MousePosition pos = event.GetPosition();
m_lastMousePosition = MouseModule::MouseManager::ToNormalizedPosition(pos);
}
void ModMenuModule::PointAtMouseWorker::UpdateLastMousePosition()
{
MouseModule::MousePosition currentPos = MouseModule::MouseManager::FetchMouseState().position;
m_lastMousePosition = MouseModule::MouseManager::ToNormalizedPosition(currentPos);
}
std::optional<float> ModMenuModule::PointAtMouseWorker::GetTargetRotation(MouseModule::NormalizedMousePosition normalizedPos)
{
normalizedPos.x -= 0.5f;
normalizedPos.y -= 0.5f;
normalizedPos.x *= -1.0f;
normalizedPos.x *= MouseModule::MouseManager::GetClientAreaAspectRatio();
float targetAngle;
CameraCheat* cameraCheat = CameraCheat::GetInstance();
if (cameraCheat->IsEnabled()) {
CameraCheatOptions cameraOptions = cameraCheat->GetOptions();
normalizedPos.x *= std::cos(cameraOptions.cameraTransform.horizontalAngleRad);
if (normalizedPos.x == 0.0f && normalizedPos.y == 0.0f) return std::nullopt;
targetAngle = std::atan2(normalizedPos.x, normalizedPos.y);
float cameraRotation = cameraCheat->GetOptions().cameraTransform.verticalAngleRad;
targetAngle -= cameraRotation;
}
else {
targetAngle = std::atan2(normalizedPos.x, normalizedPos.y);
}
return Utils::Angle::NormalizeAngle(targetAngle);
}
float ModMenuModule::PointAtMouseWorker::GetPlayerPedRotation()
{
Game::Ped* playerPed = Game::Utils::GetPlayerCurrentPed();
if (!playerPed || !playerPed->gameObject) return 0;
return -Game::Utils::FromGTAAngleToRad(playerPed->gameObject->spriteRotation);
}

View File

@ -0,0 +1,28 @@
#pragma once
#include "mouse-control-worker.h"
#include "../force-controls.h"
namespace ModMenuModule {
class PointAtMouseWorker : public MouseControlWorker, public Core::EventListenerSupport {
public:
PointAtMouseWorker() = default;
PointAtMouseWorker(const PointAtMouseWorker&) = delete;
PointAtMouseWorker& operator=(const PointAtMouseWorker&) = delete;
void Start() override;
void Stop() override;
bool IsRunning() const override { return m_running; }
void Update() override;
private:
void OnMouseMove(MouseModule::MouseMoveEvent& event);
void UpdateLastMousePosition();
static std::optional<float> GetTargetRotation(MouseModule::NormalizedMousePosition normalizedPos);
static float GetPlayerPedRotation();
bool m_running = false;
MouseModule::NormalizedMousePosition m_lastMousePosition{ 0.0f, 0.0f };
};
}