diff --git a/README.md b/README.md index 4153868..ed2cedf 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ## Features 1. fixes cockpit camera so it works out of the box in most situations 1. adds 'unstuck cockpit instruments' button (left start/select button on Xbox controller) for those rare occurrences where automation fails. -1. adds optional controls remap selectable in plugin overlay in UEVR menu. After remap controls are in line with typical flight sim - LS is elevator and rudder, RS is aileron and throttle. +1. Adds optional controls remap selectable in plugin overlay in UEVR menu. For remap any standard RC control mode can be selected (Mode 1-4). ## Building diff --git a/profile_template/ace_combat_plugin.ini b/profile_template/ace_combat_plugin.ini index d6f4058..b6e893d 100644 --- a/profile_template/ace_combat_plugin.ini +++ b/profile_template/ace_combat_plugin.ini @@ -14,5 +14,5 @@ Size=493,65 Collapsed=0 [UserData][Ace Combat Plugin] -AlternateControlScheme=0 +ControlScheme=0 diff --git a/release-template.md b/release-template.md index e549edc..352ed45 100644 --- a/release-template.md +++ b/release-template.md @@ -1,15 +1,27 @@ # Ace Combat 7 UEVR compatibility mod ## Mod Features -1. fixes cockpit camera so it works out of the box in most situations -1. adds 'unstuck cockpit instruments' button (left start/select button on Xbox controller) for those rare occurrences where automation fails. -1. adds optional controls remap selectable in plugin overlay in UEVR menu. After remap controls are in line with typical flight sim - LS is elevator and rudder, RS is aileron and throttle. -1. works well **only with VR plane skins added by Konan's VR Planes mod** (see below). +1. **Fixes cockpit camera** so it works out of the box in most situations +1. Adds **'unstuck cockpit instruments' button** (left start/select button on Xbox controller) for those rare occurrences where automation fails. + + **Button 3** on the image below: + ![](https://compass-ssl.xboxlive.com/assets/4a/5a/4a5a376a-50b3-444c-813d-248d4f82b51c.png?n=SXC-Article-MerlinControllerBackTopCallouts-L-16x9794x445-01.png) +1. Adds **optional controls remap** selectable in plugin overlay in UEVR menu. For remap any standard RC control mode can be selected: ![RC control modes](https://i.stack.imgur.com/3O98c.png) +1. works **only with VR plane skins added by Konan's VR Planes mod** (see below). **You need to beat campaign at least once** on any difficulty for skin slots to unlock. Otherwise there's no way to select VR version of a plane. + + I've tried quite a few ways to set 'campaign complete' flag in game but no luck. If you know a way let the Discord community know. ## Note -This is an alternative for [kosnag's UEVR compatibility mod](https://discord.com/channels/747967102895390741/1071072263820021840/1200018686317187092) (`~~~UEVR_Compatibility_Mod_P.pak`). **Do NOT install both** at the same time. The game will be very broken if you do. +* This is an alternative for [kosnag's UEVR compatibility mod](https://discord.com/channels/747967102895390741/1071072263820021840/1200018686317187092) (`~~~UEVR_Compatibility_Mod_P.pak`). **Do NOT install both** at the same time. The game will be very broken if you do. +* **This is early version of the mod**. Please give me a hand in testing - you see broken camera somewhere with the mod? Leave a note in Ace Combat 7 Flat2VR discord channel (instructions below). -**This is early version of the mod**. Please give me a hand in testing - you see broken camera somewhere with the mod? Leave a note in Ace Combat 7 Flat2VR discord channel (instructions below). +## Status/known issues +* Currently **whole game has been tested** using this mod. Status: + * Mission 13 has broken MFDs. Even if that will be fixed special weapon scope is not rendered in VR. Skip or play in flat screen. + * Mission 19 has broken long range radar. Still playable. + * in takeoff sequences where plane is stationary throttle up as soon as you can and let the plane move to fix the camera. This is a tradeoff for camera working well in so many other places. + * if you retry checkpoint you have to push 'unstuck cockpit instruments' button after you have control over the plane. So far this worked perfectly for me unlike with OG mod. + * otherwise it just works. If [kosnag's UEVR compatibility mod](https://discord.com/channels/747967102895390741/1071072263820021840/1200018686317187092) just worked 95% of the time, this works 99% with 1% being stated above. ## Getting started @@ -30,11 +42,11 @@ Flat2VR channel list is tiered. You need to tell the server that you want to see 1. [this is how your ~mods folder should look like](https://cdn.discordapp.com/attachments/1071072263820021840/1216517542667878440/image.png?ex=6600ad3f&is=65ee383f&hm=e09af0ea62b131f90924a7b526ff11ffd12d2782bac72f808f599de842bbaf77&). If in doubt use [Konan's VR Planes mod](https://discord.com/channels/747967102895390741/1071072263820021840/1216021454563446835) install instructions over the picture. ### UEVR Nightly `067279517877c0b3dd8a1bb46a7124c1e1aac113` or newer is required -* Vanilla version [here](https://github.com/praydog/UEVR-nightly/releases/tag/nightly-829-067279517877c0b3dd8a1bb46a7124c1e1aac113) -* if you have anything **but Pico 4** clouds will look broken. You'll need mrbelowski's patch on top of latest UEVR nightly. At the time of writing `UEVR.zip` from [my discord message](https://discord.com/channels/747967102895390741/1071072263820021840/1216517229051383838) is the only source I know. +* On **Quest 2/3/Pro** or any other headset with canted displays clouds will look broken. **You'll need a special UEVR version** with mrbelowski's patch on top of latest UEVR nightly. At the time of writing `UEVR.zip` from [my discord message](https://discord.com/channels/747967102895390741/1071072263820021840/1216995248350433310) is the only source I know. +* Vanilla UEVR Nightly version [here](https://github.com/praydog/UEVR-nightly/releases/tag/nightly-829-067279517877c0b3dd8a1bb46a7124c1e1aac113). **This will have broken clouds unless you use Pico 4** ### Installing the mod -1. download `Ace7Game.zip` from [here](https://github.com/keton/ace-combat-uevr/releases/latest) +1. download `Ace7Game.zip` from [here](https://github.com/keton/ace-combat-uevr/releases/latest/download/Ace7Game.zip) 1. import it as profile into UEVR using dedicated button in UEVR injector or unpack to `%APPDATA%\UnrealVRMod\Ace7Game` (you'll need to create `Ace7Game` or empty it before unpacking) ### Running @@ -44,7 +56,7 @@ Flat2VR channel list is tiered. You need to tell the server that you want to see 1. inject using UEVR nightly from above (optionally with mrbelowski's patch) 1. (for first time verification only) press and hold L3+R3 till UEVR overlay pops up. It's set to long press so flares work in game. Look for blue overlay panel labelled `Ace Combat Plugin`. If you see it plugin is loaded. If not something went wrong. 1. go into a mission, pick a plane -1. **important** in plane customization screen go into skin selection menu and go to the very bottom. It should have 10 extra skin slots and you want the last one. If you don't see those Additional Skin Slots mod is not installed correctly. Go trough the steps again or ask on Discord. +1. **important** in plane customization screen go into skin selection menu and go to the very bottom. It should have 10 extra skin slots and you want the last one. For that you have to have **completed campaign at least once** on any difficulty. Without that 'select skin' option won't show in game. If you don't see 10 extra skin slots under standard skins in menu Additional Skin Slots mod is not installed correctly. Go trough the steps again or ask on Discord. 1. **you really need to select 10th additional skin slot**. This is what triggers Konan's VR Planes mod. Without it displays in the cockpit and HUD won't work. Not all planes are already converted. Check documentation. 1. start mission as usual. Multi Function Displays (MFDs) should work. In case they get stuck press 'Unstuck cockpit button' - Xbox controller 'back' button (left small round one opposite to menu button) 1. Camera breaks in some missions/planes? 'Unstuck cockpit button' fails to work? Please help by reporting on Discord. diff --git a/src/Plugin.cpp b/src/Plugin.cpp index b415acf..5b19326 100644 --- a/src/Plugin.cpp +++ b/src/Plugin.cpp @@ -175,11 +175,12 @@ class AceCombatPlugin : public uevr::Plugin } // allow control remap to be bypassed - if(m_alternate_control_scheme == false) { + if(m_control_scheme == ControlScheme::DefaultControls) { return; } XINPUT_STATE starting_state{*target_state}; + const auto controls = remap_controls(&starting_state, m_control_scheme); target_state->Gamepad.bLeftTrigger = 0; target_state->Gamepad.bRightTrigger = 0; @@ -190,18 +191,23 @@ class AceCombatPlugin : public uevr::Plugin target_state->Gamepad.sThumbRX = 0; target_state->Gamepad.sThumbRY = 0; - // pitch - do nothing - target_state->Gamepad.sThumbLY = starting_state.Gamepad.sThumbLY; + // pitch + if((controls.pitch < -controls.pitch_deadzone) || + (controls.pitch > controls.pitch_deadzone)) { + target_state->Gamepad.sThumbLY = controls.pitch; + } // yaw, negative values == left - if(starting_state.Gamepad.sThumbLX < -XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) { + if(controls.yaw < -controls.yaw_deadzone) { target_state->Gamepad.wButtons |= XINPUT_GAMEPAD_LEFT_SHOULDER; - } else if(starting_state.Gamepad.sThumbLX > XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) { + } else if(controls.yaw > controls.yaw_deadzone) { target_state->Gamepad.wButtons |= XINPUT_GAMEPAD_RIGHT_SHOULDER; } // roll - target_state->Gamepad.sThumbLX = starting_state.Gamepad.sThumbRX; + if((controls.roll < -controls.roll_deadzone) || (controls.roll > controls.roll_deadzone)) { + target_state->Gamepad.sThumbLX = controls.roll; + } // throttle if(starting_state.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD || @@ -214,14 +220,14 @@ class AceCombatPlugin : public uevr::Plugin // user is not holding triggers // negative values == down - if(starting_state.Gamepad.sThumbRY < -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { - target_state->Gamepad.bLeftTrigger = std::abs(linear_scale( - starting_state.Gamepad.sThumbRY, -SHRT_MAX, -254, - -XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, -XINPUT_GAMEPAD_TRIGGER_THRESHOLD)); - } else if(starting_state.Gamepad.sThumbRY > XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) { - target_state->Gamepad.bRightTrigger = std::abs(linear_scale( - starting_state.Gamepad.sThumbRY, SHRT_MAX, 255, - XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, XINPUT_GAMEPAD_TRIGGER_THRESHOLD)); + if(controls.throttle < -controls.throttle_deadzone) { + target_state->Gamepad.bLeftTrigger = std::abs( + linear_scale(controls.throttle, -SHRT_MAX, -254, -controls.throttle_deadzone, + -XINPUT_GAMEPAD_TRIGGER_THRESHOLD)); + } else if(controls.throttle > controls.throttle_deadzone) { + target_state->Gamepad.bRightTrigger = std::abs( + linear_scale(controls.throttle, SHRT_MAX, 255, controls.throttle_deadzone, + XINPUT_GAMEPAD_TRIGGER_THRESHOLD)); } } } @@ -235,6 +241,28 @@ class AceCombatPlugin : public uevr::Plugin WaitingToStartShake }; + // Refer to standard RC control modes for example: https://i.stack.imgur.com/3O98c.png + enum ControlScheme + { + DefaultControls = 0, + Mode1, + Mode2, + Mode3, + Mode4, + }; + + struct ControlInputs + { + SHORT pitch; // elevator + SHORT pitch_deadzone; + SHORT yaw; // rudder + SHORT yaw_deadzone; + SHORT roll; // aileron + SHORT roll_deadzone; + SHORT throttle; + SHORT throttle_deadzone; + }; + static void *ini_handler_read_open(ImGuiContext *, ImGuiSettingsHandler *, const char *name) { ImGuiID id = ImHashStr(name); @@ -249,8 +277,9 @@ class AceCombatPlugin : public uevr::Plugin { int alternate_control_scheme = 0; - if(sscanf(line, "AlternateControlScheme=%d", &alternate_control_scheme) == 1) { - m_alternate_control_scheme = (alternate_control_scheme == 1); + if(sscanf(line, "ControlScheme=%d", &alternate_control_scheme) == 1 && + alternate_control_scheme >= 0 && alternate_control_scheme <= ControlScheme::Mode4) { + m_control_scheme = (ControlScheme)alternate_control_scheme; } } @@ -259,7 +288,7 @@ class AceCombatPlugin : public uevr::Plugin { buf->reserve(buf->size() + 200); // ballpark reserve buf->append("[UserData][Ace Combat Plugin]\n"); - buf->appendf("AlternateControlScheme=%d\n", m_alternate_control_scheme); + buf->appendf("ControlScheme=%d\n", m_control_scheme); buf->append("\n"); } @@ -319,7 +348,17 @@ class AceCombatPlugin : public uevr::Plugin void internal_frame() { if(ImGui::Begin("Ace Combat Plugin")) { - ImGui::Checkbox("Use alternate control scheme", &m_alternate_control_scheme); + const char *labels[] = { + "Default", "Mode 1", "Mode 2", "Mode 3", "Mode 4", + }; + + int selection = m_control_scheme; + + if(ImGui::Combo("Control Scheme", &selection, labels, IM_ARRAYSIZE(labels))) { + if(selection >= 0 && selection <= ControlScheme::Mode4) { + m_control_scheme = (ControlScheme)selection; + } + } } ImGui::End(); @@ -578,6 +617,61 @@ class AceCombatPlugin : public uevr::Plugin return std::floor((double)(val - val_min) * ratio + target_min); } + ControlInputs remap_controls(const XINPUT_STATE *const starting_state, + const ControlScheme control_scheme) + { + switch(control_scheme) { + case ControlScheme::Mode1: + return { + .pitch = starting_state->Gamepad.sThumbLY, + .pitch_deadzone = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, + .yaw = starting_state->Gamepad.sThumbLX, + .yaw_deadzone = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, + .roll = starting_state->Gamepad.sThumbRX, + .roll_deadzone = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, + .throttle = starting_state->Gamepad.sThumbRY, + .throttle_deadzone = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, + }; + case ControlScheme::Mode2: + return { + .pitch = starting_state->Gamepad.sThumbRY, + .pitch_deadzone = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, + .yaw = starting_state->Gamepad.sThumbLX, + .yaw_deadzone = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, + .roll = starting_state->Gamepad.sThumbRX, + .roll_deadzone = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, + .throttle = starting_state->Gamepad.sThumbLY, + .throttle_deadzone = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, + }; + + case ControlScheme::Mode4: + return { + .pitch = starting_state->Gamepad.sThumbRY, + .pitch_deadzone = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, + .yaw = starting_state->Gamepad.sThumbRX, + .yaw_deadzone = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, + .roll = starting_state->Gamepad.sThumbLX, + .roll_deadzone = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, + .throttle = starting_state->Gamepad.sThumbLY, + .throttle_deadzone = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, + }; + + case ControlScheme::Mode3: + [[fallthrough]]; + default: + return { + .pitch = starting_state->Gamepad.sThumbLY, + .pitch_deadzone = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, + .yaw = starting_state->Gamepad.sThumbRX, + .yaw_deadzone = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, + .roll = starting_state->Gamepad.sThumbLX, + .roll_deadzone = XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, + .throttle = starting_state->Gamepad.sThumbRY, + .throttle_deadzone = XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, + }; + } + } + private: HWND m_wnd{}; bool m_initialized{false}; @@ -602,7 +696,7 @@ class AceCombatPlugin : public uevr::Plugin UEVR_Vector3f m_last_root_location{0}; UEVR_Rotatorf m_last_root_rotation{0}; - static inline bool m_alternate_control_scheme = false; + static inline ControlScheme m_control_scheme = ControlScheme::DefaultControls; bool last_is_is_drawing_ui = false; };