├── .gitattributes ├── .gitignore ├── README.md ├── SystemShockVR.sln └── SystemShockVR ├── CH_Hacker_AnimBP_C.hpp ├── COMP_HackerInventory_C.hpp ├── COMP_MoveControlManager_C.hpp ├── CON_Hacker_C.hpp ├── CharacterAction_C.hpp ├── Controller.hpp ├── INTERACT_Laptop_C.hpp ├── MOVECONTROL_FocusableInteract_C.hpp ├── PAWN_Hacker_Simple_C.hpp ├── PAWN_SystemShockCharacter_C.hpp ├── SAVE_Settings_C.hpp ├── SystemShockVR.vcxproj ├── SystemShockVR.vcxproj.filters ├── WIDGET_InventoryContextMenu_C.hpp ├── WIDGET_MainMenu_InGame_C.hpp ├── WIDGET_PlayerHUD_C.hpp ├── cxxtimer.hpp ├── dllmain.cpp ├── framework.h ├── pch.cpp ├── pch.h └── uevr ├── API.h ├── API.hpp └── Plugin.hpp /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # System Shock Remake UEVR Plugin 2 | This System Shock mod brings VR support complete with 6DOF motion controls to one of the greatest immersive sims of all time! This VR mod consists of: 3 | 4 | 1) A UEVR profile and C++ plugin (Bundled together). Required UEVR Nightly 940 or newer! 5 | 2) An optional PAK mod 6 | 7 | The UEVR profile adds 6DOF motion controls to System Shock. The C++ plugin adds a wide variety of advanced VR features and fixes to the game. The PAK mod updates the button prompts to match Oculus Touch controllers. 8 | 9 | ## VR Mod Features 10 | ### UEVR Profile 11 | * 6DOF motion controls. 12 | * ADS support. Projectiles are aligned with gun sights for added VR immersion. 13 | * Disable On Screen Effects to fix intro cutscene. 14 | * Fix issue with transparency effects only appearing in left eye. 15 | * The culler is attached to the HMD with an increased FOV to prevent "blackout" when looking around in VR. 16 | * Lua script with the following features: 17 | * Hides unnecessary floating arm meshes when weapons are holstered. 18 | * Motion control fixes to better align weapons with your hands in real life. 19 | 20 | ### VR Plugin 21 | * In-game settings automatically configured for VR. 22 | * Improved object interaction. Hold **LT** to enable "interact" mode. You do not need to enter "interact" mode to interact with objects, but this mode will display a cursor so you can more easily interact with things like weapons, ammo, switches, levers, etc. Weapons are disabled in this mode as enabling motion controls prevents the cursor from working properly in VR. This mode also temporarily disables Roomscale which allows you to lean closely towards objects and walls without any pushback. Releasing **LT**, crouching, or picking up auto equip items will exit "interact" mode and re-center you in your playspace. "Interact" mode is also automatically activated and exited when you use or stop using a keypad, circuit puzzle, or vending machine. 23 | * Motion controller gesture support. You can melee attack with the lead pipe, wrench, or laser rapier by swinging your right hand forward in real life. Holding **RT** while performing a gesture will perform a heavy melee attack. You can alternatively melee with only **RT**. 24 | * Improved VR controls when using menus. The main menu can be accessed by holding **System** for less than 1 second. The main menu can alternatively be accessed by entering Interact Mode (hold **LT**) and holding **Y** for less than 1 second. The Multi-Function Display (MFD) can be accessed by holding **System** for greater than 1 second. The MFD can alternatively be accessed by entering Interact Mode (hold **LT**) and holding **Y** for greater than 1 second. The mouse cursor can be controlled with **RS**. Inventory items can be selected with **LT** and moved with **RS**. Inventory sub-menu options (shown with **Y**) can also be selected with **Y**. Inventory sub-menu options can be scrolled by flicking **RS** (Dpad) up/down/left/right. Press **RT** to display additional information on selected inventory items. Press **B** to sort items. Hold **A** for 2 seconds to vaporize junk (note that food may also be vaporized). **LGrip** abd **RGrip** can be used to cycle the MFD tabs. 25 | * Roomscale support with Automatic Height Adjustment. 26 | * Snap Turn support. Enabled by default. 27 | * Smooth Turn support. Press **L3+R3** to open up the UEVR overlay. Select the "Input" tab and set the "Snap Turn Angle" to 359 (for Meta, Index, HTC, and Pimax controllers) or 2 (for Varjo controllers). This enables smooth turning for seated play. 28 | * Physical crouching support. You can crouch and crawl through tight collision areas by crouching in real life. The profile also prevents the camera from "rubber banding" to the ground when crouching is active. You can recalibrate your height and physical crouching threshold by pressing **LT+L3**. Note that pressing **X** to crouch will no longer lower the camera to the ground if physical crouching is active. If you wish to disable physical crouching and use **X** to crouch for seated play, delete "17105528413843615245_props.json" from your UEVR global directory. 29 | * Disables leaning as this causes movement to freeze in VR. 30 | * Cyberspace fixes. Gameplay in cyberspace is similar to Descent. You can steer with the right thumb stick and pressing **RT** will fire at the center of the screen. 31 | * First person animation fixes. The apartment intro arrest sequence, cryobeds, surgical beds, cyberspace terminals, and death sequences now play properly in VR. 32 | * Cutscenes fixes. 33 | * UI fixes. 34 | * Snap Turn is temporarily disabled when menus are active or keypads/puzzles are being used. 35 | * The camera will not move with your arm when the title menu is active. 36 | * The UI and HUD are attached to the right hand. 37 | 38 | ### PAK Mod (Optional) 39 | * SystemShock-VRFixes_p.pak: Oculus Touch button prompts. 40 | 41 | ## Installation 42 | * Install a clean copy of System Shock Remake and delete any existing System Shock profiles from your UEVR global folder if installed. 43 | * Disconnect any gamepads from your PC or turning will not work properly in VR. 44 | 45 | 1) Install the nightly build of UEVR (at least UEVR Nightly 940 or newer required) from [**HERE**](https://github.com/praydog/UEVR-nightly/releases). 46 | 2) Download the latest release of the System Shock UEVR plugin from [**HERE**](https://github.com/Ashok0/SystemShock-UEVR/releases). Click "Import Config" in the UEVR UI and navigate to "SystemReShock-Win64-Shipping.zip" (make sure the filename does not have any numbers in it) and click on it and accept the DLL warning. 47 | 3) (Optional) Download the latest PAK mod from [**HERE**](https://github.com/Ashok0/SystemShock-UEVR/releases). Copy the .pak file to \Steam\steamapps\common\System Shock Remake\SystemShock\Content\Paks 48 | 4) (Optional) For seated play, delete "17105528413843615245_props.json" from your UEVR global directory to disable physical crouching. This will allow you to manually crouch by pressing **X**. 49 | 5) Launch System Shock and inject the game with UEVR! 50 | 51 | ## Controls 52 | * In-Game Controls should be left at default settings as key bindings are managed by the VR plugin. 53 | 54 | ### General 55 | * Move: LS 56 | * Jump: A 57 | * Crouch: X (Seated Mode Only) 58 | * Sprint: LGrip 59 | 60 | ### Interaction & Combat 61 | * Interact Mode: LT (Hold LT for interaction cursor) 62 | * Interact: B 63 | * Exit Interact: X 64 | * Attack: RT 65 | * Reload: B 66 | * Mode Switch: Y 67 | * Main Menu: System (Hold < 1s) OR Interact Mode + Y (Hold < 1s) 68 | * MFD: System (Hold > 1s) OR Interact Mode + Y (Hold > 1s) 69 | * Previous/Next Hotbar Item: Dpad (Right Thumbrest OR RGrip) + LS 70 | 71 | ### MFD 72 | * Context menu: Y 73 | * Select menu options: Y 74 | * Scroll menu options: RS (Flick Up/Dn/Left/Right) 75 | * Select item: LT 76 | * Move item: RS 77 | * Sort: B 78 | * Analyze: RT 79 | * Vaporize: A (Hold > 2s) 80 | * Exit: X 81 | * Previous Tab: LGrip 82 | * Next Tab: RGrip 83 | 84 | ### Cyberspace 85 | * Move: LS 86 | * Ascend: A 87 | * Descend: X 88 | * Roll Left: LB 89 | * Roll Right: RB 90 | * Pulser: RT 91 | * I.C.E. Drill: LT 92 | * Turbo Dash: LS (Dn) 93 | * Recall: Y 94 | * Decoy: B 95 | 96 | ## Known Issues 97 | 1) When you rotate your right motion controller in real life, weapons pivot slightly off center from your hand. It's a subtle issue which isn't terribly gamebreaking. 98 | 2) Minor UI issues 99 | * When interacting with keypads/circuit puzzles/vending machines, you may need to manually recenter the crosshair using the right stick. 100 | * Moving around in your play area after interacting with keypads/circuit puzzles/vending machines can cause the crosshair to become misaligned. 101 | * Inject the game during the opening splash screens. If you inject after the title screen loads, this can result in a bug where a mouse cursor is visible in your view. 102 | 3) If you have issues with motion control "jitter", you can minimize the issue by reducing your "Graphics" settings. 103 | 4) If you get "stuck" while crawling through the vents in Research Labs, you can get "unstuck" by jumping (A) and/or standing up and crouching again while holding the left stick forward. 104 | 105 | ## FAQ 106 | **Can I adjust settings for the game when using this mod?** 107 | You can change select settings safely. Do not change any of the following, however, as these settings are managed directly by the VR plugin. Modifying the default settings related to crouching will break physical crouching. 108 | 109 | *UEVR SETTINGS* 110 | * VR_RoomscaleMovement 111 | * UI_Y_Offset 112 | * UI_Distance 113 | * UI_Size 114 | * VR_SnapTurn 115 | * VR_AimMethod 116 | * VR_DecoupledPitch 117 | * VR_CameraForwardOffset 118 | * VR_CameraRightOffset 119 | * VR_CameraUpOffset 120 | 121 | *IN-GAME SETTINGS* 122 | * Toggle Crouch 123 | * In-Game Controls should be left at default settings as key bindings are managed by the VR plugin. 124 | 125 | **I pressed *X* and crouching is not working!** 126 | With default settings, you must crouch by crouching in real life. The physical crouching feature prevents the crouch button (**X**) from adjusting the camera height. If you wish to use the **X** button to crouch, you must delete "17105528413843615245_props.json" from your UEVR global directory. 127 | 128 | **Pressing *LT* no longer activates Interact mode!** 129 | If **LT** is not working for you, you may be in a crouched state. Press **X** to toggle crouching off. Interact mode is disabled when you are crouched as the cursor only works properly in VR at a standing height. 130 | 131 | **My game crashes to the desktop with a "LowLevelFatalError" message!** 132 | Performing a clean install of System Shock will resolve the issue. 133 | 134 | **My game crashes to the desktop when using the inventory menu!** 135 | Uninstall prior installed System Shock mods from your Steam folder as incompatible mods may cause System Shock to crash to the desktop. 136 | 137 | **My weapons are invisible!** This issue can occur when using an older flat saved game in VR. Restarting the game with a clean saved game will resolve the issue. 138 | 139 | **Is this mod still in development?** 140 | The mod is currently in maintenance mode but I will be continuing to support the mod if any new bugs are found. If you experience any issues with the mod, please send me a bug report so I can continue to improve the System Shock VR plugin! 141 | 142 | ## Recommended Mods 143 | * [**Adjusted Respawn Limits**](https://www.nexusmods.com/systemshock2023/mods/93) - Reduce enemy respawning 144 | * Extract the contents and copy "v1.2_Reinforcements_0.5x_P.pak" to *\Steam\steamapps\common\System Shock Remake\SystemShock\Content\Paks* 145 | * [**Cyberspace Adjustments**](https://www.nexusmods.com/systemshock2023/mods/83) - Reduce the difficulty of cyberspace sections of the game 146 | * [**Classic Evil Shodan Portrait**](https://www.nexusmods.com/systemshock2023/mods/101) - Replace SHODAN's evil portrait with the classic ones from System Shock 1 or 2. 147 | 148 | ## Credits 149 | Special thanks to Praydog, TimBurton, Markmon, and CJ117 for their support while developing this VR mod! 150 | 151 | Feel free to buy me a coffee on [**Patreon**](http://www.patreon.com/ashok0) or [**Ko-Fi**](https://ko-fi.com/ashok0) :coffee: 152 | -------------------------------------------------------------------------------- /SystemShockVR.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.9.34622.214 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SystemShockVR", "SystemShockVR\SystemShockVR.vcxproj", "{B47FC2A4-ACEF-42E0-813B-6F5573C4D1B2}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {B47FC2A4-ACEF-42E0-813B-6F5573C4D1B2}.Debug|x64.ActiveCfg = Debug|x64 17 | {B47FC2A4-ACEF-42E0-813B-6F5573C4D1B2}.Debug|x64.Build.0 = Debug|x64 18 | {B47FC2A4-ACEF-42E0-813B-6F5573C4D1B2}.Debug|x86.ActiveCfg = Debug|Win32 19 | {B47FC2A4-ACEF-42E0-813B-6F5573C4D1B2}.Debug|x86.Build.0 = Debug|Win32 20 | {B47FC2A4-ACEF-42E0-813B-6F5573C4D1B2}.Release|x64.ActiveCfg = Release|x64 21 | {B47FC2A4-ACEF-42E0-813B-6F5573C4D1B2}.Release|x64.Build.0 = Release|x64 22 | {B47FC2A4-ACEF-42E0-813B-6F5573C4D1B2}.Release|x86.ActiveCfg = Release|Win32 23 | {B47FC2A4-ACEF-42E0-813B-6F5573C4D1B2}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {F1AAE51A-DE3F-4C7B-BB77-AABCEE84E3CA} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /SystemShockVR/CH_Hacker_AnimBP_C.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "uevr/API.hpp" 4 | 5 | using namespace uevr; 6 | 7 | class CH_Hacker_AnimBP_C : public API::UObject 8 | { 9 | public: 10 | using API::UObject::get_full_name; 11 | 12 | static API::UClass *static_class() 13 | { 14 | static API::UClass *result = nullptr; 15 | if(!result) { 16 | result = API::get()->find_uobject(L"AnimBlueprintGeneratedClass /Game/Art/FirstPerson/CH_Hacker_AnimBP.CH_Hacker_AnimBP_C"); 17 | } 18 | return result; 19 | } 20 | 21 | static CH_Hacker_AnimBP_C*get_instance() 22 | { 23 | auto klass = CH_Hacker_AnimBP_C::static_class(); 24 | CH_Hacker_AnimBP_C* Object = nullptr; 25 | 26 | if(klass) { 27 | std::vector List = klass->get_objects_matching(); 28 | for(size_t i = 0; i < List.size(); i++) { 29 | Object = List[i]; 30 | 31 | std::wstring ObjName = Object->get_full_name(); 32 | // API::get()->log_info("CH_Hacker_AnimBP_C: Object %d of %d, Object name: %ls",i, List.size(), ObjName.c_str()); 33 | 34 | if(ObjName.find(L"CH_Hacker_AnimBP_C") != std::wstring::npos) 35 | { 36 | // API::get()->log_info("CH_Hacker_AnimBP_C found"); 37 | } 38 | else 39 | { 40 | // API::get()->log_info("CH_Hacker_AnimBP_C not found"); 41 | } 42 | } 43 | 44 | return Object; 45 | } 46 | return nullptr; 47 | } 48 | 49 | void SetUseIK(bool value) 50 | { 51 | static const auto func = CH_Hacker_AnimBP_C::static_class()->find_function(L"SetUseIK"); 52 | if (!func) { 53 | // API::get()->log_info("SetUseIK not found"); 54 | return; 55 | } 56 | else 57 | { 58 | // API::get()->log_info("SetUseIK found"); 59 | } 60 | 61 | struct 62 | { 63 | bool value; 64 | bool res; 65 | } params{ .value = value, .res = false }; 66 | 67 | process_event(func, ¶ms); 68 | } 69 | }; -------------------------------------------------------------------------------- /SystemShockVR/COMP_HackerInventory_C.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "uevr/API.hpp" 4 | 5 | using namespace uevr; 6 | 7 | extern bool InteractToggle; 8 | extern bool IsEquipped; 9 | extern bool melee_equipped; 10 | extern bool rapier_equipped; 11 | 12 | class COMP_HackerInventory_C : public API::UObject 13 | { 14 | public: 15 | using API::UObject::get_full_name; 16 | 17 | static API::UClass* static_class() 18 | { 19 | static API::UClass* result = nullptr; 20 | if (!result) { 21 | result = API::get()->find_uobject(L"BlueprintGeneratedClass /Game/Blueprints/Inventory/COMP_HackerInventory.COMP_HackerInventory_C"); 22 | } 23 | return result; 24 | } 25 | 26 | static COMP_HackerInventory_C* get_instance() 27 | { 28 | auto klass = COMP_HackerInventory_C::static_class(); 29 | COMP_HackerInventory_C* Object = nullptr; 30 | 31 | if (klass) { 32 | std::vector List = klass->get_objects_matching(); 33 | for (size_t i = 0; i < List.size(); i++) { 34 | Object = List[i]; 35 | 36 | std::wstring ObjName = Object->get_full_name(); 37 | // API::get()->log_info("COMP_HackerInventory_C: Object %d of %d, Object name: %ls", i, List.size(), ObjName.c_str()); 38 | 39 | if (ObjName.find(L"Implant_C") != std::wstring::npos) 40 | { 41 | // API::get()->log_info("COMP_HackerInventory_C found"); 42 | Object->GetEquippedWeapon(); 43 | 44 | if (InteractToggle == true) 45 | { 46 | Object->ToggleWeaponEquipState(); 47 | InteractToggle = false; 48 | } 49 | } 50 | else 51 | { 52 | // API::get()->log_info("COMP_HackerInventory_C not found"); 53 | } 54 | } 55 | } 56 | return nullptr; 57 | } 58 | 59 | void ToggleWeaponEquipState() 60 | { 61 | static const auto func = COMP_HackerInventory_C::static_class()->find_function(L"ToggleWeaponEquipState"); 62 | if (!func) { 63 | // API::get()->log_info("ToggleWeaponEquipState not found"); 64 | return; 65 | } 66 | else 67 | { 68 | // API::get()->log_info("ToggleWeaponEquipState found"); 69 | } 70 | 71 | struct 72 | { 73 | bool value; 74 | } params{ .value = false }; 75 | 76 | process_event(func, ¶ms); 77 | } 78 | 79 | void GetEquippedWeapon() 80 | { 81 | static const auto func = static_class()->find_function(L"GetEquippedWeapon"); 82 | 83 | if (!func) { 84 | // API::get()->log_error("GetEquippedWeapon not found"); 85 | } 86 | else 87 | { 88 | // API::get()->log_error("GetEquippedWeapon found"); 89 | } 90 | 91 | struct 92 | { 93 | API::UObject* Weapon; 94 | } params{ .Weapon = nullptr }; 95 | 96 | process_event(func, ¶ms); 97 | 98 | if (params.Weapon != nullptr) 99 | { 100 | IsEquipped = true; 101 | 102 | std::wstring WeaponName = params.Weapon->get_full_name(); 103 | // API::get()->log_info("GetEquippedWeapon: %ls", WeaponName.c_str()); 104 | 105 | if ((WeaponName.find(L"Pipe") != std::wstring::npos) || (WeaponName.find(L"Wrench") != std::wstring::npos)) 106 | { 107 | melee_equipped = true; 108 | } 109 | else 110 | { 111 | melee_equipped = false; 112 | } 113 | 114 | if (WeaponName.find(L"Rapier") != std::wstring::npos) 115 | { 116 | rapier_equipped = true; 117 | } 118 | else 119 | { 120 | rapier_equipped = false; 121 | } 122 | } 123 | else 124 | { 125 | IsEquipped = false; 126 | } 127 | } 128 | }; -------------------------------------------------------------------------------- /SystemShockVR/COMP_MoveControlManager_C.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "uevr/API.hpp" 4 | 5 | using namespace uevr; 6 | 7 | class COMP_MoveControlManager_C : public API::UObject 8 | { 9 | public: 10 | using API::UObject::get_full_name; 11 | 12 | static API::UClass* static_class() 13 | { 14 | static API::UClass* result = nullptr; 15 | if (!result) { 16 | result = API::get()->find_uobject(L"BlueprintGeneratedClass /Game/Blueprints/Characters/Hacker/ScriptedControls/COMP_MoveControlManager.COMP_MoveControlManager_C"); 17 | } 18 | return result; 19 | } 20 | 21 | static COMP_MoveControlManager_C* get_instance() 22 | { 23 | auto klass = COMP_MoveControlManager_C::static_class(); 24 | COMP_MoveControlManager_C* Object = nullptr; 25 | 26 | if (klass) { 27 | 28 | std::vector List = klass->get_objects_matching(); 29 | 30 | for (size_t i = 0; i < List.size(); i++) { 31 | Object = List[i]; 32 | 33 | std::wstring ObjName = Object->get_full_name(); 34 | // API::get()->log_info("COMP_MoveControlManager_C: Object %d of %d, Object name: %ls", i, List.size(), ObjName.c_str()); 35 | 36 | if (ObjName.find(L"PAWN_Hacker_Implant_C") != std::wstring::npos) 37 | { 38 | // API::get()->log_info("COMP_MoveControlManager_C found"); 39 | return Object; 40 | } 41 | else 42 | { 43 | // API::get()->log_info("COMP_MoveControlManager_C not found"); 44 | } 45 | } 46 | } 47 | return nullptr; 48 | } 49 | 50 | bool get_IsCrouching() 51 | { 52 | return get_bool_property(L"IsCrouching"); 53 | } 54 | }; 55 | -------------------------------------------------------------------------------- /SystemShockVR/CON_Hacker_C.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "uevr/API.hpp" 4 | 5 | using namespace uevr; 6 | 7 | class CON_Hacker_C : public API::UObject 8 | { 9 | public: 10 | using API::UObject::get_full_name; 11 | 12 | static API::UClass* static_class() 13 | { 14 | static API::UClass* result = nullptr; 15 | if (!result) { 16 | result = API::get()->find_uobject(L"BlueprintGeneratedClass /Game/Blueprints/Characters/Hacker/CON_Hacker.CON_Hacker_C"); 17 | } 18 | return result; 19 | } 20 | 21 | static CON_Hacker_C* get_instance() 22 | { 23 | auto klass = CON_Hacker_C::static_class(); 24 | CON_Hacker_C* Object = nullptr; 25 | 26 | if (klass) { 27 | std::vector List = klass->get_objects_matching(); 28 | for (size_t i = 0; i < List.size(); i++) { 29 | Object = List[i]; 30 | 31 | std::wstring ObjName = Object->get_full_name(); 32 | // API::get()->log_info("CON_Hacker_C: Object %d of %d, Object name: %ls", i, List.size(), ObjName.c_str()); 33 | 34 | if (ObjName.find(L"CON_Hacker_C") != std::wstring::npos) 35 | { 36 | // API::get()->log_info("CON_Hacker_C found"); 37 | } 38 | else 39 | { 40 | // API::get()->log_info("CON_Hacker_C not found"); 41 | } 42 | } 43 | 44 | return Object; 45 | } 46 | return nullptr; 47 | } 48 | 49 | void ForceHideCrosshairs(bool value) 50 | { 51 | static const auto func = CON_Hacker_C::static_class()->find_function(L"ForceHideCrosshairs"); 52 | if (!func) { 53 | // API::get()->log_info("ForceHideCrosshairs not found"); 54 | return; 55 | } 56 | else 57 | { 58 | // API::get()->log_info("ForceHideCrosshairs found"); 59 | } 60 | 61 | struct 62 | { 63 | bool HideState; 64 | } params{ .HideState = value }; 65 | 66 | process_event(func, ¶ms); 67 | } 68 | }; -------------------------------------------------------------------------------- /SystemShockVR/CharacterAction_C.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "uevr/API.hpp" 4 | 5 | using namespace uevr; 6 | 7 | /** The various states that the animation state machine can be in */ 8 | typedef enum eAnimationState 9 | { 10 | ANIM_NONE, 11 | ANIM_CRYOBED_WAKE, 12 | ANIM_SURGICALBED_RIGHT_ENTER, 13 | ANIM_SURGICALBED_RIGHT_EXIT, 14 | ANIM_SURGICALBED_LEFT_ENTER, 15 | ANIM_SURGICALBED_LEFT_EXIT, 16 | ANIM_DEATH_P, 17 | ANIM_TERMINAL_USE, 18 | ANIM_TERMINAL_DISMOUNT, 19 | ANIM_RESPAWN 20 | } 21 | animation_State_t; 22 | 23 | float ElapsedPlayTime = 0.0; 24 | int Montage = 0; 25 | bool CryobedWakeIsActive = false; 26 | bool RightExitIsActive = false; 27 | bool LeftExitIsActive = false; 28 | bool TerminalIsActive = false; 29 | bool RespawnIsActive = false; 30 | 31 | extern bool RoomscaleMontageOverride; 32 | 33 | class CharacterAction_C : private API::UObject 34 | { 35 | public: 36 | using API::UObject::get_full_name; 37 | 38 | static API::UClass* static_class() 39 | { 40 | static auto result = API::get()->find_uobject(L"BlueprintGeneratedClass /Game/Blueprints/Characters/Actions/CharacterAction.CharacterAction_C"); 41 | return result; 42 | } 43 | 44 | static CharacterAction_C* get_instance() 45 | { 46 | auto klass = CharacterAction_C::static_class(); 47 | CharacterAction_C* Object = nullptr; 48 | 49 | if (klass) { 50 | 51 | std::vector List = klass->get_objects_matching(); 52 | 53 | for (size_t i = 0; i < List.size(); i++) { 54 | Object = List[i]; 55 | 56 | std::wstring ObjName = Object->get_full_name(); 57 | // API::get()->log_info("CharacterAction_C: Object %d of %d, Object name: %ls", i, List.size(), ObjName.c_str()); 58 | 59 | if (ObjName.find(L"PersistentLevel.PAWN_Hacker_Implant_C") != std::wstring::npos) 60 | { 61 | // API::get()->log_info("CharacterAction_C::PersistentLevel.PAWN_Hacker_Implant_C found"); 62 | Montage = Object->GetMontage(); 63 | ElapsedPlayTime = Object->GetElapsedPlayTime(); 64 | 65 | if ( (ElapsedPlayTime == 0.0) && (Montage == ANIM_SURGICALBED_RIGHT_ENTER) ) 66 | { 67 | API::UObjectHook::set_disabled(true); 68 | 69 | auto& vr = API::get()->param()->vr; 70 | vr->set_mod_value("VR_RoomscaleMovement", "false"); 71 | RoomscaleMontageOverride = true; 72 | } 73 | else if ((ElapsedPlayTime == 0.0) && (Montage == ANIM_SURGICALBED_RIGHT_EXIT)) 74 | { 75 | RightExitIsActive = true; 76 | } 77 | else if ((ElapsedPlayTime == -1.0) && (Montage == ANIM_SURGICALBED_RIGHT_EXIT) && (RightExitIsActive == true) ) 78 | { 79 | RightExitIsActive = false; 80 | API::UObjectHook::set_disabled(false); 81 | 82 | auto& vr = API::get()->param()->vr; 83 | vr->set_mod_value("VR_RoomscaleMovement", "true"); 84 | RoomscaleMontageOverride = false; 85 | } 86 | else if ((ElapsedPlayTime == 0.0) && (Montage == ANIM_SURGICALBED_LEFT_ENTER)) 87 | { 88 | API::UObjectHook::set_disabled(true); 89 | 90 | auto& vr = API::get()->param()->vr; 91 | vr->set_mod_value("VR_RoomscaleMovement", "false"); 92 | RoomscaleMontageOverride = true; 93 | } 94 | else if ((ElapsedPlayTime == 0.0) && (Montage == ANIM_SURGICALBED_LEFT_EXIT)) 95 | { 96 | LeftExitIsActive = true; 97 | } 98 | else if ((ElapsedPlayTime == -1.0) && (Montage == ANIM_SURGICALBED_LEFT_EXIT) && (LeftExitIsActive == true)) 99 | { 100 | LeftExitIsActive = false; 101 | API::UObjectHook::set_disabled(false); 102 | 103 | auto& vr = API::get()->param()->vr; 104 | vr->set_mod_value("VR_RoomscaleMovement", "true"); 105 | RoomscaleMontageOverride = false; 106 | } 107 | else if ((ElapsedPlayTime == 0.0) && (Montage == ANIM_DEATH_P)) 108 | { 109 | API::UObjectHook::set_disabled(true); 110 | } 111 | else if ((ElapsedPlayTime >= 0.0) && (Montage == ANIM_TERMINAL_USE)) 112 | { 113 | TerminalIsActive = true; 114 | API::UObjectHook::set_disabled(true); 115 | 116 | auto& vr = API::get()->param()->vr; 117 | vr->set_mod_value("VR_RoomscaleMovement", "false"); 118 | RoomscaleMontageOverride = true; 119 | } 120 | else if ((ElapsedPlayTime == -1.0) && (Montage == ANIM_TERMINAL_USE) && (TerminalIsActive == true)) 121 | { 122 | TerminalIsActive = false; 123 | API::UObjectHook::set_disabled(false); 124 | 125 | auto& vr = API::get()->param()->vr; 126 | vr->set_mod_value("VR_RoomscaleMovement", "true"); 127 | RoomscaleMontageOverride = false; 128 | } 129 | else if ((ElapsedPlayTime >= 0.0) && (Montage == ANIM_TERMINAL_DISMOUNT)) 130 | { 131 | TerminalIsActive = true; 132 | API::UObjectHook::set_disabled(true); 133 | 134 | auto& vr = API::get()->param()->vr; 135 | vr->set_mod_value("VR_RoomscaleMovement", "false"); 136 | RoomscaleMontageOverride = true; 137 | } 138 | else if ((ElapsedPlayTime == -1.0) && (Montage == ANIM_TERMINAL_DISMOUNT) && (TerminalIsActive == true)) 139 | { 140 | TerminalIsActive = false; 141 | API::UObjectHook::set_disabled(false); 142 | 143 | auto& vr = API::get()->param()->vr; 144 | vr->set_mod_value("VR_RoomscaleMovement", "true"); 145 | RoomscaleMontageOverride = false; 146 | } 147 | else if ((ElapsedPlayTime == 0.0) && (Montage == ANIM_CRYOBED_WAKE)) 148 | { 149 | CryobedWakeIsActive = true; 150 | API::UObjectHook::set_disabled(true); 151 | 152 | auto& vr = API::get()->param()->vr; 153 | vr->set_mod_value("VR_RoomscaleMovement", "false"); 154 | RoomscaleMontageOverride = true; 155 | } 156 | else if ((ElapsedPlayTime == -1.0) && (Montage == ANIM_CRYOBED_WAKE) && (CryobedWakeIsActive == true) ) 157 | { 158 | CryobedWakeIsActive = false; 159 | API::UObjectHook::set_disabled(false); 160 | 161 | auto& vr = API::get()->param()->vr; 162 | vr->set_mod_value("VR_RoomscaleMovement", "true"); 163 | RoomscaleMontageOverride = false; 164 | } 165 | else if ((ElapsedPlayTime >= 0.0) && (Montage == ANIM_RESPAWN)) 166 | { 167 | RespawnIsActive = true; 168 | API::UObjectHook::set_disabled(true); 169 | 170 | auto& vr = API::get()->param()->vr; 171 | vr->set_mod_value("VR_RoomscaleMovement", "false"); 172 | RoomscaleMontageOverride = true; 173 | } 174 | else if ((ElapsedPlayTime == -1.0) && (Montage == ANIM_RESPAWN) && (RespawnIsActive == true)) 175 | { 176 | RespawnIsActive = false; 177 | API::UObjectHook::set_disabled(false); 178 | 179 | auto& vr = API::get()->param()->vr; 180 | vr->set_mod_value("VR_RoomscaleMovement", "true"); 181 | RoomscaleMontageOverride = false; 182 | } 183 | } 184 | else 185 | { 186 | // API::get()->log_info("CharacterAction_C::PersistentLevel.PAWN_Hacker_Implant_C not found"); 187 | } 188 | } 189 | } 190 | return nullptr; 191 | } 192 | 193 | int GetMontage() 194 | { 195 | static const auto func = static_class()->find_function(L"GetMontage"); 196 | 197 | if (!func) { 198 | // API::get()->log_error("GetMontage not found"); 199 | } 200 | else 201 | { 202 | // API::get()->log_error("GetMontage found"); 203 | } 204 | 205 | struct 206 | { 207 | API::UObject* Result; 208 | } params{ .Result = nullptr }; 209 | 210 | process_event(func, ¶ms); 211 | 212 | if (params.Result != nullptr) 213 | { 214 | std::wstring ObjName = params.Result->get_full_name(); 215 | // API::get()->log_info("GetMontage: %ls", ObjName.c_str()); 216 | 217 | if (ObjName.find(L"CH_Hacker_SurgicalBed_Right_Enter_Montage") != std::wstring::npos) 218 | { 219 | return ANIM_SURGICALBED_RIGHT_ENTER; 220 | } 221 | else if (ObjName.find(L"CH_Hacker_SurgicalBed_Right_Exit_Montage") != std::wstring::npos) 222 | { 223 | return ANIM_SURGICALBED_RIGHT_EXIT; 224 | } 225 | else if (ObjName.find(L"CH_Hacker_SurgicalBed_Left_Enter_Montage") != std::wstring::npos) 226 | { 227 | return ANIM_SURGICALBED_LEFT_ENTER; 228 | } 229 | else if (ObjName.find(L"CH_Hacker_SurgicalBed_Left_Exit_Montage") != std::wstring::npos) 230 | { 231 | return ANIM_SURGICALBED_LEFT_EXIT; 232 | } 233 | else if (ObjName.find(L"CH_Hacker_death_p_Montage") != std::wstring::npos) 234 | { 235 | return ANIM_DEATH_P; 236 | } 237 | else if (ObjName.find(L"CH_Hacker_Terminal_Use_Montage") != std::wstring::npos) 238 | { 239 | return ANIM_TERMINAL_USE; 240 | } 241 | else if (ObjName.find(L"CH_Hacker_Terminal_Dismount_Montage") != std::wstring::npos) 242 | { 243 | return ANIM_TERMINAL_DISMOUNT; 244 | } 245 | else if (ObjName.find(L"CH_Hacker_Cryobed_Wake_Montage") != std::wstring::npos) 246 | { 247 | return ANIM_CRYOBED_WAKE; 248 | } 249 | else if (ObjName.find(L"CH_Hacker_respawn") != std::wstring::npos) 250 | { 251 | return ANIM_RESPAWN; 252 | } 253 | else 254 | { 255 | return ANIM_NONE; 256 | } 257 | } 258 | else 259 | { 260 | return ANIM_NONE; 261 | } 262 | } 263 | 264 | float GetElapsedPlayTime() 265 | { 266 | static const auto func = static_class()->find_function(L"GetElapsedPlayTime"); 267 | if (!func) { 268 | // API::get()->log_error("CharacterAction_C::GetElapsedPlayTime not found"); 269 | return 0.0f; 270 | } 271 | else 272 | { 273 | // API::get()->log_error("CharacterAction_C::GetElapsedPlayTime found"); 274 | } 275 | 276 | struct 277 | { 278 | float Result; 279 | } params{ .Result = 0.0 }; 280 | 281 | process_event(func, ¶ms); 282 | // API::get()->log_error("GetElapsedPlayTime: %f", params.Result); 283 | 284 | return params.Result; 285 | } 286 | }; -------------------------------------------------------------------------------- /SystemShockVR/Controller.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "uevr/API.hpp" 4 | 5 | using namespace uevr; 6 | 7 | extern UEVR_Rotatorf ref; 8 | 9 | class Controller : public API::UObject 10 | { 11 | public: 12 | using API::UObject::get_full_name; 13 | 14 | static API::UClass* static_class() 15 | { 16 | static API::UClass* result = nullptr; 17 | if (!result) { 18 | result = API::get()->find_uobject(L"Class /Script/Engine.Controller"); 19 | } 20 | return result; 21 | } 22 | 23 | static Controller* get_instance() 24 | { 25 | auto klass = Controller::static_class(); 26 | Controller* Object = nullptr; 27 | 28 | if (klass) { 29 | std::vector List = klass->get_objects_matching(); 30 | for (size_t i = 0; i < List.size(); i++) { 31 | Object = List[i]; 32 | 33 | std::wstring ObjName = Object->get_full_name(); 34 | // API::get()->log_info("Controller: Object %d of %d, Object name: %ls",i, List.size(), ObjName.c_str()); 35 | 36 | if (ObjName.find(L"CON_Hacker_C") != std::wstring::npos) 37 | { 38 | // API::get()->log_info("Controller found"); 39 | return Object; 40 | } 41 | else 42 | { 43 | // API::get()->log_info("Controller not found"); 44 | } 45 | } 46 | 47 | return Object; 48 | } 49 | return nullptr; 50 | } 51 | 52 | void SetControlRotation(UEVR_Rotatorf* const value) 53 | { 54 | static const auto func = Controller::static_class()->find_function(L"SetControlRotation"); 55 | if (!func) { 56 | // API::get()->log_info("SetControlRotation not found"); 57 | return; 58 | } 59 | else 60 | { 61 | // API::get()->log_info("SetControlRotation found"); 62 | } 63 | 64 | struct 65 | { 66 | UEVR_Rotatorf NewRotation; 67 | } params{ .NewRotation = *value }; 68 | 69 | process_event(func, ¶ms); 70 | } 71 | 72 | UEVR_Rotatorf GetControlRotation() 73 | { 74 | static const auto func = Controller::static_class()->find_function(L"GetControlRotation"); 75 | if (!func) { 76 | // API::get()->log_error("GetControlRotation not found"); 77 | return { 0 }; 78 | } 79 | else 80 | { 81 | // API::get()->log_info("GetControlRotation found"); 82 | } 83 | 84 | struct 85 | { 86 | UEVR_Rotatorf rotation; 87 | } params{ 0 }; 88 | 89 | process_event(func, ¶ms); 90 | return params.rotation; 91 | } 92 | }; -------------------------------------------------------------------------------- /SystemShockVR/INTERACT_Laptop_C.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "uevr/API.hpp" 4 | 5 | using namespace uevr; 6 | 7 | class INTERACT_Laptop_C : public API::UObject 8 | { 9 | public: 10 | using API::UObject::get_full_name; 11 | 12 | static API::UClass* static_class() 13 | { 14 | static API::UClass* result = nullptr; 15 | if (!result) { 16 | result = API::get()->find_uobject(L"BlueprintGeneratedClass /Game/Art/Props/Hacker_Apartment/Laptop/INTERACT_Laptop.INTERACT_Laptop_C"); 17 | } 18 | return result; 19 | } 20 | 21 | static INTERACT_Laptop_C* get_instance() 22 | { 23 | auto klass = INTERACT_Laptop_C::static_class(); 24 | INTERACT_Laptop_C* Object = nullptr; 25 | 26 | if (klass) { 27 | 28 | std::vector List = klass->get_objects_matching(); 29 | 30 | for (size_t i = 0; i < List.size(); i++) { 31 | Object = List[i]; 32 | 33 | std::wstring ObjName = Object->get_full_name(); 34 | // API::get()->log_info("INTERACT_Laptop_C: Object %d of %d, Object name: %ls", i, List.size(), ObjName.c_str()); 35 | 36 | if (ObjName.find(L"INTERACT_Laptop_C") != std::wstring::npos) 37 | { 38 | // API::get()->log_info("INTERACT_Laptop_C found"); 39 | } 40 | else 41 | { 42 | // API::get()->log_info("INTERACT_Laptop_C not found"); 43 | } 44 | } 45 | return Object; 46 | } 47 | return nullptr; 48 | } 49 | 50 | bool get_IsInteracting() 51 | { 52 | return get_bool_property(L"IsInteracting"); 53 | } 54 | }; -------------------------------------------------------------------------------- /SystemShockVR/MOVECONTROL_FocusableInteract_C.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "uevr/API.hpp" 4 | 5 | using namespace uevr; 6 | 7 | class MOVECONTROL_FocusableInteract_C : public API::UObject 8 | { 9 | public: 10 | using API::UObject::get_full_name; 11 | 12 | static API::UClass* static_class() 13 | { 14 | static API::UClass* result = nullptr; 15 | if (!result) { 16 | result = API::get()->find_uobject(L"BlueprintGeneratedClass /Game/Blueprints/Interactables/MOVECONTROL_FocusableInteract.MOVECONTROL_FocusableInteract_C"); 17 | } 18 | return result; 19 | } 20 | 21 | static MOVECONTROL_FocusableInteract_C* get_instance() 22 | { 23 | auto klass = MOVECONTROL_FocusableInteract_C::static_class(); 24 | MOVECONTROL_FocusableInteract_C* Object = nullptr; 25 | 26 | if (klass) { 27 | 28 | std::vector List = klass->get_objects_matching(); 29 | 30 | for (size_t i = 0; i < List.size(); i++) { 31 | Object = List[i]; 32 | 33 | std::wstring ObjName = Object->get_full_name(); 34 | // API::get()->log_info("MOVECONTROL_FocusableInteract_C: Object %d of %d, Object name: %ls", i, List.size(), ObjName.c_str()); 35 | 36 | if (ObjName.find(L"MOVECONTROL_FocusableInteract_C") != std::wstring::npos) 37 | { 38 | // API::get()->log_info("MOVECONTROL_FocusableInteract_C found"); 39 | } 40 | else 41 | { 42 | // API::get()->log_info("MOVECONTROL_FocusableInteract_C not found"); 43 | } 44 | 45 | bool ShouldUseCharacterMovement = Object->get_ShouldUseCharacterMovement(); 46 | bool IsExpiring = Object->get_IsExpiring(); 47 | 48 | if ( (ShouldUseCharacterMovement == false) && (IsExpiring == false) ) 49 | { 50 | return Object; /* Return most recent object */ 51 | } 52 | } 53 | 54 | return Object; 55 | } 56 | return nullptr; 57 | } 58 | 59 | bool get_ShouldUseCharacterMovement() 60 | { 61 | return get_bool_property(L"ShouldUseCharacterMovement"); 62 | } 63 | 64 | bool get_IsExpiring() 65 | { 66 | return get_bool_property(L"IsExpiring"); 67 | } 68 | 69 | 70 | }; 71 | -------------------------------------------------------------------------------- /SystemShockVR/PAWN_Hacker_Simple_C.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "uevr/API.hpp" 4 | 5 | using namespace uevr; 6 | 7 | class PAWN_Hacker_Simple_C : public API::UObject 8 | { 9 | public: 10 | using API::UObject::get_full_name; 11 | 12 | static API::UClass* static_class() 13 | { 14 | static API::UClass* result = nullptr; 15 | if (!result) { 16 | result = API::get()->find_uobject(L"BlueprintGeneratedClass /Game/Blueprints/Characters/Hacker/PAWN_Hacker_Simple.PAWN_Hacker_Simple_C"); 17 | } 18 | return result; 19 | } 20 | 21 | static PAWN_Hacker_Simple_C* get_instance() 22 | { 23 | auto klass = PAWN_Hacker_Simple_C::static_class(); 24 | PAWN_Hacker_Simple_C* Object = nullptr; 25 | 26 | if (klass) { 27 | 28 | std::vector List = klass->get_objects_matching(); 29 | 30 | for (size_t i = 0; i < List.size(); i++) { 31 | Object = List[i]; 32 | 33 | std::wstring ObjName = Object->get_full_name(); 34 | // API::get()->log_info("PAWN_Hacker_Simple_C: Object %d of %d, Object name: %ls", i, List.size(), ObjName.c_str()); 35 | } 36 | 37 | return Object; 38 | } 39 | return nullptr; 40 | } 41 | 42 | bool get_IsTryingToLean() 43 | { 44 | return get_bool_property(L"IsTryingToLean"); 45 | } 46 | 47 | void set_IsTryingToLean(const bool val) 48 | { 49 | set_bool_property(L"IsTryingToLean", val); 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /SystemShockVR/PAWN_SystemShockCharacter_C.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "uevr/API.hpp" 4 | 5 | using namespace uevr; 6 | 7 | class PAWN_SystemShockCharacter_C : public API::UObject 8 | { 9 | public: 10 | using API::UObject::get_full_name; 11 | 12 | static API::UClass* static_class() 13 | { 14 | static API::UClass* result = nullptr; 15 | if (!result) { 16 | result = API::get()->find_uobject(L"BlueprintGeneratedClass /Game/Blueprints/Characters/PAWN_SystemShockCharacter.PAWN_SystemShockCharacter_C"); 17 | } 18 | return result; 19 | } 20 | 21 | static PAWN_SystemShockCharacter_C* get_instance() 22 | { 23 | auto klass = PAWN_SystemShockCharacter_C::static_class(); 24 | PAWN_SystemShockCharacter_C* Object = nullptr; 25 | 26 | if (klass) { 27 | 28 | std::vector List = klass->get_objects_matching(); 29 | 30 | for (size_t i = 0; i < List.size(); i++) { 31 | Object = List[i]; 32 | 33 | std::wstring ObjName = Object->get_full_name(); 34 | // API::get()->log_info("PAWN_SystemShockCharacter_C: Object %d of %d, Object name: %ls", i, List.size(), ObjName.c_str()); 35 | 36 | if (ObjName.find(L"PAWN_Hacker_Implant_C") != std::wstring::npos) 37 | { 38 | // API::get()->log_info("PAWN_SystemShockCharacter_C found"); 39 | return Object; 40 | } 41 | else 42 | { 43 | // API::get()->log_info("PAWN_SystemShockCharacter_C not found"); 44 | } 45 | } 46 | } 47 | return nullptr; 48 | } 49 | 50 | bool get_IsAlive() 51 | { 52 | return get_bool_property(L"IsAlive"); 53 | } 54 | }; 55 | -------------------------------------------------------------------------------- /SystemShockVR/SAVE_Settings_C.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "uevr/API.hpp" 4 | 5 | using namespace uevr; 6 | 7 | class SAVE_Settings_C : public API::UObject 8 | { 9 | public: 10 | using API::UObject::get_full_name; 11 | 12 | static API::UClass* static_class() 13 | { 14 | static API::UClass* result = nullptr; 15 | if (!result) { 16 | result = API::get()->find_uobject(L"BlueprintGeneratedClass /Game/Blueprints/UI/HUD/Widgets/Settings/SAVE_Settings.SAVE_Settings_C"); 17 | } 18 | return result; 19 | } 20 | 21 | static SAVE_Settings_C* get_instance() 22 | { 23 | auto klass = SAVE_Settings_C::static_class(); 24 | SAVE_Settings_C* Object = nullptr; 25 | 26 | if (klass) { 27 | 28 | std::vector List = klass->get_objects_matching(); 29 | 30 | for (size_t i = 0; i < List.size(); i++) { 31 | Object = List[i]; 32 | 33 | std::wstring ObjName = Object->get_full_name(); 34 | // API::get()->log_info("SAVE_Settings_C: Object %d of %d, Object name: %ls", i, List.size(), ObjName.c_str()); 35 | 36 | if (ObjName.find(L"SAVE_Settings_C") != std::wstring::npos) 37 | { 38 | // API::get()->log_info("SAVE_Settings_C found"); 39 | Object->set_CrouchToggle(true); 40 | Object->set_FocusAimToggle(false); 41 | Object->SetFOV(120.0, true); 42 | Object->ApplyFOV(120.0, true); 43 | Object->SetReloadCanUseBatteries(false); 44 | Object->ApplyReloadCanUseBatteries(false); 45 | } 46 | else 47 | { 48 | // API::get()->log_info("SAVE_Settings_C not found"); 49 | } 50 | } 51 | } 52 | return nullptr; 53 | } 54 | 55 | void set_CrouchToggle(const bool val) 56 | { 57 | set_bool_property(L"CrouchToggle", val); 58 | } 59 | 60 | void set_FocusAimToggle(const bool val) 61 | { 62 | set_bool_property(L"FocusAimToggle", val); 63 | } 64 | 65 | void SetFOV(float NewValue, bool IsRealspace) 66 | { 67 | static const auto func = SAVE_Settings_C::static_class()->find_function(L"SetFOV"); 68 | if (!func) { 69 | // API::get()->log_info("SetFOV not found"); 70 | return; 71 | } 72 | else 73 | { 74 | // API::get()->log_info("SetFOV found"); 75 | } 76 | 77 | struct 78 | { 79 | float NewValue; 80 | bool IsRealspace; 81 | } params{ .NewValue = NewValue, .IsRealspace = IsRealspace }; 82 | 83 | process_event(func, ¶ms); 84 | } 85 | 86 | void ApplyFOV(float NewValue, bool IsRealspace) 87 | { 88 | static const auto func = SAVE_Settings_C::static_class()->find_function(L"ApplyFOV"); 89 | if (!func) { 90 | // API::get()->log_info("ApplyFOV not found"); 91 | return; 92 | } 93 | else 94 | { 95 | // API::get()->log_info("ApplyFOV found"); 96 | } 97 | 98 | struct 99 | { 100 | float NewValue; 101 | bool IsRealspace; 102 | } params{ .NewValue = NewValue, .IsRealspace = IsRealspace }; 103 | 104 | process_event(func, ¶ms); 105 | } 106 | 107 | void SetReloadCanUseBatteries(bool NewValue) 108 | { 109 | static const auto func = SAVE_Settings_C::static_class()->find_function(L"SetReloadCanUseBatteries"); 110 | if (!func) { 111 | // API::get()->log_info("SetReloadCanUseBatteries not found"); 112 | return; 113 | } 114 | else 115 | { 116 | // API::get()->log_info("SetReloadCanUseBatteries found"); 117 | } 118 | 119 | struct 120 | { 121 | bool NewValue; 122 | } params{ .NewValue = NewValue }; 123 | 124 | process_event(func, ¶ms); 125 | } 126 | 127 | void ApplyReloadCanUseBatteries(bool NewValue) 128 | { 129 | static const auto func = SAVE_Settings_C::static_class()->find_function(L"ApplyReloadCanUseBatteries"); 130 | if (!func) { 131 | // API::get()->log_info("ApplyReloadCanUseBatteries not found"); 132 | return; 133 | } 134 | else 135 | { 136 | // API::get()->log_info("ApplyReloadCanUseBatteries found"); 137 | } 138 | 139 | struct 140 | { 141 | bool NewValue; 142 | } params{ .NewValue = NewValue }; 143 | 144 | process_event(func, ¶ms); 145 | } 146 | }; 147 | -------------------------------------------------------------------------------- /SystemShockVR/SystemShockVR.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 17.0 23 | Win32Proj 24 | {b47fc2a4-acef-42e0-813b-6f5573c4d1b2} 25 | SystemShockVR 26 | 10.0 27 | 28 | 29 | 30 | DynamicLibrary 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | DynamicLibrary 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | DynamicLibrary 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | DynamicLibrary 50 | false 51 | v143 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | Level3 76 | true 77 | WIN32;_DEBUG;SYSTEMSHOCKVR_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 78 | true 79 | Use 80 | pch.h 81 | 82 | 83 | Windows 84 | true 85 | false 86 | 87 | 88 | 89 | 90 | Level3 91 | true 92 | true 93 | true 94 | WIN32;NDEBUG;SYSTEMSHOCKVR_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 95 | true 96 | Use 97 | pch.h 98 | 99 | 100 | Windows 101 | true 102 | true 103 | true 104 | false 105 | 106 | 107 | 108 | 109 | Level3 110 | true 111 | _DEBUG;SYSTEMSHOCKVR_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 112 | true 113 | NotUsing 114 | pch.h 115 | stdcpp20 116 | 117 | 118 | Windows 119 | true 120 | false 121 | 122 | 123 | 124 | 125 | Level3 126 | true 127 | true 128 | true 129 | NDEBUG;SYSTEMSHOCKVR_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) 130 | true 131 | NotUsing 132 | pch.h 133 | stdcpp20 134 | 135 | 136 | Windows 137 | true 138 | true 139 | true 140 | false 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | Create 166 | Create 167 | Create 168 | Create 169 | 170 | 171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /SystemShockVR/SystemShockVR.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Header Files 20 | 21 | 22 | Header Files 23 | 24 | 25 | Header Files 26 | 27 | 28 | Header Files 29 | 30 | 31 | Header Files 32 | 33 | 34 | Header Files 35 | 36 | 37 | Header Files 38 | 39 | 40 | Header Files 41 | 42 | 43 | Header Files 44 | 45 | 46 | Header Files 47 | 48 | 49 | Header Files 50 | 51 | 52 | Header Files 53 | 54 | 55 | Header Files 56 | 57 | 58 | Header Files 59 | 60 | 61 | Header Files 62 | 63 | 64 | Header Files 65 | 66 | 67 | Header Files 68 | 69 | 70 | 71 | 72 | Source Files 73 | 74 | 75 | Source Files 76 | 77 | 78 | -------------------------------------------------------------------------------- /SystemShockVR/WIDGET_InventoryContextMenu_C.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "uevr/API.hpp" 4 | 5 | using namespace uevr; 6 | 7 | class WIDGET_InventoryContextMenu_C : public API::UObject 8 | { 9 | public: 10 | using API::UObject::get_full_name; 11 | 12 | static API::UClass* static_class() 13 | { 14 | static API::UClass* result = nullptr; 15 | if (!result) { 16 | result = API::get()->find_uobject(L"WidgetBlueprintGeneratedClass /Game/Blueprints/UI/HUD/Widgets/ContextMenu/WIDGET_InventoryContextMenu.WIDGET_InventoryContextMenu_C"); 17 | } 18 | return result; 19 | } 20 | 21 | static WIDGET_InventoryContextMenu_C* get_instance() 22 | { 23 | auto klass = WIDGET_InventoryContextMenu_C::static_class(); 24 | WIDGET_InventoryContextMenu_C* Object = nullptr; 25 | 26 | if (klass) { 27 | 28 | std::vector List = klass->get_objects_matching(); 29 | 30 | for (size_t i = 0; i < List.size(); i++) { 31 | Object = List[i]; 32 | 33 | std::wstring ObjName = Object->get_full_name(); 34 | // API::get()->log_info("PAWN_Hacker_Simple_C: Object %d of %d, Object name: %ls", i, List.size(), ObjName.c_str()); 35 | 36 | bool IsInventoryContextMenuEnabled = Object->get_IsInventoryContextMenuEnabled(); 37 | if (IsInventoryContextMenuEnabled == true) 38 | { 39 | return Object; 40 | } 41 | } 42 | 43 | return Object; 44 | } 45 | return nullptr; 46 | } 47 | 48 | bool get_IsInventoryContextMenuEnabled() 49 | { 50 | return get_bool_property(L"IsInventoryContextMenuEnabled"); 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /SystemShockVR/WIDGET_MainMenu_InGame_C.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "uevr/API.hpp" 4 | 5 | using namespace uevr; 6 | 7 | class WIDGET_MainMenu_InGame_C : public API::UObject 8 | { 9 | public: 10 | using API::UObject::get_full_name; 11 | 12 | static API::UClass* static_class() 13 | { 14 | static API::UClass* result = nullptr; 15 | if (!result) { 16 | result = API::get()->find_uobject(L"WidgetBlueprintGeneratedClass /Game/Blueprints/UI/MainMenu/WIDGET_MainMenu_InGame.WIDGET_MainMenu_InGame_C"); 17 | } 18 | return result; 19 | } 20 | 21 | static WIDGET_MainMenu_InGame_C* get_instance() 22 | { 23 | auto klass = WIDGET_MainMenu_InGame_C::static_class(); 24 | WIDGET_MainMenu_InGame_C* Object = nullptr; 25 | 26 | if (klass) { 27 | 28 | std::vector List = klass->get_objects_matching(); 29 | 30 | for (size_t i = 0; i < List.size(); i++) { 31 | Object = List[i]; 32 | 33 | std::wstring ObjName = Object->get_full_name(); 34 | // API::get()->log_info("WIDGET_MainMenu_InGame_C: Object %d of %d, Object name: %ls", i, List.size(), ObjName.c_str()); 35 | 36 | if (ObjName.find(L"GI_SinglePlayer_C") != std::wstring::npos) 37 | { 38 | // API::get()->log_info("WIDGET_MainMenu_InGame_C found"); 39 | if ((Object->get_IsMainMenuEnabled() == false) || (i == List.size() - 1)) { 40 | return Object; 41 | } 42 | } 43 | else 44 | { 45 | // API::get()->log_info("WIDGET_MainMenu_InGame_C not found"); 46 | } 47 | } 48 | } 49 | return nullptr; 50 | } 51 | 52 | bool get_IsMainMenuEnabled() 53 | { 54 | return get_bool_property(L"IsMainMenuEnabled"); 55 | } 56 | }; 57 | -------------------------------------------------------------------------------- /SystemShockVR/WIDGET_PlayerHUD_C.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "uevr/API.hpp" 4 | 5 | using namespace uevr; 6 | 7 | extern bool IsBracket; 8 | extern bool ContextMode; 9 | 10 | class WIDGET_PlayerHUD_C : public API::UObject 11 | { 12 | public: 13 | using API::UObject::get_full_name; 14 | 15 | static API::UClass* static_class() 16 | { 17 | static API::UClass* result = nullptr; 18 | if (!result) { 19 | result = API::get()->find_uobject(L"WidgetBlueprintGeneratedClass /Game/Blueprints/UI/HUD/Widgets/WIDGET_PlayerHUD.WIDGET_PlayerHUD_C"); 20 | } 21 | return result; 22 | } 23 | 24 | static WIDGET_PlayerHUD_C* get_instance() 25 | { 26 | auto klass = WIDGET_PlayerHUD_C::static_class(); 27 | WIDGET_PlayerHUD_C* Object = nullptr; 28 | 29 | if (klass) { 30 | 31 | std::vector List = klass->get_objects_matching(); 32 | 33 | for (size_t i = 0; i < List.size(); i++) { 34 | Object = List[i]; 35 | 36 | std::wstring ObjName = Object->get_full_name(); 37 | // API::get()->log_info("WIDGET_PlayerHUD_C: Object %d of %d, Object name: %ls", i, List.size(), ObjName.c_str()); 38 | 39 | if (ObjName.find(L"WIDGET_PlayerHUD_C") != std::wstring::npos) 40 | { 41 | // API::get()->log_info("WIDGET_PlayerHUD_C found"); 42 | } 43 | else 44 | { 45 | // API::get()->log_info("WIDGET_PlayerHUD_C not found"); 46 | } 47 | 48 | if (IsBracket == false) 49 | { 50 | Object->set_ShouldShowBrackets(false); 51 | Object->ShowTargetBrackets(false); 52 | } 53 | else 54 | { 55 | Object->set_ShouldShowBrackets(true); 56 | Object->ShowTargetBrackets(true); 57 | } 58 | 59 | bool bIsMFDVisible = Object->get_is_mfd_visible(); 60 | 61 | if (ContextMode == true) { 62 | Object->SetMouseCursorVisibility(false); 63 | } 64 | else 65 | { 66 | Object->SetMouseCursorVisibility(true); 67 | } 68 | 69 | if (bIsMFDVisible == true) 70 | { 71 | return Object; /* Return most recent object */ 72 | } 73 | } 74 | return Object; 75 | } 76 | return nullptr; 77 | } 78 | 79 | bool get_is_mfd_visible() 80 | { 81 | return get_bool_property(L"bIsMFDVisible"); 82 | } 83 | 84 | void set_ShouldShowBrackets(const bool val) 85 | { 86 | set_bool_property(L"ShouldShowBrackets", val); 87 | } 88 | 89 | void SetMouseCursorVisibility(bool Visible) 90 | { 91 | static const auto func = static_class()->find_function(L"SetMouseCursorVisibility"); 92 | if (!func) { 93 | // API::get()->log_info("SetMouseCursorVisibility not found"); 94 | return; 95 | } 96 | else 97 | { 98 | // API::get()->log_info("SetMouseCursorVisibility found"); 99 | } 100 | 101 | struct 102 | { 103 | bool Visible; 104 | } params{ .Visible = Visible }; 105 | 106 | process_event(func, ¶ms); 107 | } 108 | 109 | void ShowTargetBrackets(bool Visible) 110 | { 111 | static const auto func = WIDGET_PlayerHUD_C::static_class()->find_function(L"ShowTargetBrackets"); 112 | if (!func) { 113 | // API::get()->log_info("ShowTargetBrackets not found"); 114 | return; 115 | } 116 | else 117 | { 118 | // API::get()->log_info("ShowTargetBrackets found"); 119 | } 120 | 121 | struct 122 | { 123 | bool Visible; 124 | } params{ .Visible = Visible }; 125 | 126 | process_event(func, ¶ms); 127 | } 128 | 129 | void set_IsTryingToVaporizeInventory(const bool val) 130 | { 131 | set_bool_property(L"IsTryingToVaporizeInventory", val); 132 | } 133 | }; 134 | -------------------------------------------------------------------------------- /SystemShockVR/cxxtimer.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | MIT License 4 | 5 | Copyright (c) 2017 André L. Maravilha 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | 25 | */ 26 | 27 | #ifndef CXX_TIMER_HPP 28 | #define CXX_TIMER_HPP 29 | 30 | #include 31 | 32 | 33 | namespace cxxtimer { 34 | 35 | /** 36 | * This class works as a stopwatch. 37 | */ 38 | class Timer { 39 | 40 | public: 41 | 42 | /** 43 | * Constructor. 44 | * 45 | * @param start 46 | * If true, the timer is started just after construction. 47 | * Otherwise, it will not be automatically started. 48 | */ 49 | Timer(bool start = false); 50 | 51 | /** 52 | * Copy constructor. 53 | * 54 | * @param other 55 | * The object to be copied. 56 | */ 57 | Timer(const Timer& other) = default; 58 | 59 | /** 60 | * Transfer constructor. 61 | * 62 | * @param other 63 | * The object to be transfered. 64 | */ 65 | Timer(Timer&& other) = default; 66 | 67 | /** 68 | * Destructor. 69 | */ 70 | virtual ~Timer() = default; 71 | 72 | /** 73 | * Assignment operator by copy. 74 | * 75 | * @param other 76 | * The object to be copied. 77 | * 78 | * @return A reference to this object. 79 | */ 80 | Timer& operator=(const Timer& other) = default; 81 | 82 | /** 83 | * Assignment operator by transfer. 84 | * 85 | * @param other 86 | * The object to be transferred. 87 | * 88 | * @return A reference to this object. 89 | */ 90 | Timer& operator=(Timer&& other) = default; 91 | 92 | /** 93 | * Start/resume the timer. 94 | */ 95 | void start(); 96 | 97 | /** 98 | * Stop/pause the timer. 99 | */ 100 | void stop(); 101 | 102 | /** 103 | * Reset the timer. 104 | */ 105 | void reset(); 106 | 107 | /** 108 | * Return the elapsed time. 109 | * 110 | * @param duration_t 111 | * The duration type used to return the time elapsed. If not 112 | * specified, it returns the time as represented by 113 | * std::chrono::milliseconds. 114 | * 115 | * @return The elapsed time. 116 | */ 117 | template 118 | typename duration_t::rep count() const; 119 | 120 | private: 121 | 122 | bool started_; 123 | bool paused_; 124 | std::chrono::steady_clock::time_point reference_; 125 | std::chrono::duration accumulated_; 126 | }; 127 | 128 | } 129 | 130 | 131 | inline cxxtimer::Timer::Timer(bool start) : 132 | started_(false), paused_(false), 133 | reference_(std::chrono::steady_clock::now()), 134 | accumulated_(std::chrono::duration(0)) { 135 | if (start) { 136 | this->start(); 137 | } 138 | } 139 | 140 | inline void cxxtimer::Timer::start() { 141 | if (!started_) { 142 | started_ = true; 143 | paused_ = false; 144 | accumulated_ = std::chrono::duration(0); 145 | reference_ = std::chrono::steady_clock::now(); 146 | } else if (paused_) { 147 | reference_ = std::chrono::steady_clock::now(); 148 | paused_ = false; 149 | } 150 | } 151 | 152 | inline void cxxtimer::Timer::stop() { 153 | if (started_ && !paused_) { 154 | std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); 155 | accumulated_ = accumulated_ + std::chrono::duration_cast< std::chrono::duration >(now - reference_); 156 | paused_ = true; 157 | } 158 | } 159 | 160 | inline void cxxtimer::Timer::reset() { 161 | if (started_) { 162 | started_ = false; 163 | paused_ = false; 164 | reference_ = std::chrono::steady_clock::now(); 165 | accumulated_ = std::chrono::duration(0); 166 | } 167 | } 168 | 169 | template 170 | typename duration_t::rep cxxtimer::Timer::count() const { 171 | if (started_) { 172 | if (paused_) { 173 | return std::chrono::duration_cast(accumulated_).count(); 174 | } else { 175 | return std::chrono::duration_cast( 176 | accumulated_ + (std::chrono::steady_clock::now() - reference_)).count(); 177 | } 178 | } else { 179 | return duration_t(0).count(); 180 | } 181 | } 182 | 183 | 184 | #endif 185 | -------------------------------------------------------------------------------- /SystemShockVR/dllmain.cpp: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_WARNINGS 1 2 | #define _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING 1 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "cxxtimer.hpp" 12 | 13 | #include "uevr/Plugin.hpp" 14 | #include "CH_Hacker_AnimBP_C.hpp" 15 | #include "CharacterAction_C.hpp" 16 | #include "COMP_HackerInventory_C.hpp" 17 | #include "COMP_MoveControlManager_C.hpp" 18 | #include "CON_Hacker_C.hpp" 19 | #include "Controller.hpp" 20 | #include "INTERACT_Laptop_C.hpp" 21 | #include "MOVECONTROL_FocusableInteract_C.hpp" 22 | #include "PAWN_Hacker_Simple_C.hpp" 23 | #include "PAWN_SystemShockCharacter_C.hpp" 24 | #include "SAVE_Settings_C.hpp" 25 | #include "WIDGET_InventoryContextMenu_C.hpp" 26 | #include "WIDGET_MainMenu_InGame_C.hpp" 27 | #include "WIDGET_PlayerHUD_C.hpp" 28 | #include "pch.h" 29 | 30 | void smooth_rotation(XINPUT_STATE* state); 31 | void smooth_rotation_cyberspace(XINPUT_STATE* state); 32 | void send_key(WORD key, bool key_up); 33 | void anim_cine_intro_height_recalibration(); 34 | void reset_height(); 35 | void pawn_Process(uint8_t current_pawn_state); 36 | 37 | typedef struct _TIMER_STRUCT 38 | { 39 | bool* XPressed; 40 | std::time_t* TimeStamp; 41 | } TIMER_STRUCT; 42 | 43 | /** The various states that the pawn state machine can be in */ 44 | typedef enum ePawnState 45 | { 46 | PAWN_UNKNOWN, 47 | PAWN_PLAYERGHOST, 48 | PAWN_HACKERSIMPLE, 49 | PAWN_HACKERIMPLANT, 50 | PAWN_AVATAR, 51 | } 52 | pawn_State_t; 53 | 54 | using namespace uevr; 55 | 56 | #define PLUGIN_LOG_ONCE(...) {\ 57 | static bool _logged_ = false; \ 58 | if (!_logged_) { \ 59 | _logged_ = true; \ 60 | API::get()->log_info(__VA_ARGS__); \ 61 | }} 62 | 63 | #define INPUT_DEADZONE_HI ( 0.90f * FLOAT(0x7FFF) ) // Default to 90% of the +/- 32767 range. 64 | #define INPUT_DEADZONE_MED ( 0.45f * FLOAT(0x7FFF) ) // Default to 45% of the +/- 32767 range. 65 | #define INPUT_DEADZONE_LO ( 0.01f * FLOAT(0x7FFF) ) // Default to 01% of the +/- 32767 range. 66 | 67 | // Instantiate cxxtimer::Timer object 68 | cxxtimer::Timer timer; 69 | 70 | bool KEY_DN = false; 71 | 72 | bool IsLaptop = false; 73 | 74 | bool IsMFDPrev = false; 75 | bool IsMFDCurrent = false; 76 | bool IsContextMenu = false; 77 | bool ContextMode = false; 78 | bool SendKeyDebounce = false; 79 | bool VaporizeInventory = false; 80 | 81 | bool IsMovementPrev = true; 82 | bool IsMovementCurrent = true; 83 | 84 | bool ActivateIK = false; 85 | bool DeactivateIK = false; 86 | 87 | bool IsCrosshairPrev = false; 88 | bool IsCrosshairCurrent = false; 89 | 90 | bool IsAlivePrev = false; 91 | bool IsAliveCurrent = false; 92 | 93 | bool IsMainMenuPrev = false; 94 | bool IsMainMenuCurrent = false; 95 | 96 | float pose_y_current = 0.0; 97 | float pose_y_max = 0.0; 98 | float pose_y_min = 0.0; 99 | 100 | bool IsPhysicalCrouching = false; 101 | bool IsCrouchingCurrent = false; 102 | 103 | bool IsInteractMode = false; 104 | bool InteractToggle = false; 105 | bool IsEquipped = false; 106 | bool IKSanityCheck = false; 107 | bool InteractExit = false; 108 | bool RoomscaleMontageOverride = false; 109 | bool IsLean = false; 110 | 111 | bool IsSprinting = false; 112 | bool IsSmoothTurn = false; 113 | bool InitSmoothTurn = false; 114 | char snap_angle; 115 | 116 | bool IsBracket = false; 117 | bool ButtonsDownY = false; 118 | 119 | bool disable_equip = false; 120 | bool validate_equip = false; 121 | 122 | uint8_t last_pawn_state = PAWN_UNKNOWN; 123 | uint8_t current_pawn_state = PAWN_UNKNOWN; 124 | 125 | UEVR_Vector3f pose; 126 | UEVR_Quaternionf rot, offset; 127 | UEVR_Rotatorf ref; 128 | float controller_rot_y = 0.0f; 129 | 130 | float old_position[3] = { 0.0 }; 131 | float difference[3] = { 0.0 }; 132 | float difference_adjusted[3] = { 0.0 }; 133 | float abs_difference[3] = { 0.0 }; 134 | float old_difference = 0.0; 135 | 136 | bool melee_attack = false; 137 | bool rapier_attack = false; 138 | bool melee_equipped = false; 139 | bool rapier_equipped = false; 140 | bool melee_debounce = false; 141 | bool melee_dn = false; 142 | bool melee_fwd = false; 143 | 144 | int melee_dn_count = 0; 145 | int melee_fwd_count = 0; 146 | 147 | class SystemShockPlugin : public uevr::Plugin { 148 | public: 149 | SystemShockPlugin() = default; 150 | 151 | void on_dllmain() override {} 152 | 153 | void on_initialize() override { 154 | // Logs to the appdata UnrealVRMod log.txt file 155 | API::get()->log_info("%s", "Salt the fries"); 156 | } 157 | 158 | void on_pre_engine_tick(API::UGameEngine* engine, float delta) override 159 | { 160 | PLUGIN_LOG_ONCE("Pre Engine Tick: %f", delta); 161 | 162 | /* ========== GESTURE MELEE ========== */ 163 | const UEVR_VRData* vr = API::get()->param()->vr; 164 | 165 | if (!vr->is_runtime_ready()) 166 | return; 167 | 168 | UEVR_Vector3f left_position, right_position; 169 | UEVR_Quaternionf left_rotation, right_rotation; 170 | 171 | vr->get_pose(vr->get_right_controller_index(), &right_position, &right_rotation); 172 | 173 | difference[0] = right_position.x - old_position[0]; 174 | abs_difference[0] = abs(difference[0]); 175 | difference[1] = right_position.y - old_position[1]; 176 | abs_difference[1] = abs(difference[1]); 177 | difference[2] = right_position.z - old_position[2]; 178 | abs_difference[2] = abs(difference[2]); 179 | 180 | difference_adjusted[2] = difference[2] + old_difference; 181 | // API::get()->log_info("speed.y = %f", difference[1]); 182 | // API::get()->log_info("speed.z = %f", difference[2]); 183 | 184 | if (difference[1] <= -.02) 185 | { 186 | melee_dn = true; 187 | } 188 | 189 | if (difference_adjusted[2] <= -.012) 190 | { 191 | melee_fwd = true; 192 | } 193 | 194 | if ((melee_dn == true) && (melee_fwd == true) && (melee_debounce == true)) 195 | { 196 | melee_attack = true; 197 | 198 | melee_dn = false; 199 | melee_fwd = false; 200 | 201 | melee_dn_count = 0; 202 | melee_fwd_count = 0; 203 | } 204 | 205 | if (difference[2] <= -.02) 206 | { 207 | rapier_attack = true; 208 | } 209 | 210 | old_position[0] = right_position.x; 211 | old_position[1] = right_position.y; 212 | old_position[2] = right_position.z; 213 | 214 | old_difference = 0; 215 | if (difference[2] < 0)old_difference = difference[2]; 216 | 217 | if (melee_fwd == true)melee_fwd_count++; 218 | if (melee_dn == true)melee_dn_count++; 219 | 220 | if ((melee_dn_count > 1) || (melee_fwd_count > 1)) 221 | { 222 | melee_dn = false; 223 | melee_fwd = false; 224 | 225 | melee_dn_count = 0; 226 | melee_fwd_count = 0; 227 | } 228 | 229 | melee_debounce = true; 230 | } 231 | 232 | void on_post_engine_tick(API::UGameEngine* engine, float delta) override 233 | { 234 | PLUGIN_LOG_ONCE("Post Engine Tick: %f", delta); 235 | } 236 | 237 | void on_pre_slate_draw_window(UEVR_FSlateRHIRendererHandle renderer, UEVR_FViewportInfoHandle viewport_info) override { 238 | PLUGIN_LOG_ONCE("Pre Slate Draw Window"); 239 | } 240 | 241 | void on_post_slate_draw_window(UEVR_FSlateRHIRendererHandle renderer, UEVR_FViewportInfoHandle viewport_info) override { 242 | PLUGIN_LOG_ONCE("Post Slate Draw Window"); 243 | } 244 | 245 | //******************************************************************************************* 246 | // This is the controller input routine. Everything happens here. 247 | //******************************************************************************************* 248 | void on_xinput_get_state(uint32_t* retval, uint32_t user_index, XINPUT_STATE* state) { 249 | 250 | if (state != NULL) { 251 | 252 | /* ========== CAMERA FIX ========== */ 253 | 254 | pawn_Process(current_pawn_state); 255 | 256 | const auto pawn = API::get()->get_local_pawn(0); 257 | 258 | if (pawn->get_full_name().starts_with(L"PAWN_Avatar")) { 259 | 260 | current_pawn_state = PAWN_AVATAR; 261 | } 262 | else if (pawn->get_full_name().starts_with(L"PAWN_PlayerGhost")) 263 | { 264 | current_pawn_state = PAWN_PLAYERGHOST; 265 | } 266 | else if (pawn->get_full_name().starts_with(L"PAWN_Hacker_Simple")) 267 | { 268 | current_pawn_state = PAWN_HACKERSIMPLE; 269 | } 270 | else if (pawn->get_full_name().starts_with(L"PAWN_Hacker_Implant")) 271 | { 272 | current_pawn_state = PAWN_HACKERIMPLANT; 273 | } 274 | 275 | /* ========== MAIN GAME FIXES ========== */ 276 | 277 | if (current_pawn_state == PAWN_HACKERSIMPLE) /* Intro fix */ 278 | { 279 | const auto PHSC = PAWN_Hacker_Simple_C::get_instance(); 280 | 281 | if (PHSC) 282 | { 283 | IsLean = PHSC->get_IsTryingToLean(); 284 | 285 | if (IsLean) 286 | { 287 | PHSC->set_IsTryingToLean(false); /* Disable lean input as this can freeze movement in VR */ 288 | } 289 | } 290 | 291 | const auto ILC = INTERACT_Laptop_C::get_instance(); 292 | if (ILC) 293 | { 294 | IsLaptop = ILC->get_IsInteracting(); 295 | 296 | if (IsLaptop == true) 297 | { 298 | API::UObjectHook::set_disabled(true); /* Fix shaky cam during intro */ 299 | anim_cine_intro_height_recalibration(); /* Disabling hooks requires height recalibration for laptop sequence */ 300 | } 301 | } 302 | } 303 | else 304 | { 305 | IsLaptop = false; 306 | } 307 | 308 | if (current_pawn_state == PAWN_AVATAR) 309 | { 310 | smooth_rotation_cyberspace(state); 311 | 312 | if (state->Gamepad.sThumbRY <= -200) 313 | { 314 | state->Gamepad.wButtons |= XINPUT_GAMEPAD_B; 315 | } 316 | else if (state->Gamepad.sThumbRY >= 200) 317 | { 318 | state->Gamepad.wButtons |= XINPUT_GAMEPAD_A; 319 | } 320 | } 321 | 322 | if (current_pawn_state == PAWN_HACKERIMPLANT) 323 | { 324 | /* ========== CONFIGURE IN-GAME SETTINGS ========== */ 325 | 326 | const auto SSC = SAVE_Settings_C::get_instance(); 327 | 328 | const auto PHSC = PAWN_Hacker_Simple_C::get_instance(); 329 | 330 | if (PHSC) 331 | { 332 | IsLean = PHSC->get_IsTryingToLean(); 333 | 334 | if (IsLean) 335 | { 336 | PHSC->set_IsTryingToLean(false); /* Disable lean input as this can freeze movement in VR */ 337 | } 338 | } 339 | 340 | /* ========== GESTURE MELEE ========== */ 341 | 342 | COMP_HackerInventory_C::get_instance(); 343 | 344 | if (melee_attack == true && melee_equipped == true) /* Check if gesture performed while pipe or wrench is equipped */ 345 | { 346 | if (state->Gamepad.bRightTrigger >= 200) 347 | { 348 | state->Gamepad.bRightTrigger = 0; /* Perform heavy melee */ 349 | } 350 | else 351 | { 352 | state->Gamepad.bRightTrigger = 200; /* Perform light melee */ 353 | } 354 | melee_attack = false; 355 | } 356 | 357 | if (rapier_attack == true && rapier_equipped == true) /* Check if gesture performed while rapier is equipped */ 358 | { 359 | if (state->Gamepad.bRightTrigger >= 200) 360 | { 361 | state->Gamepad.bRightTrigger = 0; /* Perform heavy melee */ 362 | } 363 | else 364 | { 365 | state->Gamepad.bRightTrigger = 200; /* Perform light melee */ 366 | } 367 | rapier_attack = false; 368 | } 369 | 370 | /* ========== INGAME MENU FIXES ========== */ 371 | 372 | IsMainMenuPrev = IsMainMenuCurrent; 373 | 374 | const auto WMMIGC = WIDGET_MainMenu_InGame_C::get_instance(); 375 | 376 | if (WMMIGC) { 377 | 378 | IsMainMenuCurrent = WMMIGC->get_IsMainMenuEnabled(); 379 | 380 | if (IsMainMenuPrev != IsMainMenuCurrent) 381 | { 382 | if (IsMainMenuCurrent == false) 383 | { 384 | // API::get()->log_info("Main menu open"); 385 | auto& vr = API::get()->param()->vr; 386 | vr->set_snap_turn_enabled(true); 387 | } 388 | else 389 | { 390 | // API::get()->log_info("Main menu closed"); 391 | auto& vr = API::get()->param()->vr; 392 | vr->set_snap_turn_enabled(false); /* Prevent snap turn when camera superimposed with menu screen */ 393 | } 394 | } 395 | } 396 | 397 | /* ========== PHYSICAL CROUCHING ========== */ 398 | 399 | const auto hmd_index = API::get()->param()->vr->get_hmd_index(); 400 | 401 | API::get()->param()->vr->get_pose(hmd_index, &pose, &rot); 402 | API::get()->param()->vr->get_rotation_offset(&offset); 403 | // API::get()->log_info("pose.y = %f", pose.y); 404 | 405 | pose_y_current = pose.y; 406 | 407 | if (pose_y_current >= pose_y_max) 408 | { 409 | pose_y_max = pose_y_current; /* Track max position of HMD */ 410 | // API::get()->log_info("pose_y_max = %f", pose_y_max); 411 | } 412 | else if (pose_y_current <= pose_y_min) 413 | { 414 | pose_y_min = pose_y_current; /* Track min position of HMD */ 415 | // API::get()->log_info("pose_y_min = %f", pose_y_min); 416 | } 417 | 418 | if ( (IsPhysicalCrouching == false) && (pose_y_current < (pose_y_max-0.4) ) ) /* Check HMD position delta to detect if player is physically crouched */ 419 | { 420 | IsPhysicalCrouching = true; 421 | // API::get()->log_info("IsPhysicalCrouching = 1"); 422 | 423 | KEY_DN = true; 424 | send_key(VK_LCONTROL, KEY_DN); 425 | } 426 | else if ( (IsPhysicalCrouching == true) && (pose_y_current > (pose_y_min + 0.4))) 427 | { 428 | IsPhysicalCrouching = false; 429 | // API::get()->log_info("IsPhysicalCrouching = 0"); 430 | 431 | KEY_DN = true; 432 | send_key(VK_LCONTROL, KEY_DN); 433 | } 434 | 435 | /* ========== DEATH ANIMATION FIX ========== */ 436 | 437 | IsAlivePrev = IsAliveCurrent; 438 | 439 | const auto PSSC = PAWN_SystemShockCharacter_C::get_instance(); 440 | 441 | if (PSSC) { 442 | 443 | IsAliveCurrent = PSSC->get_IsAlive(); 444 | 445 | if (IsAlivePrev != IsAliveCurrent) 446 | { 447 | if (IsAliveCurrent == false) 448 | { 449 | API::UObjectHook::set_disabled(true); /* Disable hooks if killed to allow death animations to play in VR */ 450 | 451 | auto& vr = API::get()->param()->vr; 452 | vr->set_mod_value("VR_RoomscaleMovement", "false"); 453 | } 454 | else 455 | { 456 | API::UObjectHook::set_disabled(false); 457 | 458 | auto& vr = API::get()->param()->vr; 459 | vr->set_mod_value("VR_RoomscaleMovement", "true"); 460 | } 461 | } 462 | } 463 | 464 | /* ========== MONTAGE FIX ========== */ 465 | 466 | const auto CAC = CharacterAction_C::get_instance(); 467 | 468 | /* ========== CROSSHAIR TOGGLE ========== */ 469 | 470 | const auto CMCMC = COMP_MoveControlManager_C::get_instance(); 471 | 472 | if (CMCMC) 473 | { 474 | IsCrouchingCurrent = CMCMC->get_IsCrouching(); /* Verify player not crouching before attempting to enable Interact mode as cursor requires default collision cylinder settings */ 475 | } 476 | 477 | /* Toggle between interact and combat mode with right trigger */ 478 | 479 | IsCrosshairPrev = IsCrosshairCurrent; 480 | 481 | if (state->Gamepad.bLeftTrigger <= 100) 482 | { 483 | IsCrosshairCurrent = false; 484 | } 485 | else if (state->Gamepad.bLeftTrigger >= 200) 486 | { 487 | IsCrosshairCurrent = true; 488 | } 489 | 490 | if (IsCrosshairPrev != IsCrosshairCurrent) 491 | { 492 | if ( (IsMFDCurrent == false) && (IsMovementCurrent == true) && (IsAliveCurrent == true) && (IsMainMenuCurrent == false) && (InteractExit == false) && (IsCrouchingCurrent == false) ) /* Verify if cursor should be hidden */ 493 | { 494 | if (IsCrosshairCurrent == false) 495 | { 496 | auto& vr = API::get()->param()->vr; 497 | 498 | const auto CHC = CON_Hacker_C::get_instance(); 499 | if (CHC) { 500 | CHC->ForceHideCrosshairs(true); /* Hide crosshair */ 501 | IsBracket = false; 502 | } 503 | 504 | if (RoomscaleMontageOverride == false) 505 | { 506 | vr->set_mod_value("VR_RoomscaleMovement", "true"); /* Re-enable Roomscale */ 507 | } 508 | 509 | const auto AnimBP = CH_Hacker_AnimBP_C::get_instance(); 510 | if (AnimBP) { 511 | AnimBP->SetUseIK(true); /* Enable IK for motion controls */ 512 | } 513 | 514 | vr->set_mod_value("UI_Y_Offset", "-0.200000"); /* Adjust UI placement */ 515 | 516 | if (IsEquipped == false) 517 | { 518 | InteractToggle = true; /* If inventory item is holstered when exiting Interact mode, force weapon draw animation to show item */ 519 | COMP_HackerInventory_C::get_instance(); 520 | } 521 | 522 | IKSanityCheck = false; 523 | IsInteractMode = false; 524 | } 525 | else 526 | { 527 | const auto CHC = CON_Hacker_C::get_instance(); 528 | if (CHC) { 529 | CHC->ForceHideCrosshairs(false); /* Show crosshair */ 530 | IsBracket = true; 531 | } 532 | 533 | auto& vr = API::get()->param()->vr; 534 | vr->set_mod_value("VR_RoomscaleMovement", "false"); /* Temporarily disable Roomscale to disable pushback for Interact mode */ 535 | 536 | const auto AnimBP = CH_Hacker_AnimBP_C::get_instance(); 537 | if (AnimBP) { 538 | AnimBP->SetUseIK(false); /* Disable IK for Interact mode */ 539 | } 540 | 541 | vr->set_mod_value("UI_Y_Offset", "0.000000"); /* Adjust UI placement */ 542 | 543 | if (IsEquipped == true) 544 | { 545 | InteractToggle = true; /* If inventory item is equipped when exiting combat mode, force weapon holster animation to hide item */ 546 | COMP_HackerInventory_C::get_instance(); 547 | } 548 | 549 | IsInteractMode = true; 550 | } 551 | } 552 | } 553 | 554 | if ( (IsInteractMode == true) && (IsEquipped == false) ) 555 | { 556 | IKSanityCheck = true; /* Check if any events occur in Interact mode that would require motion controls to be re-enabled */ 557 | 558 | if (IsMFDCurrent == false) 559 | { 560 | // Vibration flag, clear it on Y button release. 561 | if (ButtonsDownY == true) 562 | { 563 | if (timer.count() >= 500) /* Open MFD if Y held down 500ms in Interact mode */ 564 | { 565 | KEY_DN = true; 566 | send_key(VK_TAB, KEY_DN); 567 | 568 | timer.stop(); 569 | timer.reset(); 570 | 571 | IsMFDCurrent = true; 572 | ButtonsDownY = false; 573 | } 574 | 575 | if (!(state->Gamepad.wButtons & XINPUT_GAMEPAD_Y)) /* Open menu if Y held down <500ms in Interact mode */ 576 | { 577 | ButtonsDownY = false; 578 | 579 | timer.stop(); 580 | 581 | if (timer.count() < 500) 582 | { 583 | KEY_DN = true; 584 | send_key(VK_ESCAPE, KEY_DN); 585 | } 586 | 587 | timer.reset(); 588 | IsMainMenuCurrent = true; 589 | } 590 | } 591 | else 592 | { 593 | if (state->Gamepad.wButtons & XINPUT_GAMEPAD_Y) 594 | { 595 | ButtonsDownY = true; 596 | 597 | // Start the timer 598 | timer.start(); 599 | } 600 | } 601 | } 602 | } 603 | 604 | // API::get()->log_error("IsInteractMode = %d, IsEquipped = %d, InteractExit = %d, IsCrouchingCurrent = %d, IsMFDCurrent = %d, IsMainMenuCurrent = %d, IsMovementCurrent = %d, IsAliveCurrent = %d, IsCrosshairCurrent = %d, IKSanityCheck = %d", IsInteractMode, IsEquipped, InteractExit, IsCrouchingCurrent, IsMFDCurrent, IsMainMenuCurrent, IsMovementCurrent, IsAliveCurrent, IsCrosshairCurrent, IKSanityCheck); 605 | 606 | if (IKSanityCheck == true) 607 | { 608 | if ((IsEquipped == true) || (InteractExit == true) || (IsCrouchingCurrent == true) || (IsMFDCurrent == true) || (IsMainMenuCurrent == true)) /* Restore motion controls if pickup item, montage, or crouching is active */ 609 | { 610 | // API::get()->log_error("InteractExit = %d", InteractExit); 611 | 612 | const auto CHC = CON_Hacker_C::get_instance(); 613 | if (CHC) { 614 | CHC->ForceHideCrosshairs(true); 615 | IsBracket = false; 616 | } 617 | 618 | auto& vr = API::get()->param()->vr; 619 | vr->set_mod_value("VR_RoomscaleMovement", "true"); 620 | 621 | const auto AnimBP = CH_Hacker_AnimBP_C::get_instance(); 622 | if (AnimBP) { 623 | AnimBP->SetUseIK(true); 624 | } 625 | 626 | vr->set_mod_value("UI_Y_Offset", "-0.200000"); 627 | 628 | if (IsEquipped == false) /* If inventory item is holstered while restoring motion controls, force weapon draw animation to show item */ 629 | { 630 | InteractToggle = true; 631 | COMP_HackerInventory_C::get_instance(); 632 | 633 | IsCrosshairPrev = true; 634 | IsCrosshairCurrent = true; 635 | } 636 | 637 | InteractExit = false; 638 | IsInteractMode = false; 639 | IKSanityCheck = false; 640 | } 641 | } 642 | 643 | if (IsCrosshairCurrent == false && IsMovementCurrent == true) /* Force hide crosshair when returning to combat mode after exiting puzzle */ 644 | { 645 | const auto CHC = CON_Hacker_C::get_instance(); 646 | if (CHC) { 647 | CHC->ForceHideCrosshairs(true); 648 | IsBracket = false; 649 | } 650 | } 651 | 652 | /* ========== MFD FIX ========== */ 653 | 654 | IsMFDPrev = IsMFDCurrent; 655 | 656 | const auto WICMC = WIDGET_InventoryContextMenu_C::get_instance(); 657 | 658 | if (WICMC) 659 | { 660 | IsContextMenu = WICMC->get_IsInventoryContextMenuEnabled(); 661 | } 662 | 663 | const auto HUD = WIDGET_PlayerHUD_C::get_instance(); 664 | if (HUD) { 665 | IsMFDCurrent = HUD->get_is_mfd_visible(); 666 | 667 | if (VaporizeInventory == false) { 668 | HUD->set_IsTryingToVaporizeInventory(false); /* Prevent RT from vaporizing items */ 669 | } 670 | else 671 | { 672 | HUD->set_IsTryingToVaporizeInventory(true); /* Vaporize items on rebinded input */ 673 | } 674 | } 675 | 676 | if (IsMFDPrev != IsMFDCurrent) 677 | { 678 | if (IsMFDCurrent == true) 679 | { 680 | // API::get()->log_error("MFD Found"); 681 | auto& vr = API::get()->param()->vr; 682 | vr->set_snap_turn_enabled(false); /* Disable snap turn when MFD is active */ 683 | } 684 | else 685 | { 686 | // API::get()->log_error("MFD Not Found"); 687 | auto& vr = API::get()->param()->vr; 688 | vr->set_snap_turn_enabled(true); 689 | } 690 | } 691 | 692 | if (IsMFDCurrent == true) /* Remap inventory controls for VR */ 693 | { 694 | if (IsContextMenu == true) 695 | { 696 | if (!(state->Gamepad.wButtons & XINPUT_GAMEPAD_Y)) 697 | { 698 | ContextMode = true; 699 | SendKeyDebounce = false; 700 | } 701 | 702 | if ((state->Gamepad.wButtons & XINPUT_GAMEPAD_Y) && ContextMode == true) 703 | { 704 | if (SendKeyDebounce == false) 705 | { 706 | SendKeyDebounce = true; 707 | KEY_DN = true; 708 | send_key(VK_RETURN, KEY_DN); 709 | } 710 | } 711 | 712 | if (state->Gamepad.sThumbRY <= -200) 713 | { 714 | state->Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_DOWN; 715 | } 716 | else if (state->Gamepad.sThumbRY >= 200) 717 | { 718 | state->Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_UP; 719 | } 720 | else if (state->Gamepad.sThumbRX <= -200) 721 | { 722 | state->Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_LEFT; 723 | } 724 | else if (state->Gamepad.sThumbRX >= 200) 725 | { 726 | state->Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_RIGHT; 727 | } 728 | 729 | if (state->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) 730 | { 731 | state->Gamepad.wButtons = state->Gamepad.wButtons & ~XINPUT_GAMEPAD_LEFT_SHOULDER; /* Disable LGrip */ 732 | state->Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_UP; /* Cycle context menu with grip buttons */ 733 | } 734 | else if (state->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) 735 | { 736 | state->Gamepad.wButtons = state->Gamepad.wButtons & ~XINPUT_GAMEPAD_RIGHT_SHOULDER; /* Disable RGrip */ 737 | state->Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_DOWN; /* Cycle context menu with grip buttons */ 738 | } 739 | } 740 | else 741 | { 742 | ContextMode = false; 743 | 744 | if (state->Gamepad.bLeftTrigger > 0 && validate_equip == false) { 745 | disable_equip = true; /* Set flag to prevent auto equipping items while opening inventory when LT is held down */ 746 | } 747 | else if (state->Gamepad.bLeftTrigger == 0) 748 | { 749 | disable_equip = false; 750 | } 751 | 752 | if (timer.count() >= 1000) 753 | { 754 | VaporizeInventory = true; 755 | timer.stop(); 756 | timer.reset(); 757 | } 758 | 759 | if (state->Gamepad.wButtons & XINPUT_GAMEPAD_A) { 760 | 761 | timer.start(); 762 | } 763 | else 764 | { 765 | VaporizeInventory = false; 766 | timer.stop(); 767 | timer.reset(); 768 | } 769 | 770 | if (state->Gamepad.bLeftTrigger >= 200) { 771 | if(disable_equip == false) state->Gamepad.wButtons = (state->Gamepad.wButtons | XINPUT_GAMEPAD_A); 772 | } 773 | else { 774 | state->Gamepad.wButtons = (state->Gamepad.wButtons & ~XINPUT_GAMEPAD_A); 775 | } 776 | 777 | validate_equip = true; 778 | } 779 | 780 | if (state->Gamepad.bRightTrigger >= 200) { 781 | state->Gamepad.bLeftTrigger = 200; 782 | } 783 | else { 784 | state->Gamepad.bLeftTrigger = 0; 785 | } 786 | } 787 | else 788 | { 789 | validate_equip = false; 790 | } 791 | 792 | /* ========== KEYPAD CIRCUIT PUZZLE FIX ========== */ 793 | 794 | IsMovementPrev = IsMovementCurrent; 795 | 796 | const auto MC = MOVECONTROL_FocusableInteract_C::get_instance(); 797 | if (MC) { 798 | 799 | IsMovementCurrent = MC->get_ShouldUseCharacterMovement(); 800 | 801 | if (IsMovementPrev != IsMovementCurrent) 802 | { 803 | if (IsMovementCurrent == false) 804 | { 805 | // API::get()->log_info("Camera is framed. IK disabled."); 806 | DeactivateIK = true; /* Fis cursor alignment issues if focused on puzzle */ 807 | } 808 | else 809 | { 810 | // API::get()->log_info("Camera is not framed. IK enabled."); 811 | ActivateIK = true; /* Re-enable motion controls after player exits puzzle */ 812 | } 813 | } 814 | } 815 | 816 | if (DeactivateIK == true) 817 | { 818 | const auto AnimBP = CH_Hacker_AnimBP_C::get_instance(); 819 | if (AnimBP) { 820 | AnimBP->SetUseIK(false); 821 | } 822 | 823 | reset_height(); /* Recalibrate height for keypads and puzzles */ 824 | 825 | auto& vr = API::get()->param()->vr; 826 | vr->set_mod_value("UI_Distance", "0.500000"); 827 | vr->set_mod_value("UI_Size", "0.500000"); 828 | vr->set_mod_value("UI_Y_Offset", "0.000000"); 829 | vr->set_mod_value("VR_RoomscaleMovement", "false"); 830 | vr->set_snap_turn_enabled(false); 831 | 832 | IsBracket = true; 833 | DeactivateIK = false; 834 | } 835 | 836 | if (ActivateIK == true) 837 | { 838 | const auto AnimBP = CH_Hacker_AnimBP_C::get_instance(); 839 | if (AnimBP) { 840 | AnimBP->SetUseIK(true); 841 | } 842 | 843 | auto& vr = API::get()->param()->vr; 844 | vr->set_mod_value("UI_Distance", "2.000000"); 845 | vr->set_mod_value("UI_Size", "2.000000"); 846 | vr->set_mod_value("UI_Y_Offset", "-0.200000"); 847 | vr->set_mod_value("VR_RoomscaleMovement", "true"); 848 | vr->set_snap_turn_enabled(true); 849 | 850 | if(IKSanityCheck == true)InteractExit = true; 851 | if(IsInteractMode == false)IsBracket = false; 852 | ActivateIK = false; 853 | } 854 | 855 | /* =================== REBIND GRIP BUTTONS =================== */ 856 | 857 | if ((IsMFDCurrent == false) && (IsMainMenuCurrent == false)) 858 | { 859 | if (abs(state->Gamepad.sThumbLX) <= INPUT_DEADZONE_MED && abs(state->Gamepad.sThumbLY) <= INPUT_DEADZONE_MED) 860 | { 861 | IsSprinting = false; /* Disable sprinting state */ 862 | } 863 | 864 | if (IsSprinting) 865 | { 866 | state->Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_THUMB; /* Enable sprinting */ 867 | } 868 | 869 | if (state->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) 870 | { 871 | state->Gamepad.wButtons = state->Gamepad.wButtons & ~XINPUT_GAMEPAD_LEFT_SHOULDER; /* Disable LGrip */ 872 | 873 | if (abs(state->Gamepad.sThumbLX) >= INPUT_DEADZONE_HI || abs(state->Gamepad.sThumbLY) >= INPUT_DEADZONE_HI) 874 | { 875 | IsSprinting = true; /* Enable sprinting state */ 876 | } 877 | } 878 | 879 | if (state->Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER) 880 | { 881 | state->Gamepad.wButtons = state->Gamepad.wButtons & ~XINPUT_GAMEPAD_RIGHT_SHOULDER; /* Disable RGrip */ 882 | 883 | if (state->Gamepad.sThumbLX >= INPUT_DEADZONE_LO) 884 | { 885 | state->Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_RIGHT; /* Remap DPad Right for hotbar */ 886 | } 887 | else if (state->Gamepad.sThumbLX <= -INPUT_DEADZONE_LO) 888 | { 889 | state->Gamepad.wButtons |= XINPUT_GAMEPAD_DPAD_LEFT; /* Remap DPad Left for hotbar */ 890 | } 891 | 892 | state->Gamepad.sThumbLX = 0; /* Prevent rotating while holding RGrip */ 893 | } 894 | } 895 | } 896 | 897 | /* =================== SMOOTH TURNING =================== */ 898 | 899 | if ((current_pawn_state == PAWN_HACKERSIMPLE) || (current_pawn_state == PAWN_HACKERIMPLANT)) /* Verify game has loaded before attempting smooth turn */ 900 | { 901 | auto& vr = API::get()->param()->vr; 902 | char snap_angle[16] = { 0 }; 903 | vr->get_mod_value("VR_SnapturnTurnAngle", snap_angle, sizeof(snap_angle)); 904 | int snap_angle_int = atoi(snap_angle); 905 | 906 | if ( (snap_angle_int == 359) && ((IsMFDCurrent == false) && (IsMovementCurrent == true) && (IsMainMenuCurrent == false) || (current_pawn_state == PAWN_HACKERSIMPLE)) ) /* Verify if smooth turning conditions have been met */ 907 | { 908 | IsSmoothTurn = true; 909 | vr->set_snap_turn_enabled(false); 910 | 911 | if (InitSmoothTurn == false) 912 | { 913 | InitSmoothTurn = true; 914 | } 915 | 916 | smooth_rotation(state); 917 | } 918 | else if (snap_angle_int != 359) 919 | { 920 | if (IsSmoothTurn == true) /* Disable snap turn when angle decreased in UEVR overlay */ 921 | { 922 | IsSmoothTurn = false; 923 | InitSmoothTurn = false; 924 | 925 | vr->set_snap_turn_enabled(true); 926 | } 927 | } 928 | } 929 | 930 | /* ========== MANUAL HEIGHT CALIBRATION ========== */ 931 | 932 | if (state->Gamepad.bLeftTrigger >= 200 && (state->Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB)) { 933 | reset_height(); 934 | } 935 | } 936 | } 937 | }; 938 | 939 | void smooth_rotation(XINPUT_STATE* state) 940 | { 941 | if (state->Gamepad.sThumbRX >= INPUT_DEADZONE_HI) /* Fast speed smooth rotation */ 942 | { 943 | ref.yaw += 3; 944 | const auto CONT = Controller::get_instance(); 945 | if (CONT) { 946 | CONT->SetControlRotation(&ref); 947 | } 948 | 949 | // API::get()->log_info("New SetControlRotation x = %f y = %f z = %f", ref.pitch, ref.yaw, ref.roll); 950 | } 951 | else if ((state->Gamepad.sThumbRX > INPUT_DEADZONE_MED) && (state->Gamepad.sThumbRX < INPUT_DEADZONE_HI)) /* Medium speed smooth rotation */ 952 | { 953 | ref.yaw += 2; 954 | const auto CONT = Controller::get_instance(); 955 | if (CONT) { 956 | CONT->SetControlRotation(&ref); 957 | } 958 | 959 | // API::get()->log_info("New SetControlRotation x = %f y = %f z = %f", ref.pitch, ref.yaw, ref.roll); 960 | } 961 | else if ((state->Gamepad.sThumbRX > INPUT_DEADZONE_LO) && (state->Gamepad.sThumbRX <= INPUT_DEADZONE_MED)) /* Slow speed smooth rotation */ 962 | { 963 | ref.yaw += 1; 964 | const auto CONT = Controller::get_instance(); 965 | if (CONT) { 966 | CONT->SetControlRotation(&ref); 967 | } 968 | 969 | // API::get()->log_info("New SetControlRotation x = %f y = %f z = %f", ref.pitch, ref.yaw, ref.roll); 970 | } 971 | else if (state->Gamepad.sThumbRX <= -INPUT_DEADZONE_HI) /* Fast speed smooth rotation */ 972 | { 973 | ref.yaw -= 3; // = controller_rot_y; 974 | const auto CONT = Controller::get_instance(); 975 | if (CONT) { 976 | CONT->SetControlRotation(&ref); 977 | } 978 | 979 | // API::get()->log_info("New SetControlRotation x = %f y = %f z = %f", ref.pitch, ref.yaw, ref.roll); 980 | } 981 | else if ((state->Gamepad.sThumbRX < -INPUT_DEADZONE_MED) && (state->Gamepad.sThumbRX > -INPUT_DEADZONE_HI)) /* Medium speed smooth rotation */ 982 | { 983 | ref.yaw -= 2; // = controller_rot_y; 984 | const auto CONT = Controller::get_instance(); 985 | if (CONT) { 986 | CONT->SetControlRotation(&ref); 987 | } 988 | 989 | // API::get()->log_info("New SetControlRotation x = %f y = %f z = %f", ref.pitch, ref.yaw, ref.roll); 990 | } 991 | else if ((state->Gamepad.sThumbRX < -INPUT_DEADZONE_LO) && (state->Gamepad.sThumbRX >= -INPUT_DEADZONE_MED)) /* Medium speed smooth rotation */ 992 | { 993 | ref.yaw -= 1; // = controller_rot_y; 994 | const auto CONT = Controller::get_instance(); 995 | if (CONT) { 996 | CONT->SetControlRotation(&ref); 997 | } 998 | 999 | // API::get()->log_info("New SetControlRotation x = %f y = %f z = %f", ref.pitch, ref.yaw, ref.roll); 1000 | } 1001 | else 1002 | { 1003 | const auto CONT = Controller::get_instance(); 1004 | if (CONT) { 1005 | ref = CONT->GetControlRotation(); /* Get camera settings before attempting smooth rotation */ 1006 | } 1007 | 1008 | // API::get()->log_info("Old SetControlRotation x = %f y = %f z = %f", ref.pitch, ref.yaw, ref.roll); 1009 | } 1010 | } 1011 | 1012 | void smooth_rotation_cyberspace(XINPUT_STATE* state) 1013 | { 1014 | if (state->Gamepad.sThumbRX >= INPUT_DEADZONE_HI) /* Fast speed smooth rotation */ 1015 | { 1016 | ref.yaw += 2; 1017 | const auto CONT = Controller::get_instance(); 1018 | if (CONT) { 1019 | CONT->SetControlRotation(&ref); 1020 | } 1021 | 1022 | // API::get()->log_info("New SetControlRotation x = %f y = %f z = %f", ref.pitch, ref.yaw, ref.roll); 1023 | } 1024 | else if ((state->Gamepad.sThumbRX > INPUT_DEADZONE_MED) && (state->Gamepad.sThumbRX < INPUT_DEADZONE_HI)) /* Medium speed smooth rotation */ 1025 | { 1026 | ref.yaw += 1.5; 1027 | const auto CONT = Controller::get_instance(); 1028 | if (CONT) { 1029 | CONT->SetControlRotation(&ref); 1030 | } 1031 | 1032 | // API::get()->log_info("New SetControlRotation x = %f y = %f z = %f", ref.pitch, ref.yaw, ref.roll); 1033 | } 1034 | else if ((state->Gamepad.sThumbRX > INPUT_DEADZONE_LO) && (state->Gamepad.sThumbRX <= INPUT_DEADZONE_MED)) /* Slow speed smooth rotation */ 1035 | { 1036 | ref.yaw += .5; 1037 | const auto CONT = Controller::get_instance(); 1038 | if (CONT) { 1039 | CONT->SetControlRotation(&ref); 1040 | } 1041 | 1042 | // API::get()->log_info("New SetControlRotation x = %f y = %f z = %f", ref.pitch, ref.yaw, ref.roll); 1043 | } 1044 | else if (state->Gamepad.sThumbRX <= -INPUT_DEADZONE_HI) /* Fast speed smooth rotation */ 1045 | { 1046 | ref.yaw -= 2; // = controller_rot_y; 1047 | const auto CONT = Controller::get_instance(); 1048 | if (CONT) { 1049 | CONT->SetControlRotation(&ref); 1050 | } 1051 | 1052 | // API::get()->log_info("New SetControlRotation x = %f y = %f z = %f", ref.pitch, ref.yaw, ref.roll); 1053 | } 1054 | else if ((state->Gamepad.sThumbRX < -INPUT_DEADZONE_MED) && (state->Gamepad.sThumbRX > -INPUT_DEADZONE_HI)) /* Medium speed smooth rotation */ 1055 | { 1056 | ref.yaw -= 1.5; // = controller_rot_y; 1057 | const auto CONT = Controller::get_instance(); 1058 | if (CONT) { 1059 | CONT->SetControlRotation(&ref); 1060 | } 1061 | 1062 | // API::get()->log_info("New SetControlRotation x = %f y = %f z = %f", ref.pitch, ref.yaw, ref.roll); 1063 | } 1064 | else if ((state->Gamepad.sThumbRX < -INPUT_DEADZONE_LO) && (state->Gamepad.sThumbRX >= -INPUT_DEADZONE_MED)) /* Medium speed smooth rotation */ 1065 | { 1066 | ref.yaw -= .5; // = controller_rot_y; 1067 | const auto CONT = Controller::get_instance(); 1068 | if (CONT) { 1069 | CONT->SetControlRotation(&ref); 1070 | } 1071 | 1072 | // API::get()->log_info("New SetControlRotation x = %f y = %f z = %f", ref.pitch, ref.yaw, ref.roll); 1073 | } 1074 | else 1075 | { 1076 | const auto CONT = Controller::get_instance(); 1077 | if (CONT) { 1078 | ref = CONT->GetControlRotation(); /* Get camera settings before attempting smooth rotation */ 1079 | } 1080 | 1081 | // API::get()->log_info("Old SetControlRotation x = %f y = %f z = %f", ref.pitch, ref.yaw, ref.roll); 1082 | } 1083 | } 1084 | 1085 | void send_key(WORD key, bool key_dn) 1086 | { 1087 | INPUT input; 1088 | ZeroMemory(&input, sizeof(INPUT)); 1089 | input.type = INPUT_KEYBOARD; 1090 | input.ki.wVk = key; 1091 | if (key_dn == true) 1092 | { 1093 | key_dn = false; 1094 | 1095 | input.ki.dwFlags = 0; 1096 | SendInput(1, &input, sizeof(INPUT)); 1097 | 1098 | input.ki.dwFlags = KEYEVENTF_KEYUP; 1099 | SendInput(1, &input, sizeof(INPUT)); 1100 | } 1101 | } 1102 | 1103 | void anim_cine_intro_height_recalibration() { 1104 | 1105 | auto& api = API::get(); 1106 | auto vr = api->param()->vr; 1107 | UEVR_Vector3f origin{}; 1108 | vr->get_standing_origin(&origin); 1109 | 1110 | UEVR_Vector3f hmd_pos{}; 1111 | UEVR_Quaternionf hmd_rot{}; 1112 | vr->get_pose(vr->get_hmd_index(), &hmd_pos, &hmd_rot); 1113 | 1114 | origin.y = (hmd_pos.y)-(float)0.3; /* Recalibrate height for intro laptop animation */ 1115 | 1116 | vr->set_standing_origin(&origin); 1117 | } 1118 | 1119 | void reset_height() { 1120 | 1121 | auto& api = API::get(); 1122 | auto vr = api->param()->vr; 1123 | UEVR_Vector3f origin{}; 1124 | vr->get_standing_origin(&origin); 1125 | 1126 | UEVR_Vector3f hmd_pos{}; 1127 | UEVR_Quaternionf hmd_rot{}; 1128 | vr->get_pose(vr->get_hmd_index(), &hmd_pos, &hmd_rot); 1129 | 1130 | origin.y = hmd_pos.y; 1131 | 1132 | vr->set_standing_origin(&origin); 1133 | 1134 | pose_y_min = 0.0; 1135 | pose_y_max = 0.0; 1136 | } 1137 | 1138 | void pawn_Process(uint8_t current_pawn_state) 1139 | { 1140 | auto& vr = API::get()->param()->vr; 1141 | 1142 | if (last_pawn_state != current_pawn_state) 1143 | { 1144 | switch (current_pawn_state) 1145 | { 1146 | case PAWN_PLAYERGHOST: 1147 | // API::get()->log_info("New pawn found: PlayerGhost %d %d", last_pawn_state, current_pawn_state); /* Title screen or cutscene */ 1148 | vr->set_aim_method(0); 1149 | vr->set_snap_turn_enabled(true); 1150 | vr->set_decoupled_pitch_enabled(true); 1151 | vr->set_mod_value("VR_RoomscaleMovement", "false"); 1152 | vr->set_mod_value("UI_Distance", "2.000000"); 1153 | vr->set_mod_value("UI_Size", "2.000000"); 1154 | vr->set_mod_value("UI_Y_Offset", "-0.200000"); 1155 | vr->set_mod_value("VR_CameraForwardOffset", "0.000000"); 1156 | vr->set_mod_value("VR_CameraRightOffset", "0.000000"); 1157 | vr->set_mod_value("VR_CameraUpOffset", "0.000000"); 1158 | API::UObjectHook::set_disabled(true); 1159 | reset_height(); 1160 | 1161 | break; 1162 | case PAWN_HACKERSIMPLE: 1163 | // API::get()->log_info("New pawn found: HackerSimple %d %d", last_pawn_state, current_pawn_state); /* Animation */ 1164 | vr->set_aim_method(2); 1165 | vr->set_snap_turn_enabled(true); 1166 | vr->set_decoupled_pitch_enabled(true); 1167 | vr->set_mod_value("VR_RoomscaleMovement", "false"); 1168 | vr->set_mod_value("UI_Distance", "2.000000"); 1169 | vr->set_mod_value("UI_Size", "2.000000"); 1170 | vr->set_mod_value("UI_Y_Offset", "-0.200000"); 1171 | vr->set_mod_value("VR_CameraForwardOffset", "0.000000"); 1172 | vr->set_mod_value("VR_CameraRightOffset", "0.000000"); 1173 | vr->set_mod_value("VR_CameraUpOffset", "0.000000"); 1174 | API::UObjectHook::set_disabled(false); 1175 | reset_height(); 1176 | 1177 | break; 1178 | case PAWN_HACKERIMPLANT: 1179 | // API::get()->log_info("New pawn found: HackerImplant %d %d", last_pawn_state, current_pawn_state); /* Main game */ 1180 | vr->set_aim_method(2); 1181 | vr->set_snap_turn_enabled(true); 1182 | vr->set_decoupled_pitch_enabled(true); 1183 | vr->set_mod_value("VR_RoomscaleMovement", "true"); 1184 | vr->set_mod_value("UI_Distance", "2.000000"); 1185 | vr->set_mod_value("UI_Size", "2.000000"); 1186 | vr->set_mod_value("UI_Y_Offset", "-0.200000"); 1187 | vr->set_mod_value("VR_CameraForwardOffset", "0.000000"); 1188 | vr->set_mod_value("VR_CameraRightOffset", "0.000000"); 1189 | vr->set_mod_value("VR_CameraUpOffset", "0.000000"); 1190 | API::UObjectHook::set_disabled(false); 1191 | reset_height(); 1192 | 1193 | break; 1194 | case PAWN_AVATAR: 1195 | // API::get()->log_info("New pawn found: Avatar %d %d", last_pawn_state, current_pawn_state); /* Cyberspace */ 1196 | vr->set_aim_method(2); 1197 | vr->set_snap_turn_enabled(false); 1198 | vr->set_decoupled_pitch_enabled(false); 1199 | vr->set_mod_value("VR_RoomscaleMovement", "true"); 1200 | vr->set_mod_value("UI_Distance", "2.000000"); 1201 | vr->set_mod_value("UI_Size", "2.000000"); 1202 | vr->set_mod_value("UI_Y_Offset", "-0.200000"); 1203 | vr->set_mod_value("VR_CameraForwardOffset", "0.000000"); 1204 | vr->set_mod_value("VR_CameraRightOffset", "0.000000"); 1205 | vr->set_mod_value("VR_CameraUpOffset", "0.000000"); 1206 | API::UObjectHook::set_disabled(true); 1207 | reset_height(); 1208 | 1209 | break; 1210 | default: 1211 | break; 1212 | } 1213 | } 1214 | 1215 | last_pawn_state = current_pawn_state; 1216 | } 1217 | 1218 | // Actually creates the plugin. Very important that this global is created. 1219 | // The fact that it's using std::unique_ptr is not important, as long as the constructor is called in some way. 1220 | std::unique_ptr g_plugin{ new SystemShockPlugin() }; -------------------------------------------------------------------------------- /SystemShockVR/framework.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 4 | // Windows Header Files 5 | #include 6 | -------------------------------------------------------------------------------- /SystemShockVR/pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: source file corresponding to the pre-compiled header 2 | 3 | #include "pch.h" 4 | 5 | // When you are using pre-compiled headers, this source file is necessary for compilation to succeed. 6 | -------------------------------------------------------------------------------- /SystemShockVR/pch.h: -------------------------------------------------------------------------------- 1 | // pch.h: This is a precompiled header file. 2 | // Files listed below are compiled only once, improving build performance for future builds. 3 | // This also affects IntelliSense performance, including code completion and many code browsing features. 4 | // However, files listed here are ALL re-compiled if any one of them is updated between builds. 5 | // Do not add files here that you will be updating frequently as this negates the performance advantage. 6 | 7 | #ifndef PCH_H 8 | #define PCH_H 9 | 10 | // add headers that you want to pre-compile here 11 | #include "framework.h" 12 | 13 | #endif //PCH_H 14 | -------------------------------------------------------------------------------- /SystemShockVR/uevr/API.h: -------------------------------------------------------------------------------- 1 | /* 2 | This file (API.h) is licensed under the MIT license and is separate from the rest of the UEVR codebase. 3 | 4 | Copyright (c) 2023 praydog 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | /* C API for UEVR. */ 26 | /* Must be ANSI C89 compatible. */ 27 | #ifndef UEVR_API_H 28 | #define UEVR_API_H 29 | 30 | #ifndef __cplusplus 31 | #include 32 | #include 33 | #endif 34 | 35 | #define UEVR_IN 36 | #define UEVR_OUT 37 | 38 | #define UEVR_PLUGIN_VERSION_MAJOR 2 39 | #define UEVR_PLUGIN_VERSION_MINOR 29 40 | #define UEVR_PLUGIN_VERSION_PATCH 0 41 | 42 | #define UEVR_RENDERER_D3D11 0 43 | #define UEVR_RENDERER_D3D12 1 44 | 45 | typedef struct { 46 | int major; 47 | int minor; 48 | int patch; 49 | } UEVR_PluginVersion; 50 | 51 | /* strong typedefs */ 52 | #define DECLARE_UEVR_HANDLE(name) struct name##__ { int unused; }; \ 53 | typedef struct name##__ *name 54 | 55 | DECLARE_UEVR_HANDLE(UEVR_UGameEngineHandle); 56 | DECLARE_UEVR_HANDLE(UEVR_UEngineHandle); 57 | DECLARE_UEVR_HANDLE(UEVR_FSlateRHIRendererHandle); 58 | DECLARE_UEVR_HANDLE(UEVR_FViewportInfoHandle); 59 | DECLARE_UEVR_HANDLE(UEVR_UGameViewportClientHandle); 60 | DECLARE_UEVR_HANDLE(UEVR_FViewportHandle); 61 | DECLARE_UEVR_HANDLE(UEVR_FCanvasHandle); 62 | DECLARE_UEVR_HANDLE(UEVR_UObjectArrayHandle); 63 | DECLARE_UEVR_HANDLE(UEVR_UObjectHandle); 64 | DECLARE_UEVR_HANDLE(UEVR_FFieldHandle); 65 | DECLARE_UEVR_HANDLE(UEVR_UFieldHandle); 66 | DECLARE_UEVR_HANDLE(UEVR_FPropertyHandle); 67 | DECLARE_UEVR_HANDLE(UEVR_UStructHandle); 68 | DECLARE_UEVR_HANDLE(UEVR_UClassHandle); 69 | DECLARE_UEVR_HANDLE(UEVR_UFunctionHandle); 70 | DECLARE_UEVR_HANDLE(UEVR_FNameHandle); 71 | DECLARE_UEVR_HANDLE(UEVR_FFieldClassHandle); 72 | DECLARE_UEVR_HANDLE(UEVR_FConsoleManagerHandle); 73 | DECLARE_UEVR_HANDLE(UEVR_IConsoleObjectHandle); 74 | DECLARE_UEVR_HANDLE(UEVR_IConsoleCommandHandle); 75 | DECLARE_UEVR_HANDLE(UEVR_IConsoleVariableHandle); 76 | DECLARE_UEVR_HANDLE(UEVR_TArrayHandle); 77 | DECLARE_UEVR_HANDLE(UEVR_FMallocHandle); 78 | DECLARE_UEVR_HANDLE(UEVR_FRHITexture2DHandle); 79 | DECLARE_UEVR_HANDLE(UEVR_UScriptStructHandle); 80 | DECLARE_UEVR_HANDLE(UEVR_FArrayPropertyHandle); 81 | DECLARE_UEVR_HANDLE(UEVR_FBoolPropertyHandle); 82 | DECLARE_UEVR_HANDLE(UEVR_FStructPropertyHandle); 83 | DECLARE_UEVR_HANDLE(UEVR_FEnumPropertyHandle); 84 | DECLARE_UEVR_HANDLE(UEVR_UEnumHandle); 85 | DECLARE_UEVR_HANDLE(UEVR_FNumericPropertyHandle); 86 | 87 | /* OpenXR stuff */ 88 | DECLARE_UEVR_HANDLE(UEVR_XrInstance); 89 | DECLARE_UEVR_HANDLE(UEVR_XrSession); 90 | DECLARE_UEVR_HANDLE(UEVR_XrSpace); 91 | 92 | #define UEVR_LEFT_EYE 0 93 | #define UEVR_RIGHT_EYE 1 94 | 95 | #define UEVR_OPENXR_SWAPCHAIN_LEFT_EYE 0 96 | #define UEVR_OPENXR_SWAPCHAIN_RIGHT_EYE 1 97 | #define UEVR_OPENXR_SWAPCHAIN_UI 2 98 | 99 | typedef int UEVR_Eye; 100 | typedef int UEVR_TrackedDeviceIndex; 101 | typedef int UEVR_OpenXRSwapchainIndex; 102 | 103 | typedef struct { 104 | float x; 105 | float y; 106 | } UEVR_Vector2f; 107 | 108 | typedef struct { 109 | float x; 110 | float y; 111 | float z; 112 | } UEVR_Vector3f; 113 | 114 | typedef struct { 115 | double x; 116 | double y; 117 | double z; 118 | } UEVR_Vector3d; 119 | 120 | typedef struct { 121 | float x; 122 | float y; 123 | float z; 124 | float w; 125 | } UEVR_Vector4f; 126 | 127 | typedef struct { 128 | float w; 129 | float x; 130 | float y; 131 | float z; 132 | } UEVR_Quaternionf; 133 | 134 | typedef struct { 135 | float pitch; 136 | float yaw; 137 | float roll; 138 | } UEVR_Rotatorf; 139 | 140 | typedef struct { 141 | double pitch; 142 | double yaw; 143 | double roll; 144 | } UEVR_Rotatord; 145 | 146 | typedef struct { 147 | float m[4][4]; 148 | } UEVR_Matrix4x4f; 149 | 150 | typedef struct { 151 | double m[4][4]; 152 | } UEVR_Matrix4x4d; 153 | 154 | /* Generic DX renderer callbacks */ 155 | typedef void (*UEVR_OnPresentCb)(); 156 | typedef void (*UEVR_OnDeviceResetCb)(); 157 | 158 | /* VR Specific renderer callbacks */ 159 | typedef void (*UEVR_OnPostRenderVRFrameworkDX11Cb)(void*, void*, void*); /* immediate_context, ID3D11Texture2D* resource, ID3D11RenderTargetView* rtv */ 160 | /* On DX12 the resource state is D3D12_RESOURCE_STATE_RENDER_TARGET */ 161 | typedef void (*UEVR_OnPostRenderVRFrameworkDX12Cb)(void*, void*, void*); /* command_list, ID3D12Resource* resource, D3D12_CPU_DESCRIPTOR_HANDLE* rtv */ 162 | 163 | /* Windows callbacks*/ 164 | typedef bool (*UEVR_OnMessageCb)(void*, unsigned int, unsigned long long, long long); 165 | typedef void (*UEVR_OnXInputGetStateCb)(unsigned int*, unsigned int, void*); /* retval, dwUserIndex, pState, read MSDN for details */ 166 | typedef void (*UEVR_OnXInputSetStateCb)(unsigned int*, unsigned int, void*); /* retval, dwUserIndex, pVibration, read MSDN for details */ 167 | 168 | /* UE Callbacks */ 169 | typedef void (*UEVR_Engine_TickCb)(UEVR_UGameEngineHandle engine, float delta_seconds); 170 | typedef void (*UEVR_Slate_DrawWindow_RenderThreadCb)(UEVR_FSlateRHIRendererHandle renderer, UEVR_FViewportInfoHandle viewport_info); 171 | typedef void (*UEVR_ViewportClient_DrawCb)(UEVR_UGameViewportClientHandle viewport_client, UEVR_FViewportHandle viewport, UEVR_FCanvasHandle canvas); 172 | 173 | DECLARE_UEVR_HANDLE(UEVR_StereoRenderingDeviceHandle); 174 | /* the position and rotation must be converted to double format based on the is_double parameter. */ 175 | typedef void (*UEVR_Stereo_CalculateStereoViewOffsetCb)(UEVR_StereoRenderingDeviceHandle, int view_index, float world_to_meters, UEVR_Vector3f* position, UEVR_Rotatorf* rotation, bool is_double); 176 | 177 | /* Generic DX Renderer */ 178 | typedef bool (*UEVR_OnPresentFn)(UEVR_OnPresentCb); 179 | typedef bool (*UEVR_OnDeviceResetFn)(UEVR_OnDeviceResetCb); 180 | 181 | /* VR Renderer */ 182 | typedef bool (*UEVR_OnPostRenderVRFrameworkDX11Fn)(UEVR_OnPostRenderVRFrameworkDX11Cb); 183 | typedef bool (*UEVR_OnPostRenderVRFrameworkDX12Fn)(UEVR_OnPostRenderVRFrameworkDX12Cb); 184 | 185 | /* Windows */ 186 | typedef bool (*UEVR_OnMessageFn)(UEVR_OnMessageCb); 187 | typedef bool (*UEVR_OnXInputGetStateFn)(UEVR_OnXInputGetStateCb); 188 | typedef bool (*UEVR_OnXInputSetStateFn)(UEVR_OnXInputSetStateCb); 189 | 190 | /* Engine */ 191 | typedef bool (*UEVR_Engine_TickFn)(UEVR_Engine_TickCb); 192 | typedef bool (*UEVR_Slate_DrawWindow_RenderThreadFn)(UEVR_Slate_DrawWindow_RenderThreadCb); 193 | typedef bool (*UEVR_Stereo_CalculateStereoViewOffsetFn)(UEVR_Stereo_CalculateStereoViewOffsetCb); 194 | typedef bool (*UEVR_ViewportClient_DrawFn)(UEVR_ViewportClient_DrawCb); 195 | typedef bool (*UEVR_UFunction_NativeFn)(UEVR_UObjectHandle, void*, void*); /* obj, frame, ret */ 196 | typedef bool (*UEVR_UFunction_NativePreFn)(UEVR_UFunctionHandle, UEVR_UObjectHandle, void*, void*); /* obj, frame, ret */ 197 | typedef bool (*UEVR_UFunction_NativePostFn)(UEVR_UFunctionHandle, UEVR_UObjectHandle, void*, void*); /* obj, frame, ret */ 198 | 199 | typedef void (*UEVR_PluginRequiredVersionFn)(UEVR_PluginVersion*); 200 | 201 | typedef struct { 202 | UEVR_OnPresentFn on_present; 203 | UEVR_OnDeviceResetFn on_device_reset; 204 | UEVR_OnMessageFn on_message; 205 | UEVR_OnXInputGetStateFn on_xinput_get_state; 206 | UEVR_OnXInputSetStateFn on_xinput_set_state; 207 | UEVR_OnPostRenderVRFrameworkDX11Fn on_post_render_vr_framework_dx11; 208 | UEVR_OnPostRenderVRFrameworkDX12Fn on_post_render_vr_framework_dx12; 209 | } UEVR_PluginCallbacks; 210 | 211 | typedef struct { 212 | void (*log_error)(const char* format, ...); 213 | void (*log_warn)(const char* format, ...); 214 | void (*log_info)(const char* format, ...); 215 | bool (*is_drawing_ui)(); 216 | bool (*remove_callback)(void* cb); 217 | unsigned int (*get_persistent_dir)(wchar_t* buffer, unsigned int buffer_size); 218 | int (*register_inline_hook)(void* target, void* dst, void** original); 219 | void (*unregister_inline_hook)(int hook_id); 220 | } UEVR_PluginFunctions; 221 | 222 | typedef struct { 223 | UEVR_Engine_TickFn on_pre_engine_tick; 224 | UEVR_Engine_TickFn on_post_engine_tick; 225 | UEVR_Slate_DrawWindow_RenderThreadFn on_pre_slate_draw_window_render_thread; 226 | UEVR_Slate_DrawWindow_RenderThreadFn on_post_slate_draw_window_render_thread; 227 | UEVR_Stereo_CalculateStereoViewOffsetFn on_pre_calculate_stereo_view_offset; 228 | UEVR_Stereo_CalculateStereoViewOffsetFn on_post_calculate_stereo_view_offset; 229 | UEVR_ViewportClient_DrawFn on_pre_viewport_client_draw; 230 | UEVR_ViewportClient_DrawFn on_post_viewport_client_draw; 231 | } UEVR_SDKCallbacks; 232 | 233 | typedef struct { 234 | int renderer_type; 235 | void* device; 236 | void* swapchain; 237 | void* command_queue; 238 | } UEVR_RendererData; 239 | 240 | typedef struct { 241 | UEVR_UEngineHandle (*get_uengine)(); 242 | void (*set_cvar_int)(const char* module_name, const char* name, int value); 243 | UEVR_UObjectArrayHandle (*get_uobject_array)(); 244 | 245 | UEVR_UObjectHandle (*get_player_controller)(int index); 246 | UEVR_UObjectHandle (*get_local_pawn)(int index); 247 | UEVR_UObjectHandle (*spawn_object)(UEVR_UClassHandle klass, UEVR_UObjectHandle outer); 248 | 249 | /* Handles exec commands, find_console_command does not */ 250 | void (*execute_command)(const wchar_t* command); 251 | void (*execute_command_ex)(UEVR_UObjectHandle world, const wchar_t* command, void* output_device); 252 | 253 | UEVR_FConsoleManagerHandle (*get_console_manager)(); 254 | } UEVR_SDKFunctions; 255 | 256 | typedef struct { 257 | UEVR_TArrayHandle (*get_console_objects)(UEVR_FConsoleManagerHandle mgr); 258 | UEVR_IConsoleObjectHandle (*find_object)(UEVR_FConsoleManagerHandle mgr, const wchar_t* name); 259 | UEVR_IConsoleVariableHandle (*find_variable)(UEVR_FConsoleManagerHandle mgr, const wchar_t* name); 260 | UEVR_IConsoleCommandHandle (*find_command)(UEVR_FConsoleManagerHandle mgr, const wchar_t* name); 261 | 262 | UEVR_IConsoleCommandHandle (*as_command)(UEVR_IConsoleObjectHandle object); 263 | 264 | void (*variable_set)(UEVR_IConsoleVariableHandle cvar, const wchar_t* value); 265 | void (*variable_set_ex)(UEVR_IConsoleVariableHandle cvar, const wchar_t* value, unsigned int flags); 266 | int (*variable_get_int)(UEVR_IConsoleVariableHandle cvar); 267 | float (*variable_get_float)(UEVR_IConsoleVariableHandle cvar); 268 | 269 | /* better to just use execute_command if possible */ 270 | void (*command_execute)(UEVR_IConsoleCommandHandle cmd, const wchar_t* args); 271 | } UEVR_ConsoleFunctions; 272 | 273 | DECLARE_UEVR_HANDLE(UEVR_FUObjectItemHandle); 274 | 275 | typedef struct { 276 | UEVR_UObjectHandle (*find_uobject)(const wchar_t* name); 277 | 278 | bool (*is_chunked)(); 279 | bool (*is_inlined)(); 280 | unsigned int (*get_objects_offset)(); 281 | unsigned int (*get_item_distance)(); 282 | 283 | int (*get_object_count)(UEVR_UObjectArrayHandle array); 284 | void* (*get_objects_ptr)(UEVR_UObjectArrayHandle array); 285 | UEVR_UObjectHandle (*get_object)(UEVR_UObjectArrayHandle array, int index); 286 | UEVR_FUObjectItemHandle (*get_item)(UEVR_UObjectArrayHandle array, int index); 287 | } UEVR_UObjectArrayFunctions; 288 | 289 | typedef struct { 290 | UEVR_FFieldHandle (*get_next)(UEVR_FFieldHandle field); 291 | UEVR_FFieldClassHandle (*get_class)(UEVR_FFieldHandle field); 292 | UEVR_FNameHandle (*get_fname)(UEVR_FFieldHandle field); 293 | } UEVR_FFieldFunctions; 294 | 295 | typedef struct { 296 | UEVR_UFieldHandle (*get_next)(UEVR_UFieldHandle field); 297 | } UEVR_UFieldFunctions; 298 | 299 | typedef struct { 300 | int (*get_offset)(UEVR_FPropertyHandle prop); 301 | unsigned long long (*get_property_flags)(UEVR_FPropertyHandle prop); 302 | bool (*is_param)(UEVR_FPropertyHandle prop); 303 | bool (*is_out_param)(UEVR_FPropertyHandle prop); 304 | bool (*is_return_param)(UEVR_FPropertyHandle prop); 305 | bool (*is_reference_param)(UEVR_FPropertyHandle prop); 306 | bool (*is_pod)(UEVR_FPropertyHandle prop); 307 | } UEVR_FPropertyFunctions; 308 | 309 | typedef struct { 310 | UEVR_UStructHandle (*get_super_struct)(UEVR_UStructHandle klass); 311 | UEVR_FFieldHandle (*get_child_properties)(UEVR_UStructHandle klass); 312 | UEVR_UFunctionHandle (*find_function)(UEVR_UStructHandle klass, const wchar_t* name); 313 | UEVR_FPropertyHandle (*find_property)(UEVR_UStructHandle klass, const wchar_t* name); 314 | int (*get_properties_size)(UEVR_UStructHandle klass); /* size in bytes */ 315 | int (*get_min_alignment)(UEVR_UStructHandle klass); 316 | UEVR_UFieldHandle (*get_children)(UEVR_UStructHandle klass); 317 | } UEVR_UStructFunctions; 318 | 319 | typedef struct { 320 | UEVR_UObjectHandle (*get_class_default_object)(UEVR_UClassHandle klass); 321 | } UEVR_UClassFunctions; 322 | 323 | typedef struct { 324 | void* (*get_native_function)(UEVR_UFunctionHandle function); 325 | bool (*hook_ptr)(UEVR_UFunctionHandle function, UEVR_UFunction_NativePreFn pre_hook, UEVR_UFunction_NativePostFn post_hook); 326 | } UEVR_UFunctionFunctions; 327 | 328 | typedef struct { 329 | UEVR_UClassHandle (*get_class)(UEVR_UObjectHandle object); 330 | UEVR_UObjectHandle (*get_outer)(UEVR_UObjectHandle object); 331 | 332 | /* pointer to the property data, not the UProperty/FProperty */ 333 | void* (*get_property_data)(UEVR_UObjectHandle object, const wchar_t* name); 334 | bool (*is_a)(UEVR_UObjectHandle object, UEVR_UClassHandle other); 335 | 336 | void (*process_event)(UEVR_UObjectHandle object, UEVR_UFunctionHandle function, void* params); 337 | void (*call_function)(UEVR_UObjectHandle object, const wchar_t* name, void* params); 338 | 339 | UEVR_FNameHandle (*get_fname)(UEVR_UObjectHandle object); 340 | 341 | bool (*get_bool_property)(UEVR_UObjectHandle object, const wchar_t* name); 342 | void (*set_bool_property)(UEVR_UObjectHandle object, const wchar_t* name, bool value); 343 | } UEVR_UObjectFunctions; 344 | 345 | DECLARE_UEVR_HANDLE(UEVR_UObjectHookMotionControllerStateHandle); 346 | 347 | typedef struct { 348 | void (*set_rotation_offset)(UEVR_UObjectHookMotionControllerStateHandle, const UEVR_Quaternionf* rotation); 349 | void (*set_location_offset)(UEVR_UObjectHookMotionControllerStateHandle, const UEVR_Vector3f* location); 350 | void (*set_hand)(UEVR_UObjectHookMotionControllerStateHandle, unsigned int hand); 351 | void (*set_permanent)(UEVR_UObjectHookMotionControllerStateHandle, bool permanent); 352 | } UEVR_UObjectHookMotionControllerStateFunctions; 353 | 354 | typedef struct { 355 | void (*activate)(); 356 | bool (*exists)(UEVR_UObjectHandle object); 357 | 358 | /* if 0 or nullptr is passed, it will return how many objects are in the array */ 359 | /* so you can allocate the right amount of memory */ 360 | int (*get_objects_by_class)(UEVR_UClassHandle klass, UEVR_UObjectHandle* out_objects, unsigned int max_objects, bool allow_default); 361 | int (*get_objects_by_class_name)(const wchar_t* class_name, UEVR_UObjectHandle* out_objects, unsigned int max_objects, bool allow_default); 362 | 363 | UEVR_UObjectHandle (*get_first_object_by_class)(UEVR_UClassHandle klass, bool allow_default); 364 | UEVR_UObjectHandle (*get_first_object_by_class_name)(const wchar_t* class_name, bool allow_default); 365 | 366 | UEVR_UObjectHookMotionControllerStateHandle (*get_or_add_motion_controller_state)(UEVR_UObjectHandle object); 367 | UEVR_UObjectHookMotionControllerStateHandle (*get_motion_controller_state)(UEVR_UObjectHandle object); 368 | 369 | UEVR_UObjectHookMotionControllerStateFunctions* mc_state; 370 | 371 | bool (*is_disabled)(); 372 | void (*set_disabled)(bool disabled); 373 | } UEVR_UObjectHookFunctions; 374 | 375 | typedef struct { 376 | UEVR_FNameHandle (*get_fname)(UEVR_FFieldClassHandle field_class); 377 | } UEVR_FFieldClassFunctions; 378 | 379 | typedef struct { 380 | unsigned int (*to_string)(UEVR_FNameHandle name, wchar_t* buffer, unsigned int buffer_size); 381 | void (*constructor)(UEVR_FNameHandle name, const wchar_t* data, unsigned int find_type); 382 | } UEVR_FNameFunctions; 383 | 384 | typedef struct { 385 | UEVR_FMallocHandle (*get)(); 386 | 387 | void* (*malloc)(UEVR_FMallocHandle instance, unsigned int size, unsigned int alignment); 388 | void* (*realloc)(UEVR_FMallocHandle instance, void* ptr, unsigned int size, unsigned int alignment); 389 | void (*free)(UEVR_FMallocHandle instance, void* ptr); 390 | } UEVR_FMallocFunctions; 391 | 392 | DECLARE_UEVR_HANDLE(UEVR_IPooledRenderTargetHandle); 393 | 394 | typedef struct { 395 | void (*activate)(); 396 | UEVR_IPooledRenderTargetHandle (*get_render_target)(const wchar_t* name); 397 | } UEVR_FRenderTargetPoolHookFunctions; 398 | 399 | typedef struct { 400 | UEVR_FRHITexture2DHandle (*get_scene_render_target)(); 401 | UEVR_FRHITexture2DHandle (*get_ui_render_target)(); 402 | } UEVR_FFakeStereoRenderingHookFunctions; 403 | 404 | typedef struct { 405 | void* (*get_native_resource)(UEVR_FRHITexture2DHandle texture); 406 | } UEVR_FRHITexture2DFunctions; 407 | 408 | DECLARE_UEVR_HANDLE(UEVR_StructOpsHandle); 409 | 410 | typedef struct { 411 | UEVR_StructOpsHandle (*get_struct_ops)(UEVR_UScriptStructHandle script_struct); 412 | int (*get_struct_size)(UEVR_UScriptStructHandle script_struct); 413 | } UEVR_UScriptStructFunctions; 414 | 415 | typedef struct { 416 | UEVR_FPropertyHandle (*get_inner)(UEVR_FArrayPropertyHandle prop); 417 | } UEVR_FArrayPropertyFunctions; 418 | 419 | typedef struct { 420 | unsigned int (*get_field_size)(UEVR_FBoolPropertyHandle prop); 421 | unsigned int (*get_byte_offset)(UEVR_FBoolPropertyHandle prop); 422 | unsigned int (*get_byte_mask)(UEVR_FBoolPropertyHandle prop); 423 | unsigned int (*get_field_mask)(UEVR_FBoolPropertyHandle prop); 424 | bool (*get_value_from_object)(UEVR_FBoolPropertyHandle prop, void* object); 425 | bool (*get_value_from_propbase)(UEVR_FBoolPropertyHandle prop, void* addr); 426 | void (*set_value_in_object)(UEVR_FBoolPropertyHandle prop, void* object, bool value); 427 | void (*set_value_in_propbase)(UEVR_FBoolPropertyHandle prop, void* addr, bool value); 428 | } UEVR_FBoolPropertyFunctions; 429 | 430 | typedef struct { 431 | UEVR_UScriptStructHandle (*get_struct)(UEVR_FStructPropertyHandle prop); 432 | } UEVR_FStructPropertyFunctions; 433 | 434 | typedef struct { 435 | UEVR_FNumericPropertyHandle (*get_underlying_prop)(UEVR_FEnumPropertyHandle prop); 436 | UEVR_UEnumHandle (*get_enum)(UEVR_FEnumPropertyHandle prop); 437 | } UEVR_FEnumPropertyFunctions; 438 | 439 | typedef struct { 440 | const UEVR_SDKFunctions* functions; 441 | const UEVR_SDKCallbacks* callbacks; 442 | const UEVR_UObjectFunctions* uobject; 443 | const UEVR_UObjectArrayFunctions* uobject_array; 444 | const UEVR_FFieldFunctions* ffield; 445 | const UEVR_FPropertyFunctions* fproperty; 446 | const UEVR_UStructFunctions* ustruct; 447 | const UEVR_UClassFunctions* uclass; 448 | const UEVR_UFunctionFunctions* ufunction; 449 | const UEVR_UObjectHookFunctions* uobject_hook; 450 | const UEVR_FFieldClassFunctions* ffield_class; 451 | const UEVR_FNameFunctions* fname; 452 | const UEVR_ConsoleFunctions* console; 453 | const UEVR_FMallocFunctions* malloc; 454 | const UEVR_FRenderTargetPoolHookFunctions* render_target_pool_hook; 455 | const UEVR_FFakeStereoRenderingHookFunctions* stereo_hook; 456 | const UEVR_FRHITexture2DFunctions* frhitexture2d; 457 | const UEVR_UScriptStructFunctions* uscriptstruct; 458 | const UEVR_FArrayPropertyFunctions* farrayproperty; 459 | const UEVR_FBoolPropertyFunctions* fboolproperty; 460 | const UEVR_FStructPropertyFunctions* fstructproperty; 461 | const UEVR_FEnumPropertyFunctions* fenumproperty; 462 | const UEVR_UFieldFunctions* ufield; 463 | } UEVR_SDKData; 464 | 465 | DECLARE_UEVR_HANDLE(UEVR_IVRSystem); 466 | DECLARE_UEVR_HANDLE(UEVR_IVRChaperone); 467 | DECLARE_UEVR_HANDLE(UEVR_IVRChaperoneSetup); 468 | DECLARE_UEVR_HANDLE(UEVR_IVRCompositor); 469 | DECLARE_UEVR_HANDLE(UEVR_IVROverlay); 470 | DECLARE_UEVR_HANDLE(UEVR_IVROverlayView); 471 | DECLARE_UEVR_HANDLE(UEVR_IVRHeadsetView); 472 | DECLARE_UEVR_HANDLE(UEVR_IVRScreenshots); 473 | DECLARE_UEVR_HANDLE(UEVR_IVRRenderModels); 474 | DECLARE_UEVR_HANDLE(UEVR_IVRApplications); 475 | DECLARE_UEVR_HANDLE(UEVR_IVRSettings); 476 | DECLARE_UEVR_HANDLE(UEVR_IVRResources); 477 | DECLARE_UEVR_HANDLE(UEVR_IVRExtendedDisplay); 478 | DECLARE_UEVR_HANDLE(UEVR_IVRTrackedCamera); 479 | DECLARE_UEVR_HANDLE(UEVR_IVRDriverManager); 480 | DECLARE_UEVR_HANDLE(UEVR_IVRInput); 481 | DECLARE_UEVR_HANDLE(UEVR_IVRIOBuffer); 482 | DECLARE_UEVR_HANDLE(UEVR_IVRSpatialAnchors); 483 | DECLARE_UEVR_HANDLE(UEVR_IVRNotifications); 484 | DECLARE_UEVR_HANDLE(UEVR_IVRDebug); 485 | 486 | typedef struct { 487 | UEVR_IVRSystem (*get_vr_system)(); 488 | UEVR_IVRChaperone (*get_vr_chaperone)(); 489 | UEVR_IVRChaperoneSetup (*get_vr_chaperone_setup)(); 490 | UEVR_IVRCompositor (*get_vr_compositor)(); 491 | UEVR_IVROverlay (*get_vr_overlay)(); 492 | UEVR_IVROverlayView (*get_vr_overlay_view)(); 493 | UEVR_IVRHeadsetView (*get_vr_headset_view)(); 494 | UEVR_IVRScreenshots (*get_vr_screenshots)(); 495 | UEVR_IVRRenderModels (*get_vr_render_models)(); 496 | UEVR_IVRApplications (*get_vr_applications)(); 497 | UEVR_IVRSettings (*get_vr_settings)(); 498 | UEVR_IVRResources (*get_vr_resources)(); 499 | UEVR_IVRExtendedDisplay (*get_vr_extended_display)(); 500 | UEVR_IVRTrackedCamera (*get_vr_tracked_camera)(); 501 | UEVR_IVRDriverManager (*get_vr_driver_manager)(); 502 | UEVR_IVRInput (*get_vr_input)(); 503 | UEVR_IVRIOBuffer (*get_vr_io_buffer)(); 504 | UEVR_IVRSpatialAnchors (*get_vr_spatial_anchors)(); 505 | UEVR_IVRNotifications (*get_vr_notifications)(); 506 | UEVR_IVRDebug (*get_vr_debug)(); 507 | } UEVR_OpenVRData; 508 | 509 | typedef struct { 510 | UEVR_XrInstance (*get_xr_instance)(); 511 | UEVR_XrSession (*get_xr_session)(); 512 | 513 | UEVR_XrSpace (*get_stage_space)(); /* XrSpace */ 514 | UEVR_XrSpace (*get_view_space)(); /* XrSpace */ 515 | } UEVR_OpenXRData; 516 | 517 | DECLARE_UEVR_HANDLE(UEVR_ActionHandle); 518 | DECLARE_UEVR_HANDLE(UEVR_InputSourceHandle); 519 | 520 | typedef struct { 521 | bool (*is_runtime_ready)(); 522 | bool (*is_openvr)(); 523 | bool (*is_openxr)(); 524 | 525 | bool (*is_hmd_active)(); 526 | 527 | void (*get_standing_origin)(UEVR_Vector3f* out_origin); 528 | void (*get_rotation_offset)(UEVR_Quaternionf* out_rotation); 529 | void (*set_standing_origin)(const UEVR_Vector3f* origin); 530 | void (*set_rotation_offset)(const UEVR_Quaternionf* rotation); 531 | 532 | UEVR_TrackedDeviceIndex (*get_hmd_index)(); 533 | UEVR_TrackedDeviceIndex (*get_left_controller_index)(); 534 | UEVR_TrackedDeviceIndex (*get_right_controller_index)(); 535 | 536 | /* OpenVR/OpenXR space. */ 537 | void (*get_pose)(UEVR_TrackedDeviceIndex index, UEVR_Vector3f* out_position, UEVR_Quaternionf* out_rotation); 538 | void (*get_transform)(UEVR_TrackedDeviceIndex index, UEVR_Matrix4x4f* out_transform); 539 | 540 | /* For getting grip or aim poses for the controllers */ 541 | void (*get_grip_pose)(UEVR_TrackedDeviceIndex index, UEVR_Vector3f* out_position, UEVR_Quaternionf* out_rotation); 542 | void (*get_aim_pose)(UEVR_TrackedDeviceIndex index, UEVR_Vector3f* out_position, UEVR_Quaternionf* out_rotation); 543 | void (*get_grip_transform)(UEVR_TrackedDeviceIndex index, UEVR_Matrix4x4f* out_transform); 544 | void (*get_aim_transform)(UEVR_TrackedDeviceIndex index, UEVR_Matrix4x4f* out_transform); 545 | 546 | void (*get_eye_offset)(UEVR_Eye eye, UEVR_Vector3f* out_position); 547 | 548 | /* Converted to UE projection matrix */ 549 | void (*get_ue_projection_matrix)(UEVR_Eye eye, UEVR_Matrix4x4f* out_projection); 550 | 551 | UEVR_InputSourceHandle (*get_left_joystick_source)(); 552 | UEVR_InputSourceHandle (*get_right_joystick_source)(); 553 | 554 | UEVR_ActionHandle (*get_action_handle)(const char* action_path); 555 | 556 | bool (*is_action_active)(UEVR_ActionHandle action, UEVR_InputSourceHandle source); 557 | bool (*is_action_active_any_joystick)(UEVR_ActionHandle action); 558 | void (*get_joystick_axis)(UEVR_InputSourceHandle source, UEVR_Vector2f* out_axis); 559 | void (*trigger_haptic_vibration)(float seconds_from_now, float duration, float frequency, float amplitude, UEVR_InputSourceHandle source); 560 | /* if any controller action is active or has been active within certain previous timeframe */ 561 | bool (*is_using_controllers)(); 562 | bool (*is_decoupled_pitch_enabled)(); 563 | 564 | unsigned int (*get_movement_orientation)(); 565 | unsigned int (*get_lowest_xinput_index)(); 566 | 567 | void (*recenter_view)(); 568 | void (*recenter_horizon)(); 569 | 570 | unsigned int (*get_aim_method)(); 571 | void (*set_aim_method)(unsigned int method); 572 | bool (*is_aim_allowed)(); 573 | void (*set_aim_allowed)(bool allowed); 574 | 575 | unsigned int (*get_hmd_width)(); 576 | unsigned int (*get_hmd_height)(); 577 | unsigned int (*get_ui_width)(); 578 | unsigned int (*get_ui_height)(); 579 | 580 | bool (*is_snap_turn_enabled)(); 581 | void (*set_snap_turn_enabled)(bool enabled); 582 | void (*set_decoupled_pitch_enabled)(bool enabled); 583 | 584 | void (*set_mod_value)(const char* key, const char* value); 585 | void (*get_mod_value)(const char* key, char* value, unsigned int value_size); 586 | void (*save_config)(); 587 | void (*reload_config)(); 588 | } UEVR_VRData; 589 | 590 | typedef struct { 591 | void* uevr_module; 592 | const UEVR_PluginVersion* version; 593 | const UEVR_PluginFunctions* functions; 594 | const UEVR_PluginCallbacks* callbacks; 595 | const UEVR_RendererData* renderer; 596 | 597 | const UEVR_VRData* vr; 598 | const UEVR_OpenVRData* openvr; 599 | const UEVR_OpenXRData* openxr; 600 | 601 | /* Engine/Game specific functions and data */ 602 | const UEVR_SDKData* sdk; 603 | } UEVR_PluginInitializeParam; 604 | 605 | typedef bool (*UEVR_PluginInitializeFn)(const UEVR_PluginInitializeParam*); 606 | 607 | #endif -------------------------------------------------------------------------------- /SystemShockVR/uevr/API.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file (API.hpp) is licensed under the MIT license and is separate from the rest of the UEVR codebase. 3 | 4 | Copyright (c) 2023 praydog 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | #pragma once 26 | 27 | extern "C" { 28 | #include "API.h" 29 | } 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | namespace uevr { 44 | class API { 45 | private: 46 | static inline std::unique_ptr s_instance{}; 47 | 48 | public: 49 | // ALWAYS call initialize first in uevr_plugin_initialize 50 | static auto& initialize(const UEVR_PluginInitializeParam* param) { 51 | if (param == nullptr) { 52 | throw std::runtime_error("param is null"); 53 | } 54 | 55 | if (s_instance != nullptr) { 56 | return s_instance; 57 | } 58 | 59 | s_instance = std::make_unique(param); 60 | return s_instance; 61 | } 62 | 63 | // only call this AFTER calling initialize 64 | static auto& get() { 65 | if (s_instance == nullptr) { 66 | throw std::runtime_error("API not initialized"); 67 | } 68 | 69 | return s_instance; 70 | } 71 | 72 | public: 73 | API(const UEVR_PluginInitializeParam* param) 74 | : m_param{param}, 75 | m_sdk{param->sdk} 76 | { 77 | } 78 | 79 | virtual ~API() { 80 | 81 | } 82 | 83 | inline const UEVR_PluginInitializeParam* param() const { 84 | return m_param; 85 | } 86 | 87 | inline const UEVR_SDKData* sdk() const { 88 | return m_sdk; 89 | } 90 | 91 | std::filesystem::path get_persistent_dir(std::optional file = std::nullopt) { 92 | static const auto fn = param()->functions->get_persistent_dir; 93 | const auto size = fn(nullptr, 0); 94 | if (size == 0) { 95 | return std::filesystem::path{}; 96 | } 97 | 98 | std::wstring result(size, L'\0'); 99 | fn(result.data(), size + 1); 100 | 101 | if (file.has_value()) { 102 | return std::filesystem::path{result} / file.value(); 103 | } 104 | 105 | return result; 106 | } 107 | 108 | template void log_error(const char* format, Args... args) { m_param->functions->log_error(format, args...); } 109 | template void log_warn(const char* format, Args... args) { m_param->functions->log_warn(format, args...); } 110 | template void log_info(const char* format, Args... args) { m_param->functions->log_info(format, args...); } 111 | 112 | public: 113 | // C++ wrapper structs for the C structs 114 | struct UObject; 115 | struct UEngine; 116 | struct UGameEngine; 117 | struct UWorld; 118 | struct UStruct; 119 | struct UClass; 120 | struct UFunction; 121 | struct UScriptStruct; 122 | struct UStructOps; 123 | struct FField; 124 | struct UField; 125 | struct FProperty; 126 | struct FFieldClass; 127 | struct FUObjectArray; 128 | struct FName; 129 | struct FConsoleManager; 130 | struct IConsoleObject; 131 | struct IConsoleVariable; 132 | struct IConsoleCommand; 133 | struct ConsoleObjectElement; 134 | struct UObjectHook; 135 | struct FRHITexture2D; 136 | struct IPooledRenderTarget; 137 | 138 | template 139 | struct TArray; 140 | 141 | // dynamic_cast variant 142 | template 143 | static T* dcast(UObject* obj) { 144 | if (obj == nullptr) { 145 | return nullptr; 146 | } 147 | 148 | if (obj->is_a(T::static_class())) { 149 | return static_cast(obj); 150 | } 151 | 152 | return nullptr; 153 | } 154 | 155 | template 156 | T* find_uobject(std::wstring_view name) { 157 | static const auto fn = sdk()->uobject_array->find_uobject; 158 | return (T*)fn(name.data()); 159 | } 160 | 161 | UEngine* get_engine() { 162 | static const auto fn = sdk()->functions->get_uengine; 163 | return (UEngine*)fn(); 164 | } 165 | 166 | UObject* get_player_controller(int32_t index) { 167 | static const auto fn = sdk()->functions->get_player_controller; 168 | return (UObject*)fn(index); 169 | } 170 | 171 | UObject* get_local_pawn(int32_t index) { 172 | static const auto fn = sdk()->functions->get_local_pawn; 173 | return (UObject*)fn(index); 174 | } 175 | 176 | UObject* spawn_object(UClass* klass, UObject* outer) { 177 | static const auto fn = sdk()->functions->spawn_object; 178 | return (UObject*)fn(klass->to_handle(), outer->to_handle()); 179 | } 180 | 181 | void execute_command(std::wstring_view command) { 182 | static const auto fn = sdk()->functions->execute_command; 183 | fn(command.data()); 184 | } 185 | 186 | void execute_command_ex(UWorld* world, std::wstring_view command, void* output_device) { 187 | static const auto fn = sdk()->functions->execute_command_ex; 188 | fn((UEVR_UObjectHandle)world, command.data(), output_device); 189 | } 190 | 191 | FUObjectArray* get_uobject_array() { 192 | static const auto fn = sdk()->functions->get_uobject_array; 193 | return (FUObjectArray*)fn(); 194 | } 195 | 196 | FConsoleManager* get_console_manager() { 197 | static const auto fn = sdk()->functions->get_console_manager; 198 | return (FConsoleManager*)fn(); 199 | } 200 | 201 | struct FMalloc { 202 | static FMalloc* get() { 203 | static const auto fn = initialize()->get; 204 | return (FMalloc*)fn(); 205 | } 206 | 207 | inline UEVR_FMallocHandle to_handle() { return (UEVR_FMallocHandle)this; } 208 | inline UEVR_FMallocHandle to_handle() const { return (UEVR_FMallocHandle)this; } 209 | 210 | using FMallocSizeT = uint32_t; // because of C89 211 | 212 | void* malloc(FMallocSizeT size, uint32_t alignment = 0) { 213 | static const auto fn = initialize()->malloc; 214 | return fn(to_handle(), size, alignment); 215 | } 216 | 217 | void* realloc(void* original, FMallocSizeT size, uint32_t alignment = 0) { 218 | static const auto fn = initialize()->realloc; 219 | return fn(to_handle(), original, size, alignment); 220 | } 221 | 222 | void free(void* original) { 223 | static const auto fn = initialize()->free; 224 | fn(to_handle(), original); 225 | } 226 | 227 | private: 228 | static inline const UEVR_FMallocFunctions* s_functions{nullptr}; 229 | static inline const UEVR_FMallocFunctions* initialize() { 230 | if (s_functions == nullptr) { 231 | s_functions = API::get()->sdk()->malloc; 232 | } 233 | 234 | return s_functions; 235 | } 236 | }; 237 | 238 | struct FName { 239 | inline UEVR_FNameHandle to_handle() { return (UEVR_FNameHandle)this; } 240 | inline UEVR_FNameHandle to_handle() const { return (UEVR_FNameHandle)this; } 241 | 242 | enum class EFindName : uint32_t { 243 | Find, 244 | Add 245 | }; 246 | 247 | FName() = default; 248 | FName(std::wstring_view name, EFindName find_type = EFindName::Add) { 249 | static const auto fn = initialize()->constructor; 250 | fn(to_handle(), name.data(), (uint32_t)find_type); 251 | } 252 | 253 | std::wstring to_string() const { 254 | static const auto fn = initialize()->to_string; 255 | const auto size = fn(to_handle(), nullptr, 0); 256 | if (size == 0) { 257 | return L""; 258 | } 259 | 260 | std::wstring result(size, L'\0'); 261 | fn(to_handle(), result.data(), size + 1); 262 | return result; 263 | } 264 | 265 | int32_t comparison_index{}; 266 | int32_t number{}; 267 | 268 | private: 269 | static inline const UEVR_FNameFunctions* s_functions{nullptr}; 270 | static inline const UEVR_FNameFunctions* initialize() { 271 | if (s_functions == nullptr) { 272 | s_functions = API::get()->sdk()->fname; 273 | } 274 | 275 | return s_functions; 276 | } 277 | }; 278 | 279 | struct UObject { 280 | inline UEVR_UObjectHandle to_handle() { return (UEVR_UObjectHandle)this; } 281 | inline UEVR_UObjectHandle to_handle() const { return (UEVR_UObjectHandle)this; } 282 | 283 | static UClass* static_class() { 284 | static auto result = API::get()->find_uobject(L"Class /Script/CoreUObject.Object"); 285 | return result; 286 | } 287 | 288 | inline UClass* get_class() const { 289 | static const auto fn = initialize()->get_class; 290 | return (UClass*)fn(to_handle()); 291 | } 292 | 293 | inline UObject* get_outer() const { 294 | static const auto fn = initialize()->get_outer; 295 | return (UObject*)fn(to_handle()); 296 | } 297 | 298 | inline bool is_a(UClass* cmp) const { 299 | static const auto fn = initialize()->is_a; 300 | return fn(to_handle(), cmp->to_handle()); 301 | } 302 | 303 | void process_event(UFunction* function, void* params) { 304 | static const auto fn = initialize()->process_event; 305 | fn(to_handle(), function->to_handle(), params); 306 | } 307 | 308 | void call_function(std::wstring_view name, void* params) { 309 | static const auto fn = initialize()->call_function; 310 | fn(to_handle(), name.data(), params); 311 | } 312 | 313 | // Pointer that points to the address of the data within the object, not the data itself 314 | template 315 | T* get_property_data(std::wstring_view name) const { 316 | static const auto fn = initialize()->get_property_data; 317 | return (T*)fn(to_handle(), name.data()); 318 | } 319 | 320 | // Pointer that points to the address of the data within the object, not the data itself 321 | void* get_property_data(std::wstring_view name) const { 322 | return get_property_data(name); 323 | } 324 | 325 | // use this if you know for sure that the property exists 326 | // or you will cause an access violation 327 | template 328 | T& get_property(std::wstring_view name) const { 329 | return *get_property_data(name); 330 | } 331 | 332 | bool get_bool_property(std::wstring_view name) const { 333 | static const auto fn = initialize()->get_bool_property; 334 | return fn(to_handle(), name.data()); 335 | } 336 | 337 | void set_bool_property(std::wstring_view name, bool value) { 338 | static const auto fn = initialize()->set_bool_property; 339 | fn(to_handle(), name.data(), value); 340 | } 341 | 342 | FName* get_fname() const { 343 | static const auto fn = initialize()->get_fname; 344 | return (FName*)fn(to_handle()); 345 | } 346 | 347 | std::wstring get_full_name() const { 348 | const auto c = get_class(); 349 | 350 | if (c == nullptr) { 351 | return L""; 352 | } 353 | 354 | auto obj_name = get_fname()->to_string(); 355 | 356 | for (auto outer = this->get_outer(); outer != nullptr && outer != this; outer = outer->get_outer()) { 357 | obj_name = outer->get_fname()->to_string() + L'.' + obj_name; 358 | } 359 | 360 | return c->get_fname()->to_string() + L' ' + obj_name; 361 | } 362 | 363 | // dynamic_cast variant 364 | template 365 | T* dcast() { 366 | if (this->is_a(T::static_class())) { 367 | return static_cast(this); 368 | } 369 | 370 | return nullptr; 371 | } 372 | 373 | private: 374 | static inline const UEVR_UObjectFunctions* s_functions{nullptr}; 375 | inline static const UEVR_UObjectFunctions* initialize() { 376 | if (s_functions == nullptr) { 377 | s_functions = API::get()->sdk()->uobject; 378 | } 379 | 380 | return s_functions; 381 | } 382 | }; 383 | 384 | struct UField : public UObject { 385 | inline UEVR_UFieldHandle to_handle() { return (UEVR_UFieldHandle)this; } 386 | inline UEVR_UFieldHandle to_handle() const { return (UEVR_UFieldHandle)this; } 387 | 388 | static UClass* static_class() { 389 | static auto result = API::get()->find_uobject(L"Class /Script/CoreUObject.Field"); 390 | return result; 391 | } 392 | 393 | inline UField* get_next() const { 394 | static const auto fn = initialize()->get_next; 395 | return (UField*)fn(to_handle()); 396 | } 397 | 398 | private: 399 | static inline const UEVR_UFieldFunctions* s_functions{nullptr}; 400 | static inline const UEVR_UFieldFunctions* initialize() { 401 | if (s_functions == nullptr) { 402 | s_functions = API::get()->sdk()->ufield; 403 | } 404 | 405 | return s_functions; 406 | } 407 | }; 408 | 409 | struct UStruct : public UField { 410 | inline UEVR_UStructHandle to_handle() { return (UEVR_UStructHandle)this; } 411 | inline UEVR_UStructHandle to_handle() const { return (UEVR_UStructHandle)this; } 412 | 413 | static UClass* static_class() { 414 | static auto result = API::get()->find_uobject(L"Class /Script/CoreUObject.Struct"); 415 | return result; 416 | } 417 | 418 | UStruct* get_super_struct() const { 419 | static const auto fn = initialize()->get_super_struct; 420 | return (UStruct*)fn(to_handle()); 421 | } 422 | 423 | UStruct* get_super() const { 424 | return get_super_struct(); 425 | } 426 | 427 | UFunction* find_function(std::wstring_view name) const { 428 | static const auto fn = initialize()->find_function; 429 | return (UFunction*)fn(to_handle(), name.data()); 430 | } 431 | 432 | FProperty* find_property(std::wstring_view name) const { 433 | static const auto fn = initialize()->find_property; 434 | return (FProperty*)fn(to_handle(), name.data()); 435 | } 436 | 437 | // Not an array, it's a linked list. Meant to call ->get_next() until nullptr 438 | FField* get_child_properties() const { 439 | static const auto fn = initialize()->get_child_properties; 440 | return (FField*)fn(to_handle()); 441 | } 442 | 443 | UField* get_children() const { 444 | static const auto fn = initialize()->get_children; 445 | return (UField*)fn(to_handle()); 446 | } 447 | 448 | int32_t get_properties_size() const { 449 | static const auto fn = initialize()->get_properties_size; 450 | return fn(to_handle()); 451 | } 452 | 453 | int32_t get_min_alignment() const { 454 | static const auto fn = initialize()->get_min_alignment; 455 | return fn(to_handle()); 456 | } 457 | 458 | private: 459 | static inline const UEVR_UStructFunctions* s_functions{nullptr}; 460 | inline static const UEVR_UStructFunctions* initialize() { 461 | if (s_functions == nullptr) { 462 | s_functions = API::get()->sdk()->ustruct; 463 | } 464 | 465 | return s_functions; 466 | } 467 | }; 468 | 469 | struct UClass : public UStruct { 470 | inline UEVR_UClassHandle to_handle() { return (UEVR_UClassHandle)this; } 471 | inline UEVR_UClassHandle to_handle() const { return (UEVR_UClassHandle)this; } 472 | 473 | static UClass* static_class() { 474 | static auto result = API::get()->find_uobject(L"Class /Script/CoreUObject.Class"); 475 | return result; 476 | } 477 | 478 | UObject* get_class_default_object() const { 479 | static const auto fn = initialize()->get_class_default_object; 480 | return (UObject*)fn(to_handle()); 481 | } 482 | 483 | std::vector get_objects_matching(bool allow_default = false) const { 484 | static auto activate_fn = API::get()->sdk()->uobject_hook->activate; 485 | static auto fn = API::get()->sdk()->uobject_hook->get_objects_by_class; 486 | activate_fn(); 487 | std::vector result{}; 488 | const auto size = fn(to_handle(), nullptr, 0, allow_default); 489 | if (size == 0) { 490 | return result; 491 | } 492 | 493 | result.resize(size); 494 | 495 | fn(to_handle(), (UEVR_UObjectHandle*)result.data(), size, allow_default); 496 | return result; 497 | } 498 | 499 | UObject* get_first_object_matching(bool allow_default = false) const { 500 | static auto activate_fn = API::get()->sdk()->uobject_hook->activate; 501 | static auto fn = API::get()->sdk()->uobject_hook->get_first_object_by_class; 502 | activate_fn(); 503 | return (UObject*)fn(to_handle(), allow_default); 504 | } 505 | 506 | template 507 | std::vector get_objects_matching(bool allow_default = false) const { 508 | std::vector objects = get_objects_matching(allow_default); 509 | 510 | return *reinterpret_cast*>(&objects); 511 | } 512 | 513 | template 514 | T* get_first_object_matching(bool allow_default = false) const { 515 | return (T*)get_first_object_matching(allow_default); 516 | } 517 | 518 | private: 519 | static inline const UEVR_UClassFunctions* s_functions{nullptr}; 520 | inline static const UEVR_UClassFunctions* initialize() { 521 | if (s_functions == nullptr) { 522 | s_functions = API::get()->sdk()->uclass; 523 | } 524 | 525 | return s_functions; 526 | } 527 | }; 528 | 529 | struct UFunction : public UStruct { 530 | inline UEVR_UFunctionHandle to_handle() { return (UEVR_UFunctionHandle)this; } 531 | inline UEVR_UFunctionHandle to_handle() const { return (UEVR_UFunctionHandle)this; } 532 | 533 | static UClass* static_class() { 534 | static auto result = API::get()->find_uobject(L"Class /Script/CoreUObject.Function"); 535 | return result; 536 | } 537 | 538 | void call(UObject* obj, void* params) { 539 | if (obj == nullptr) { 540 | return; 541 | } 542 | 543 | obj->process_event(this, params); 544 | } 545 | 546 | void* get_native_function() const { 547 | static const auto fn = initialize()->get_native_function; 548 | return fn(to_handle()); 549 | } 550 | 551 | using UEVR_UFunction_CPPPreNative = bool(*)(API::UFunction*, API::UObject*, void*, void*); 552 | using UEVR_UFunction_CPPPostNative = void(*)(API::UFunction*, API::UObject*, void*, void*); 553 | 554 | bool hook_ptr(UEVR_UFunction_CPPPreNative pre, UEVR_UFunction_CPPPostNative post) { 555 | static const auto fn = initialize()->hook_ptr; 556 | return fn(to_handle(), (UEVR_UFunction_NativePreFn)pre, (UEVR_UFunction_NativePostFn)post); 557 | } 558 | 559 | private: 560 | static inline const UEVR_UFunctionFunctions* s_functions{nullptr}; 561 | inline static const UEVR_UFunctionFunctions* initialize() { 562 | if (s_functions == nullptr) { 563 | s_functions = API::get()->sdk()->ufunction; 564 | } 565 | 566 | return s_functions; 567 | } 568 | }; 569 | 570 | struct UScriptStruct : public UStruct { 571 | inline UEVR_UScriptStructHandle to_handle() { return (UEVR_UScriptStructHandle)this; } 572 | inline UEVR_UScriptStructHandle to_handle() const { return (UEVR_UScriptStructHandle)this; } 573 | 574 | static UClass* static_class() { 575 | static auto result = API::get()->find_uobject(L"Class /Script/CoreUObject.ScriptStruct"); 576 | return result; 577 | } 578 | 579 | struct StructOps { 580 | virtual ~StructOps() {}; 581 | 582 | int32_t size; 583 | int32_t alignment; 584 | }; 585 | 586 | StructOps* get_struct_ops() const { 587 | static const auto fn = initialize()->get_struct_ops; 588 | return (StructOps*)fn(to_handle()); 589 | } 590 | 591 | int32_t get_struct_size() const { 592 | static const auto fn = initialize()->get_struct_size; 593 | return fn(to_handle()); 594 | } 595 | 596 | private: 597 | static inline const UEVR_UScriptStructFunctions* s_functions{nullptr}; 598 | inline static const UEVR_UScriptStructFunctions* initialize() { 599 | if (s_functions == nullptr) { 600 | s_functions = API::get()->sdk()->uscriptstruct; 601 | } 602 | 603 | return s_functions; 604 | } 605 | }; 606 | 607 | // Wrapper class for UField AND FField 608 | struct FField { 609 | inline UEVR_FFieldHandle to_handle() { return (UEVR_FFieldHandle)this; } 610 | inline UEVR_FFieldHandle to_handle() const { return (UEVR_FFieldHandle)this; } 611 | 612 | inline FField* get_next() const { 613 | static const auto fn = initialize()->get_next; 614 | return (FField*)fn(to_handle()); 615 | } 616 | 617 | FName* get_fname() const { 618 | static const auto fn = initialize()->get_fname; 619 | return (FName*)fn(to_handle()); 620 | } 621 | 622 | FFieldClass* get_class() const { 623 | static const auto fn = initialize()->get_class; 624 | return (FFieldClass*)fn(to_handle()); 625 | } 626 | 627 | private: 628 | static inline const UEVR_FFieldFunctions* s_functions{nullptr}; 629 | static inline const UEVR_FFieldFunctions* initialize() { 630 | if (s_functions == nullptr) { 631 | s_functions = API::get()->sdk()->ffield; 632 | } 633 | 634 | return s_functions; 635 | } 636 | }; 637 | 638 | // Wrapper class for FProperty AND UProperty 639 | struct FProperty : public FField { 640 | inline UEVR_FPropertyHandle to_handle() { return (UEVR_FPropertyHandle)this; } 641 | inline UEVR_FPropertyHandle to_handle() const { return (UEVR_FPropertyHandle)this; } 642 | 643 | int32_t get_offset() const { 644 | static const auto fn = initialize()->get_offset; 645 | return fn(to_handle()); 646 | } 647 | 648 | uint64_t get_property_flags() const { 649 | static const auto fn = initialize()->get_property_flags; 650 | return fn(to_handle()); 651 | } 652 | 653 | bool is_param() const { 654 | static const auto fn = initialize()->is_param; 655 | return fn(to_handle()); 656 | } 657 | 658 | bool is_out_param() const { 659 | static const auto fn = initialize()->is_out_param; 660 | return fn(to_handle()); 661 | } 662 | 663 | bool is_return_param() const { 664 | static const auto fn = initialize()->is_return_param; 665 | return fn(to_handle()); 666 | } 667 | 668 | bool is_reference_param() const { 669 | static const auto fn = initialize()->is_reference_param; 670 | return fn(to_handle()); 671 | } 672 | 673 | bool is_pod() const { 674 | static const auto fn = initialize()->is_pod; 675 | return fn(to_handle()); 676 | } 677 | 678 | private: 679 | static inline const UEVR_FPropertyFunctions* s_functions{nullptr}; 680 | static inline const UEVR_FPropertyFunctions* initialize() { 681 | if (s_functions == nullptr) { 682 | s_functions = API::get()->sdk()->fproperty; 683 | } 684 | 685 | return s_functions; 686 | } 687 | }; 688 | 689 | struct FArrayProperty : public FProperty { 690 | inline UEVR_FArrayPropertyHandle to_handle() { return (UEVR_FArrayPropertyHandle)this; } 691 | inline UEVR_FArrayPropertyHandle to_handle() const { return (UEVR_FArrayPropertyHandle)this; } 692 | 693 | FProperty* get_inner() const { 694 | static const auto fn = initialize()->get_inner; 695 | return (FProperty*)fn(to_handle()); 696 | } 697 | 698 | private: 699 | static inline const UEVR_FArrayPropertyFunctions* s_functions{nullptr}; 700 | static inline const UEVR_FArrayPropertyFunctions* initialize() { 701 | if (s_functions == nullptr) { 702 | s_functions = API::get()->sdk()->farrayproperty; 703 | } 704 | 705 | return s_functions; 706 | } 707 | }; 708 | 709 | struct FBoolProperty : public FProperty { 710 | inline UEVR_FBoolPropertyHandle to_handle() { return (UEVR_FBoolPropertyHandle)this; } 711 | inline UEVR_FBoolPropertyHandle to_handle() const { return (UEVR_FBoolPropertyHandle)this; } 712 | 713 | uint32_t get_field_size() const { 714 | static const auto fn = initialize()->get_field_size; 715 | return fn(to_handle()); 716 | } 717 | 718 | uint32_t get_byte_offset() const { 719 | static const auto fn = initialize()->get_byte_offset; 720 | return fn(to_handle()); 721 | } 722 | 723 | uint32_t get_byte_mask() const { 724 | static const auto fn = initialize()->get_byte_mask; 725 | return fn(to_handle()); 726 | } 727 | 728 | uint32_t get_field_mask() const { 729 | static const auto fn = initialize()->get_field_mask; 730 | return fn(to_handle()); 731 | } 732 | 733 | bool get_value_from_object(void* object) const { 734 | static const auto fn = initialize()->get_value_from_object; 735 | return fn(to_handle(), object); 736 | } 737 | 738 | bool get_value_from_propbase(void* addr) const { 739 | static const auto fn = initialize()->get_value_from_propbase; 740 | return fn(to_handle(), addr); 741 | } 742 | 743 | void set_value_in_object(void* object, bool value) const { 744 | static const auto fn = initialize()->set_value_in_object; 745 | fn(to_handle(), object, value); 746 | } 747 | 748 | void set_value_in_propbase(void* addr, bool value) const { 749 | static const auto fn = initialize()->set_value_in_propbase; 750 | fn(to_handle(), addr, value); 751 | } 752 | 753 | private: 754 | static inline const UEVR_FBoolPropertyFunctions* s_functions{nullptr}; 755 | static inline const UEVR_FBoolPropertyFunctions* initialize() { 756 | if (s_functions == nullptr) { 757 | s_functions = API::get()->sdk()->fboolproperty; 758 | } 759 | 760 | return s_functions; 761 | } 762 | }; 763 | 764 | struct FStructProperty : public FProperty { 765 | inline UEVR_FStructPropertyHandle to_handle() { return (UEVR_FStructPropertyHandle)this; } 766 | inline UEVR_FStructPropertyHandle to_handle() const { return (UEVR_FStructPropertyHandle)this; } 767 | 768 | UScriptStruct* get_struct() const { 769 | static const auto fn = initialize()->get_struct; 770 | return (UScriptStruct*)fn(to_handle()); 771 | } 772 | 773 | private: 774 | static inline const UEVR_FStructPropertyFunctions* s_functions{nullptr}; 775 | static inline const UEVR_FStructPropertyFunctions* initialize() { 776 | if (s_functions == nullptr) { 777 | s_functions = API::get()->sdk()->fstructproperty; 778 | } 779 | 780 | return s_functions; 781 | } 782 | }; 783 | 784 | struct UEnum : public UObject { 785 | 786 | }; 787 | 788 | struct FNumericProperty : public FProperty { 789 | 790 | }; 791 | 792 | struct FEnumProperty : public FProperty { 793 | inline UEVR_FEnumPropertyHandle to_handle() { return (UEVR_FEnumPropertyHandle)this; } 794 | inline UEVR_FEnumPropertyHandle to_handle() const { return (UEVR_FEnumPropertyHandle)this; } 795 | 796 | FNumericProperty* get_underlying_prop() const { 797 | static const auto fn = initialize()->get_underlying_prop; 798 | return (FNumericProperty*)fn(to_handle()); 799 | } 800 | 801 | UEnum* get_enum() const { 802 | static const auto fn = initialize()->get_enum; 803 | return (UEnum*)fn(to_handle()); 804 | } 805 | 806 | private: 807 | static inline const UEVR_FEnumPropertyFunctions* s_functions{nullptr}; 808 | static inline const UEVR_FEnumPropertyFunctions* initialize() { 809 | if (s_functions == nullptr) { 810 | s_functions = API::get()->sdk()->fenumproperty; 811 | } 812 | 813 | return s_functions; 814 | } 815 | }; 816 | 817 | struct FFieldClass { 818 | inline UEVR_FFieldClassHandle to_handle() { return (UEVR_FFieldClassHandle)this; } 819 | inline UEVR_FFieldClassHandle to_handle() const { return (UEVR_FFieldClassHandle)this; } 820 | 821 | FName* get_fname() const { 822 | static const auto fn = initialize()->get_fname; 823 | return (FName*)fn(to_handle()); 824 | } 825 | 826 | std::wstring get_name() const { 827 | return get_fname()->to_string(); 828 | } 829 | 830 | private: 831 | static inline const UEVR_FFieldClassFunctions* s_functions{nullptr}; 832 | static inline const UEVR_FFieldClassFunctions* initialize() { 833 | if (s_functions == nullptr) { 834 | s_functions = API::get()->sdk()->ffield_class; 835 | } 836 | 837 | return s_functions; 838 | } 839 | }; 840 | 841 | struct ConsoleObjectElement { 842 | wchar_t* key; 843 | int32_t unk[2]; 844 | IConsoleObject* value; 845 | int32_t unk2[2]; 846 | }; 847 | 848 | struct FConsoleManager { 849 | inline UEVR_FConsoleManagerHandle to_handle() { return (UEVR_FConsoleManagerHandle)this; } 850 | inline UEVR_FConsoleManagerHandle to_handle() const { return (UEVR_FConsoleManagerHandle)this; } 851 | 852 | TArray& get_console_objects() { 853 | static const auto fn = initialize()->get_console_objects; 854 | return *(TArray*)fn(to_handle()); 855 | } 856 | 857 | IConsoleObject* find_object(std::wstring_view name) { 858 | static const auto fn = initialize()->find_object; 859 | return (IConsoleObject*)fn(to_handle(), name.data()); 860 | } 861 | 862 | IConsoleVariable* find_variable(std::wstring_view name) { 863 | static const auto fn = initialize()->find_variable; 864 | return (IConsoleVariable*)fn(to_handle(), name.data()); 865 | } 866 | 867 | IConsoleCommand* find_command(std::wstring_view name) { 868 | static const auto fn = initialize()->find_command; 869 | return (IConsoleCommand*)fn(to_handle(), name.data()); 870 | } 871 | 872 | private: 873 | static inline const UEVR_ConsoleFunctions* s_functions{nullptr}; 874 | static inline const UEVR_ConsoleFunctions* initialize() { 875 | if (s_functions == nullptr) { 876 | s_functions = API::get()->sdk()->console; 877 | } 878 | 879 | return s_functions; 880 | } 881 | }; 882 | 883 | struct IConsoleObject { 884 | inline UEVR_IConsoleObjectHandle to_handle() { return (UEVR_IConsoleObjectHandle)this; } 885 | inline UEVR_IConsoleObjectHandle to_handle() const { return (UEVR_IConsoleObjectHandle)this; } 886 | 887 | IConsoleCommand* as_command() { 888 | static const auto fn = initialize()->as_command; 889 | return (IConsoleCommand*)fn(to_handle()); 890 | } 891 | 892 | private: 893 | static inline const UEVR_ConsoleFunctions* s_functions{nullptr}; 894 | static inline const UEVR_ConsoleFunctions* initialize() { 895 | if (s_functions == nullptr) { 896 | s_functions = API::get()->sdk()->console; 897 | } 898 | 899 | return s_functions; 900 | } 901 | }; 902 | 903 | struct IConsoleVariable : public IConsoleObject { 904 | inline UEVR_IConsoleVariableHandle to_handle() { return (UEVR_IConsoleVariableHandle)this; } 905 | inline UEVR_IConsoleVariableHandle to_handle() const { return (UEVR_IConsoleVariableHandle)this; } 906 | 907 | void set(std::wstring_view value) { 908 | static const auto fn = initialize()->variable_set; 909 | fn(to_handle(), value.data()); 910 | } 911 | 912 | void set_ex(std::wstring_view value, uint32_t flags = 0x80000000) { 913 | static const auto fn = initialize()->variable_set_ex; 914 | fn(to_handle(), value.data(), flags); 915 | } 916 | 917 | void set(float value) { 918 | set(std::to_wstring(value)); 919 | } 920 | 921 | void set(int value) { 922 | set(std::to_wstring(value)); 923 | } 924 | 925 | int get_int() const { 926 | static const auto fn = initialize()->variable_get_int; 927 | return fn(to_handle()); 928 | } 929 | 930 | float get_float() const { 931 | static const auto fn = initialize()->variable_get_float; 932 | return fn(to_handle()); 933 | } 934 | 935 | private: 936 | static inline const UEVR_ConsoleFunctions* s_functions{nullptr}; 937 | static inline const UEVR_ConsoleFunctions* initialize() { 938 | if (s_functions == nullptr) { 939 | s_functions = API::get()->sdk()->console; 940 | } 941 | 942 | return s_functions; 943 | } 944 | }; 945 | 946 | struct IConsoleCommand : public IConsoleObject { 947 | inline UEVR_IConsoleCommandHandle to_handle() { return (UEVR_IConsoleCommandHandle)this; } 948 | inline UEVR_IConsoleCommandHandle to_handle() const { return (UEVR_IConsoleCommandHandle)this; } 949 | 950 | void execute(std::wstring_view args) { 951 | static const auto fn = initialize()->command_execute; 952 | fn(to_handle(), args.data()); 953 | } 954 | 955 | private: 956 | static inline const UEVR_ConsoleFunctions* s_functions{nullptr}; 957 | static inline const UEVR_ConsoleFunctions* initialize() { 958 | if (s_functions == nullptr) { 959 | s_functions = API::get()->sdk()->console; 960 | } 961 | 962 | return s_functions; 963 | } 964 | }; 965 | 966 | // TODO 967 | struct UEngine : public UObject { 968 | static UEngine* get() { 969 | return API::get()->get_engine(); 970 | } 971 | }; 972 | 973 | struct UGameEngine : public UEngine { 974 | 975 | }; 976 | 977 | struct UWorld : public UObject { 978 | 979 | }; 980 | 981 | struct FUObjectArray { 982 | inline UEVR_UObjectArrayHandle to_handle() { return (UEVR_UObjectArrayHandle)this; } 983 | inline UEVR_UObjectArrayHandle to_handle() const { return (UEVR_UObjectArrayHandle)this; } 984 | 985 | static FUObjectArray* get() { 986 | return API::get()->get_uobject_array(); 987 | } 988 | 989 | static bool is_chunked() { 990 | static const auto fn = initialize()->is_chunked; 991 | return fn(); 992 | } 993 | 994 | static bool is_inlined() { 995 | static const auto fn = initialize()->is_inlined; 996 | return fn(); 997 | } 998 | 999 | static size_t get_objects_offset() { 1000 | static const auto fn = initialize()->get_objects_offset; 1001 | return (size_t)fn(); 1002 | } 1003 | 1004 | static size_t get_item_distance() { 1005 | static const auto fn = initialize()->get_item_distance; 1006 | return (size_t)fn(); 1007 | } 1008 | 1009 | int32_t get_object_count() const { 1010 | static const auto fn = initialize()->get_object_count; 1011 | return fn(to_handle()); 1012 | } 1013 | 1014 | void* get_objects_ptr() const { 1015 | static const auto fn = initialize()->get_objects_ptr; 1016 | return fn(to_handle()); 1017 | } 1018 | 1019 | UObject* get_object(int32_t index) const { 1020 | static const auto fn = initialize()->get_object; 1021 | return (UObject*)fn(to_handle(), index); 1022 | } 1023 | 1024 | // Generally the same structure most of the time, not too much to worry about 1025 | // Not that this would generally be used raw - instead prefer get_object 1026 | struct FUObjectItem { 1027 | API::UObject* object; 1028 | int32_t flags; 1029 | int32_t cluster_index; 1030 | int32_t serial_number; 1031 | }; 1032 | 1033 | FUObjectItem* get_item(int32_t index) const { 1034 | static const auto fn = initialize()->get_item; 1035 | return (FUObjectItem*)fn(to_handle(), index); 1036 | } 1037 | 1038 | private: 1039 | static inline const UEVR_UObjectArrayFunctions* s_functions{nullptr}; 1040 | static inline const UEVR_UObjectArrayFunctions* initialize() { 1041 | if (s_functions == nullptr) { 1042 | s_functions = API::get()->sdk()->uobject_array; 1043 | } 1044 | 1045 | return s_functions; 1046 | } 1047 | }; 1048 | 1049 | // One of the very few non-opaque structs 1050 | // because these have never changed, if they do its because of bespoke code 1051 | template 1052 | struct TArray { 1053 | T* data; 1054 | int32_t count; 1055 | int32_t capacity; 1056 | 1057 | ~TArray() { 1058 | if (data != nullptr) { 1059 | FMalloc::get()->free(data); 1060 | data = nullptr; 1061 | } 1062 | } 1063 | 1064 | T* begin() { 1065 | return data; 1066 | } 1067 | 1068 | T* end() { 1069 | if (data == nullptr) { 1070 | return nullptr; 1071 | } 1072 | 1073 | return data + count; 1074 | } 1075 | 1076 | T* begin() const { 1077 | return data; 1078 | } 1079 | 1080 | T* end() const { 1081 | if (data == nullptr) { 1082 | return nullptr; 1083 | } 1084 | 1085 | return data + count; 1086 | } 1087 | 1088 | bool empty() const { 1089 | return count == 0 || data == nullptr; 1090 | } 1091 | }; 1092 | 1093 | struct FRHITexture2D { 1094 | inline UEVR_FRHITexture2DHandle to_handle() { return (UEVR_FRHITexture2DHandle)this; } 1095 | inline UEVR_FRHITexture2DHandle to_handle() const { return (UEVR_FRHITexture2DHandle)this; } 1096 | 1097 | void* get_native_resource() const { 1098 | static const auto fn = initialize()->get_native_resource; 1099 | return fn(to_handle()); 1100 | } 1101 | 1102 | private: 1103 | static inline const UEVR_FRHITexture2DFunctions* s_functions{nullptr}; 1104 | static inline const UEVR_FRHITexture2DFunctions* initialize() { 1105 | if (s_functions == nullptr) { 1106 | s_functions = API::get()->sdk()->frhitexture2d; 1107 | } 1108 | 1109 | return s_functions; 1110 | } 1111 | }; 1112 | 1113 | public: 1114 | // UEVR specific stuff 1115 | struct VR { 1116 | static bool is_runtime_ready() { 1117 | static const auto fn = initialize()->is_runtime_ready; 1118 | return fn(); 1119 | } 1120 | 1121 | static bool is_openvr() { 1122 | static const auto fn = initialize()->is_openvr; 1123 | return fn(); 1124 | } 1125 | 1126 | static bool is_openxr() { 1127 | static const auto fn = initialize()->is_openxr; 1128 | return fn(); 1129 | } 1130 | 1131 | static bool is_hmd_active() { 1132 | static const auto fn = initialize()->is_hmd_active; 1133 | return fn(); 1134 | } 1135 | 1136 | static UEVR_Vector3f get_standing_origin() { 1137 | static const auto fn = initialize()->get_standing_origin; 1138 | UEVR_Vector3f result{}; 1139 | 1140 | fn(&result); 1141 | return result; 1142 | } 1143 | 1144 | static UEVR_Quaternionf get_rotation_offset() { 1145 | static const auto fn = initialize()->get_rotation_offset; 1146 | UEVR_Quaternionf result{}; 1147 | 1148 | fn(&result); 1149 | return result; 1150 | } 1151 | 1152 | static void set_standing_origin(const UEVR_Vector3f& origin) { 1153 | static const auto fn = initialize()->set_standing_origin; 1154 | fn(&origin); 1155 | } 1156 | 1157 | static void set_rotation_offset(const UEVR_Quaternionf& offset) { 1158 | static const auto fn = initialize()->set_rotation_offset; 1159 | fn(&offset); 1160 | } 1161 | 1162 | static void set_rotation_offset(const UEVR_Quaternionf* offset) { 1163 | static const auto fn = initialize()->set_rotation_offset; 1164 | fn(offset); 1165 | } 1166 | 1167 | static UEVR_TrackedDeviceIndex get_hmd_index() { 1168 | static const auto fn = initialize()->get_hmd_index; 1169 | return fn(); 1170 | } 1171 | 1172 | static UEVR_TrackedDeviceIndex get_left_controller_index() { 1173 | static const auto fn = initialize()->get_left_controller_index; 1174 | return fn(); 1175 | } 1176 | 1177 | static UEVR_TrackedDeviceIndex get_right_controller_index() { 1178 | static const auto fn = initialize()->get_right_controller_index; 1179 | return fn(); 1180 | } 1181 | 1182 | struct Pose { 1183 | UEVR_Vector3f position; 1184 | UEVR_Quaternionf rotation; 1185 | }; 1186 | 1187 | static Pose get_pose(UEVR_TrackedDeviceIndex index) { 1188 | static const auto fn = initialize()->get_pose; 1189 | Pose result{}; 1190 | 1191 | fn(index, &result.position, &result.rotation); 1192 | return result; 1193 | } 1194 | 1195 | UEVR_Matrix4x4f get_transform(UEVR_TrackedDeviceIndex index) { 1196 | static const auto fn = initialize()->get_transform; 1197 | UEVR_Matrix4x4f result{}; 1198 | 1199 | fn(index, &result); 1200 | return result; 1201 | } 1202 | 1203 | static Pose get_grip_pose(UEVR_TrackedDeviceIndex index) { 1204 | static const auto fn = initialize()->get_grip_pose; 1205 | Pose result{}; 1206 | 1207 | fn(index, &result.position, &result.rotation); 1208 | return result; 1209 | } 1210 | 1211 | static Pose get_aim_pose(UEVR_TrackedDeviceIndex index) { 1212 | static const auto fn = initialize()->get_aim_pose; 1213 | Pose result{}; 1214 | 1215 | fn(index, &result.position, &result.rotation); 1216 | return result; 1217 | } 1218 | 1219 | static UEVR_Matrix4x4f get_grip_transform(UEVR_TrackedDeviceIndex index) { 1220 | static const auto fn = initialize()->get_grip_transform; 1221 | UEVR_Matrix4x4f result{}; 1222 | 1223 | fn(index, &result); 1224 | return result; 1225 | } 1226 | 1227 | static UEVR_Matrix4x4f get_aim_transform(UEVR_TrackedDeviceIndex index) { 1228 | static const auto fn = initialize()->get_aim_transform; 1229 | UEVR_Matrix4x4f result{}; 1230 | 1231 | fn(index, &result); 1232 | return result; 1233 | } 1234 | 1235 | enum class Eye : int32_t { 1236 | LEFT, 1237 | RIGHT 1238 | }; 1239 | 1240 | static UEVR_Vector3f get_eye_offset(Eye eye) { 1241 | static const auto fn = initialize()->get_eye_offset; 1242 | UEVR_Vector3f result{}; 1243 | 1244 | fn((int32_t)eye, &result); 1245 | return result; 1246 | } 1247 | 1248 | static UEVR_Matrix4x4f get_ue_projection_matrix(Eye eye) { 1249 | static const auto fn = initialize()->get_ue_projection_matrix; 1250 | UEVR_Matrix4x4f result{}; 1251 | 1252 | fn((int32_t)eye, &result); 1253 | return result; 1254 | } 1255 | 1256 | static UEVR_InputSourceHandle get_left_joystick_source() { 1257 | static const auto fn = initialize()->get_left_joystick_source; 1258 | return fn(); 1259 | } 1260 | 1261 | static UEVR_InputSourceHandle get_right_joystick_source() { 1262 | static const auto fn = initialize()->get_right_joystick_source; 1263 | return fn(); 1264 | } 1265 | 1266 | static UEVR_ActionHandle get_action_handle(std::string_view name) { 1267 | static const auto fn = initialize()->get_action_handle; 1268 | return fn(name.data()); 1269 | } 1270 | 1271 | static bool is_action_active(UEVR_ActionHandle handle, UEVR_InputSourceHandle source) { 1272 | static const auto fn = initialize()->is_action_active; 1273 | return fn(handle, source); 1274 | } 1275 | 1276 | static bool is_action_active_any_joystick(UEVR_ActionHandle handle) { 1277 | static const auto fn = initialize()->is_action_active_any_joystick; 1278 | return fn(handle); 1279 | } 1280 | 1281 | static UEVR_Vector2f get_joystick_axis(UEVR_InputSourceHandle source) { 1282 | static const auto fn = initialize()->get_joystick_axis; 1283 | UEVR_Vector2f result{}; 1284 | 1285 | fn(source, &result); 1286 | return result; 1287 | } 1288 | 1289 | static void trigger_haptic_vibration(UEVR_TrackedDeviceIndex index, float amplitude, float frequency, float duration, UEVR_InputSourceHandle source) { 1290 | static const auto fn = initialize()->trigger_haptic_vibration; 1291 | fn(index, amplitude, frequency, duration, source); 1292 | } 1293 | 1294 | static bool is_using_contriollers() { 1295 | static const auto fn = initialize()->is_using_controllers; 1296 | return fn(); 1297 | } 1298 | 1299 | static bool is_decoupled_pitch_enabled() { 1300 | static const auto fn = initialize()->is_decoupled_pitch_enabled; 1301 | return fn(); 1302 | } 1303 | 1304 | enum class AimMethod : int32_t { 1305 | GAME, 1306 | HEAD, 1307 | RIGHT_CONTROLLER, 1308 | LEFT_CONTROLLER, 1309 | TWO_HANDED_RIGHT, 1310 | TWO_HANDED_LEFT, 1311 | }; 1312 | 1313 | static AimMethod get_movement_orientation() { 1314 | static const auto fn = initialize()->get_movement_orientation; 1315 | return (AimMethod)fn(); 1316 | } 1317 | 1318 | static uint32_t get_lowest_xinput_index() { 1319 | static const auto fn = initialize()->get_lowest_xinput_index; 1320 | return fn(); 1321 | } 1322 | 1323 | static void recenter_view() { 1324 | static const auto fn = initialize()->recenter_view; 1325 | fn(); 1326 | } 1327 | 1328 | static void recenter_horizon() { 1329 | static const auto fn = initialize()->recenter_horizon; 1330 | fn(); 1331 | } 1332 | 1333 | static AimMethod get_aim_method() { 1334 | static const auto fn = initialize()->get_aim_method; 1335 | return (AimMethod)fn(); 1336 | } 1337 | 1338 | static void set_aim_method(AimMethod method) { 1339 | static const auto fn = initialize()->set_aim_method; 1340 | fn((uint32_t)method); 1341 | } 1342 | 1343 | static bool is_aim_allowed() { 1344 | static const auto fn = initialize()->is_aim_allowed; 1345 | return fn(); 1346 | } 1347 | 1348 | static void set_aim_allowed(bool allowed) { 1349 | static const auto fn = initialize()->set_aim_allowed; 1350 | fn(allowed); 1351 | } 1352 | 1353 | static uint32_t get_hmd_width() { 1354 | static const auto fn = initialize()->get_hmd_width; 1355 | return fn(); 1356 | } 1357 | 1358 | static uint32_t get_hmd_height() { 1359 | static const auto fn = initialize()->get_hmd_height; 1360 | return fn(); 1361 | } 1362 | 1363 | static uint32_t get_ui_width() { 1364 | static const auto fn = initialize()->get_ui_width; 1365 | return fn(); 1366 | } 1367 | 1368 | static uint32_t get_ui_height() { 1369 | static const auto fn = initialize()->get_ui_height; 1370 | return fn(); 1371 | } 1372 | 1373 | static bool is_snap_turn_enabled() { 1374 | static const auto fn = initialize()->is_snap_turn_enabled; 1375 | return fn(); 1376 | } 1377 | 1378 | static void set_snap_turn_enabled(bool enabled) { 1379 | static const auto fn = initialize()->set_snap_turn_enabled; 1380 | fn(enabled); 1381 | } 1382 | 1383 | static void set_decoupled_pitch_enabled(bool enabled) { 1384 | static const auto fn = initialize()->set_decoupled_pitch_enabled; 1385 | fn(enabled); 1386 | } 1387 | 1388 | template 1389 | static void set_mod_value(std::string_view key, const T& value) { 1390 | static const auto fn = initialize()->set_mod_value; 1391 | 1392 | if constexpr (std::is_same_v || std::is_same_v || std::is_same_v) { 1393 | fn(key.data(), value); 1394 | return; 1395 | } else if constexpr (std::is_same_v || std::is_same_v) { 1396 | fn(key.data(), value.data()); 1397 | return; 1398 | } else if constexpr (std::is_same_v) { 1399 | if (value) { 1400 | fn(key.data(), "true"); 1401 | } else { 1402 | fn(key.data(), "false"); 1403 | } 1404 | 1405 | return; 1406 | } 1407 | 1408 | 1409 | fn(key.data(), std::to_string(value).c_str()); 1410 | } 1411 | 1412 | template 1413 | static T get_mod_value(std::string_view key) { 1414 | static const auto fn = initialize()->get_mod_value; 1415 | char result[256]{}; 1416 | fn(key.data(), result, sizeof(result)); 1417 | 1418 | static_assert(!std::is_same_v, "Cannot get a void value"); 1419 | static_assert(!std::is_same_v, "Cannot get a const char* value"); 1420 | static_assert(!std::is_same_v, "Cannot get a const char* value"); 1421 | static_assert(!std::is_same_v, "Cannot get a char* value"); 1422 | 1423 | if constexpr (std::is_same_v) { 1424 | return std::string{result}; 1425 | } else if constexpr (std::is_same_v) { 1426 | return std::string_view{result} == "true"; 1427 | } else if constexpr (std::is_integral_v) { 1428 | if constexpr (std::is_unsigned_v) { 1429 | return (T)std::stoul(result); 1430 | } 1431 | 1432 | return (T)std::stoi(result); 1433 | } else if constexpr (std::is_floating_point_v) { 1434 | return (T)std::stod(result); 1435 | } 1436 | 1437 | return T{}; 1438 | } 1439 | 1440 | static void save_config() { 1441 | static const auto fn = initialize()->save_config; 1442 | fn(); 1443 | } 1444 | 1445 | static void reload_config() { 1446 | static const auto fn = initialize()->reload_config; 1447 | fn(); 1448 | } 1449 | 1450 | private: 1451 | static inline const UEVR_VRData* s_functions{nullptr}; 1452 | static inline const UEVR_VRData* initialize() { 1453 | if (s_functions == nullptr) { 1454 | s_functions = API::get()->param()->vr; 1455 | } 1456 | 1457 | return s_functions; 1458 | } 1459 | }; 1460 | 1461 | struct UObjectHook { 1462 | struct MotionControllerState; 1463 | 1464 | static void activate() { 1465 | static const auto fn = initialize()->activate; 1466 | fn(); 1467 | } 1468 | 1469 | static bool exists(UObject* obj) { 1470 | static const auto fn = initialize()->exists; 1471 | return fn(obj->to_handle()); 1472 | } 1473 | 1474 | static bool is_disabled() { 1475 | static const auto fn = initialize()->is_disabled; 1476 | return fn(); 1477 | } 1478 | 1479 | static void set_disabled(bool disabled) { 1480 | static const auto fn = initialize()->set_disabled; 1481 | fn(disabled); 1482 | } 1483 | 1484 | static std::vector get_objects_by_class(UClass* c, bool allow_default = false) { 1485 | if (c == nullptr) { 1486 | return {}; 1487 | } 1488 | 1489 | return c->get_objects_matching(allow_default); 1490 | } 1491 | 1492 | static UObject* get_first_object_by_class(UClass* c, bool allow_default = false) { 1493 | if (c == nullptr) { 1494 | return nullptr; 1495 | } 1496 | 1497 | return c->get_first_object_matching(allow_default); 1498 | } 1499 | 1500 | // Must be a USceneComponent 1501 | // Also, do NOT keep the pointer around, it will be invalidated at any time 1502 | // Call it every time you need it 1503 | static MotionControllerState* get_or_add_motion_controller_state(UObject* obj) { 1504 | static const auto fn = initialize()->get_or_add_motion_controller_state; 1505 | return (MotionControllerState*)fn(obj->to_handle()); 1506 | } 1507 | 1508 | static MotionControllerState* get_motion_controller_state(UObject* obj) { 1509 | static const auto fn = initialize()->get_motion_controller_state; 1510 | return (MotionControllerState*)fn(obj->to_handle()); 1511 | } 1512 | 1513 | struct MotionControllerState { 1514 | inline UEVR_UObjectHookMotionControllerStateHandle to_handle() { return (UEVR_UObjectHookMotionControllerStateHandle)this; } 1515 | inline UEVR_UObjectHookMotionControllerStateHandle to_handle() const { return (UEVR_UObjectHookMotionControllerStateHandle)this; } 1516 | 1517 | void set_rotation_offset(const UEVR_Quaternionf* offset) { 1518 | static const auto fn = initialize()->set_rotation_offset; 1519 | fn(to_handle(), offset); 1520 | } 1521 | 1522 | void set_location_offset(const UEVR_Vector3f* offset) { 1523 | static const auto fn = initialize()->set_location_offset; 1524 | fn(to_handle(), offset); 1525 | } 1526 | 1527 | void set_hand(uint32_t hand) { 1528 | static const auto fn = initialize()->set_hand; 1529 | fn(to_handle(), hand); 1530 | } 1531 | 1532 | void set_permanent(bool permanent) { 1533 | static const auto fn = initialize()->set_permanent; 1534 | fn(to_handle(), permanent); 1535 | } 1536 | 1537 | private: 1538 | static inline const UEVR_UObjectHookMotionControllerStateFunctions* s_functions{nullptr}; 1539 | static inline const UEVR_UObjectHookMotionControllerStateFunctions* initialize() { 1540 | if (s_functions == nullptr) { 1541 | s_functions = API::get()->sdk()->uobject_hook->mc_state; 1542 | } 1543 | 1544 | return s_functions; 1545 | } 1546 | }; 1547 | 1548 | private: 1549 | static inline const UEVR_UObjectHookFunctions* s_functions{nullptr}; 1550 | static inline const UEVR_UObjectHookFunctions* initialize() { 1551 | if (s_functions == nullptr) { 1552 | s_functions = API::get()->sdk()->uobject_hook; 1553 | } 1554 | 1555 | return s_functions; 1556 | } 1557 | }; 1558 | 1559 | struct RenderTargetPoolHook { 1560 | static void activate() { 1561 | static const auto fn = initialize()->activate; 1562 | fn(); 1563 | } 1564 | 1565 | static IPooledRenderTarget* get_render_target(const wchar_t* name) { 1566 | static const auto fn = initialize()->get_render_target; 1567 | return (IPooledRenderTarget*)fn(name); 1568 | } 1569 | 1570 | private: 1571 | static inline const UEVR_FRenderTargetPoolHookFunctions* s_functions{nullptr}; 1572 | static inline const UEVR_FRenderTargetPoolHookFunctions* initialize() { 1573 | if (s_functions == nullptr) { 1574 | s_functions = API::get()->sdk()->render_target_pool_hook; 1575 | } 1576 | 1577 | return s_functions; 1578 | } 1579 | }; 1580 | 1581 | struct StereoHook { 1582 | static FRHITexture2D* get_scene_render_target() { 1583 | static const auto fn = initialize()->get_scene_render_target; 1584 | return (FRHITexture2D*)fn(); 1585 | } 1586 | 1587 | static FRHITexture2D* get_ui_render_target() { 1588 | static const auto fn = initialize()->get_ui_render_target; 1589 | return (FRHITexture2D*)fn(); 1590 | } 1591 | 1592 | private: 1593 | static inline const UEVR_FFakeStereoRenderingHookFunctions* s_functions{nullptr}; 1594 | static inline const UEVR_FFakeStereoRenderingHookFunctions* initialize() { 1595 | if (s_functions == nullptr) { 1596 | s_functions = API::get()->sdk()->stereo_hook; 1597 | } 1598 | 1599 | return s_functions; 1600 | } 1601 | }; 1602 | 1603 | private: 1604 | const UEVR_PluginInitializeParam* m_param; 1605 | const UEVR_SDKData* m_sdk; 1606 | }; 1607 | } -------------------------------------------------------------------------------- /SystemShockVR/uevr/Plugin.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file (Plugin.hpp) is licensed under the MIT license and is separate from the rest of the UEVR codebase. 3 | 4 | Copyright (c) 2023 praydog 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | // Helper header to easily instantiate a plugin 26 | // and get some initial callbacks setup 27 | // the user can inherit from the Plugin class 28 | // and set uevr::g_plugin to their plugin instance 29 | #pragma once 30 | 31 | #include 32 | #include 33 | 34 | #include 35 | #include 36 | 37 | #include "API.hpp" 38 | 39 | namespace uevr { 40 | class Plugin; 41 | 42 | namespace detail { 43 | static inline ::uevr::Plugin* g_plugin{nullptr}; 44 | } 45 | 46 | class Plugin { 47 | public: 48 | Plugin() { detail::g_plugin = this; } 49 | 50 | virtual ~Plugin() = default; 51 | 52 | // Main plugin callbacks 53 | virtual void on_dllmain() {} 54 | virtual void on_initialize() {} 55 | virtual void on_present() {} 56 | virtual void on_post_render_vr_framework_dx11(ID3D11DeviceContext* context, ID3D11Texture2D* texture, ID3D11RenderTargetView* rtv) {} 57 | virtual void on_post_render_vr_framework_dx12(ID3D12GraphicsCommandList* command_list, ID3D12Resource* rt, D3D12_CPU_DESCRIPTOR_HANDLE* rtv) {} 58 | virtual void on_device_reset() {} 59 | virtual bool on_message(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { return true; } 60 | virtual void on_xinput_get_state(uint32_t* retval, uint32_t user_index, XINPUT_STATE* state) {} 61 | virtual void on_xinput_set_state(uint32_t* retval, uint32_t user_index, XINPUT_VIBRATION* vibration) {} 62 | 63 | // Game/Engine callbacks 64 | virtual void on_pre_engine_tick(API::UGameEngine* engine, float delta) {} 65 | virtual void on_post_engine_tick(API::UGameEngine* engine, float delta) {} 66 | virtual void on_pre_slate_draw_window(UEVR_FSlateRHIRendererHandle renderer, UEVR_FViewportInfoHandle viewport_info) {} 67 | virtual void on_post_slate_draw_window(UEVR_FSlateRHIRendererHandle renderer, UEVR_FViewportInfoHandle viewport_info) {} 68 | virtual void on_pre_calculate_stereo_view_offset(UEVR_StereoRenderingDeviceHandle, int view_index, float world_to_meters, 69 | UEVR_Vector3f* position, UEVR_Rotatorf* rotation, bool is_double) {}; 70 | virtual void on_post_calculate_stereo_view_offset(UEVR_StereoRenderingDeviceHandle, int view_index, float world_to_meters, 71 | UEVR_Vector3f* position, UEVR_Rotatorf* rotation, bool is_double) {}; 72 | 73 | virtual void on_pre_viewport_client_draw(UEVR_UGameViewportClientHandle viewport_client, UEVR_FViewportHandle viewport, UEVR_FCanvasHandle) {} 74 | virtual void on_post_viewport_client_draw(UEVR_UGameViewportClientHandle viewport_client, UEVR_FViewportHandle viewport, UEVR_FCanvasHandle) {} 75 | 76 | protected: 77 | }; 78 | } 79 | 80 | extern "C" __declspec(dllexport) void uevr_plugin_required_version(UEVR_PluginVersion* version) { 81 | version->major = UEVR_PLUGIN_VERSION_MAJOR; 82 | version->minor = UEVR_PLUGIN_VERSION_MINOR; 83 | version->patch = UEVR_PLUGIN_VERSION_PATCH; 84 | } 85 | 86 | extern "C" __declspec(dllexport) bool uevr_plugin_initialize(const UEVR_PluginInitializeParam* param) { 87 | auto& api = uevr::API::initialize(param); 88 | uevr::detail::g_plugin->on_initialize(); 89 | 90 | auto callbacks = param->callbacks; 91 | auto sdk_callbacks = param->sdk->callbacks; 92 | 93 | callbacks->on_device_reset([]() { 94 | uevr::detail::g_plugin->on_device_reset(); 95 | }); 96 | 97 | callbacks->on_present([]() { 98 | uevr::detail::g_plugin->on_present(); 99 | }); 100 | 101 | callbacks->on_post_render_vr_framework_dx11([](void* context, void* texture, void* rtv) { 102 | uevr::detail::g_plugin->on_post_render_vr_framework_dx11((ID3D11DeviceContext*)context, (ID3D11Texture2D*)texture, (ID3D11RenderTargetView*)rtv); 103 | }); 104 | 105 | callbacks->on_post_render_vr_framework_dx12([](void* command_list, void* rt, void* rtv) { 106 | uevr::detail::g_plugin->on_post_render_vr_framework_dx12((ID3D12GraphicsCommandList*)command_list, (ID3D12Resource*)rt, (D3D12_CPU_DESCRIPTOR_HANDLE*)rtv); 107 | }); 108 | 109 | callbacks->on_message([](void* hwnd, unsigned int msg, unsigned long long wparam, long long lparam) { 110 | return uevr::detail::g_plugin->on_message((HWND)hwnd, msg, wparam, lparam); 111 | }); 112 | 113 | callbacks->on_xinput_get_state([](unsigned int* retval, unsigned int user_index, void* state) { 114 | uevr::detail::g_plugin->on_xinput_get_state(retval, user_index, (XINPUT_STATE*)state); 115 | }); 116 | 117 | callbacks->on_xinput_set_state([](unsigned int* retval, unsigned int user_index, void* vibration) { 118 | uevr::detail::g_plugin->on_xinput_set_state(retval, user_index, (XINPUT_VIBRATION*)vibration); 119 | }); 120 | 121 | sdk_callbacks->on_pre_engine_tick([](UEVR_UGameEngineHandle engine, float delta) { 122 | uevr::detail::g_plugin->on_pre_engine_tick((uevr::API::UGameEngine*)engine, delta); 123 | }); 124 | 125 | sdk_callbacks->on_post_engine_tick([](UEVR_UGameEngineHandle engine, float delta) { 126 | uevr::detail::g_plugin->on_post_engine_tick((uevr::API::UGameEngine*)engine, delta); 127 | }); 128 | 129 | sdk_callbacks->on_pre_slate_draw_window_render_thread([](UEVR_FSlateRHIRendererHandle renderer, UEVR_FViewportInfoHandle viewport_info) { 130 | uevr::detail::g_plugin->on_pre_slate_draw_window(renderer, viewport_info); 131 | }); 132 | 133 | sdk_callbacks->on_post_slate_draw_window_render_thread([](UEVR_FSlateRHIRendererHandle renderer, UEVR_FViewportInfoHandle viewport_info) { 134 | uevr::detail::g_plugin->on_post_slate_draw_window(renderer, viewport_info); 135 | }); 136 | 137 | sdk_callbacks->on_pre_calculate_stereo_view_offset([](UEVR_StereoRenderingDeviceHandle device, int view_index, float world_to_meters, 138 | UEVR_Vector3f* position, UEVR_Rotatorf* rotation, bool is_double) { 139 | uevr::detail::g_plugin->on_pre_calculate_stereo_view_offset(device, view_index, world_to_meters, position, rotation, is_double); 140 | }); 141 | 142 | sdk_callbacks->on_post_calculate_stereo_view_offset([](UEVR_StereoRenderingDeviceHandle device, int view_index, float world_to_meters, 143 | UEVR_Vector3f* position, UEVR_Rotatorf* rotation, bool is_double) { 144 | uevr::detail::g_plugin->on_post_calculate_stereo_view_offset(device, view_index, world_to_meters, position, rotation, is_double); 145 | }); 146 | 147 | sdk_callbacks->on_pre_viewport_client_draw([](UEVR_UGameViewportClientHandle viewport_client, UEVR_FViewportHandle viewport, UEVR_FCanvasHandle canvas) { 148 | uevr::detail::g_plugin->on_pre_viewport_client_draw(viewport_client, viewport, canvas); 149 | }); 150 | 151 | sdk_callbacks->on_post_viewport_client_draw([](UEVR_UGameViewportClientHandle viewport_client, UEVR_FViewportHandle viewport, UEVR_FCanvasHandle canvas) { 152 | uevr::detail::g_plugin->on_post_viewport_client_draw(viewport_client, viewport, canvas); 153 | }); 154 | 155 | return true; 156 | } 157 | 158 | BOOL APIENTRY DllMain(HANDLE handle, DWORD reason, LPVOID reserved) { 159 | if (reason == DLL_PROCESS_ATTACH) { 160 | uevr::detail::g_plugin->on_dllmain(); 161 | } 162 | 163 | return TRUE; 164 | } 165 | --------------------------------------------------------------------------------