diff --git a/src/Features.hpp b/src/Features.hpp index 527d337c..b88c4701 100644 --- a/src/Features.hpp +++ b/src/Features.hpp @@ -26,3 +26,4 @@ #include "Features/Timer/PauseTimer.hpp" #include "Features/Timer/Timer.hpp" #include "Features/WorkshopList.hpp" +#include "Features/Camera.hpp" diff --git a/src/Features/Camera.cpp b/src/Features/Camera.cpp new file mode 100644 index 00000000..9b1daa47 --- /dev/null +++ b/src/Features/Camera.cpp @@ -0,0 +1,647 @@ +#include "Camera.hpp" + +#include "Modules/Client.hpp" +#include "Modules/Console.hpp" +#include "Modules/Engine.hpp" +#include "Modules/InputSystem.hpp" +#include "Modules/Server.hpp" +#include "Modules/VGui.hpp" + +#include "Features/EntityList.hpp" +#include "Features/Timer/PauseTimer.hpp" + +#include "Command.hpp" +#include "Offsets.hpp" +#include "Utils.hpp" + +Camera* camera; + +Variable sar_cam_control("sar_cam_control", "0", 0, 2, + "sar_cam_control : Change type of camera control.\n" + "0 = Default (camera is controlled by game engine),\n" + "1 = Drive mode (camera is separated and can be controlled by user input),\n" + "2 = Cinematic mode (camera is controlled by predefined path).\n"); + +Variable sar_cam_drive("sar_cam_drive", "1", 0, 1, + "Enables or disables camera drive mode in-game " + "(turning it on is not required for demo player)\n"); + +Variable cl_skip_player_render_in_main_view; +Variable r_drawviewmodel; +Variable ss_force_primary_fullscreen; + +void ResetCameraRelatedCvars() +{ + cl_skip_player_render_in_main_view.SetValue(cl_skip_player_render_in_main_view.GetString()); + r_drawviewmodel.SetValue(r_drawviewmodel.GetString()); + ss_force_primary_fullscreen.SetValue(ss_force_primary_fullscreen.GetString()); + in_forceuser.SetValue(in_forceuser.GetString()); +} + +Camera::Camera() +{ + controlType = Default; + + //a bunch of console variables later used in a janky hack + cl_skip_player_render_in_main_view = Variable("cl_skip_player_render_in_main_view"); + r_drawviewmodel = Variable("r_drawviewmodel"); + ss_force_primary_fullscreen = Variable("ss_force_primary_fullscreen"); +} + +Camera::~Camera() +{ + camera->states.clear(); + ResetCameraRelatedCvars(); +} + +//if in drive mode, checks if player wants to control the camera +//for now it requires LMB input (as in demo drive mode) +bool Camera::IsDriving() +{ + bool drivingInGame = sar_cam_drive.GetBool() && sv_cheats.GetBool() && engine->hoststate->m_activeGame; + bool drivingInDemo = engine->demoplayer->IsPlaying(); + bool wantingToDrive = inputSystem->IsKeyDown(ButtonCode_t::MOUSE_LEFT); + bool isUI = vgui->IsUIVisible(); + + return camera->controlType == Drive && wantingToDrive && (drivingInGame || drivingInDemo) && !isUI; +} + +//used by camera state interpolation function. all the math is here. +float InterpolateCurve(std::vector points, float x, bool dealingWithAngles = false) +{ + enum { FIRST, + PREV, + NEXT, + LAST }; + + //fixing Y values in case they're angles, for proper interpolation + if (dealingWithAngles) { + float oldY = 0; + for (int i = 0; i < 4; i++) { + float angDif = points[i].y - oldY; + angDif += (angDif > 180) ? -360 : (angDif < -180) ? 360 : 0; + points[i].y = oldY += angDif; + oldY = points[i].y; + } + } + + if (x <= points[PREV].x) + return points[PREV].y; + if (x >= points[NEXT].x) + return points[NEXT].y; + + float t = (x - points[PREV].x) / (points[NEXT].x - points[PREV].x); + + //linear interp. in case you dont want anything cool + //return points[PREV].y + (points[NEXT].y - points[PREV].y) * t; + + //cubic hermite spline... kind of? maybe? no fucking clue, just leave me alone + float t2 = t * t, t3 = t * t * t; + float x0 = (points[FIRST].x - points[PREV].x) / (points[NEXT].x - points[PREV].x); + float x1 = 0, x2 = 1; + float x3 = (points[LAST].x - points[PREV].x) / (points[NEXT].x - points[PREV].x); + float m1 = ((points[NEXT].y - points[PREV].y) / (x2 - x1) + (points[PREV].y - points[FIRST].y) / (x1 - x0)) / 2; + float m2 = ((points[LAST].y - points[NEXT].y) / (x3 - x2) + (points[NEXT].y - points[PREV].y) / (x2 - x1)) / 2; + + return (2 * t3 - 3 * t2 + 1) * points[PREV].y + (t3 - 2 * t2 + t) * m1 + (-2 * t3 + 3 * t2) * points[NEXT].y + (t3 - t2) * m2; +} + +//creates vector array for specified parameter. it can probably be done in much more elegant way +std::vector CameraStatesToInterpolationPoints(float* x, CameraState* y, CameraStateParameter param) +{ + std::vector points; + for (int i = 0; i < 4; i++) { + Vector v; + v.x = x[i]; + CameraState cs = y[i]; + switch (param) { + case ORIGIN_X: + v.y = cs.origin.x; + break; + case ORIGIN_Y: + v.y = cs.origin.y; + break; + case ORIGIN_Z: + v.y = cs.origin.z; + break; + case ANGLES_X: + v.y = cs.angles.x; + break; + case ANGLES_Y: + v.y = cs.angles.y; + break; + case ANGLES_Z: + v.y = cs.angles.z; + break; + case FOV: + v.y = cs.fov; + break; + } + points.push_back(v); + } + return points; +} + +//Creates interpolated camera state based on states array and given time. +//This is the closest I could get to valve's demo spline camera path +CameraState Camera::InterpolateStates(float time) +{ + enum { FIRST, + PREV, + NEXT, + LAST }; + + //reading 4 frames closest to time + float frameTime = time * 60.0f; + int frames[4] = { INT_MIN, INT_MIN, INT_MAX, INT_MAX }; + for (auto const& state : camera->states) { + int stateTime = state.first; + if (frameTime - stateTime >= 0 && frameTime - stateTime < frameTime - frames[PREV]) { + frames[FIRST] = frames[PREV]; + frames[PREV] = stateTime; + } + if (stateTime - frameTime >= 0 && stateTime - frameTime < frames[NEXT] - frameTime) { + frames[LAST] = frames[NEXT]; + frames[NEXT] = stateTime; + } + if (stateTime > frames[FIRST] && stateTime < frames[PREV]) { + frames[FIRST] = stateTime; + } + if (stateTime > frames[NEXT] && stateTime < frames[LAST]) { + frames[LAST] = stateTime; + } + } + + //x values for interpolation + float x[4]; + for (int i = 0; i < 4; i++) { + x[i] = (float)frames[i]; + } + + //making sure all X values are correct before reading Y values, + //"frames" fixed for read, "x" fixed for interpolation. + //if there's at least one cam state in the map, none of these should be MIN or MAX after this. + if (frames[PREV] == INT_MIN) { + x[PREV] = (float)frames[NEXT]; + frames[PREV] = frames[NEXT]; + } + if (frames[NEXT] == INT_MAX) { + x[NEXT] = (float)frames[PREV]; + frames[NEXT] = frames[PREV]; + } + if (frames[FIRST] == INT_MIN) { + x[FIRST] = (float)(2 * frames[PREV] - frames[NEXT]); + frames[FIRST] = frames[PREV]; + } + if (frames[LAST] == INT_MAX) { + x[LAST] = (float)(2 * frames[NEXT] - frames[PREV]); + frames[LAST] = frames[NEXT]; + } + + //filling Y values + CameraState y[4]; + for (int i = 0; i < 4; i++) { + y[i] = camera->states[frames[i]]; + } + + //interpolating each parameter + CameraState interp; + interp.origin.x = InterpolateCurve(CameraStatesToInterpolationPoints(x, y, ORIGIN_X), frameTime); + interp.origin.y = InterpolateCurve(CameraStatesToInterpolationPoints(x, y, ORIGIN_Y), frameTime); + interp.origin.z = InterpolateCurve(CameraStatesToInterpolationPoints(x, y, ORIGIN_Z), frameTime); + interp.angles.x = InterpolateCurve(CameraStatesToInterpolationPoints(x, y, ANGLES_X), frameTime, true); + interp.angles.y = InterpolateCurve(CameraStatesToInterpolationPoints(x, y, ANGLES_Y), frameTime, true); + interp.angles.z = InterpolateCurve(CameraStatesToInterpolationPoints(x, y, ANGLES_Z), frameTime, true); + interp.fov = InterpolateCurve(CameraStatesToInterpolationPoints(x, y, FOV), frameTime); + + return interp; +} + +//Overrides view. +void Camera::OverrideView(CPortalViewSetup1* m_View) +{ + if (timeOffsetRefreshRequested) { + timeOffset = engine->GetClientTime() - engine->demoplayer->GetTick() / 60.0f; + timeOffsetRefreshRequested = false; + } + + auto newControlType = static_cast(sar_cam_control.GetInt()); + + //don't allow cinematic mode outside of demo player + if (!engine->demoplayer->IsPlaying() && newControlType == Cinematic) { + if (controlType != Cinematic) { + console->Print("Cinematic mode cannot be used outside of demo player.\n"); + } else { + controlType = Default; + ResetCameraRelatedCvars(); + } + newControlType = controlType; + sar_cam_control.SetValue(controlType); + } + + //don't allow drive mode when not using sv_cheats + if (newControlType == Drive && !sv_cheats.GetBool() && !engine->demoplayer->IsPlaying()) { + if (controlType != Drive) { + console->Print("Drive mode requires sv_cheats 1 or demo player.\n"); + } else { + controlType = Default; + ResetCameraRelatedCvars(); + } + newControlType = controlType; + sar_cam_control.SetValue(controlType); + } + + //janky hack mate + //overriding cvar values, boolean (int) values only. + //this way the cvar themselves are unchanged + if (newControlType != Default) { + cl_skip_player_render_in_main_view.ThisPtr()->m_nValue = 0; + r_drawviewmodel.ThisPtr()->m_nValue = 0; + ss_force_primary_fullscreen.ThisPtr()->m_nValue = 1; + } + + //handling camera control type switching + if (newControlType != controlType) { + if (controlType == Default && newControlType != Default) { + //enabling + //sorry nothing + } else if (controlType != Default && newControlType == Default) { + //disabling + //resetting cvars to their actual values when swithing control off + ResetCameraRelatedCvars(); + this->manualActive = false; + } + controlType = newControlType; + } + + //don't do anything if not in game or demo player + if (engine->hoststate->m_activeGame || engine->demoplayer->IsPlaying()) { + if (controlType == Default || cameraRefreshRequested) { + currentState.origin = m_View->origin; + currentState.angles = m_View->angles; + currentState.fov = m_View->fov; + if (cameraRefreshRequested) + cameraRefreshRequested = false; + } + if (controlType != Default) { + //manual camera view control + if (controlType == Drive) { + if (IsDriving()) { + if (!manualActive) { + inputSystem->GetCursorPos(mouseHoldPos[0], mouseHoldPos[1]); + manualActive = true; + } + } else { + if (manualActive) { + in_forceuser.SetValue(in_forceuser.GetString()); + manualActive = false; + } + } + if (manualActive) { + //even junkier hack. lock mouse movement using fake in_forceuser 2 LMAO + if (engine->hoststate->m_activeGame) + in_forceuser.ThisPtr()->m_nValue = 2; + + //getting mouse movement and resetting the cursor position + int mX, mY; + inputSystem->GetCursorPos(mX, mY); + mX -= mouseHoldPos[0]; + mY -= mouseHoldPos[1]; + inputSystem->SetCursorPos(mouseHoldPos[0], mouseHoldPos[1]); + + if (inputSystem->IsKeyDown(ButtonCode_t::MOUSE_RIGHT)) { + //allow fov manipulation + currentState.fov = fminf(fmaxf(currentState.fov + mY * 0.22f, 0.1f), 179.0f); + currentState.angles.z += (float)mX * 0.22f; + } else { + currentState.angles.x += (float)mY * 0.22f; + currentState.angles.y -= (float)mX * 0.22f; + } + + //limit both values between -180 and 180 + currentState.angles.x = remainderf(currentState.angles.x, 360.0f); + currentState.angles.y = remainderf(currentState.angles.y, 360.0f); + + //calculating vectors out of angles for movement + Vector forward, right; + float cp, sp, cy, sy, cr, sr; + cp = cosf(DEG2RAD(currentState.angles.x)); + sp = sinf(DEG2RAD(currentState.angles.x)); + cy = cosf(DEG2RAD(currentState.angles.y)); + sy = sinf(DEG2RAD(currentState.angles.y)); + cr = cosf(DEG2RAD(currentState.angles.z)); + sr = sinf(DEG2RAD(currentState.angles.z)); + + forward.x = cp * cy; + forward.y = cp * sy; + forward.z = -sp; + + right.x = (-1 * sr * sp * cy + -1 * cr * -sy); + right.y = (-1 * sr * sp * sy + -1 * cr * cy); + right.z = -1 * sr * cp; + + //applying movement + bool shiftdown = inputSystem->IsKeyDown(KEY_LSHIFT) || inputSystem->IsKeyDown(KEY_RSHIFT); + bool controldown = inputSystem->IsKeyDown(KEY_LCONTROL) || inputSystem->IsKeyDown(KEY_RCONTROL); + float speed = shiftdown ? 525.0f : (controldown ? 60.0f : 175.0f); + speed *= engine->GetHostFrameTime(); + + if (inputSystem->IsKeyDown(ButtonCode_t::KEY_W)) { + currentState.origin = currentState.origin + (forward * speed); + } + if (inputSystem->IsKeyDown(ButtonCode_t::KEY_S)) { + currentState.origin = currentState.origin + (forward * -speed); + } + if (inputSystem->IsKeyDown(ButtonCode_t::KEY_A)) { + currentState.origin = currentState.origin + (right * -speed); + } + if (inputSystem->IsKeyDown(ButtonCode_t::KEY_D)) { + currentState.origin = currentState.origin + (right * speed); + } + } + } + //cinematic camera - move it along predefined path + if (controlType == Cinematic) { + //don't do interpolation when there are no points + if (states.size() > 0) { + float currentTime = engine->GetClientTime() - timeOffset; + currentState = InterpolateStates(currentTime); + } + } + //applying custom view + m_View->origin = currentState.origin; + m_View->angles = currentState.angles; + m_View->fov = currentState.fov; + } + } +} + +void Camera::RequestTimeOffsetRefresh() +{ + timeOffsetRefreshRequested = true; +} + +void Camera::RequestCameraRefresh() +{ + cameraRefreshRequested = true; +} + +void Camera::OverrideMovement(CUserCmd* cmd) +{ + //blocking keyboard inputs on manual mode + if (IsDriving()) { + cmd->buttons = 0; + cmd->forwardmove = 0; + cmd->sidemove = 0; + cmd->upmove = 0; + } +} + +//COMMANDS + +DECL_COMMAND_COMPLETION(sar_cam_path_setkf) +{ + for (auto const& state : camera->states) { + if (items.size() == COMMAND_COMPLETION_MAXITEMS) { + break; + } + + char camString[512] = {}; + CameraState cam = state.second; + sprintf(camString, "%d %.0f %.0f %.0f %.0f %.0f %.0f %.0f", + state.first, cam.origin.x, cam.origin.y, cam.origin.z, + cam.angles.x, cam.angles.y, cam.angles.z, cam.fov); + + camString[COMMAND_COMPLETION_ITEM_LENGTH - 1 - 19] = '\0'; + std::string camStringString(&camString[0], COMMAND_COMPLETION_ITEM_LENGTH - 19); + + if (std::strlen(match) != std::strlen(cmd)) { + if (std::strstr(camStringString.c_str(), match)) { + items.push_back(camStringString); + } + } else { + items.push_back(camStringString); + } + } + + FINISH_COMMAND_COMPLETION(); +} + +CON_COMMAND_F_COMPLETION( + sar_cam_path_setkf, + "sar_cam_path_setkf [frame] [x] [y] [z] [yaw] [pitch] [roll] [fov]: Sets the camera path keyframe.\n", + 0, AUTOCOMPLETION_FUNCTION(sar_cam_path_setkf)) +{ + if (!engine->demoplayer->IsPlaying()) + return console->Print("Cinematic mode cannot be used outside of demo player.\n"); + + if (args.ArgC() >= 1 && args.ArgC() <= 9) { + CameraState campos = camera->currentState; + int curFrame = engine->demoplayer->GetTick(); + if (args.ArgC() > 1) { + curFrame = std::atoi(args[1]); + } + if (args.ArgC() > 2) { + float nums[7] = { + campos.origin.x, campos.origin.x, campos.origin.x, + campos.angles.x, campos.angles.x, campos.angles.x, + campos.fov + }; + for (int i = 0; i < args.ArgC() - 2; i++) { + nums[i] = std::stof(args[i + 2]); + } + campos.origin.x = nums[0]; + campos.origin.y = nums[1]; + campos.origin.z = nums[2]; + campos.angles.x = nums[3]; + campos.angles.y = nums[4]; + campos.angles.z = nums[5]; + campos.fov = nums[6]; + } + camera->states[curFrame] = campos; + console->Print("Camera key frame %d created: ", curFrame); + console->Print(std::string(campos).c_str()); + console->Print("\n"); + } else { + return console->Print(sar_cam_path_setkf.ThisPtr()->m_pszHelpString); + } +} + +DECL_COMMAND_COMPLETION(sar_cam_path_showkf) +{ + for (auto const& state : camera->states) { + if (items.size() == COMMAND_COMPLETION_MAXITEMS) { + break; + } + + char camString[64] = {}; + CameraState cam = state.second; + sprintf(camString, "%d", state.first); + + std::string camStringString = camString; + + if (std::strlen(match) != std::strlen(cmd)) { + if (std::strstr(camStringString.c_str(), match)) { + items.push_back(camStringString); + } + } else { + items.push_back(camString); + } + } + + FINISH_COMMAND_COMPLETION(); +} + +CON_COMMAND_F_COMPLETION( + sar_cam_path_showkf, + "sar_cam_path_showkf [frame] : Display information about camera path keyframe at specified frame.\n", + 0, AUTOCOMPLETION_FUNCTION(sar_cam_path_showkf)) +{ + if (!engine->demoplayer->IsPlaying()) + return console->Print("Cinematic mode cannot be used outside of demo player.\n"); + + if (args.ArgC() == 2) { + int i = std::atoi(args[1]); + if (camera->states.count(i)) { + console->Print(std::string(camera->states[i]).c_str()); + console->Print("\n"); + } else { + console->Print("This keyframe does not exist.\n"); + } + } else { + return console->Print(sar_cam_path_showkf.ThisPtr()->m_pszHelpString); + } +} + +CON_COMMAND(sar_cam_path_showkfs, "sar_cam_path_showkfs : Display information about all camera path keyframes.\n") +{ + if (!engine->demoplayer->IsPlaying()) + return console->Print("Cinematic mode cannot be used outside of demo player.\n"); + + if (args.ArgC() == 1) { + for (auto const& state : camera->states) { + console->Print("%d: ", state.first); + console->Print(std::string(state.second).c_str()); + console->Print("\n"); + } + } else { + return console->Print(sar_cam_path_showkfs.ThisPtr()->m_pszHelpString); + } +} + +CON_COMMAND(sar_cam_path_getkfs, "sar_cam_path_getkfs : Exports commands for recreating currently made camera path.\n") +{ + if (!engine->demoplayer->IsPlaying()) + return console->Print("Cinematic mode cannot be used outside of demo player.\n"); + + if (args.ArgC() == 1) { + for (auto const& state : camera->states) { + CameraState cam = state.second; + console->Print("sar_cam_path_setkf %d %f %f %f %f %f %f %f;\n", + state.first, cam.origin.x, cam.origin.y, cam.origin.z, + cam.angles.x, cam.angles.y, cam.angles.z, cam.fov); + } + } else { + return console->Print(sar_cam_path_getkfs.ThisPtr()->m_pszHelpString); + } +} + +CON_COMMAND_F_COMPLETION( + sar_cam_path_remkf, + "sar_cam_path_remkf [frame] : Removes camera path keyframe at specified frame.\n", + 0, AUTOCOMPLETION_FUNCTION(sar_cam_path_showkf)) +{ + if (!engine->demoplayer->IsPlaying()) + return console->Print("Cinematic mode cannot be used outside of demo player.\n"); + + if (args.ArgC() == 2) { + int i = std::atoi(args[1]); + if (camera->states.count(i)) { + camera->states.erase(i); + console->Print("Camera path keyframe at frame %d removed.\n", i); + } else { + console->Print("This keyframe does not exist.\n"); + } + } else { + return console->Print(sar_cam_path_remkf.ThisPtr()->m_pszHelpString); + } +} + +CON_COMMAND(sar_cam_path_remkfs, "sar_cam_path_remkfs : Removes all camera path keyframes.\n") +{ + if (!engine->demoplayer->IsPlaying()) + return console->Print("Cinematic mode cannot be used outside of demo player.\n"); + + if (args.ArgC() == 1) { + camera->states.clear(); + console->Print("All camera path keyframes have been removed.\n"); + } else { + return console->Print(sar_cam_path_remkfs.ThisPtr()->m_pszHelpString); + } +} + +CON_COMMAND(sar_cam_setang, "sar_cam_setang [roll] : Sets camera angle (requires camera Drive Mode).\n") +{ + if (camera->controlType != Drive) { + console->Print("Camera not in drive mode! Switching.\n"); + sar_cam_control.SetValue(CameraControlType::Drive); + } + + if (args.ArgC() == 3 || args.ArgC() == 4) { + float angles[3] = { 0, 0, 0 }; + for (int i = 0; i < args.ArgC() - 1; i++) { + angles[i] = std::stof(args[i + 1]); + } + camera->currentState.angles.x = angles[0]; + camera->currentState.angles.y = angles[1]; + camera->currentState.angles.z = angles[2]; + } else { + return console->Print(sar_cam_setang.ThisPtr()->m_pszHelpString); + } +} + +CON_COMMAND(sar_cam_setpos, "sar_cam_setpos : Sets camera position (requires camera Drive Mode).\n") +{ + if (camera->controlType != Drive) { + console->Print("Camera not in drive mode! Switching.\n"); + sar_cam_control.SetValue(CameraControlType::Drive); + } + + if (args.ArgC() == 4) { + float pos[3] = { 0, 0, 0 }; + for (int i = 0; i < args.ArgC() - 1; i++) { + pos[i] = std::stof(args[i + 1]); + } + camera->currentState.origin.x = pos[0]; + camera->currentState.origin.y = pos[1]; + camera->currentState.origin.z = pos[2]; + } else { + return console->Print(sar_cam_setpos.ThisPtr()->m_pszHelpString); + } +} + +CON_COMMAND(sar_cam_setfov, "sar_cam_setfov : Sets camera field of view (requires camera Drive Mode).\n") +{ + if (camera->controlType != Drive) { + console->Print("Camera not in drive mode! Switching.\n"); + sar_cam_control.SetValue(CameraControlType::Drive); + } + + if (args.ArgC() == 2) { + float fov = (float)std::atof(args[1]); + camera->currentState.fov = fov; + } else { + return console->Print(sar_cam_setfov.ThisPtr()->m_pszHelpString); + } +} + +CON_COMMAND(sar_cam_reset, "sar_cam_reset: Resets camera to its default position.\n") +{ + if (args.ArgC() == 1) { + if (camera->controlType == Drive) { + camera->RequestCameraRefresh(); + } + } else { + return console->Print(sar_cam_reset.ThisPtr()->m_pszHelpString); + } +} \ No newline at end of file diff --git a/src/Features/Camera.hpp b/src/Features/Camera.hpp new file mode 100644 index 00000000..830b0db3 --- /dev/null +++ b/src/Features/Camera.hpp @@ -0,0 +1,72 @@ +#pragma once +#include +#include + +#include "Feature.hpp" + +#include "Utils/SDK.hpp" +#include "Variable.hpp" +#include "Modules/Engine.hpp" + + +extern Variable sar_cam_control; +extern Variable sar_cam_drive; + +extern Variable cl_skip_player_render_in_main_view; +extern Variable r_drawviewmodel; +extern Variable ss_force_primary_fullscreen; +extern Variable crosshair; + +struct CameraState { + Vector origin = Vector(); + QAngle angles = QAngle(); + float fov = 90; + operator std::string() const{ + std::ostringstream s; + s << "pos: " << origin.x << " " << origin.y << " " << origin.z << "; "; + s << "rot: " << angles.x << " " << angles.y << " " << angles.z << "; "; + s << "fov: " << fov; + return s.str(); + } +}; + +enum CameraStateParameter { + ORIGIN_X, + ORIGIN_Y, + ORIGIN_Z, + ANGLES_X, + ANGLES_Y, + ANGLES_Z, + FOV +}; + +enum CameraControlType { + Default, + Drive, + Cinematic, +}; + +class Camera : public Feature { +private: + bool manualActive = false; + bool cameraRefreshRequested = false; + bool timeOffsetRefreshRequested = true; + int mouseHoldPos[2] = {0,0}; + float timeOffset = 0.0; +public: + CameraControlType controlType = Default; + CameraState currentState; + std::map states; + Camera(); + ~Camera(); + bool IsDriving(); + void OverrideView(CPortalViewSetup1* m_View); + CameraState InterpolateStates(float time); + void RequestTimeOffsetRefresh(); + void RequestCameraRefresh(); + void OverrideMovement(CUserCmd* cmd); +}; + +extern Camera* camera; + +extern DECL_DECLARE_AUTOCOMPLETION_FUNCTION(sar_cam_path_setkf); \ No newline at end of file diff --git a/src/Games/Linux/Portal2.cpp b/src/Games/Linux/Portal2.cpp index 11a30db2..4e619c8c 100644 --- a/src/Games/Linux/Portal2.cpp +++ b/src/Games/Linux/Portal2.cpp @@ -36,6 +36,9 @@ void Portal2::LoadOffsets() GetSteamAPIContext = 178; // CEngineClient StringToButtonCode = 31; // CInputSystem SleepUntilInput = 33; // CInputSystem + IsButtonDown = 14; //CInputSystem + GetCursorPosition = 45; //CInputSystem + SetCursorPosition = 38; //CInputSystem GetRecordingTick = 1; // CDemoRecorder net_time = 28; // CDemoRecorder::GetRecordingTick SetSignonState = 3; // CDemoRecorder @@ -47,6 +50,7 @@ void Portal2::LoadOffsets() m_szDemoBaseName = 1344; // CDemoRecorder::StartupDemoFile m_nDemoNumber = 1608; // CDemoRecorder::StartupDemoFile m_bRecording = 1606; // CDemoRecorder::SetSignonState + IsGameUIVisible = 2; // IEngineGui Paint = 15; // CEngineVGui ProcessTick = 12; // CClientState tickcount = 73; // CClientState::ProcessTick @@ -57,6 +61,8 @@ void Portal2::LoadOffsets() demoplayer = 93; // CClientState::Disconnect demorecorder = 106; // CClientState::Disconnect GetCurrentMap = 26; // CEngineTool + HostFrameTime = 40; //CEngineTool + ClientTime = 48; //CEngineTool m_szLevelName = 72; // CEngineTool::GetCurrentMap AddListener = 4; // CGameEventManager RemoveListener = 6; // CGameEventManager @@ -146,6 +152,7 @@ void Portal2::LoadOffsets() JoyStickApplyMovement = 64; // CInput KeyDown = 295; // CInput::JoyStickApplyMovement KeyUp = 341; // CInput::JoyStickApplyMovement + OverrideView = 19; // ClientModeShared // vguimatsurface.so diff --git a/src/Games/Windows/Portal2.cpp b/src/Games/Windows/Portal2.cpp index 4f8adc66..472d1d1b 100644 --- a/src/Games/Windows/Portal2.cpp +++ b/src/Games/Windows/Portal2.cpp @@ -36,6 +36,9 @@ void Portal2::LoadOffsets() GetSteamAPIContext = 177; // CEngineClient StringToButtonCode = 31; // CInputSystem SleepUntilInput = 33; // CInputSystem + IsButtonDown = 14; //CInputSystem + GetCursorPosition = 45; //CInputSystem + SetCursorPosition = 38; //CInputSystem GetRecordingTick = 1; // CDemoRecorder net_time = 19; // CDemoRecorder::GetRecordingTick SetSignonState = 3; // CDemoRecorder @@ -47,6 +50,7 @@ void Portal2::LoadOffsets() m_szDemoBaseName = 1344; // CDemoRecorder::StartupDemoFile m_nDemoNumber = 1608; // CDemoRecorder::StartupDemoFile m_bRecording = 1606; // CDemoRecorder::SetSignonState + IsGameUIVisible = 2; //IEngineGui Paint = 14; // CEngineVGui ProcessTick = 1; // CClientState/IServerMessageHandler tickcount = 95; // CClientState::ProcessTick @@ -57,6 +61,8 @@ void Portal2::LoadOffsets() demoplayer = 74; // CClientState::Disconnect demorecorder = 87; // CClientState::Disconnect GetCurrentMap = 25; // CEngineTool + HostFrameTime = 39; //CEngineTool + ClientTime = 47; //CEngineTool m_szLevelName = 36; // CEngineTool::GetCurrentMap AddListener = 3; // CGameEventManager RemoveListener = 5; // CGameEventManager @@ -145,6 +151,7 @@ void Portal2::LoadOffsets() JoyStickApplyMovement = 64; // CInput KeyDown = 398; // CInput::JoyStickApplyMovement KeyUp = 377; // CInput::JoyStickApplyMovement + OverrideView = 18; // ClientModeShared // vguimatsurface.dll diff --git a/src/Modules/Client.cpp b/src/Modules/Client.cpp index 0c8efbc0..9b0f0c00 100644 --- a/src/Modules/Client.cpp +++ b/src/Modules/Client.cpp @@ -12,6 +12,7 @@ #include "Features/Session.hpp" #include "Features/Tas/AutoStrafer.hpp" #include "Features/Tas/CommandQueuer.hpp" +#include "Features/Camera.hpp" #include "Console.hpp" #include "Engine.hpp" @@ -38,6 +39,7 @@ REDECL(Client::DecodeUserCmdFromBuffer2); REDECL(Client::CInput_CreateMove); REDECL(Client::GetButtonBits); REDECL(Client::playvideo_end_level_transition_callback); +REDECL(Client::OverrideView); MDECL(Client::GetAbsOrigin, Vector, C_m_vecAbsOrigin); MDECL(Client::GetAbsAngles, QAngle, C_m_angAbsRotation); @@ -118,6 +120,11 @@ DETOUR(Client::CreateMove, float flInputSampleTime, CUserCmd* cmd) inputHud.SetButtonBits(0, cmd->buttons); } + if (sv_cheats.GetBool() && engine->hoststate->m_activeGame) { + camera->OverrideMovement(cmd); + } + + return Client::CreateMove(thisptr, flInputSampleTime, cmd); } DETOUR(Client::CreateMove2, float flInputSampleTime, CUserCmd* cmd) @@ -211,6 +218,12 @@ DETOUR_COMMAND(Client::playvideo_end_level_transition) return Client::playvideo_end_level_transition_callback(args); } +DETOUR(Client::OverrideView, CPortalViewSetup1* m_View) +{ + camera->OverrideView(m_View); + return Client::OverrideView(thisptr, m_View); +} + bool Client::Init() { bool readJmp = false; @@ -291,6 +304,9 @@ bool Client::Init() if (this->g_pClientMode = Interface::Create(clientMode)) { this->g_pClientMode->Hook(Client::CreateMove_Hook, Client::CreateMove, Offsets::CreateMove); + if (sar.game->Is(SourceGame_Portal2Engine)) { + this->g_pClientMode->Hook(Client::OverrideView_Hook, Client::OverrideView, Offsets::OverrideView); + } } if (this->g_pClientMode2 = Interface::Create(clientMode2)) { diff --git a/src/Modules/Client.hpp b/src/Modules/Client.hpp index 27b662d9..74ab077e 100644 --- a/src/Modules/Client.hpp +++ b/src/Modules/Client.hpp @@ -60,6 +60,9 @@ class Client : public Module { // CInput::GetButtonBits DECL_DETOUR(GetButtonBits, bool bResetState); + // ClientModeShared::OverrideView + DECL_DETOUR(OverrideView, CPortalViewSetup1* m_View); + DECL_DETOUR_COMMAND(playvideo_end_level_transition); bool Init() override; diff --git a/src/Modules/Engine.cpp b/src/Modules/Engine.cpp index 1ecc4eee..cd43eb4e 100644 --- a/src/Modules/Engine.cpp +++ b/src/Modules/Engine.cpp @@ -128,6 +128,16 @@ void Engine::SafeUnload(const char* postCommand) } } +float Engine::GetHostFrameTime() +{ + return this->HostFrameTime(this->engineTool->ThisPtr()); +} + +float Engine::GetClientTime() +{ + return this->ClientTime(this->engineTool->ThisPtr()); +} + // CClientState::Disconnect DETOUR(Engine::Disconnect, bool bShowMainMenu) { @@ -356,8 +366,8 @@ bool Engine::Init() } } - if (auto tool = Interface::Create(this->Name(), "VENGINETOOL0", false)) { - auto GetCurrentMap = tool->Original(Offsets::GetCurrentMap); + if (this->engineTool = Interface::Create(this->Name(), "VENGINETOOL0", false)) { + auto GetCurrentMap = this->engineTool->Original(Offsets::GetCurrentMap); this->m_szLevelName = Memory::Deref(GetCurrentMap + Offsets::m_szLevelName); if (sar.game->Is(SourceGame_HalfLife2Engine) && std::strlen(this->m_szLevelName) != 0) { @@ -366,7 +376,9 @@ bool Engine::Init() } this->m_bLoadgame = reinterpret_cast((uintptr_t)this->m_szLevelName + Offsets::m_bLoadGame); - Interface::Delete(tool); + + this->HostFrameTime = this->engineTool->Original<_HostFrameTime>(Offsets::HostFrameTime); + this->ClientTime = this->engineTool->Original<_ClientTime>(Offsets::ClientTime); } if (auto s_EngineAPI = Interface::Create(this->Name(), "VENGINE_LAUNCHER_API_VERSION0", false)) { @@ -448,6 +460,7 @@ void Engine::Shutdown() Interface::Delete(this->cl); Interface::Delete(this->eng); Interface::Delete(this->s_GameEventManager); + Interface::Delete(this->engineTool); #ifdef _WIN32 Command::Unhook("connect", Engine::connect_callback); diff --git a/src/Modules/Engine.hpp b/src/Modules/Engine.hpp index 1e182613..440388f9 100644 --- a/src/Modules/Engine.hpp +++ b/src/Modules/Engine.hpp @@ -21,6 +21,7 @@ class Engine : public Module { Interface* eng = nullptr; Interface* debugoverlay = nullptr; Interface* s_ServerPlugin = nullptr; + Interface* engineTool = nullptr; using _ClientCmd = int(__rescall*)(void* thisptr, const char* szCmdString); using _GetLocalPlayer = int(__rescall*)(void* thisptr); @@ -34,6 +35,8 @@ class Engine : public Module { using _AddText = void(__rescall*)(void* thisptr, const char* pText, int nTickDelay); using _ClientCommand = int (*)(void* thisptr, void* pEdict, const char* szFmt, ...); using _GetLocalClient = int (*)(int index); + using _HostFrameTime = float (*)(void* thisptr); + using _ClientTime = float (*)(void* thisptr); #ifdef _WIN32 using _GetScreenSize = int(__stdcall*)(int& width, int& height); using _GetActiveSplitScreenPlayerSlot = int (*)(); @@ -62,6 +65,8 @@ class Engine : public Module { _ConPrintEvent ConPrintEvent = nullptr; _ClientCommand ClientCommand = nullptr; _GetLocalClient GetLocalClient = nullptr; + _HostFrameTime HostFrameTime = nullptr; + _ClientTime ClientTime = nullptr; EngineDemoPlayer* demoplayer = nullptr; EngineDemoRecorder* demorecorder = nullptr; @@ -89,6 +94,8 @@ class Engine : public Module { void SendToCommandBuffer(const char* text, int delay); int PointToScreen(const Vector& point, Vector& screen); void SafeUnload(const char* postCommand = nullptr); + float GetHostFrameTime(); + float GetClientTime(); // CClientState::Disconnect DECL_DETOUR(Disconnect, bool bShowMainMenu); diff --git a/src/Modules/EngineDemoPlayer.cpp b/src/Modules/EngineDemoPlayer.cpp index b411675c..cd3a5d00 100644 --- a/src/Modules/EngineDemoPlayer.cpp +++ b/src/Modules/EngineDemoPlayer.cpp @@ -2,6 +2,7 @@ #include "Features/Demo/Demo.hpp" #include "Features/Demo/DemoParser.hpp" +#include "Features/Camera.hpp" #include "Console.hpp" #include "Engine.hpp" @@ -43,6 +44,9 @@ DETOUR(EngineDemoPlayer::StartPlayback, const char* filename, bool bAsTimeDemo) console->Print("Could not parse \"%s\"!\n", engine->demoplayer->DemoName); } } + + camera->RequestTimeOffsetRefresh(); + return result; } diff --git a/src/Modules/InputSystem.cpp b/src/Modules/InputSystem.cpp index 5a820ec5..6af303f7 100644 --- a/src/Modules/InputSystem.cpp +++ b/src/Modules/InputSystem.cpp @@ -15,6 +15,21 @@ int InputSystem::GetButton(const char* pString) return this->StringToButtonCode(this->g_InputSystem->ThisPtr(), pString); } +bool InputSystem::IsKeyDown(ButtonCode_t key) +{ + return this->IsButtonDown(this->g_InputSystem->ThisPtr(), key); +} + +void InputSystem::GetCursorPos(int& x, int& y) +{ + return this->GetCursorPosition(this->g_InputSystem->ThisPtr(), x, y); +} + +void InputSystem::SetCursorPos(int x, int y) +{ + return this->SetCursorPosition(this->g_InputSystem->ThisPtr(), x, y); +} + // CInputSystem::SleepUntilInput DETOUR(InputSystem::SleepUntilInput, int nMaxSleepTimeMS) { @@ -33,6 +48,9 @@ bool InputSystem::Init() if (sar.game->Is(SourceGame_Portal2Engine)) { this->g_InputSystem->Hook(InputSystem::SleepUntilInput_Hook, InputSystem::SleepUntilInput, Offsets::SleepUntilInput); + this->IsButtonDown = this->g_InputSystem->Original<_IsButtonDown>(Offsets::IsButtonDown); + this->GetCursorPosition = this->g_InputSystem->Original<_GetCursorPosition>(Offsets::GetCursorPosition); + this->SetCursorPosition = this->g_InputSystem->Original<_SetCursorPosition>(Offsets::SetCursorPosition); } } diff --git a/src/Modules/InputSystem.hpp b/src/Modules/InputSystem.hpp index f83bff1c..291f4a7a 100644 --- a/src/Modules/InputSystem.hpp +++ b/src/Modules/InputSystem.hpp @@ -6,6 +6,211 @@ #include "Offsets.hpp" #include "Utils.hpp" +enum { + JOYSTICK_MAX_BUTTON_COUNT = 32, + JOYSTICK_POV_BUTTON_COUNT = 4, + JOYSTICK_AXIS_BUTTON_COUNT = MAX_JOYSTICK_AXES * 2, +}; + +#define MAX_JOYSTICKS 1 +#define JOYSTICK_BUTTON_INTERNAL(_joystick, _button) (JOYSTICK_FIRST_BUTTON + ((_joystick)*JOYSTICK_MAX_BUTTON_COUNT) + (_button)) +#define JOYSTICK_POV_BUTTON_INTERNAL(_joystick, _button) (JOYSTICK_FIRST_POV_BUTTON + ((_joystick)*JOYSTICK_POV_BUTTON_COUNT) + (_button)) +#define JOYSTICK_AXIS_BUTTON_INTERNAL(_joystick, _button) (JOYSTICK_FIRST_AXIS_BUTTON + ((_joystick)*JOYSTICK_AXIS_BUTTON_COUNT) + (_button)) + +#define JOYSTICK_BUTTON(_joystick, _button) ((ButtonCode_t)JOYSTICK_BUTTON_INTERNAL(_joystick, _button)) +#define JOYSTICK_POV_BUTTON(_joystick, _button) ((ButtonCode_t)JOYSTICK_POV_BUTTON_INTERNAL(_joystick, _button)) +#define JOYSTICK_AXIS_BUTTON(_joystick, _button) ((ButtonCode_t)JOYSTICK_AXIS_BUTTON_INTERNAL(_joystick, _button)) + +enum ButtonCode_t { + BUTTON_CODE_INVALID = -1, + BUTTON_CODE_NONE = 0, + + KEY_FIRST = 0, + + KEY_NONE = KEY_FIRST, + KEY_0, + KEY_1, + KEY_2, + KEY_3, + KEY_4, + KEY_5, + KEY_6, + KEY_7, + KEY_8, + KEY_9, + KEY_A, + KEY_B, + KEY_C, + KEY_D, + KEY_E, + KEY_F, + KEY_G, + KEY_H, + KEY_I, + KEY_J, + KEY_K, + KEY_L, + KEY_M, + KEY_N, + KEY_O, + KEY_P, + KEY_Q, + KEY_R, + KEY_S, + KEY_T, + KEY_U, + KEY_V, + KEY_W, + KEY_X, + KEY_Y, + KEY_Z, + KEY_PAD_0, + KEY_PAD_1, + KEY_PAD_2, + KEY_PAD_3, + KEY_PAD_4, + KEY_PAD_5, + KEY_PAD_6, + KEY_PAD_7, + KEY_PAD_8, + KEY_PAD_9, + KEY_PAD_DIVIDE, + KEY_PAD_MULTIPLY, + KEY_PAD_MINUS, + KEY_PAD_PLUS, + KEY_PAD_ENTER, + KEY_PAD_DECIMAL, + KEY_LBRACKET, + KEY_RBRACKET, + KEY_SEMICOLON, + KEY_APOSTROPHE, + KEY_BACKQUOTE, + KEY_COMMA, + KEY_PERIOD, + KEY_SLASH, + KEY_BACKSLASH, + KEY_MINUS, + KEY_EQUAL, + KEY_ENTER, + KEY_SPACE, + KEY_BACKSPACE, + KEY_TAB, + KEY_CAPSLOCK, + KEY_NUMLOCK, + KEY_ESCAPE, + KEY_SCROLLLOCK, + KEY_INSERT, + KEY_DELETE, + KEY_HOME, + KEY_END, + KEY_PAGEUP, + KEY_PAGEDOWN, + KEY_BREAK, + KEY_LSHIFT, + KEY_RSHIFT, + KEY_LALT, + KEY_RALT, + KEY_LCONTROL, + KEY_RCONTROL, + KEY_LWIN, + KEY_RWIN, + KEY_APP, + KEY_UP, + KEY_LEFT, + KEY_DOWN, + KEY_RIGHT, + KEY_F1, + KEY_F2, + KEY_F3, + KEY_F4, + KEY_F5, + KEY_F6, + KEY_F7, + KEY_F8, + KEY_F9, + KEY_F10, + KEY_F11, + KEY_F12, + KEY_CAPSLOCKTOGGLE, + KEY_NUMLOCKTOGGLE, + KEY_SCROLLLOCKTOGGLE, + + KEY_LAST = KEY_SCROLLLOCKTOGGLE, + KEY_COUNT = KEY_LAST - KEY_FIRST + 1, + + // Mouse + MOUSE_FIRST = KEY_LAST + 1, + + MOUSE_LEFT = MOUSE_FIRST, + MOUSE_RIGHT, + MOUSE_MIDDLE, + MOUSE_4, + MOUSE_5, + MOUSE_WHEEL_UP, // A fake button which is 'pressed' and 'released' when the wheel is moved up + MOUSE_WHEEL_DOWN, // A fake button which is 'pressed' and 'released' when the wheel is moved down + + MOUSE_LAST = MOUSE_WHEEL_DOWN, + MOUSE_COUNT = MOUSE_LAST - MOUSE_FIRST + 1, + + // Joystick + JOYSTICK_FIRST = MOUSE_LAST + 1, + + JOYSTICK_FIRST_BUTTON = JOYSTICK_FIRST, + JOYSTICK_LAST_BUTTON = JOYSTICK_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_MAX_BUTTON_COUNT - 1), + JOYSTICK_FIRST_POV_BUTTON, + JOYSTICK_LAST_POV_BUTTON = JOYSTICK_POV_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_POV_BUTTON_COUNT - 1), + JOYSTICK_FIRST_AXIS_BUTTON, + JOYSTICK_LAST_AXIS_BUTTON = JOYSTICK_AXIS_BUTTON_INTERNAL(MAX_JOYSTICKS - 1, JOYSTICK_AXIS_BUTTON_COUNT - 1), + + JOYSTICK_LAST = JOYSTICK_LAST_AXIS_BUTTON, + +#if !defined(_X360) + NOVINT_FIRST = JOYSTICK_LAST + 2, // plus 1 missing key. +1 seems to cause issues on the first button. + + NOVINT_LOGO_0 = NOVINT_FIRST, + NOVINT_TRIANGLE_0, + NOVINT_BOLT_0, + NOVINT_PLUS_0, + NOVINT_LOGO_1, + NOVINT_TRIANGLE_1, + NOVINT_BOLT_1, + NOVINT_PLUS_1, + + NOVINT_LAST = NOVINT_PLUS_1, +#endif + + BUTTON_CODE_LAST, + BUTTON_CODE_COUNT = BUTTON_CODE_LAST - KEY_FIRST + 1, + + // Helpers for XBox 360 + KEY_XBUTTON_UP = JOYSTICK_FIRST_POV_BUTTON, // POV buttons + KEY_XBUTTON_RIGHT, + KEY_XBUTTON_DOWN, + KEY_XBUTTON_LEFT, + + KEY_XBUTTON_A = JOYSTICK_FIRST_BUTTON, // Buttons + KEY_XBUTTON_B, + KEY_XBUTTON_X, + KEY_XBUTTON_Y, + KEY_XBUTTON_LEFT_SHOULDER, + KEY_XBUTTON_RIGHT_SHOULDER, + KEY_XBUTTON_BACK, + KEY_XBUTTON_START, + KEY_XBUTTON_STICK1, + KEY_XBUTTON_STICK2, + + KEY_XSTICK1_RIGHT = JOYSTICK_FIRST_AXIS_BUTTON, // XAXIS POSITIVE + KEY_XSTICK1_LEFT, // XAXIS NEGATIVE + KEY_XSTICK1_DOWN, // YAXIS POSITIVE + KEY_XSTICK1_UP, // YAXIS NEGATIVE + KEY_XBUTTON_LTRIGGER, // ZAXIS POSITIVE + KEY_XBUTTON_RTRIGGER, // ZAXIS NEGATIVE + KEY_XSTICK2_RIGHT, // UAXIS POSITIVE + KEY_XSTICK2_LEFT, // UAXIS NEGATIVE + KEY_XSTICK2_DOWN, // VAXIS POSITIVE + KEY_XSTICK2_UP, // VAXIS NEGATIVE +}; + #define BUTTON_CODE_INVALID -1 #define KEY_ESCAPE 70 @@ -14,13 +219,22 @@ class InputSystem : public Module { Interface* g_InputSystem = nullptr; using _StringToButtonCode = int(__rescall*)(void* thisptr, const char* pString); + using _IsButtonDown = bool(__rescall*)(void* thisptr, ButtonCode_t key); + using _GetCursorPosition = void(__rescall*)(void* thisptr, int& x, int& y); + using _SetCursorPosition = void(__rescall*)(void* thisptr, int x, int y); using _KeySetBinding = void(__cdecl*)(int keynum, const char* pBinding); _StringToButtonCode StringToButtonCode = nullptr; + _IsButtonDown IsButtonDown = nullptr; + _GetCursorPosition GetCursorPosition = nullptr; + _SetCursorPosition SetCursorPosition = nullptr; _KeySetBinding KeySetBinding = nullptr; public: int GetButton(const char* pString); + bool IsKeyDown(ButtonCode_t); + void GetCursorPos(int& x, int& y); + void SetCursorPos(int x, int y); // CInputSystem::SleepUntilInput DECL_DETOUR(SleepUntilInput, int nMaxSleepTimeMS); diff --git a/src/Modules/VGui.cpp b/src/Modules/VGui.cpp index a3d6a1bf..1ba0aef2 100644 --- a/src/Modules/VGui.cpp +++ b/src/Modules/VGui.cpp @@ -66,10 +66,16 @@ DETOUR(VGui::Paint, PaintMode_t mode) return result; } +bool VGui::IsUIVisible(){ + return this->IsGameUIVisible(this->enginevgui->ThisPtr()); +} + bool VGui::Init() { this->enginevgui = Interface::Create(this->Name(), "VEngineVGui0"); if (this->enginevgui) { + this->IsGameUIVisible = this->enginevgui->Original<_IsGameUIVisible>(Offsets::IsGameUIVisible); + this->enginevgui->Hook(VGui::Paint_Hook, VGui::Paint, Offsets::Paint); for (auto& hud : Hud::GetList()) { diff --git a/src/Modules/VGui.hpp b/src/Modules/VGui.hpp index 6342c00e..82cde36d 100644 --- a/src/Modules/VGui.hpp +++ b/src/Modules/VGui.hpp @@ -24,6 +24,13 @@ class VGui : public Module { void Draw(HudElement* const& element); public: + + using _IsGameUIVisible = bool(__rescall*)(void* thisptr); + + _IsGameUIVisible IsGameUIVisible = nullptr; + + bool IsUIVisible(); + // CEngineVGui::Paint DECL_DETOUR(Paint, PaintMode_t mode); diff --git a/src/Offsets.cpp b/src/Offsets.cpp index fb583fc0..d8787e82 100644 --- a/src/Offsets.cpp +++ b/src/Offsets.cpp @@ -34,6 +34,7 @@ int JoyStickApplyMovement; // ClientModeShared int CreateMove; +int OverrideView; // ConVar int Dtor; @@ -56,6 +57,9 @@ int DrawTextLen; // CInputSystem int StringToButtonCode; int SleepUntilInput; +int IsButtonDown; +int GetCursorPosition; +int SetCursorPosition; // CInput int GetButtonBits; @@ -119,10 +123,13 @@ int m_pSurfaceData; int iNumPortalsPlaced; // IEngineVGuiInternal +int IsGameUIVisible; int Paint; // CEngineTool int GetCurrentMap; +int HostFrameTime; +int ClientTime; // CSchemeManager int GetIScheme; diff --git a/src/Offsets.hpp b/src/Offsets.hpp index fc84558d..1c605d98 100644 --- a/src/Offsets.hpp +++ b/src/Offsets.hpp @@ -34,6 +34,7 @@ extern int JoyStickApplyMovement; // ClientModeShared extern int CreateMove; +extern int OverrideView; // ConVar extern int Dtor; @@ -56,6 +57,9 @@ extern int DrawTextLen; // CInputSystem extern int StringToButtonCode; extern int SleepUntilInput; +extern int IsButtonDown; +extern int GetCursorPosition; +extern int SetCursorPosition; // CInput extern int GetButtonBits; @@ -119,10 +123,13 @@ extern int m_pSurfaceData; extern int iNumPortalsPlaced; // IEngineVGuiInternal +extern int IsGameUIVisible; extern int Paint; // CEngineTool extern int GetCurrentMap; +extern int HostFrameTime; +extern int ClientTime; // CSchemeManager extern int GetIScheme; diff --git a/src/SAR.cpp b/src/SAR.cpp index cb3e40c0..dd40322d 100644 --- a/src/SAR.cpp +++ b/src/SAR.cpp @@ -61,6 +61,7 @@ bool SAR::Load(CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerF this->features->AddFeature(&pauseTimer); this->features->AddFeature(&dataMapDumper); this->features->AddFeature(&fovChanger); + this->features->AddFeature(&camera); this->modules->AddModule(&inputSystem); this->modules->AddModule(&scheme); diff --git a/src/SourceAutoRecord.vcxproj b/src/SourceAutoRecord.vcxproj index f6944ec0..ffff8a0e 100644 --- a/src/SourceAutoRecord.vcxproj +++ b/src/SourceAutoRecord.vcxproj @@ -124,6 +124,7 @@ + @@ -265,6 +266,7 @@ + diff --git a/src/SourceAutoRecord.vcxproj.filters b/src/SourceAutoRecord.vcxproj.filters index 8099f56d..7dbf163a 100644 --- a/src/SourceAutoRecord.vcxproj.filters +++ b/src/SourceAutoRecord.vcxproj.filters @@ -319,6 +319,9 @@ SourceAutoRecord\Features + + SourceAutoRecord\Features + @@ -666,6 +669,9 @@ SourceAutoRecord\Features + + SourceAutoRecord\Features + diff --git a/src/Utils/SDK.hpp b/src/Utils/SDK.hpp index b5d84bda..71dc0c6b 100644 --- a/src/Utils/SDK.hpp +++ b/src/Utils/SDK.hpp @@ -918,3 +918,58 @@ enum PaintMode_t { PAINT_UIPANELS = (1 << 0), PAINT_INGAMEPANELS = (1 << 1), }; + +struct CPortalViewSetup1 { + int x; + int m_nUnscaledX; + int y; + int m_nUnscaledY; + int width; + int m_nUnscaledWidth; + int height; + int m_nUnscaledHeight; + bool m_bOrtho; + float m_OrthoLeft; + float m_OrthoTop; + float m_OrthoRight; + float m_OrthoBottom; + bool m_bCustomViewMatrix; + float m_matCustomViewMatrix[3][4]; + float fov; + float fovViewmodel; + Vector origin; + QAngle angles; + + //TODO: stuff below not tested - verify + float zNear; + float zFar; + float zNearViewmodel; + float zFarViewmodel; + float m_flAspectRatio; + float m_flNearBlurDepth; + float m_flNearFocusDepth; + float m_flFarFocusDepth; + float m_flFarBlurDepth; + float m_flNearBlurRadius; + float m_flFarBlurRadius; + int m_nDoFQuality; + int m_nMotionBlurMode; + float m_flShutterTime; + Vector m_vShutterOpenPosition; + QAngle m_shutterOpenAngles; + Vector m_vShutterClosePosition; + QAngle m_shutterCloseAngles; + float m_flOffCenterTop; + float m_flOffCenterBottom; + float m_flOffCenterLeft; + float m_flOffCenterRight; + bool m_bOffCenter : 1; + bool m_bRenderToSubrectOfLargerScreen : 1; + bool m_bDoBloomAndToneMapping : 1; + bool m_bDoDepthOfField : 1; + bool m_bHDRTarget : 1; + bool m_bDrawWorldNormal : 1; + bool m_bCullFrontFaces : 1; + bool m_bCacheFullSceneState : 1; + bool m_bCSMView : 1; +};