mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-12-21 23:58:51 -05:00
Controller Configuration UI and JSON Config (#760)
* Initial controller hud ui * Reverted fbdemo changes * Moved config to json and implemented controller config * fix build on linux, gitignore new config file * fix build * Fix compilation and file directory paths * Call save on cvar save * Fixed cvar loading and added deck slots to the config * Changed control deck port 0 to use a physical device by default * Added gyro and rumble & fixed loading errors * Save config on toggle menubar * fix linux build * Fixed drift calculation * Controller config now saves when pressing F1 * Removed ExitGame hook from ImGuiImpl * Moved mappings to a map * Added GetKeyName * untranslate scancodes * Fixed hud layout on keyboard device * Fixed keyboard read on hud * Fixed crash when reloading controllers * Removed ConfigFile and changed file extension * Changed Dummy to Disconnected and fixed filters * Removed function leftover * Changed ControllerHud to InputEditor Co-authored-by: briaguya <briaguya@alice> Co-authored-by: David Chavez <david@dcvz.io>
This commit is contained in:
parent
cb6876792e
commit
219804cbe4
1
.gitignore
vendored
1
.gitignore
vendored
@ -405,3 +405,4 @@ tags
|
||||
oot.otr
|
||||
*.sav
|
||||
shipofharkinian.ini
|
||||
shipofharkinian.json
|
@ -82,6 +82,7 @@ CXX_FILES := \
|
||||
$(shell find libultraship/Lib/Fast3D -name "*.cpp") \
|
||||
$(shell find libultraship -maxdepth 1 -name "*.cpp") \
|
||||
$(shell find libultraship/Lib/ImGui -maxdepth 1 -name "*.cpp") \
|
||||
$(shell find libultraship/Lib/Mercury -maxdepth 1 -name "*.cpp") \
|
||||
libultraship/Lib/ImGui/backends/imgui_impl_opengl3.cpp \
|
||||
libultraship/Lib/ImGui/backends/imgui_impl_sdl.cpp \
|
||||
libultraship/Lib/StrHash64.cpp \
|
||||
@ -114,6 +115,7 @@ INC_DIRS := $(addprefix -I, \
|
||||
libultraship/Lib/spdlog \
|
||||
libultraship/Lib/spdlog/include \
|
||||
libultraship/Lib/ImGui \
|
||||
libultraship/Lib/Mercury \
|
||||
libultraship \
|
||||
../StormLib/src \
|
||||
)
|
||||
|
@ -1,164 +0,0 @@
|
||||
#include "ConfigFile.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "GlobalCtx2.h"
|
||||
#include "Window.h"
|
||||
#include "GameSettings.h"
|
||||
|
||||
namespace Ship {
|
||||
ConfigFile::ConfigFile(std::shared_ptr<GlobalCtx2> Context, const std::string& Path) : Context(Context), Path(Path), File(Path.c_str()) {
|
||||
if (Path.empty()) {
|
||||
SPDLOG_ERROR("ConfigFile received an empty file name");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!File.read(Val)) {
|
||||
if (!CreateDefaultConfig()) {
|
||||
SPDLOG_ERROR("Failed to create default configs");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ConfigFile::~ConfigFile() {
|
||||
if (!Save()) {
|
||||
SPDLOG_ERROR("Failed to save configs!!!");
|
||||
}
|
||||
|
||||
SPDLOG_INFO("destruct configfile");
|
||||
}
|
||||
|
||||
mINI::INIMap<std::string>& ConfigFile::operator[](const std::string& Section) {
|
||||
return Val[Section];
|
||||
}
|
||||
|
||||
mINI::INIMap<std::string> ConfigFile::get(const std::string& Section) {
|
||||
return Val.get(Section);
|
||||
}
|
||||
|
||||
bool ConfigFile::has(const std::string& Section) {
|
||||
return Val.has(Section);
|
||||
}
|
||||
|
||||
bool ConfigFile::remove(const std::string& Section) {
|
||||
return Val.remove(Section);
|
||||
}
|
||||
|
||||
void ConfigFile::clear() {
|
||||
Val.clear();
|
||||
}
|
||||
|
||||
std::size_t ConfigFile::size() const {
|
||||
return Val.size();
|
||||
}
|
||||
|
||||
bool ConfigFile::Save() {
|
||||
return File.write(Val);
|
||||
}
|
||||
|
||||
bool ConfigFile::CreateDefaultConfig() {
|
||||
(*this)["ARCHIVE"]["Main Archive"] = "";
|
||||
(*this)["ARCHIVE"]["Patches Directory"] = "";
|
||||
|
||||
(*this)["SAVE"]["Save Filename"] = "";
|
||||
|
||||
(*this)["CONTROLLERS"]["CONTROLLER 1"] = "Auto";
|
||||
(*this)["CONTROLLERS"]["CONTROLLER 2"] = "Unplugged";
|
||||
(*this)["CONTROLLERS"]["CONTROLLER 3"] = "Unplugged";
|
||||
(*this)["CONTROLLERS"]["CONTROLLER 4"] = "Unplugged";
|
||||
|
||||
(*this)["KEYBOARD SHORTCUTS"]["KEY_FULLSCREEN"] = std::to_string(0x044);
|
||||
(*this)["KEYBOARD SHORTCUTS"]["KEY_CONSOLE"] = std::to_string(0x029);
|
||||
|
||||
(*this)["WINDOW"]["WINDOW WIDTH"] = std::to_string(640);
|
||||
(*this)["WINDOW"]["WINDOW HEIGHT"] = std::to_string(480);
|
||||
(*this)["WINDOW"]["FULLSCREEN WIDTH"] = std::to_string(1920);
|
||||
(*this)["WINDOW"]["FULLSCREEN HEIGHT"] = std::to_string(1080);
|
||||
(*this)["WINDOW"]["FULLSCREEN"] = std::to_string(false);
|
||||
(*this)["WINDOW"]["GFX BACKEND"] = "";
|
||||
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 1"][STR(BTN_CRIGHT)] = std::to_string(0x14D);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 1"][STR(BTN_CLEFT)] = std::to_string(0x14B);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 1"][STR(BTN_CDOWN)] = std::to_string(0x150);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 1"][STR(BTN_CUP)] = std::to_string(0x148);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 1"][STR(BTN_R)] = std::to_string(0x013);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 1"][STR(BTN_L)] = std::to_string(0x012);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 1"][STR(BTN_DRIGHT)] = std::to_string(0x023);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 1"][STR(BTN_DLEFT)] = std::to_string(0x021);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 1"][STR(BTN_DDOWN)] = std::to_string(0x022);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 1"][STR(BTN_DUP)] = std::to_string(0x014);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 1"][STR(BTN_START)] = std::to_string(0x039);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 1"][STR(BTN_Z)] = std::to_string(0x02C);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 1"][STR(BTN_B)] = std::to_string(0x02E);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 1"][STR(BTN_A)] = std::to_string(0x02D);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 1"][STR(BTN_STICKRIGHT)] = std::to_string(0x020);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 1"][STR(BTN_STICKLEFT)] = std::to_string(0x01E);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 1"][STR(BTN_STICKDOWN)] = std::to_string(0x01F);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 1"][STR(BTN_STICKUP)] = std::to_string(0x011);
|
||||
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 2"][STR(BTN_CRIGHT)] = std::to_string(0x14D);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 2"][STR(BTN_CLEFT)] = std::to_string(0x14B);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 2"][STR(BTN_CDOWN)] = std::to_string(0x150);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 2"][STR(BTN_CUP)] = std::to_string(0x148);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 2"][STR(BTN_R)] = std::to_string(0x013);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 2"][STR(BTN_L)] = std::to_string(0x012);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 2"][STR(BTN_DRIGHT)] = std::to_string(0x023);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 2"][STR(BTN_DLEFT)] = std::to_string(0x021);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 2"][STR(BTN_DDOWN)] = std::to_string(0x022);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 2"][STR(BTN_DUP)] = std::to_string(0x014);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 2"][STR(BTN_START)] = std::to_string(0x039);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 2"][STR(BTN_Z)] = std::to_string(0x02C);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 2"][STR(BTN_B)] = std::to_string(0x02E);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 2"][STR(BTN_A)] = std::to_string(0x02D);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 2"][STR(BTN_STICKRIGHT)] = std::to_string(0x020);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 2"][STR(BTN_STICKLEFT)] = std::to_string(0x01E);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 2"][STR(BTN_STICKDOWN)] = std::to_string(0x01F);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 2"][STR(BTN_STICKUP)] = std::to_string(0x011);
|
||||
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 3"][STR(BTN_CRIGHT)] = std::to_string(0x14D);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 3"][STR(BTN_CLEFT)] = std::to_string(0x14B);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 3"][STR(BTN_CDOWN)] = std::to_string(0x150);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 3"][STR(BTN_CUP)] = std::to_string(0x148);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 3"][STR(BTN_R)] = std::to_string(0x013);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 3"][STR(BTN_L)] = std::to_string(0x012);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 3"][STR(BTN_DRIGHT)] = std::to_string(0x023);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 3"][STR(BTN_DLEFT)] = std::to_string(0x021);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 3"][STR(BTN_DDOWN)] = std::to_string(0x022);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 3"][STR(BTN_DUP)] = std::to_string(0x014);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 3"][STR(BTN_START)] = std::to_string(0x039);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 3"][STR(BTN_Z)] = std::to_string(0x02C);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 3"][STR(BTN_B)] = std::to_string(0x02E);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 3"][STR(BTN_A)] = std::to_string(0x02D);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 3"][STR(BTN_STICKRIGHT)] = std::to_string(0x020);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 3"][STR(BTN_STICKLEFT)] = std::to_string(0x01E);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 3"][STR(BTN_STICKDOWN)] = std::to_string(0x01F);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 3"][STR(BTN_STICKUP)] = std::to_string(0x011);
|
||||
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 4"][STR(BTN_CRIGHT)] = std::to_string(0x14D);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 4"][STR(BTN_CLEFT)] = std::to_string(0x14B);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 4"][STR(BTN_CDOWN)] = std::to_string(0x150);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 4"][STR(BTN_CUP)] = std::to_string(0x148);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 4"][STR(BTN_R)] = std::to_string(0x013);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 4"][STR(BTN_L)] = std::to_string(0x012);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 4"][STR(BTN_DRIGHT)] = std::to_string(0x023);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 4"][STR(BTN_DLEFT)] = std::to_string(0x021);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 4"][STR(BTN_DDOWN)] = std::to_string(0x022);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 4"][STR(BTN_DUP)] = std::to_string(0x014);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 4"][STR(BTN_START)] = std::to_string(0x039);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 4"][STR(BTN_Z)] = std::to_string(0x02C);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 4"][STR(BTN_B)] = std::to_string(0x02E);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 4"][STR(BTN_A)] = std::to_string(0x02D);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 4"][STR(BTN_STICKRIGHT)] = std::to_string(0x020);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 4"][STR(BTN_STICKLEFT)] = std::to_string(0x01E);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 4"][STR(BTN_STICKDOWN)] = std::to_string(0x01F);
|
||||
(*this)["KEYBOARD CONTROLLER BINDING 4"][STR(BTN_STICKUP)] = std::to_string(0x011);
|
||||
|
||||
(*this)["ENHANCEMENT SETTINGS"]["TEXT_SPEED"] = "1";
|
||||
|
||||
(*this)["SDL CONTROLLER 1"]["GUID"] = "";
|
||||
(*this)["SDL CONTROLLER 2"]["GUID"] = "";
|
||||
(*this)["SDL CONTROLLER 3"]["GUID"] = "";
|
||||
(*this)["SDL CONTROLLER 4"]["GUID"] = "";
|
||||
|
||||
return File.generate(Val);
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
#ifndef CONFIG_FILE_H
|
||||
#define CONFIG_FILE_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include "Lib/mINI/src/mini/ini.h"
|
||||
#include "UltraController.h"
|
||||
#include "LUSMacros.h"
|
||||
|
||||
namespace Ship {
|
||||
class GlobalCtx2;
|
||||
|
||||
class ConfigFile {
|
||||
public:
|
||||
ConfigFile(std::shared_ptr<GlobalCtx2> Context, const std::string& Path);
|
||||
~ConfigFile();
|
||||
|
||||
bool Save();
|
||||
|
||||
// Expose the ini values.
|
||||
mINI::INIMap<std::string>& operator[](const std::string& Section);
|
||||
mINI::INIMap<std::string> get(const std::string& Section);
|
||||
bool has(const std::string& Section);
|
||||
bool remove(const std::string& Section);
|
||||
void clear();
|
||||
std::size_t size() const;
|
||||
std::shared_ptr<GlobalCtx2> GetContext() { return Context.lock(); }
|
||||
|
||||
protected:
|
||||
bool CreateDefaultConfig();
|
||||
|
||||
private:
|
||||
mINI::INIStructure Val;
|
||||
std::weak_ptr<GlobalCtx2> Context;
|
||||
std::string Path;
|
||||
mINI::INIFile File;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
155
libultraship/libultraship/ControlDeck.cpp
Normal file
155
libultraship/libultraship/ControlDeck.cpp
Normal file
@ -0,0 +1,155 @@
|
||||
#include "ControlDeck.h"
|
||||
|
||||
#include "Window.h"
|
||||
#include "Controller.h"
|
||||
#include "DisconnectedController.h"
|
||||
#include "KeyboardController.h"
|
||||
#include "SDLController.h"
|
||||
#include <Utils/StringHelper.h>
|
||||
|
||||
uint8_t* controllerBits;
|
||||
|
||||
void Ship::ControlDeck::Init(uint8_t* bits) {
|
||||
ScanPhysicalDevices();
|
||||
controllerBits = bits;
|
||||
}
|
||||
|
||||
void Ship::ControlDeck::ScanPhysicalDevices() {
|
||||
|
||||
virtualDevices.clear();
|
||||
physicalDevices.clear();
|
||||
|
||||
for (int i = 0; i < SDL_NumJoysticks(); i++) {
|
||||
if (SDL_IsGameController(i)) {
|
||||
auto sdl = std::make_shared<SDLController>(i);
|
||||
sdl->Open();
|
||||
physicalDevices.push_back(sdl);
|
||||
}
|
||||
}
|
||||
|
||||
physicalDevices.push_back(std::make_shared<KeyboardController>());
|
||||
physicalDevices.push_back(std::make_shared<DisconnectedController>());
|
||||
|
||||
for (const auto& device : physicalDevices) {
|
||||
for (int i = 0; i < MAXCONTROLLERS; i++) {
|
||||
device->CreateDefaultBinding(i);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAXCONTROLLERS; i++) {
|
||||
virtualDevices.push_back(i == 0 ? 0 : static_cast<int>(physicalDevices.size()) - 1);
|
||||
}
|
||||
|
||||
LoadControllerSettings();
|
||||
}
|
||||
|
||||
void Ship::ControlDeck::SetPhysicalDevice(int slot, int deviceSlot) {
|
||||
const std::shared_ptr<Controller> backend = physicalDevices[deviceSlot];
|
||||
virtualDevices[slot] = deviceSlot;
|
||||
*controllerBits |= (backend->Connected()) << slot;
|
||||
}
|
||||
|
||||
void Ship::ControlDeck::WriteToPad(OSContPad* pad) const {
|
||||
for (size_t i = 0; i < virtualDevices.size(); i++) {
|
||||
physicalDevices[virtualDevices[i]]->Read(&pad[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
#define NESTED(key, ...) StringHelper::Sprintf("Controllers.%s.Slot_%d." key, device->GetGuid().c_str(), slot, __VA_ARGS__)
|
||||
|
||||
void Ship::ControlDeck::LoadControllerSettings() {
|
||||
std::shared_ptr<Mercury> Config = GlobalCtx2::GetInstance()->GetConfig();
|
||||
|
||||
for (auto const& val : Config->rjson["Controllers"]["Deck"].items()) {
|
||||
int slot = std::stoi(val.key().substr(5));
|
||||
|
||||
for (size_t dev = 0; dev < physicalDevices.size(); dev++) {
|
||||
std::string guid = physicalDevices[dev]->GetGuid();
|
||||
if(guid != val.value()) continue;
|
||||
|
||||
virtualDevices[slot] = dev;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < virtualDevices.size(); i++) {
|
||||
std::shared_ptr<Controller> backend = physicalDevices[virtualDevices[i]];
|
||||
Config->setString(StringHelper::Sprintf("Controllers.Deck.Slot_%d", (int)i), backend->GetGuid());
|
||||
}
|
||||
|
||||
for (const auto& device : physicalDevices) {
|
||||
|
||||
std::string guid = device->GetGuid();
|
||||
|
||||
for (int slot = 0; slot < MAXCONTROLLERS; slot++) {
|
||||
|
||||
if (!(Config->rjson["Controllers"].contains(guid) && Config->rjson["Controllers"][guid].contains(StringHelper::Sprintf("Slot_%d", slot)))) continue;
|
||||
|
||||
auto& profile = device->profiles[slot];
|
||||
auto rawProfile = Config->rjson["Controllers"][guid][StringHelper::Sprintf("Slot_%d", slot)];
|
||||
|
||||
profile.Mappings.clear();
|
||||
profile.Thresholds.clear();
|
||||
profile.GyroThresholds.clear();
|
||||
profile.UseRumble = Config->getBool(NESTED("Rumble.Enabled", ""));
|
||||
profile.RumbleStrength = Config->getBool(NESTED("Rumble.Strength", ""));
|
||||
profile.UseGyro = Config->getBool(NESTED("Gyro.Enabled", ""));
|
||||
|
||||
for (auto const& val : rawProfile["Gyro"]["Thresholds"].items()) {
|
||||
profile.GyroThresholds[std::stoi(val.key())] = val.value();
|
||||
}
|
||||
|
||||
for (auto const& val : rawProfile["Thresholds"].items()) {
|
||||
profile.Thresholds[static_cast<ControllerThresholds>(std::stoi(val.key()))] = val.value();
|
||||
}
|
||||
|
||||
for (auto const& val : rawProfile["Mappings"].items()) {
|
||||
device->SetButtonMapping(slot, std::stoi(val.key().substr(4)), val.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Ship::ControlDeck::SaveControllerSettings() {
|
||||
std::shared_ptr<Mercury> Config = GlobalCtx2::GetInstance()->GetConfig();
|
||||
|
||||
for (size_t i = 0; i < virtualDevices.size(); i++) {
|
||||
std::shared_ptr<Controller> backend = physicalDevices[virtualDevices[i]];
|
||||
Config->setString(StringHelper::Sprintf("Controllers.Deck.Slot_%d", (int)i), backend->GetGuid());
|
||||
}
|
||||
|
||||
for (const auto& device : physicalDevices) {
|
||||
|
||||
int slot = 0;
|
||||
std::string guid = device->GetGuid();
|
||||
|
||||
for (const auto& profile : device->profiles) {
|
||||
|
||||
if (!device->Connected()) continue;
|
||||
|
||||
auto rawProfile = Config->rjson["Controllers"][guid][StringHelper::Sprintf("Slot_%d", slot)];
|
||||
Config->setBool(NESTED("Rumble.Enabled", ""), profile.UseRumble);
|
||||
Config->setFloat(NESTED("Rumble.Strength", ""), profile.RumbleStrength);
|
||||
Config->setBool(NESTED("Gyro.Enabled", ""), profile.UseGyro);
|
||||
|
||||
for (auto const& val : rawProfile["Mappings"].items()) {
|
||||
Config->setInt(NESTED("Mappings.%s", val.key().c_str()), -1);
|
||||
}
|
||||
|
||||
for (auto const& [key, val] : profile.GyroThresholds) {
|
||||
Config->setInt(NESTED("Gyro.Thresholds.%d", key), val);
|
||||
}
|
||||
|
||||
for (auto const& [key, val] : profile.Thresholds) {
|
||||
Config->setInt(NESTED("Thresholds.%d", key), val);
|
||||
}
|
||||
|
||||
for (auto const& [key, val] : profile.Mappings) {
|
||||
Config->setInt(NESTED("Mappings.BTN_%d", val), key);
|
||||
}
|
||||
|
||||
slot++;
|
||||
}
|
||||
}
|
||||
|
||||
Config->save();
|
||||
}
|
20
libultraship/libultraship/ControlDeck.h
Normal file
20
libultraship/libultraship/ControlDeck.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "Controller.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace Ship {
|
||||
|
||||
class ControlDeck {
|
||||
public:
|
||||
std::vector<int> virtualDevices;
|
||||
std::vector<std::shared_ptr<Controller>> physicalDevices = {};
|
||||
void Init(uint8_t* controllerBits);
|
||||
void ScanPhysicalDevices();
|
||||
void WriteToPad(OSContPad* pad) const;
|
||||
void LoadControllerSettings();
|
||||
void SaveControllerSettings();
|
||||
void SetPhysicalDevice(int slot, int deviceSlot);
|
||||
};
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
#include "Controller.h"
|
||||
#include "GlobalCtx2.h"
|
||||
#include "stox.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#if __APPLE__
|
||||
#include <SDL_events.h>
|
||||
#else
|
||||
@ -10,29 +8,29 @@
|
||||
#endif
|
||||
|
||||
namespace Ship {
|
||||
Controller::Controller(int32_t dwControllerNumber) : dwControllerNumber(dwControllerNumber) {
|
||||
dwPressedButtons = 0;
|
||||
wStickX = 0;
|
||||
wStickY = 0;
|
||||
wGyroX = 0;
|
||||
wGyroY = 0;
|
||||
|
||||
Controller::Controller() : isRumbling(false), wStickX(0), wStickY(0), wGyroX(0), wGyroY(0), dwPressedButtons(0){
|
||||
Attachment = nullptr;
|
||||
profiles.resize(MAXCONTROLLERS);
|
||||
for(int slot = 0; slot < MAXCONTROLLERS; slot++) {
|
||||
dwPressedButtons.push_back(0);
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::Read(OSContPad* pad) {
|
||||
ReadFromSource();
|
||||
void Controller::Read(OSContPad* pad, int32_t slot) {
|
||||
ReadFromSource(slot);
|
||||
|
||||
SDL_PumpEvents();
|
||||
|
||||
// Button Inputs
|
||||
pad->button |= dwPressedButtons & 0xFFFF;
|
||||
pad->button |= dwPressedButtons[slot] & 0xFFFF;
|
||||
|
||||
// Stick Inputs
|
||||
if (pad->stick_x == 0) {
|
||||
if (dwPressedButtons & BTN_STICKLEFT) {
|
||||
if (dwPressedButtons[slot] & BTN_STICKLEFT) {
|
||||
pad->stick_x = -128;
|
||||
}
|
||||
else if (dwPressedButtons & BTN_STICKRIGHT) {
|
||||
else if (dwPressedButtons[slot] & BTN_STICKRIGHT) {
|
||||
pad->stick_x = 127;
|
||||
}
|
||||
else {
|
||||
@ -41,10 +39,10 @@ namespace Ship {
|
||||
}
|
||||
|
||||
if (pad->stick_y == 0) {
|
||||
if (dwPressedButtons & BTN_STICKDOWN) {
|
||||
if (dwPressedButtons[slot] & BTN_STICKDOWN) {
|
||||
pad->stick_y = -128;
|
||||
}
|
||||
else if (dwPressedButtons & BTN_STICKUP) {
|
||||
else if (dwPressedButtons[slot] & BTN_STICKUP) {
|
||||
pad->stick_y = 127;
|
||||
}
|
||||
else {
|
||||
@ -52,60 +50,38 @@ namespace Ship {
|
||||
}
|
||||
}
|
||||
|
||||
// Stick Inputs
|
||||
if (pad->cam_x == 0) {
|
||||
if (dwPressedButtons[slot] & BTN_VSTICKLEFT) {
|
||||
pad->cam_x = -128 * 10.0f;
|
||||
}
|
||||
else if (dwPressedButtons[slot] & BTN_VSTICKRIGHT) {
|
||||
pad->cam_x = 127 * 10.0f;
|
||||
}
|
||||
else {
|
||||
pad->cam_x = wCamX;
|
||||
}
|
||||
}
|
||||
if (pad->cam_y == 0) {
|
||||
if (dwPressedButtons[slot] & BTN_VSTICKDOWN) {
|
||||
pad->cam_y = -128 * 10.0f;
|
||||
}
|
||||
else if (dwPressedButtons[slot] & BTN_VSTICKUP) {
|
||||
pad->cam_y = 127 * 10.0f;
|
||||
}
|
||||
else {
|
||||
pad->cam_y = wCamY;
|
||||
}
|
||||
}
|
||||
|
||||
// Gyro
|
||||
pad->gyro_x = wGyroX;
|
||||
pad->gyro_y = wGyroY;
|
||||
|
||||
// Right Stick
|
||||
pad->cam_x = wCamX;
|
||||
pad->cam_y = wCamY;
|
||||
}
|
||||
|
||||
void Controller::SetButtonMapping(const std::string& szButtonName, int32_t dwScancode) {
|
||||
// Update the config value.
|
||||
std::string ConfSection = GetBindingConfSection();
|
||||
std::shared_ptr<ConfigFile> pConf = GlobalCtx2::GetInstance()->GetConfig();
|
||||
ConfigFile& Conf = *pConf.get();
|
||||
Conf[ConfSection][szButtonName] = dwScancode;
|
||||
|
||||
// Reload the button mapping from Config
|
||||
LoadBinding();
|
||||
}
|
||||
|
||||
void Controller::LoadBinding() {
|
||||
std::string ConfSection = GetBindingConfSection();
|
||||
std::shared_ptr<ConfigFile> pConf = GlobalCtx2::GetInstance()->GetConfig();
|
||||
ConfigFile& Conf = *pConf.get();
|
||||
|
||||
ButtonMapping[Ship::stoi(Conf[ConfSection][STR(BTN_CRIGHT)])] = BTN_CRIGHT;
|
||||
ButtonMapping[Ship::stoi(Conf[ConfSection][STR(BTN_CLEFT)])] = BTN_CLEFT;
|
||||
ButtonMapping[Ship::stoi(Conf[ConfSection][STR(BTN_CDOWN)])] = BTN_CDOWN;
|
||||
ButtonMapping[Ship::stoi(Conf[ConfSection][STR(BTN_CUP)])] = BTN_CUP;
|
||||
//ButtonMapping[Ship::stoi(Conf[ConfSection][STR(BTN_CRIGHT + "_2")])] = BTN_CRIGHT;
|
||||
//ButtonMapping[Ship::stoi(Conf[ConfSection][STR(BTN_CLEFT + "_2")])] = BTN_CLEFT;
|
||||
//ButtonMapping[Ship::stoi(Conf[ConfSection][STR(BTN_CDOWN + "_2")])] = BTN_CDOWN;
|
||||
//ButtonMapping[Ship::stoi(Conf[ConfSection][STR(BTN_CUP + "_2")])] = BTN_CUP;
|
||||
ButtonMapping[Ship::stoi(Conf[ConfSection][STR(BTN_R)])] = BTN_R;
|
||||
ButtonMapping[Ship::stoi(Conf[ConfSection][STR(BTN_L)])] = BTN_L;
|
||||
ButtonMapping[Ship::stoi(Conf[ConfSection][STR(BTN_DRIGHT)])] = BTN_DRIGHT;
|
||||
ButtonMapping[Ship::stoi(Conf[ConfSection][STR(BTN_DLEFT)])] = BTN_DLEFT;
|
||||
ButtonMapping[Ship::stoi(Conf[ConfSection][STR(BTN_DDOWN)])] = BTN_DDOWN;
|
||||
ButtonMapping[Ship::stoi(Conf[ConfSection][STR(BTN_DUP)])] = BTN_DUP;
|
||||
ButtonMapping[Ship::stoi(Conf[ConfSection][STR(BTN_START)])] = BTN_START;
|
||||
ButtonMapping[Ship::stoi(Conf[ConfSection][STR(BTN_Z)])] = BTN_Z;
|
||||
ButtonMapping[Ship::stoi(Conf[ConfSection][STR(BTN_B)])] = BTN_B;
|
||||
ButtonMapping[Ship::stoi(Conf[ConfSection][STR(BTN_A)])] = BTN_A;
|
||||
ButtonMapping[Ship::stoi(Conf[ConfSection][STR(BTN_STICKRIGHT)])] = BTN_STICKRIGHT;
|
||||
ButtonMapping[Ship::stoi(Conf[ConfSection][STR(BTN_STICKLEFT)])] = BTN_STICKLEFT;
|
||||
ButtonMapping[Ship::stoi(Conf[ConfSection][STR(BTN_STICKDOWN)])] = BTN_STICKDOWN;
|
||||
ButtonMapping[Ship::stoi(Conf[ConfSection][STR(BTN_STICKUP)])] = BTN_STICKUP;
|
||||
}
|
||||
|
||||
std::string Controller::GetConfSection() {
|
||||
return GetControllerType() + " CONTROLLER " + std::to_string(GetControllerNumber() + 1);
|
||||
}
|
||||
|
||||
std::string Controller::GetBindingConfSection() {
|
||||
return GetControllerType() + " CONTROLLER BINDING " + std::to_string(GetControllerNumber() + 1);
|
||||
void Controller::SetButtonMapping(int slot, int32_t n64Button, int32_t dwScancode) {
|
||||
std::map<int32_t, int32_t>& Mappings = profiles[slot].Mappings;
|
||||
std::erase_if(Mappings, [n64Button](const std::pair<int32_t, int32_t>& bin) { return bin.second == n64Button; });
|
||||
Mappings[dwScancode] = n64Button;
|
||||
}
|
||||
}
|
||||
|
@ -1,52 +1,82 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include "stdint.h"
|
||||
#include "UltraController.h"
|
||||
#include "ControllerAttachment.h"
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
#define EXTENDED_SCANCODE_BIT (1 << 8)
|
||||
#define AXIS_SCANCODE_BIT (1 << 9)
|
||||
|
||||
namespace Ship {
|
||||
|
||||
enum ControllerThresholds {
|
||||
LEFT_STICK = 1,
|
||||
RIGHT_STICK = 2,
|
||||
LEFT_TRIGGER = 3,
|
||||
RIGHT_TRIGGER = 4,
|
||||
DRIFT_X = 5,
|
||||
DRIFT_Y = 6,
|
||||
SENSITIVITY = 7,
|
||||
GYRO_SENSITIVITY = 8
|
||||
};
|
||||
|
||||
struct DeviceProfile {
|
||||
bool UseRumble = false;
|
||||
bool UseGyro = false;
|
||||
float RumbleStrength = 1.0f;
|
||||
std::unordered_map<ControllerThresholds, int32_t> Thresholds;
|
||||
std::unordered_map<int32_t, int32_t> GyroThresholds;
|
||||
std::map<int32_t, int32_t> Mappings;
|
||||
};
|
||||
|
||||
class Controller {
|
||||
public:
|
||||
Controller(int32_t dwControllerNumber);
|
||||
|
||||
void Read(OSContPad* pad);
|
||||
virtual void ReadFromSource() = 0;
|
||||
virtual void WriteToSource(ControllerCallback* controller) = 0;
|
||||
virtual ~Controller() = default;
|
||||
Controller();
|
||||
void Read(OSContPad* pad, int32_t slot);
|
||||
virtual void ReadFromSource(int32_t slot) = 0;
|
||||
virtual void WriteToSource(int32_t slot, ControllerCallback* controller) = 0;
|
||||
virtual bool Connected() const = 0;
|
||||
virtual bool CanRumble() const = 0;
|
||||
virtual bool CanGyro() const = 0;
|
||||
virtual void CreateDefaultBinding(int32_t slot) = 0;
|
||||
bool isRumbling;
|
||||
std::vector<DeviceProfile> profiles;
|
||||
|
||||
void SetButtonMapping(const std::string& szButtonName, int32_t dwScancode);
|
||||
virtual void ClearRawPress() = 0;
|
||||
virtual int32_t ReadRawPress() = 0;
|
||||
void SetButtonMapping(int slot, int32_t n64Button, int32_t dwScancode);
|
||||
std::shared_ptr<ControllerAttachment> GetAttachment() { return Attachment; }
|
||||
int32_t GetControllerNumber() { return dwControllerNumber; }
|
||||
|
||||
virtual bool HasPadConf() const = 0;
|
||||
virtual std::optional<std::string> GetPadConfSection() = 0;
|
||||
std::string GetGuid() { return GUID; }
|
||||
virtual const char* GetButtonName(int slot, int n64Button) = 0;
|
||||
virtual const char* GetControllerName() = 0;
|
||||
|
||||
protected:
|
||||
int32_t dwPressedButtons;
|
||||
std::map<int32_t, int32_t> ButtonMapping;
|
||||
int8_t wStickX;
|
||||
int8_t wStickY;
|
||||
float wGyroX;
|
||||
float wGyroY;
|
||||
float wCamX;
|
||||
float wCamY;
|
||||
float wCamX;
|
||||
float wCamY;
|
||||
|
||||
protected:
|
||||
std::vector<int32_t> dwPressedButtons;
|
||||
std::string GUID;
|
||||
|
||||
virtual std::string GetControllerType() = 0;
|
||||
virtual std::string GetConfSection() = 0;
|
||||
virtual std::string GetBindingConfSection() = 0;
|
||||
void LoadBinding();
|
||||
|
||||
private:
|
||||
std::shared_ptr<ControllerAttachment> Attachment;
|
||||
int32_t dwControllerNumber;
|
||||
};
|
||||
|
||||
struct ControllerEntry {
|
||||
uint8_t* controllerBits;
|
||||
Controller* entryIO;
|
||||
};
|
||||
|
||||
}
|
||||
|
277
libultraship/libultraship/ControllerHud.cpp
Normal file
277
libultraship/libultraship/ControllerHud.cpp
Normal file
@ -0,0 +1,277 @@
|
||||
#include "InputEditor.h"
|
||||
#include "Controller.h"
|
||||
#include "Window.h"
|
||||
#include "Lib/ImGui/imgui.h"
|
||||
#include "ImGuiImpl.h"
|
||||
#include "Utils/StringHelper.h"
|
||||
#include "Lib/ImGui/imgui_internal.h"
|
||||
|
||||
namespace Ship {
|
||||
|
||||
extern "C" uint8_t __enableGameInput;
|
||||
#define SEPARATION() ImGui::Dummy(ImVec2(0, 5))
|
||||
|
||||
void InputEditor::Init() {
|
||||
BtnReading = -1;
|
||||
}
|
||||
|
||||
std::shared_ptr<Controller> GetControllerPerSlot(int slot) {
|
||||
const std::vector<int> vDevices = Window::ControllerApi->virtualDevices;
|
||||
return Window::ControllerApi->physicalDevices[vDevices[slot]];
|
||||
}
|
||||
|
||||
void InputEditor::DrawButton(const char* label, int n64Btn) {
|
||||
const std::shared_ptr<Controller> backend = GetControllerPerSlot(CurrentPort);
|
||||
|
||||
float size = 40;
|
||||
bool readingMode = BtnReading == n64Btn;
|
||||
bool disabled = BtnReading != -1 && !readingMode || !backend->Connected();
|
||||
ImVec2 len = ImGui::CalcTextSize(label);
|
||||
ImVec2 pos = ImGui::GetCursorPos();
|
||||
ImGui::SetCursorPosY(pos.y + len.y / 4);
|
||||
ImGui::SetCursorPosX(pos.x + abs(len.x - size));
|
||||
ImGui::Text("%s", label);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosY(pos.y);
|
||||
|
||||
if(disabled) {
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
|
||||
}
|
||||
|
||||
if(readingMode) {
|
||||
const int32_t btn = backend->ReadRawPress();
|
||||
|
||||
if(btn != -1) {
|
||||
backend->SetButtonMapping(CurrentPort, n64Btn, btn);
|
||||
BtnReading = -1;
|
||||
}
|
||||
}
|
||||
|
||||
const char* BtnName = backend->GetButtonName(CurrentPort, n64Btn);
|
||||
|
||||
if (ImGui::Button(StringHelper::Sprintf("%s##HBTNID_%d", readingMode ? "Press a Key..." : BtnName, n64Btn).c_str())) {
|
||||
BtnReading = n64Btn;
|
||||
backend->ClearRawPress();
|
||||
}
|
||||
|
||||
if(disabled) {
|
||||
ImGui::PopItemFlag();
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
}
|
||||
|
||||
void InputEditor::DrawVirtualStick(const char* label, ImVec2 stick) {
|
||||
ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPos().x + 5, ImGui::GetCursorPos().y));
|
||||
ImGui::BeginChild(label, ImVec2(68, 75), false);
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
const ImVec2 p = ImGui::GetCursorScreenPos();
|
||||
|
||||
float sz = 45.0f;
|
||||
float rad = sz * 0.5f;
|
||||
ImVec2 pos = ImVec2(p.x + sz * 0.5f + 12, p.y + sz * 0.5f + 11);
|
||||
|
||||
float stickX = (stick.x / 83.0f) * (rad * 0.5f);
|
||||
float stickY = -(stick.y / 83.0f) * (rad * 0.5f);
|
||||
|
||||
ImVec4 rect = ImVec4(p.x + 2, p.y + 2, 65, 65);
|
||||
draw_list->AddRect(ImVec2(rect.x, rect.y), ImVec2(rect.x + rect.z, rect.y + rect.w), ImColor(100, 100, 100, 255), 0.0f, 0, 1.5f);
|
||||
draw_list->AddCircleFilled(pos, rad, ImColor(130, 130, 130, 255), 8);
|
||||
draw_list->AddCircleFilled(ImVec2(pos.x + stickX, pos.y + stickY), 5, ImColor(15, 15, 15, 255), 7);
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void InputEditor::DrawControllerSchema() {
|
||||
|
||||
const std::vector<int> vDevices = Window::ControllerApi->virtualDevices;
|
||||
const std::vector<std::shared_ptr<Controller>> devices = Window::ControllerApi->physicalDevices;
|
||||
|
||||
std::shared_ptr<Controller> Backend = devices[vDevices[CurrentPort]];
|
||||
DeviceProfile& profile =Backend->profiles[CurrentPort];
|
||||
float sensitivity = profile.Thresholds[SENSITIVITY];
|
||||
bool IsKeyboard = Backend->GetGuid() == "Keyboard" || !Backend->Connected();
|
||||
const char* ControllerName = Backend->GetControllerName();
|
||||
|
||||
if (ControllerName != nullptr && ImGui::BeginCombo("##ControllerEntries", ControllerName)) {
|
||||
for (uint8_t i = 0; i < devices.size(); i++) {
|
||||
if (ImGui::Selectable(devices[i]->GetControllerName(), i == vDevices[CurrentPort])) {
|
||||
Window::ControllerApi->SetPhysicalDevice(CurrentPort, i);
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if(ImGui::Button("Refresh")) {
|
||||
Window::ControllerApi->ScanPhysicalDevices();
|
||||
}
|
||||
|
||||
SohImGui::BeginGroupPanel("Buttons", ImVec2(150, 20));
|
||||
DrawButton("A", BTN_A);
|
||||
DrawButton("B", BTN_B);
|
||||
DrawButton("L", BTN_L);
|
||||
DrawButton("R", BTN_R);
|
||||
DrawButton("Z", BTN_Z);
|
||||
DrawButton("START", BTN_START);
|
||||
SEPARATION();
|
||||
SohImGui::EndGroupPanel(IsKeyboard ? 7.0f : 48.0f);
|
||||
ImGui::SameLine();
|
||||
SohImGui::BeginGroupPanel("Digital Pad", ImVec2(150, 20));
|
||||
DrawButton("Up", BTN_DUP);
|
||||
DrawButton("Down", BTN_DDOWN);
|
||||
DrawButton("Left", BTN_DLEFT);
|
||||
DrawButton("Right", BTN_DRIGHT);
|
||||
SEPARATION();
|
||||
SohImGui::EndGroupPanel(IsKeyboard ? 53.0f : 94.0f);
|
||||
ImGui::SameLine();
|
||||
SohImGui::BeginGroupPanel("Analog Stick", ImVec2(150, 20));
|
||||
DrawButton("Up", BTN_STICKUP);
|
||||
DrawButton("Down", BTN_STICKDOWN);
|
||||
DrawButton("Left", BTN_STICKLEFT);
|
||||
DrawButton("Right", BTN_STICKRIGHT);
|
||||
|
||||
if (!IsKeyboard) {
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 8);
|
||||
DrawVirtualStick("##MainVirtualStick", ImVec2(Backend->wStickX, Backend->wStickY));
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5);
|
||||
|
||||
ImGui::BeginChild("##MSInput", ImVec2(90, 50), false);
|
||||
ImGui::Text("Deadzone");
|
||||
ImGui::PushItemWidth(80);
|
||||
ImGui::InputInt("##MDZone", &profile.Thresholds[LEFT_STICK]);
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::EndChild();
|
||||
} else {
|
||||
ImGui::Dummy(ImVec2(0, 6));
|
||||
}
|
||||
SohImGui::EndGroupPanel(IsKeyboard ? 52.0f : 24.0f);
|
||||
ImGui::SameLine();
|
||||
|
||||
if (!IsKeyboard) {
|
||||
ImGui::SameLine();
|
||||
SohImGui::BeginGroupPanel("Camera Stick", ImVec2(150, 20));
|
||||
DrawButton("Up", BTN_VSTICKUP);
|
||||
DrawButton("Down", BTN_VSTICKDOWN);
|
||||
DrawButton("Left", BTN_VSTICKLEFT);
|
||||
DrawButton("Right", BTN_VSTICKRIGHT);
|
||||
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 8);
|
||||
DrawVirtualStick("##CameraVirtualStick", ImVec2(Backend->wCamX / sensitivity, Backend->wCamY / sensitivity));
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5);
|
||||
ImGui::BeginChild("##CSInput", ImVec2(90, 85), false);
|
||||
ImGui::Text("Deadzone");
|
||||
ImGui::PushItemWidth(80);
|
||||
ImGui::InputInt("##MDZone", &profile.Thresholds[RIGHT_STICK]);
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::Text("Sensitivity");
|
||||
ImGui::PushItemWidth(80);
|
||||
ImGui::InputInt("##MSensitivity", &profile.Thresholds[SENSITIVITY]);
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::EndChild();
|
||||
SohImGui::EndGroupPanel(14.0f);
|
||||
}
|
||||
|
||||
if(Backend->CanGyro()) {
|
||||
ImGui::SameLine();
|
||||
|
||||
SohImGui::BeginGroupPanel("Gyro Options", ImVec2(175, 20));
|
||||
float cursorX = ImGui::GetCursorPosX() + 5;
|
||||
ImGui::SetCursorPosX(cursorX);
|
||||
ImGui::Checkbox("Enable Gyro", &profile.UseGyro);
|
||||
ImGui::SetCursorPosX(cursorX);
|
||||
ImGui::Text("Gyro Sensitivity: %d%%", profile.Thresholds[GYRO_SENSITIVITY]);
|
||||
ImGui::PushItemWidth(135.0f);
|
||||
ImGui::SetCursorPosX(cursorX);
|
||||
ImGui::SliderInt("##GSensitivity", &profile.Thresholds[GYRO_SENSITIVITY], 0, 100, "");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::Dummy(ImVec2(0, 1));
|
||||
ImGui::SetCursorPosX(cursorX);
|
||||
if (ImGui::Button("Recalibrate Gyro##RGyro")) {
|
||||
profile.Thresholds[DRIFT_X] = 0;
|
||||
profile.Thresholds[DRIFT_Y] = 0;
|
||||
}
|
||||
ImGui::SetCursorPosX(cursorX);
|
||||
DrawVirtualStick("##GyroPreview", ImVec2(Backend->wGyroX, Backend->wGyroY));
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5);
|
||||
ImGui::BeginChild("##GyInput", ImVec2(90, 85), false);
|
||||
ImGui::Text("Drift X");
|
||||
ImGui::PushItemWidth(80);
|
||||
ImGui::InputInt("##GDriftX", &profile.Thresholds[DRIFT_X]);
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::Text("Drift Y");
|
||||
ImGui::PushItemWidth(80);
|
||||
ImGui::InputInt("##GDriftY", &profile.Thresholds[DRIFT_Y]);
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::EndChild();
|
||||
SohImGui::EndGroupPanel(14.0f);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
const ImVec2 cursor = ImGui::GetCursorPos();
|
||||
|
||||
SohImGui::BeginGroupPanel("C-Buttons", ImVec2(158, 20));
|
||||
DrawButton("Up", BTN_CUP);
|
||||
DrawButton("Down", BTN_CDOWN);
|
||||
DrawButton("Left", BTN_CLEFT);
|
||||
DrawButton("Right", BTN_CRIGHT);
|
||||
ImGui::Dummy(ImVec2(0, 5));
|
||||
SohImGui::EndGroupPanel();
|
||||
|
||||
ImGui::SetCursorPosX(cursor.x);
|
||||
ImGui::SetCursorPosY(cursor.y + 120);
|
||||
SohImGui::BeginGroupPanel("Options", ImVec2(158, 20));
|
||||
float cursorX = ImGui::GetCursorPosX() + 5;
|
||||
ImGui::SetCursorPosX(cursorX);
|
||||
ImGui::Checkbox("Rumble Enabled", &profile.UseRumble);
|
||||
if (Backend->CanRumble()) {
|
||||
ImGui::SetCursorPosX(cursorX);
|
||||
ImGui::Text("Rumble Force: %d%%", static_cast<int>(100 * profile.RumbleStrength));
|
||||
ImGui::SetCursorPosX(cursorX);
|
||||
ImGui::PushItemWidth(135.0f);
|
||||
ImGui::SliderFloat("##RStrength", &profile.RumbleStrength, 0, 1.0f, "");
|
||||
ImGui::PopItemWidth();
|
||||
}
|
||||
ImGui::Dummy(ImVec2(0, 5));
|
||||
SohImGui::EndGroupPanel(IsKeyboard ? 0.0f : 2.0f);
|
||||
}
|
||||
|
||||
void InputEditor::DrawHud() {
|
||||
|
||||
__enableGameInput = true;
|
||||
|
||||
if (!this->Opened) {
|
||||
BtnReading = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(641, 250), ImVec2(1200, 290));
|
||||
//OTRTODO: Disable this stupid workaround ( ReadRawPress() only works when the window is on the main viewport )
|
||||
ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID);
|
||||
ImGui::Begin("Controller Configuration", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize);
|
||||
|
||||
ImGui::BeginTabBar("##Controllers");
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (ImGui::BeginTabItem(StringHelper::Sprintf("Port %d", i + 1).c_str())) {
|
||||
CurrentPort = i;
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
|
||||
// Draw current cfg
|
||||
|
||||
DrawControllerSchema();
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <PR/ultra64/gbi.h>
|
||||
#include "imgui_internal.h"
|
||||
|
||||
std::map<std::string, std::unique_ptr<CVar>, std::less<>> cvars;
|
||||
|
||||
@ -70,7 +71,7 @@ extern "C" void CVar_SetString(const char* name, const char* value) {
|
||||
cvar = std::make_unique<CVar>();
|
||||
}
|
||||
cvar->type = CVAR_TYPE_STRING;
|
||||
cvar->value.valueStr = value;
|
||||
cvar->value.valueStr = ImStrdup(value);
|
||||
}
|
||||
|
||||
extern "C" void CVar_RegisterS32(const char* name, s32 defaultValue) {
|
||||
|
31
libultraship/libultraship/DisconnectedController.h
Normal file
31
libultraship/libultraship/DisconnectedController.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
#include "Controller.h"
|
||||
|
||||
class DisconnectedController final : public Ship::Controller {
|
||||
public:
|
||||
DisconnectedController() {
|
||||
GUID = "Disconnected";
|
||||
}
|
||||
|
||||
std::map<std::vector<std::string>, int32_t> ReadButtonPress();
|
||||
void ReadFromSource(int32_t slot) override {}
|
||||
const char* GetControllerName() override { return "Disconnected"; }
|
||||
const char* GetButtonName(int slot, int n64Button) override { return "None"; }
|
||||
void WriteToSource(int32_t slot, ControllerCallback* controller) override { }
|
||||
bool Connected() const override { return false; }
|
||||
bool CanRumble() const override { return false; }
|
||||
bool CanGyro() const override { return false; }
|
||||
|
||||
void ClearRawPress() override {}
|
||||
int32_t ReadRawPress() override { return -1; }
|
||||
bool HasPadConf() const { return true; }
|
||||
std::optional<std::string> GetPadConfSection() { return "Unk"; }
|
||||
void CreateDefaultBinding(int32_t slot) override {}
|
||||
protected:
|
||||
std::string GetControllerType() { return "Unk"; }
|
||||
std::string GetConfSection() { return "Unk"; }
|
||||
std::string GetBindingConfSection() { return "Unk"; }
|
||||
};
|
@ -7,7 +7,6 @@
|
||||
#include <PR/ultra64/pi.h>
|
||||
#include <PR/ultra64/message.h>
|
||||
|
||||
#include "ConfigFile.h"
|
||||
#include "Cvar.h"
|
||||
#include "GlobalCtx2.h"
|
||||
#include "ImGuiImpl.h"
|
||||
@ -33,18 +32,6 @@ namespace Game {
|
||||
Audio_SetGameVolume(SEQ_SFX, CVar_GetFloat("gFanfareVolume", 1));
|
||||
}
|
||||
|
||||
void LoadPadSettings() {
|
||||
const std::shared_ptr<ConfigFile> pConf = GlobalCtx2::GetInstance()->GetConfig();
|
||||
ConfigFile& Conf = *pConf;
|
||||
|
||||
for (const auto& [i, controllers] : Ship::Window::Controllers) {
|
||||
for (const auto& controller : controllers) {
|
||||
if (auto padConfSection = controller->GetPadConfSection()) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LoadSettings() {
|
||||
DebugConsole_LoadCVars();
|
||||
}
|
||||
@ -58,6 +45,7 @@ namespace Game {
|
||||
ModInternal::RegisterHook<ModInternal::GfxInit>([] {
|
||||
gfx_get_current_rendering_api()->set_texture_filter((FilteringMode) CVar_GetS32("gTextureFilter", FILTER_THREE_POINT));
|
||||
SohImGui::console->opened = CVar_GetS32("gConsoleEnabled", 0);
|
||||
SohImGui::controller->Opened = CVar_GetS32("gControllerConfigurationEnabled", 0);
|
||||
UpdateAudio();
|
||||
});
|
||||
}
|
||||
|
@ -15,7 +15,6 @@
|
||||
namespace Ship {
|
||||
std::weak_ptr<GlobalCtx2> GlobalCtx2::Context;
|
||||
ModManager* INSTANCE;
|
||||
|
||||
std::shared_ptr<GlobalCtx2> GlobalCtx2::GetInstance() {
|
||||
return Context.lock();
|
||||
}
|
||||
@ -49,7 +48,7 @@ namespace Ship {
|
||||
return GlobalCtx2::GetAppDirectoryPath() + "/" + path;
|
||||
}
|
||||
|
||||
GlobalCtx2::GlobalCtx2(const std::string& Name) : Name(Name), MainPath(""), PatchesPath("") {
|
||||
GlobalCtx2::GlobalCtx2(std::string Name) : Name(std::move(Name)) {
|
||||
|
||||
}
|
||||
|
||||
@ -60,22 +59,19 @@ namespace Ship {
|
||||
|
||||
void GlobalCtx2::InitWindow() {
|
||||
InitLogging();
|
||||
Config = std::make_shared<ConfigFile>(GlobalCtx2::GetInstance(), GetPathRelativeToAppDirectory("shipofharkinian.ini"));
|
||||
MainPath = (*Config)["ARCHIVE"]["Main Archive"];
|
||||
if (MainPath.empty()) {
|
||||
MainPath = GetPathRelativeToAppDirectory("oot.otr");
|
||||
}
|
||||
PatchesPath = (*Config)["ARCHIVE"]["Patches Directory"];
|
||||
if (PatchesPath.empty()) {
|
||||
PatchesPath = GetAppDirectoryPath() + "/";
|
||||
}
|
||||
ResMan = std::make_shared<ResourceMgr>(GlobalCtx2::GetInstance(), MainPath, PatchesPath);
|
||||
Win = std::make_shared<Window>(GlobalCtx2::GetInstance());
|
||||
Config = std::make_shared<Mercury>(GetPathRelativeToAppDirectory("shipofharkinian.json"));
|
||||
Config->reload();
|
||||
|
||||
MainPath = Config->getString("Game.Main Archive", GetPathRelativeToAppDirectory("oot.otr"));
|
||||
PatchesPath = Config->getString("Game.Patches Archive", GetAppDirectoryPath() + "/mods");
|
||||
|
||||
ResMan = std::make_shared<ResourceMgr>(GetInstance(), MainPath, PatchesPath);
|
||||
Win = std::make_shared<Window>(GetInstance());
|
||||
|
||||
if (!ResMan->DidLoadSuccessfully())
|
||||
{
|
||||
#ifdef _WIN32
|
||||
MessageBox(NULL, L"Main OTR file not found!", L"Uh oh", MB_OK);
|
||||
MessageBox(nullptr, L"Main OTR file not found!", L"Uh oh", MB_OK);
|
||||
#else
|
||||
SPDLOG_ERROR("Main OTR file not found!");
|
||||
#endif
|
||||
@ -109,7 +105,7 @@ namespace Ship {
|
||||
}
|
||||
}
|
||||
|
||||
void GlobalCtx2::WriteSaveFile(std::filesystem::path savePath, uintptr_t addr, void* dramAddr, size_t size) {
|
||||
void GlobalCtx2::WriteSaveFile(const std::filesystem::path& savePath, const uintptr_t addr, void* dramAddr, const size_t size) {
|
||||
std::ofstream saveFile = std::ofstream(savePath, std::fstream::in | std::fstream::out | std::fstream::binary);
|
||||
saveFile.seekp(addr);
|
||||
saveFile.write((char*)dramAddr, size);
|
||||
@ -129,4 +125,4 @@ namespace Ship {
|
||||
|
||||
saveFile.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,9 @@
|
||||
#ifdef __cplusplus
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <fstream>
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "ConfigFile.h"
|
||||
#include "Lib/Mercury/Mercury.h"
|
||||
|
||||
namespace Ship {
|
||||
class ResourceMgr;
|
||||
@ -22,15 +23,15 @@ namespace Ship {
|
||||
std::shared_ptr<Window> GetWindow() { return Win; }
|
||||
std::shared_ptr<ResourceMgr> GetResourceManager() { return ResMan; }
|
||||
std::shared_ptr<spdlog::logger> GetLogger() { return Logger; }
|
||||
std::shared_ptr<ConfigFile> GetConfig() { return Config; }
|
||||
std::shared_ptr<Mercury> GetConfig() { return Config; }
|
||||
|
||||
static std::string GetAppDirectoryPath();
|
||||
static std::string GetPathRelativeToAppDirectory(const char* path);
|
||||
|
||||
void WriteSaveFile(std::filesystem::path savePath, uintptr_t addr, void* dramAddr, size_t size);
|
||||
void WriteSaveFile(const std::filesystem::path& savePath, uintptr_t addr, void* dramAddr, size_t size);
|
||||
void ReadSaveFile(std::filesystem::path savePath, uintptr_t addr, void* dramAddr, size_t size);
|
||||
|
||||
GlobalCtx2(const std::string& Name);
|
||||
GlobalCtx2(std::string Name);
|
||||
~GlobalCtx2();
|
||||
|
||||
protected:
|
||||
@ -41,7 +42,7 @@ namespace Ship {
|
||||
static std::weak_ptr <GlobalCtx2> Context;
|
||||
std::shared_ptr<spdlog::logger> Logger;
|
||||
std::shared_ptr<Window> Win;
|
||||
std::shared_ptr<ConfigFile> Config; // Config needs to be after the Window because we call the Window during it's destructor.
|
||||
std::shared_ptr<Mercury> Config; // Config needs to be after the Window because we call the Window during it's destructor.
|
||||
std::shared_ptr<ResourceMgr> ResMan;
|
||||
std::string Name;
|
||||
std::string MainPath;
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <functional>
|
||||
|
||||
#include "UltraController.h"
|
||||
#include "Controller.h"
|
||||
|
||||
#define DEFINE_HOOK(name, type) struct name { typedef std::function<type> fn; }
|
||||
|
||||
@ -28,12 +29,11 @@ namespace ModInternal {
|
||||
}
|
||||
|
||||
DEFINE_HOOK(ControllerRead, void(OSContPad* cont_pad));
|
||||
|
||||
DEFINE_HOOK(ControllerRawInput, void(Ship::Controller* backend, uint32_t raw));
|
||||
DEFINE_HOOK(AudioInit, void());
|
||||
|
||||
DEFINE_HOOK(LoadTexture, void(const char* path, uint8_t** texture));
|
||||
|
||||
DEFINE_HOOK(GfxInit, void());
|
||||
DEFINE_HOOK(ExitGame, void());
|
||||
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "GameSettings.h"
|
||||
#include "Console.h"
|
||||
#include "Hooks.h"
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#include "Lib/ImGui/imgui_internal.h"
|
||||
#include "GlobalCtx2.h"
|
||||
#include "ResourceMgr.h"
|
||||
@ -63,6 +64,8 @@ namespace SohImGui {
|
||||
ImGuiIO* io;
|
||||
Console* console = new Console;
|
||||
GameOverlay* overlay = new GameOverlay;
|
||||
InputEditor* controller = new InputEditor;
|
||||
static ImVector<ImRect> s_GroupPanelLabelStack;
|
||||
bool p_open = false;
|
||||
bool needs_save = false;
|
||||
|
||||
@ -326,6 +329,7 @@ namespace SohImGui {
|
||||
}
|
||||
console->Init();
|
||||
overlay->Init();
|
||||
controller->Init();
|
||||
ImGuiWMInit();
|
||||
ImGuiBackendInit();
|
||||
|
||||
@ -346,23 +350,17 @@ namespace SohImGui {
|
||||
LoadTexture("C-Down", "assets/ship_of_harkinian/buttons/CDown.png");
|
||||
});
|
||||
|
||||
for (const auto& [i, controllers] : Ship::Window::Controllers)
|
||||
{
|
||||
CVar_SetFloat(StringHelper::Sprintf("gCont%i_GyroDriftX", i).c_str(), 0);
|
||||
CVar_SetFloat(StringHelper::Sprintf("gCont%i_GyroDriftY", i).c_str(), 0);
|
||||
needs_save = true;
|
||||
}
|
||||
|
||||
ModInternal::RegisterHook<ModInternal::ControllerRead>([](OSContPad* cont_pad) {
|
||||
pads = cont_pad;
|
||||
});
|
||||
|
||||
Game::InitSettings();
|
||||
|
||||
CVar_SetS32("gRandoGenerating", 0);
|
||||
CVar_SetS32("gNewSeedGenerated", 0);
|
||||
CVar_SetS32("gNewFileDropped", 0);
|
||||
CVar_SetString("gDroppedFile", "");
|
||||
Game::SaveSettings();
|
||||
CVar_SetString("gDroppedFile", "None");
|
||||
// Game::SaveSettings();
|
||||
}
|
||||
|
||||
void Update(EventImpl event) {
|
||||
@ -511,7 +509,7 @@ namespace SohImGui {
|
||||
}
|
||||
|
||||
void EnhancementCombo(const std::string& name, const char* cvarName, const std::vector<std::string>& items, int defaultValue) {
|
||||
|
||||
|
||||
if (ImGui::BeginCombo(name.c_str(), items[static_cast<int>(CVar_GetS32(cvarName, defaultValue))].c_str())) {
|
||||
for (int settingIndex = 0; settingIndex < (int) items.size(); settingIndex++) {
|
||||
if (ImGui::Selectable(items[settingIndex].c_str())) {
|
||||
@ -691,36 +689,32 @@ namespace SohImGui {
|
||||
needs_save = true;
|
||||
GlobalCtx2::GetInstance()->GetWindow()->dwMenubar = menu_bar;
|
||||
ShowCursor(menu_bar, Dialogues::dMenubar);
|
||||
|
||||
GlobalCtx2::GetInstance()->GetWindow()->GetControlDeck()->SaveControllerSettings();
|
||||
if (CVar_GetS32("gControlNav", 0)) {
|
||||
if (CVar_GetS32("gOpenMenuBar", 0)) {
|
||||
io->ConfigFlags |=ImGuiConfigFlags_NavEnableGamepad | ImGuiConfigFlags_NavEnableKeyboard;
|
||||
} else {
|
||||
io->ConfigFlags &= ~ImGuiConfigFlags_NavEnableGamepad;
|
||||
}
|
||||
else
|
||||
{
|
||||
io->ConfigFlags &= ~ImGuiConfigFlags_NavEnableGamepad;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
io->ConfigFlags &= ~ImGuiConfigFlags_NavEnableGamepad;
|
||||
} else {
|
||||
io->ConfigFlags &= ~ImGuiConfigFlags_NavEnableGamepad;
|
||||
}
|
||||
}
|
||||
|
||||
#if __APPLE__
|
||||
if ((ImGui::IsKeyDown(ImGuiKey_LeftSuper) ||
|
||||
ImGui::IsKeyDown(ImGuiKey_RightSuper)) &&
|
||||
ImGui::IsKeyDown(ImGuiKey_RightSuper)) &&
|
||||
ImGui::IsKeyPressed(ImGuiKey_R, false)) {
|
||||
console->Commands["reset"].handler(emptyArgs);
|
||||
}
|
||||
#else
|
||||
if ((ImGui::IsKeyDown(ImGuiKey_LeftCtrl) ||
|
||||
ImGui::IsKeyDown(ImGuiKey_RightCtrl)) &&
|
||||
ImGui::IsKeyDown(ImGuiKey_RightCtrl)) &&
|
||||
ImGui::IsKeyPressed(ImGuiKey_R, false)) {
|
||||
console->Commands["reset"].handler(emptyArgs);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if (ImGui::BeginMenuBar()) {
|
||||
if (DefaultAssets.contains("Game_Icon")) {
|
||||
ImGui::SetCursorPos(ImVec2(5, 2.5f));
|
||||
@ -740,7 +734,7 @@ namespace SohImGui {
|
||||
console->Commands["reset"].handler(emptyArgs);
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Audio")) {
|
||||
EnhancementSliderFloat("Master Volume: %d %%", "##Master_Vol", "gGameMasterVolume", 0.0f, 1.0f, "", 1.0f, true);
|
||||
@ -758,6 +752,9 @@ namespace SohImGui {
|
||||
EnhancementCheckbox("Use Controller Navigation", "gControlNav");
|
||||
Tooltip("Allows controller navigation of the menu bar\nD-pad to move between items, A to select, and X to grab focus on the menu bar");
|
||||
|
||||
EnhancementCheckbox("Controller Configuration", "gControllerConfigurationEnabled");
|
||||
controller->Opened = CVar_GetS32("gControllerConfigurationEnabled", 0);
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// TODO mutual exclusions -- There should be some system to prevent conclifting enhancements from being selected
|
||||
@ -771,41 +768,9 @@ namespace SohImGui {
|
||||
|
||||
EnhancementCheckbox("Show Inputs", "gInputEnabled");
|
||||
Tooltip("Shows currently pressed inputs on the bottom right of the screen");
|
||||
EnhancementCheckbox("Rumble Enabled", "gRumbleEnabled");
|
||||
|
||||
EnhancementSliderFloat("Input Scale: %.1f", "##Input", "gInputScale", 1.0f, 3.0f, "", 1.0f, false);
|
||||
Tooltip("Sets the on screen size of the displayed inputs from the Show Inputs setting");
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
for (const auto& [i, controllers] : Ship::Window::Controllers)
|
||||
{
|
||||
bool hasPad = std::find_if(controllers.begin(), controllers.end(), [](const auto& c) {
|
||||
return c->HasPadConf() && c->Connected();
|
||||
}) != controllers.end();
|
||||
|
||||
if (!hasPad) continue;
|
||||
|
||||
auto menuLabel = "Controller " + std::to_string(i + 1);
|
||||
if (ImGui::BeginMenu(menuLabel.c_str()))
|
||||
{
|
||||
EnhancementSliderFloat("Gyro Sensitivity: %d %%", "##GYROSCOPE", StringHelper::Sprintf("gCont%i_GyroSensitivity", i).c_str(), 0.0f, 1.0f, "", 1.0f, true);
|
||||
|
||||
if (ImGui::Button("Recalibrate Gyro"))
|
||||
{
|
||||
CVar_SetFloat(StringHelper::Sprintf("gCont%i_GyroDriftX", i).c_str(), 0);
|
||||
CVar_SetFloat(StringHelper::Sprintf("gCont%i_GyroDriftY", i).c_str(), 0);
|
||||
needs_save = true;
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
EnhancementSliderFloat("Rumble Strength: %d %%", "##RUMBLE", StringHelper::Sprintf("gCont%i_RumbleStrength", i).c_str(), 0.0f, 1.0f, "", 1.0f, true);
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::Separator();
|
||||
}
|
||||
Tooltip("Sets the on screen size of the displayed inputs from the Show Inputs setting");
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
@ -933,7 +898,7 @@ namespace SohImGui {
|
||||
Tooltip("Disables random drops, except from the Goron Pot, Dampe, and bosses");
|
||||
EnhancementCheckbox("No Heart Drops", "gNoHeartDrops");
|
||||
Tooltip("Disables heart drops, but not heart placements, like from a Deku Scrub running off\nThis simulates Hero Mode from other games in the series");
|
||||
|
||||
|
||||
if (ImGui::BeginMenu("Potion Values"))
|
||||
{
|
||||
EnhancementCheckbox("Change Red Potion Effect", "gRedPotionEffect");
|
||||
@ -942,7 +907,7 @@ namespace SohImGui {
|
||||
Tooltip("Changes the amount of health restored by Red Potions");
|
||||
EnhancementCheckbox("Red Potion Percent Restore", "gRedPercentRestore");
|
||||
Tooltip("Toggles from Red Potions restoring a fixed amount of health to a percent of the player's current max health");
|
||||
|
||||
|
||||
EnhancementCheckbox("Change Green Potion Effect", "gGreenPotionEffect");
|
||||
Tooltip("Enable the following changes to the amount of mana restored by Green Potions");
|
||||
EnhancementSliderInt("Green Potion Mana: %d", "##GREENPOTIONMANA", "gGreenPotionMana", 1, 100, "");
|
||||
@ -956,7 +921,7 @@ namespace SohImGui {
|
||||
Tooltip("Changes the amount of health restored by Blue Potions");
|
||||
EnhancementCheckbox("Blue Potion Health Percent Restore", "gBlueHealthPercentRestore");
|
||||
Tooltip("Toggles from Blue Potions restoring a fixed amount of health to a percent of the player's current max health");
|
||||
|
||||
|
||||
EnhancementSliderInt("Blue Potion Mana: %d", "##BLUEPOTIONMANA", "gBluePotionMana", 1, 100, "");
|
||||
Tooltip("Changes the amount of mana restored by Blue Potions, base max mana is 48, max upgraded mana is 96");
|
||||
EnhancementCheckbox("Blue Potion Mana Percent Restore", "gBlueManaPercentRestore");
|
||||
@ -1019,7 +984,7 @@ namespace SohImGui {
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
|
||||
EnhancementCheckbox("Visual Stone of Agony", "gVisualAgony");
|
||||
Tooltip("Displays an icon and plays a sound when Stone of Agony\nshould be activated, for those without rumble");
|
||||
EnhancementCheckbox("Assignable Tunics and Boots", "gAssignableTunicsAndBoots");
|
||||
@ -1268,7 +1233,7 @@ namespace SohImGui {
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0));
|
||||
ImGui::SetNextWindowSize(ImVec2 (0,0));
|
||||
ImGuiWindowFlags HiddenWndFlags = ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoNavInputs |
|
||||
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoNavInputs |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNavFocus | ImGuiWindowFlags_NoMouseInputs | ImGuiWindowFlags_NoDecoration;
|
||||
ImGui::Begin(category.first.c_str(), nullptr, HiddenWndFlags);
|
||||
ImGui::End();
|
||||
@ -1292,6 +1257,7 @@ namespace SohImGui {
|
||||
}
|
||||
|
||||
console->Draw();
|
||||
controller->DrawHud();
|
||||
|
||||
for (auto& windowIter : customWindows) {
|
||||
CustomWindow& window = windowIter.second;
|
||||
@ -1457,4 +1423,124 @@ namespace SohImGui {
|
||||
#endif
|
||||
return reinterpret_cast<ImTextureID>(id);
|
||||
}
|
||||
|
||||
void BeginGroupPanel(const char* name, const ImVec2& size)
|
||||
{
|
||||
ImGui::BeginGroup();
|
||||
|
||||
// auto cursorPos = ImGui::GetCursorScreenPos();
|
||||
auto itemSpacing = ImGui::GetStyle().ItemSpacing;
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
|
||||
|
||||
auto frameHeight = ImGui::GetFrameHeight();
|
||||
ImGui::BeginGroup();
|
||||
|
||||
ImVec2 effectiveSize = size;
|
||||
if (size.x < 0.0f)
|
||||
effectiveSize.x = ImGui::GetContentRegionAvail().x;
|
||||
else
|
||||
effectiveSize.x = size.x;
|
||||
ImGui::Dummy(ImVec2(effectiveSize.x, 0.0f));
|
||||
|
||||
ImGui::Dummy(ImVec2(frameHeight * 0.5f, 0.0f));
|
||||
ImGui::SameLine(0.0f, 0.0f);
|
||||
ImGui::BeginGroup();
|
||||
ImGui::Dummy(ImVec2(frameHeight * 0.5f, 0.0f));
|
||||
ImGui::SameLine(0.0f, 0.0f);
|
||||
ImGui::TextUnformatted(name);
|
||||
auto labelMin = ImGui::GetItemRectMin();
|
||||
auto labelMax = ImGui::GetItemRectMax();
|
||||
ImGui::SameLine(0.0f, 0.0f);
|
||||
ImGui::Dummy(ImVec2(0.0, frameHeight + itemSpacing.y));
|
||||
ImGui::BeginGroup();
|
||||
|
||||
//ImGui::GetWindowDrawList()->AddRect(labelMin, labelMax, IM_COL32(255, 0, 255, 255));
|
||||
|
||||
ImGui::PopStyleVar(2);
|
||||
|
||||
#if IMGUI_VERSION_NUM >= 17301
|
||||
ImGui::GetCurrentWindow()->ContentRegionRect.Max.x -= frameHeight * 0.5f;
|
||||
ImGui::GetCurrentWindow()->WorkRect.Max.x -= frameHeight * 0.5f;
|
||||
ImGui::GetCurrentWindow()->InnerRect.Max.x -= frameHeight * 0.5f;
|
||||
#else
|
||||
ImGui::GetCurrentWindow()->ContentsRegionRect.Max.x -= frameHeight * 0.5f;
|
||||
#endif
|
||||
ImGui::GetCurrentWindow()->Size.x -= frameHeight;
|
||||
|
||||
auto itemWidth = ImGui::CalcItemWidth();
|
||||
ImGui::PushItemWidth(ImMax(0.0f, itemWidth - frameHeight));
|
||||
s_GroupPanelLabelStack.push_back(ImRect(labelMin, labelMax));
|
||||
}
|
||||
|
||||
void EndGroupPanel(float minHeight) {
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
auto itemSpacing = ImGui::GetStyle().ItemSpacing;
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
|
||||
|
||||
auto frameHeight = ImGui::GetFrameHeight();
|
||||
|
||||
ImGui::EndGroup();
|
||||
|
||||
//ImGui::GetWindowDrawList()->AddRectFilled(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(0, 255, 0, 64), 4.0f);
|
||||
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::SameLine(0.0f, 0.0f);
|
||||
ImGui::Dummy(ImVec2(frameHeight * 0.5f, 0.0f));
|
||||
ImGui::Dummy(ImVec2(0.0, std::max(frameHeight - frameHeight * 0.5f - itemSpacing.y, minHeight)));
|
||||
|
||||
ImGui::EndGroup();
|
||||
|
||||
auto itemMin = ImGui::GetItemRectMin();
|
||||
auto itemMax = ImGui::GetItemRectMax();
|
||||
//ImGui::GetWindowDrawList()->AddRectFilled(itemMin, itemMax, IM_COL32(255, 0, 0, 64), 4.0f);
|
||||
|
||||
auto labelRect = s_GroupPanelLabelStack.back();
|
||||
s_GroupPanelLabelStack.pop_back();
|
||||
|
||||
ImVec2 halfFrame = ImVec2(frameHeight * 0.25f, frameHeight) * 0.5f;
|
||||
ImRect frameRect = ImRect(itemMin + halfFrame, itemMax - ImVec2(halfFrame.x, 0.0f));
|
||||
labelRect.Min.x -= itemSpacing.x;
|
||||
labelRect.Max.x += itemSpacing.x;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
switch (i)
|
||||
{
|
||||
// left half-plane
|
||||
case 0: ImGui::PushClipRect(ImVec2(-FLT_MAX, -FLT_MAX), ImVec2(labelRect.Min.x, FLT_MAX), true); break;
|
||||
// right half-plane
|
||||
case 1: ImGui::PushClipRect(ImVec2(labelRect.Max.x, -FLT_MAX), ImVec2(FLT_MAX, FLT_MAX), true); break;
|
||||
// top
|
||||
case 2: ImGui::PushClipRect(ImVec2(labelRect.Min.x, -FLT_MAX), ImVec2(labelRect.Max.x, labelRect.Min.y), true); break;
|
||||
// bottom
|
||||
case 3: ImGui::PushClipRect(ImVec2(labelRect.Min.x, labelRect.Max.y), ImVec2(labelRect.Max.x, FLT_MAX), true); break;
|
||||
}
|
||||
|
||||
ImGui::GetWindowDrawList()->AddRect(
|
||||
frameRect.Min, frameRect.Max,
|
||||
ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Border)),
|
||||
halfFrame.x);
|
||||
|
||||
ImGui::PopClipRect();
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar(2);
|
||||
|
||||
#if IMGUI_VERSION_NUM >= 17301
|
||||
ImGui::GetCurrentWindow()->ContentRegionRect.Max.x += frameHeight * 0.5f;
|
||||
ImGui::GetCurrentWindow()->WorkRect.Max.x += frameHeight * 0.5f;
|
||||
ImGui::GetCurrentWindow()->InnerRect.Max.x += frameHeight * 0.5f;
|
||||
#else
|
||||
ImGui::GetCurrentWindow()->ContentsRegionRect.Max.x += frameHeight * 0.5f;
|
||||
#endif
|
||||
ImGui::GetCurrentWindow()->Size.x += frameHeight;
|
||||
|
||||
ImGui::Dummy(ImVec2(0.0f, 0.0f));
|
||||
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "GameOverlay.h"
|
||||
#include "Lib/ImGui/imgui.h"
|
||||
#include "Console.h"
|
||||
#include "InputEditor.h"
|
||||
|
||||
struct GameAsset {
|
||||
uint32_t textureId;
|
||||
@ -59,12 +60,13 @@ namespace SohImGui {
|
||||
} CustomWindow;
|
||||
|
||||
extern Console* console;
|
||||
extern Ship::InputEditor* controller;
|
||||
extern Ship::GameOverlay* overlay;
|
||||
extern bool needs_save;
|
||||
void Init(WindowImpl window_impl);
|
||||
void Update(EventImpl event);
|
||||
void Tooltip(const char* text);
|
||||
|
||||
|
||||
void EnhancementRadioButton(const char* text, const char* cvarName, int id);
|
||||
void EnhancementCheckbox(const char* text, const char* cvarName);
|
||||
void EnhancementButton(const char* text, const char* cvarName);
|
||||
@ -75,7 +77,7 @@ namespace SohImGui {
|
||||
void EnhancementCombo(const std::string& name, const char* cvarName, const std::vector<std::string>& items, int defaultValue = 0);
|
||||
|
||||
void DrawMainMenuAndCalculateGameSize(void);
|
||||
|
||||
|
||||
void DrawFramebufferAndGameInput(void);
|
||||
void Render(void);
|
||||
void CancelFrame(void);
|
||||
@ -90,4 +92,6 @@ namespace SohImGui {
|
||||
void ResetColor(const char* cvarName, ImVec4* colors, ImVec4 defaultcolors, bool has_alpha);
|
||||
ImTextureID GetTextureByID(int id);
|
||||
ImTextureID GetTextureByName(const std::string& name);
|
||||
void BeginGroupPanel(const char* name, const ImVec2 & size = ImVec2(0.0f, 0.0f));
|
||||
void EndGroupPanel(float minHeight = 0.0f);
|
||||
}
|
||||
|
277
libultraship/libultraship/InputEditor.cpp
Normal file
277
libultraship/libultraship/InputEditor.cpp
Normal file
@ -0,0 +1,277 @@
|
||||
#include "InputEditor.h"
|
||||
#include "Controller.h"
|
||||
#include "Window.h"
|
||||
#include "Lib/ImGui/imgui.h"
|
||||
#include "ImGuiImpl.h"
|
||||
#include "Utils/StringHelper.h"
|
||||
#include "Lib/ImGui/imgui_internal.h"
|
||||
|
||||
namespace Ship {
|
||||
|
||||
extern "C" uint8_t __enableGameInput;
|
||||
#define SEPARATION() ImGui::Dummy(ImVec2(0, 5))
|
||||
|
||||
void InputEditor::Init() {
|
||||
BtnReading = -1;
|
||||
}
|
||||
|
||||
std::shared_ptr<Controller> GetControllerPerSlot(int slot) {
|
||||
const std::vector<int> vDevices = Window::ControllerApi->virtualDevices;
|
||||
return Window::ControllerApi->physicalDevices[vDevices[slot]];
|
||||
}
|
||||
|
||||
void InputEditor::DrawButton(const char* label, int n64Btn) {
|
||||
const std::shared_ptr<Controller> backend = GetControllerPerSlot(CurrentPort);
|
||||
|
||||
float size = 40;
|
||||
bool readingMode = BtnReading == n64Btn;
|
||||
bool disabled = BtnReading != -1 && !readingMode || !backend->Connected();
|
||||
ImVec2 len = ImGui::CalcTextSize(label);
|
||||
ImVec2 pos = ImGui::GetCursorPos();
|
||||
ImGui::SetCursorPosY(pos.y + len.y / 4);
|
||||
ImGui::SetCursorPosX(pos.x + abs(len.x - size));
|
||||
ImGui::Text("%s", label);
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosY(pos.y);
|
||||
|
||||
if(disabled) {
|
||||
ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
|
||||
}
|
||||
|
||||
if(readingMode) {
|
||||
const int32_t btn = backend->ReadRawPress();
|
||||
|
||||
if(btn != -1) {
|
||||
backend->SetButtonMapping(CurrentPort, n64Btn, btn);
|
||||
BtnReading = -1;
|
||||
}
|
||||
}
|
||||
|
||||
const char* BtnName = backend->GetButtonName(CurrentPort, n64Btn);
|
||||
|
||||
if (ImGui::Button(StringHelper::Sprintf("%s##HBTNID_%d", readingMode ? "Press a Key..." : BtnName, n64Btn).c_str())) {
|
||||
BtnReading = n64Btn;
|
||||
backend->ClearRawPress();
|
||||
}
|
||||
|
||||
if(disabled) {
|
||||
ImGui::PopItemFlag();
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
}
|
||||
|
||||
void InputEditor::DrawVirtualStick(const char* label, ImVec2 stick) {
|
||||
ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPos().x + 5, ImGui::GetCursorPos().y));
|
||||
ImGui::BeginChild(label, ImVec2(68, 75), false);
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
const ImVec2 p = ImGui::GetCursorScreenPos();
|
||||
|
||||
float sz = 45.0f;
|
||||
float rad = sz * 0.5f;
|
||||
ImVec2 pos = ImVec2(p.x + sz * 0.5f + 12, p.y + sz * 0.5f + 11);
|
||||
|
||||
float stickX = (stick.x / 83.0f) * (rad * 0.5f);
|
||||
float stickY = -(stick.y / 83.0f) * (rad * 0.5f);
|
||||
|
||||
ImVec4 rect = ImVec4(p.x + 2, p.y + 2, 65, 65);
|
||||
draw_list->AddRect(ImVec2(rect.x, rect.y), ImVec2(rect.x + rect.z, rect.y + rect.w), ImColor(100, 100, 100, 255), 0.0f, 0, 1.5f);
|
||||
draw_list->AddCircleFilled(pos, rad, ImColor(130, 130, 130, 255), 8);
|
||||
draw_list->AddCircleFilled(ImVec2(pos.x + stickX, pos.y + stickY), 5, ImColor(15, 15, 15, 255), 7);
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void InputEditor::DrawControllerSchema() {
|
||||
|
||||
const std::vector<int> vDevices = Window::ControllerApi->virtualDevices;
|
||||
const std::vector<std::shared_ptr<Controller>> devices = Window::ControllerApi->physicalDevices;
|
||||
|
||||
std::shared_ptr<Controller> Backend = devices[vDevices[CurrentPort]];
|
||||
DeviceProfile& profile =Backend->profiles[CurrentPort];
|
||||
float sensitivity = profile.Thresholds[SENSITIVITY];
|
||||
bool IsKeyboard = Backend->GetGuid() == "Keyboard" || !Backend->Connected();
|
||||
const char* ControllerName = Backend->GetControllerName();
|
||||
|
||||
if (ControllerName != nullptr && ImGui::BeginCombo("##ControllerEntries", ControllerName)) {
|
||||
for (uint8_t i = 0; i < devices.size(); i++) {
|
||||
if (ImGui::Selectable(devices[i]->GetControllerName(), i == vDevices[CurrentPort])) {
|
||||
Window::ControllerApi->SetPhysicalDevice(CurrentPort, i);
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if(ImGui::Button("Refresh")) {
|
||||
Window::ControllerApi->ScanPhysicalDevices();
|
||||
}
|
||||
|
||||
SohImGui::BeginGroupPanel("Buttons", ImVec2(150, 20));
|
||||
DrawButton("A", BTN_A);
|
||||
DrawButton("B", BTN_B);
|
||||
DrawButton("L", BTN_L);
|
||||
DrawButton("R", BTN_R);
|
||||
DrawButton("Z", BTN_Z);
|
||||
DrawButton("START", BTN_START);
|
||||
SEPARATION();
|
||||
SohImGui::EndGroupPanel(IsKeyboard ? 7.0f : 48.0f);
|
||||
ImGui::SameLine();
|
||||
SohImGui::BeginGroupPanel("Digital Pad", ImVec2(150, 20));
|
||||
DrawButton("Up", BTN_DUP);
|
||||
DrawButton("Down", BTN_DDOWN);
|
||||
DrawButton("Left", BTN_DLEFT);
|
||||
DrawButton("Right", BTN_DRIGHT);
|
||||
SEPARATION();
|
||||
SohImGui::EndGroupPanel(IsKeyboard ? 53.0f : 94.0f);
|
||||
ImGui::SameLine();
|
||||
SohImGui::BeginGroupPanel("Analog Stick", ImVec2(150, 20));
|
||||
DrawButton("Up", BTN_STICKUP);
|
||||
DrawButton("Down", BTN_STICKDOWN);
|
||||
DrawButton("Left", BTN_STICKLEFT);
|
||||
DrawButton("Right", BTN_STICKRIGHT);
|
||||
|
||||
if (!IsKeyboard) {
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 8);
|
||||
DrawVirtualStick("##MainVirtualStick", ImVec2(Backend->wStickX, Backend->wStickY));
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5);
|
||||
|
||||
ImGui::BeginChild("##MSInput", ImVec2(90, 50), false);
|
||||
ImGui::Text("Deadzone");
|
||||
ImGui::PushItemWidth(80);
|
||||
ImGui::InputInt("##MDZone", &profile.Thresholds[LEFT_STICK]);
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::EndChild();
|
||||
} else {
|
||||
ImGui::Dummy(ImVec2(0, 6));
|
||||
}
|
||||
SohImGui::EndGroupPanel(IsKeyboard ? 52.0f : 24.0f);
|
||||
ImGui::SameLine();
|
||||
|
||||
if (!IsKeyboard) {
|
||||
ImGui::SameLine();
|
||||
SohImGui::BeginGroupPanel("Camera Stick", ImVec2(150, 20));
|
||||
DrawButton("Up", BTN_VSTICKUP);
|
||||
DrawButton("Down", BTN_VSTICKDOWN);
|
||||
DrawButton("Left", BTN_VSTICKLEFT);
|
||||
DrawButton("Right", BTN_VSTICKRIGHT);
|
||||
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 8);
|
||||
DrawVirtualStick("##CameraVirtualStick", ImVec2(Backend->wCamX / sensitivity, Backend->wCamY / sensitivity));
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5);
|
||||
ImGui::BeginChild("##CSInput", ImVec2(90, 85), false);
|
||||
ImGui::Text("Deadzone");
|
||||
ImGui::PushItemWidth(80);
|
||||
ImGui::InputInt("##MDZone", &profile.Thresholds[RIGHT_STICK]);
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::Text("Sensitivity");
|
||||
ImGui::PushItemWidth(80);
|
||||
ImGui::InputInt("##MSensitivity", &profile.Thresholds[SENSITIVITY]);
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::EndChild();
|
||||
SohImGui::EndGroupPanel(14.0f);
|
||||
}
|
||||
|
||||
if(Backend->CanGyro()) {
|
||||
ImGui::SameLine();
|
||||
|
||||
SohImGui::BeginGroupPanel("Gyro Options", ImVec2(175, 20));
|
||||
float cursorX = ImGui::GetCursorPosX() + 5;
|
||||
ImGui::SetCursorPosX(cursorX);
|
||||
ImGui::Checkbox("Enable Gyro", &profile.UseGyro);
|
||||
ImGui::SetCursorPosX(cursorX);
|
||||
ImGui::Text("Gyro Sensitivity: %d%%", profile.Thresholds[GYRO_SENSITIVITY]);
|
||||
ImGui::PushItemWidth(135.0f);
|
||||
ImGui::SetCursorPosX(cursorX);
|
||||
ImGui::SliderInt("##GSensitivity", &profile.Thresholds[GYRO_SENSITIVITY], 0, 100, "");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::Dummy(ImVec2(0, 1));
|
||||
ImGui::SetCursorPosX(cursorX);
|
||||
if (ImGui::Button("Recalibrate Gyro##RGyro")) {
|
||||
profile.Thresholds[DRIFT_X] = 0;
|
||||
profile.Thresholds[DRIFT_Y] = 0;
|
||||
}
|
||||
ImGui::SetCursorPosX(cursorX);
|
||||
DrawVirtualStick("##GyroPreview", ImVec2(Backend->wGyroX, Backend->wGyroY));
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 5);
|
||||
ImGui::BeginChild("##GyInput", ImVec2(90, 85), false);
|
||||
ImGui::Text("Drift X");
|
||||
ImGui::PushItemWidth(80);
|
||||
ImGui::InputInt("##GDriftX", &profile.Thresholds[DRIFT_X]);
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::Text("Drift Y");
|
||||
ImGui::PushItemWidth(80);
|
||||
ImGui::InputInt("##GDriftY", &profile.Thresholds[DRIFT_Y]);
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::EndChild();
|
||||
SohImGui::EndGroupPanel(14.0f);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
const ImVec2 cursor = ImGui::GetCursorPos();
|
||||
|
||||
SohImGui::BeginGroupPanel("C-Buttons", ImVec2(158, 20));
|
||||
DrawButton("Up", BTN_CUP);
|
||||
DrawButton("Down", BTN_CDOWN);
|
||||
DrawButton("Left", BTN_CLEFT);
|
||||
DrawButton("Right", BTN_CRIGHT);
|
||||
ImGui::Dummy(ImVec2(0, 5));
|
||||
SohImGui::EndGroupPanel();
|
||||
|
||||
ImGui::SetCursorPosX(cursor.x);
|
||||
ImGui::SetCursorPosY(cursor.y + 120);
|
||||
SohImGui::BeginGroupPanel("Options", ImVec2(158, 20));
|
||||
float cursorX = ImGui::GetCursorPosX() + 5;
|
||||
ImGui::SetCursorPosX(cursorX);
|
||||
ImGui::Checkbox("Rumble Enabled", &profile.UseRumble);
|
||||
if (Backend->CanRumble()) {
|
||||
ImGui::SetCursorPosX(cursorX);
|
||||
ImGui::Text("Rumble Force: %d%%", static_cast<int>(100 * profile.RumbleStrength));
|
||||
ImGui::SetCursorPosX(cursorX);
|
||||
ImGui::PushItemWidth(135.0f);
|
||||
ImGui::SliderFloat("##RStrength", &profile.RumbleStrength, 0, 1.0f, "");
|
||||
ImGui::PopItemWidth();
|
||||
}
|
||||
ImGui::Dummy(ImVec2(0, 5));
|
||||
SohImGui::EndGroupPanel(IsKeyboard ? 0.0f : 2.0f);
|
||||
}
|
||||
|
||||
void InputEditor::DrawHud() {
|
||||
|
||||
__enableGameInput = true;
|
||||
|
||||
if (!this->Opened) {
|
||||
BtnReading = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(641, 250), ImVec2(1200, 290));
|
||||
//OTRTODO: Disable this stupid workaround ( ReadRawPress() only works when the window is on the main viewport )
|
||||
ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID);
|
||||
ImGui::Begin("Controller Configuration", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize);
|
||||
|
||||
ImGui::BeginTabBar("##Controllers");
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (ImGui::BeginTabItem(StringHelper::Sprintf("Port %d", i + 1).c_str())) {
|
||||
CurrentPort = i;
|
||||
ImGui::EndTabItem();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
|
||||
// Draw current cfg
|
||||
|
||||
DrawControllerSchema();
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
20
libultraship/libultraship/InputEditor.h
Normal file
20
libultraship/libultraship/InputEditor.h
Normal file
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Lib/ImGui/imgui.h"
|
||||
|
||||
namespace Ship {
|
||||
|
||||
class InputEditor {
|
||||
int CurrentPort = 0;
|
||||
int BtnReading = -1;
|
||||
public:
|
||||
bool Opened = false;
|
||||
void Init();
|
||||
void DrawButton(const char* label, int n64Btn);
|
||||
void DrawVirtualStick(const char* label, ImVec2 stick);
|
||||
void DrawControllerSchema();
|
||||
void DrawHud();
|
||||
};
|
||||
}
|
@ -1,56 +1,105 @@
|
||||
#include "KeyboardController.h"
|
||||
|
||||
#if __APPLE__
|
||||
#include <SDL_keyboard.h>
|
||||
#else
|
||||
#include <SDL2/SDL_keyboard.h>
|
||||
#endif
|
||||
|
||||
#include "Hooks.h"
|
||||
#include "GlobalCtx2.h"
|
||||
#include "Window.h"
|
||||
|
||||
namespace Ship {
|
||||
KeyboardController::KeyboardController(int32_t dwControllerNumber) : Controller(dwControllerNumber) {
|
||||
LoadBinding();
|
||||
}
|
||||
|
||||
KeyboardController::~KeyboardController() {
|
||||
|
||||
KeyboardController::KeyboardController() : Controller(), lastScancode(-1) {
|
||||
GUID = "Keyboard";
|
||||
}
|
||||
|
||||
bool KeyboardController::PressButton(int32_t dwScancode) {
|
||||
if (ButtonMapping.contains(dwScancode)) {
|
||||
dwPressedButtons |= ButtonMapping[dwScancode];
|
||||
return true;
|
||||
|
||||
lastKey = dwScancode;
|
||||
|
||||
for (int slot = 0; slot < MAXCONTROLLERS; slot++) {
|
||||
|
||||
if (profiles[slot].Mappings.contains(dwScancode)) {
|
||||
dwPressedButtons[slot] |= profiles[slot].Mappings[dwScancode];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KeyboardController::ReleaseButton(int32_t dwScancode) {
|
||||
if (ButtonMapping.contains(dwScancode)) {
|
||||
dwPressedButtons &= ~ButtonMapping[dwScancode];
|
||||
return true;
|
||||
for (int slot = 0; slot < MAXCONTROLLERS; slot++) {
|
||||
if (profiles[slot].Mappings.contains(dwScancode)) {
|
||||
dwPressedButtons[slot] &= ~profiles[slot].Mappings[dwScancode];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void KeyboardController::ReleaseAllButtons() {
|
||||
dwPressedButtons = 0;
|
||||
for(int slot = 0; slot < MAXCONTROLLERS; slot++) {
|
||||
dwPressedButtons[slot] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardController::ReadFromSource() {
|
||||
void KeyboardController::ReadFromSource(int32_t slot) {
|
||||
wStickX = 0;
|
||||
wStickY = 0;
|
||||
wCamX = 0;
|
||||
wCamY = 0;
|
||||
}
|
||||
|
||||
void KeyboardController::WriteToSource(ControllerCallback* controller)
|
||||
int32_t KeyboardController::ReadRawPress() {
|
||||
return lastKey;
|
||||
}
|
||||
|
||||
|
||||
void KeyboardController::WriteToSource(int32_t slot, ControllerCallback* controller)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
std::string KeyboardController::GetControllerType() {
|
||||
return "KEYBOARD";
|
||||
const char* KeyboardController::GetButtonName(int slot, int n64Button) {
|
||||
std::map<int32_t, int32_t>& Mappings = profiles[slot].Mappings;
|
||||
const auto find = std::find_if(Mappings.begin(), Mappings.end(), [n64Button](const std::pair<int32_t, int32_t>& pair) {
|
||||
return pair.second == n64Button;
|
||||
});
|
||||
|
||||
if (find == Mappings.end()) return "Unknown";
|
||||
const char* name = GlobalCtx2::GetInstance()->GetWindow()->GetKeyName(find->first);
|
||||
return strlen(name) == 0 ? "Unknown" : name;
|
||||
}
|
||||
|
||||
std::string KeyboardController::GetConfSection() {
|
||||
return GetControllerType() + " CONTROLLER " + std::to_string(GetControllerNumber() + 1);
|
||||
|
||||
void KeyboardController::CreateDefaultBinding(int32_t slot) {
|
||||
DeviceProfile& profile = profiles[slot];
|
||||
profile.Mappings[0x14D] = BTN_CRIGHT;
|
||||
profile.Mappings[0x14B] = BTN_CLEFT;
|
||||
profile.Mappings[0x150] = BTN_CDOWN;
|
||||
profile.Mappings[0x148] = BTN_CUP;
|
||||
profile.Mappings[0x13] = BTN_R;
|
||||
profile.Mappings[0x12] = BTN_L;
|
||||
profile.Mappings[0x023] = BTN_DRIGHT;
|
||||
profile.Mappings[0x021] = BTN_DLEFT;
|
||||
profile.Mappings[0x022] = BTN_DDOWN;
|
||||
profile.Mappings[0x014] = BTN_DUP;
|
||||
profile.Mappings[0x039] = BTN_START;
|
||||
profile.Mappings[0x02C] = BTN_Z;
|
||||
profile.Mappings[0x02E] = BTN_B;
|
||||
profile.Mappings[0x02D] = BTN_A;
|
||||
profile.Mappings[0x020] = BTN_STICKRIGHT;
|
||||
profile.Mappings[0x01E] = BTN_STICKLEFT;
|
||||
profile.Mappings[0x01F] = BTN_STICKDOWN;
|
||||
profile.Mappings[0x011] = BTN_STICKUP;
|
||||
}
|
||||
|
||||
std::string KeyboardController::GetBindingConfSection() {
|
||||
return GetControllerType() + " CONTROLLER BINDING " + std::to_string(GetControllerNumber() + 1);
|
||||
const char* KeyboardController::GetControllerName() {
|
||||
return "Keyboard";
|
||||
}
|
||||
}
|
||||
|
@ -5,24 +5,34 @@
|
||||
namespace Ship {
|
||||
class KeyboardController : public Controller {
|
||||
public:
|
||||
KeyboardController(int32_t dwControllerNumber);
|
||||
~KeyboardController();
|
||||
|
||||
void ReadFromSource();
|
||||
void WriteToSource(ControllerCallback* controller);
|
||||
bool Connected() const { return true; }
|
||||
bool CanRumble() const { return false; }
|
||||
KeyboardController();
|
||||
|
||||
void ReadFromSource(int32_t slot) override;
|
||||
void WriteToSource(int32_t slot, ControllerCallback* controller) override;
|
||||
bool Connected() const override { return true; }
|
||||
bool CanRumble() const override { return false; }
|
||||
bool CanGyro() const override { return false; }
|
||||
const char* GetControllerName() override;
|
||||
const char* GetButtonName(int slot, int n64Button) override;
|
||||
bool PressButton(int32_t dwScancode);
|
||||
bool ReleaseButton(int32_t dwScancode);
|
||||
|
||||
void ClearRawPress() override {
|
||||
lastKey = -1;
|
||||
}
|
||||
|
||||
int32_t ReadRawPress() override;
|
||||
void ReleaseAllButtons();
|
||||
|
||||
bool HasPadConf() const { return false; }
|
||||
std::optional<std::string> GetPadConfSection() { return {}; }
|
||||
void SetLastScancode(int32_t key) {
|
||||
lastScancode = key;
|
||||
}
|
||||
|
||||
int32_t GetLastScancode() { return lastScancode; }
|
||||
void CreateDefaultBinding(int32_t slot) override;
|
||||
|
||||
protected:
|
||||
std::string GetControllerType();
|
||||
std::string GetConfSection();
|
||||
std::string GetBindingConfSection();
|
||||
int32_t lastScancode;
|
||||
int32_t lastKey = -1;
|
||||
};
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "gfx_pc.h"
|
||||
#include "../../ImGuiImpl.h"
|
||||
#include "../../Cvar.h"
|
||||
#include "../../Hooks.h"
|
||||
|
||||
#define DECLARE_GFX_DXGI_FUNCTIONS
|
||||
#include "gfx_dxgi.h"
|
||||
@ -240,6 +241,7 @@ static LRESULT CALLBACK gfx_dxgi_wnd_proc(HWND h_wnd, UINT message, WPARAM w_par
|
||||
dxgi.current_height = (uint32_t)(l_param >> 16);
|
||||
break;
|
||||
case WM_DESTROY:
|
||||
ModInternal::ExecuteHooks<ModInternal::ExitGame>();
|
||||
exit(0);
|
||||
case WM_PAINT:
|
||||
if (dxgi.in_paint) {
|
||||
@ -718,6 +720,12 @@ void ThrowIfFailed(HRESULT res, HWND h_wnd, const char *message) {
|
||||
}
|
||||
}
|
||||
|
||||
const char* gfx_dxgi_get_key_name(int scancode) {
|
||||
TCHAR* Text = new TCHAR[64];
|
||||
GetKeyNameText(scancode << 16, Text, 64);
|
||||
return (char*) Text;
|
||||
}
|
||||
|
||||
extern "C" struct GfxWindowManagerAPI gfx_dxgi_api = {
|
||||
gfx_dxgi_init,
|
||||
gfx_dxgi_set_keyboard_callbacks,
|
||||
@ -734,6 +742,7 @@ extern "C" struct GfxWindowManagerAPI gfx_dxgi_api = {
|
||||
gfx_dxgi_set_target_fps,
|
||||
gfx_dxgi_set_maximum_frame_latency,
|
||||
gfx_dxgi_get_detected_hz,
|
||||
gfx_dxgi_get_key_name
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include "../../ImGuiImpl.h"
|
||||
#include "../../Cvar.h"
|
||||
#include "../../Hooks.h"
|
||||
|
||||
#include "gfx_window_manager_api.h"
|
||||
#include "gfx_screen_config.h"
|
||||
@ -108,7 +109,7 @@ static void set_fullscreen(bool on, bool call_callback) {
|
||||
SDL_GetDesktopDisplayMode(0, &mode);
|
||||
window_width = mode.w;
|
||||
window_height = mode.h;
|
||||
//SDL_ShowCursor(false);
|
||||
SDL_ShowCursor(false);
|
||||
} else {
|
||||
window_width = DESIRED_SCREEN_WIDTH;
|
||||
window_height = DESIRED_SCREEN_HEIGHT;
|
||||
@ -229,6 +230,15 @@ static int translate_scancode(int scancode) {
|
||||
}
|
||||
}
|
||||
|
||||
static int untranslate_scancode(int translatedScancode) {
|
||||
for (int i = 0; i < 512; i++) {
|
||||
if (inverted_scancode_table[i] == translatedScancode) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gfx_sdl_onkeydown(int scancode) {
|
||||
int key = translate_scancode(scancode);
|
||||
if (on_key_down_callback != NULL) {
|
||||
@ -270,6 +280,7 @@ static void gfx_sdl_handle_events(void) {
|
||||
Game::SaveSettings();
|
||||
break;
|
||||
case SDL_QUIT:
|
||||
ModInternal::ExecuteHooks<ModInternal::ExitGame>();
|
||||
SDL_Quit(); // bandaid fix for linux window closing issue
|
||||
exit(0);
|
||||
}
|
||||
@ -339,6 +350,10 @@ static float gfx_sdl_get_detected_hz(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char* gfx_sdl_get_key_name(int scancode) {
|
||||
return SDL_GetScancodeName((SDL_Scancode) untranslate_scancode(scancode));
|
||||
}
|
||||
|
||||
struct GfxWindowManagerAPI gfx_sdl = {
|
||||
gfx_sdl_init,
|
||||
gfx_sdl_set_keyboard_callbacks,
|
||||
@ -354,7 +369,8 @@ struct GfxWindowManagerAPI gfx_sdl = {
|
||||
gfx_sdl_get_time,
|
||||
gfx_sdl_set_target_fps,
|
||||
gfx_sdl_set_maximum_frame_latency,
|
||||
gfx_sdl_get_detected_hz
|
||||
gfx_sdl_get_detected_hz,
|
||||
gfx_sdl_get_key_name
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -20,6 +20,7 @@ struct GfxWindowManagerAPI {
|
||||
void (*set_target_fps)(int fps);
|
||||
void (*set_maximum_frame_latency)(int latency);
|
||||
float (*get_detected_hz)(void);
|
||||
const char* (*get_key_name)(int scancode);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
134
libultraship/libultraship/Lib/Mercury/Mercury.cpp
Normal file
134
libultraship/libultraship/Lib/Mercury/Mercury.cpp
Normal file
@ -0,0 +1,134 @@
|
||||
#include "Mercury.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <unordered_map>
|
||||
#include <any>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
using json = nlohmann::json;
|
||||
|
||||
std::unordered_map<std::string, std::any> ramMap;
|
||||
|
||||
Mercury::Mercury(std::string path) : path_(std::move(path)) {
|
||||
this->reload();
|
||||
}
|
||||
|
||||
std::vector<std::string> split(const std::string& s, const char delimiter) {
|
||||
std::vector<std::string> result;
|
||||
std::stringstream ss(s);
|
||||
std::string item;
|
||||
while (getline(ss, item, delimiter)) {
|
||||
result.push_back(item);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string Mercury::formatNestedKey(const std::string& key) {
|
||||
const std::vector<std::string> dots = split(key, '.');
|
||||
std::string tmp;
|
||||
if (dots.size() > 1)
|
||||
for (const auto& dot : dots) {
|
||||
tmp += "/" + dot;
|
||||
}
|
||||
else
|
||||
tmp = "/" + dots[0];
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
json Mercury::nested(const std::string& key) {
|
||||
std::vector<std::string> dots = split(key, '.');
|
||||
if (!this->vjson.is_object())
|
||||
return this->vjson;
|
||||
json gjson = this->vjson.unflatten();
|
||||
|
||||
if (dots.size() > 1) {
|
||||
for (auto& key : dots) {
|
||||
if (key == "*" || gjson.contains(key))
|
||||
gjson = gjson[key];
|
||||
}
|
||||
return gjson;
|
||||
}
|
||||
|
||||
return gjson[key];
|
||||
}
|
||||
|
||||
std::string Mercury::getString(const std::string& key, const std::string& def) {
|
||||
json n = this->nested(key);
|
||||
if (n.is_string() && !n.get<std::string>().empty())
|
||||
return n;
|
||||
return def;
|
||||
}
|
||||
|
||||
float Mercury::getFloat(const std::string& key, float def) {
|
||||
json n = this->nested(key);
|
||||
if (n.is_number_float())
|
||||
return n;
|
||||
return def;
|
||||
}
|
||||
|
||||
bool Mercury::getBool(const std::string& key, bool def) {
|
||||
json n = this->nested(key);
|
||||
if (n.is_boolean())
|
||||
return n;
|
||||
return def;
|
||||
}
|
||||
|
||||
int Mercury::getInt(const std::string& key, int def) {
|
||||
json n = this->nested(key);
|
||||
if (n.is_number_integer())
|
||||
return n;
|
||||
return def;
|
||||
}
|
||||
|
||||
bool Mercury::contains(const std::string& key) {
|
||||
return !this->nested(key).is_null();
|
||||
}
|
||||
|
||||
void Mercury::setString(const std::string& key, const std::string& value) {
|
||||
this->vjson[formatNestedKey(key)] = value;
|
||||
}
|
||||
|
||||
void Mercury::setFloat(const std::string& key, float value) {
|
||||
this->vjson[formatNestedKey(key)] = value;
|
||||
}
|
||||
|
||||
void Mercury::setBool(const std::string& key, bool value) {
|
||||
this->vjson[formatNestedKey(key)] = value;
|
||||
}
|
||||
|
||||
void Mercury::setInt(const std::string& key, int value) {
|
||||
this->vjson[formatNestedKey(key)] = value;
|
||||
}
|
||||
|
||||
void Mercury::setUInt(const std::string& key, uint32_t value) {
|
||||
this->vjson[formatNestedKey(key)] = value;
|
||||
}
|
||||
|
||||
void Mercury::erase(const std::string& key) {
|
||||
this->vjson.erase(formatNestedKey(key));
|
||||
}
|
||||
|
||||
void Mercury::reload() {
|
||||
if (this->path_ == "None" || !fs::exists(this->path_) || !fs::is_regular_file(this->path_)) {
|
||||
this->isNewInstance = true;
|
||||
this->vjson = json::object();
|
||||
return;
|
||||
}
|
||||
std::ifstream ifs(this->path_);
|
||||
try {
|
||||
this->rjson = json::parse(ifs);
|
||||
this->vjson = this->rjson.flatten();
|
||||
}
|
||||
catch (...) {
|
||||
this->vjson = json::object();
|
||||
}
|
||||
}
|
||||
|
||||
void Mercury::save() const {
|
||||
std::ofstream file(this->path_);
|
||||
file << this->vjson.unflatten().dump(4);
|
||||
}
|
47
libultraship/libultraship/Lib/Mercury/Mercury.h
Normal file
47
libultraship/libultraship/Lib/Mercury/Mercury.h
Normal file
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include <any>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "../nlohmann/json.hpp"
|
||||
|
||||
class Mercury {
|
||||
protected:
|
||||
std::string path_;
|
||||
public:
|
||||
explicit Mercury(std::string path);
|
||||
|
||||
nlohmann::json vjson;
|
||||
nlohmann::json rjson;
|
||||
nlohmann::json nested(const std::string& key);
|
||||
static std::string formatNestedKey(const std::string& key);
|
||||
std::string getString(const std::string& key, const std::string& def = "");
|
||||
float getFloat(const std::string& key, float defValue = 0.0f);
|
||||
bool getBool(const std::string& key, bool defValue = false);
|
||||
int getInt(const std::string& key, int defValue = 0);
|
||||
bool contains(const std::string& key);
|
||||
template< typename T > std::vector<T> getArray(const std::string& key);
|
||||
void setString(const std::string& key, const std::string& value);
|
||||
void setFloat(const std::string& key, float value);
|
||||
void setBool(const std::string& key, bool value);
|
||||
void setInt(const std::string& key, int value);
|
||||
void setUInt(const std::string& key, uint32_t value);
|
||||
void erase(const std::string& key);
|
||||
void set(const std::string& key, std::any value);
|
||||
template< typename T > void setArray(const std::string& key, std::vector<T> array);
|
||||
|
||||
void reload();
|
||||
void save() const;
|
||||
bool isNewInstance = false;
|
||||
};
|
||||
|
||||
template< typename T >
|
||||
std::vector<T> Mercury::getArray(const std::string& key) {
|
||||
if (nlohmann::json tmp = this->nested(key); tmp.is_array())
|
||||
return tmp.get<std::vector<T>>();
|
||||
return std::vector<T>();
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void Mercury::setArray(const std::string& key, std::vector<T> array) {
|
||||
this->vjson[formatNestedKey(key)] = nlohmann::json(array);
|
||||
}
|
@ -1,103 +1,36 @@
|
||||
#include "SDLController.h"
|
||||
|
||||
#include "GameSettings.h"
|
||||
#include "GlobalCtx2.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "stox.h"
|
||||
#include "Window.h"
|
||||
#include "Cvar.h"
|
||||
#include <Utils/StringHelper.h>
|
||||
|
||||
extern "C" uint8_t __osMaxControllers;
|
||||
|
||||
namespace Ship {
|
||||
SDLController::SDLController(int32_t dwControllerNumber) : Controller(dwControllerNumber), Cont(nullptr), guid(INVALID_SDL_CONTROLLER_GUID) {
|
||||
|
||||
}
|
||||
|
||||
SDLController::~SDLController() {
|
||||
Close();
|
||||
}
|
||||
|
||||
bool SDLController::IsGuidInUse(const std::string& guid) {
|
||||
// Check if the GUID is loaded in any other controller;
|
||||
for (size_t i = 0; i < __osMaxControllers; i++) {
|
||||
for (size_t j = 0; j < Window::Controllers[i].size(); j++) {
|
||||
SDLController* OtherCont = dynamic_cast<SDLController*>(Window::Controllers[i][j].get());
|
||||
|
||||
if (OtherCont != nullptr && OtherCont->GetGuid().compare(guid) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SDLController::Open() {
|
||||
std::string ConfSection = GetConfSection();
|
||||
std::shared_ptr<ConfigFile> pConf = GlobalCtx2::GetInstance()->GetConfig();
|
||||
ConfigFile& Conf = *pConf.get();
|
||||
|
||||
for (int i = 0; i < SDL_NumJoysticks(); i++) {
|
||||
if (SDL_IsGameController(i)) {
|
||||
// Get the GUID from SDL
|
||||
char GuidBuf[33];
|
||||
SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(i), GuidBuf, sizeof(GuidBuf));
|
||||
auto NewGuid = std::string(GuidBuf);
|
||||
const auto NewCont = SDL_GameControllerOpen(physicalSlot);
|
||||
|
||||
// Invalid GUID read. Go to next.
|
||||
if (NewGuid.compare(INVALID_SDL_CONTROLLER_GUID) == 0) {
|
||||
SPDLOG_ERROR("SDL Controller returned invalid guid");
|
||||
continue;
|
||||
}
|
||||
|
||||
// The GUID is in use, we want to use a different physical controller. Go to next.
|
||||
if (IsGuidInUse(NewGuid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the GUID is blank from the config, OR if the config GUID matches, load the controller.
|
||||
if (Conf[ConfSection]["GUID"].compare("") == 0 || Conf[ConfSection]["GUID"].compare(INVALID_SDL_CONTROLLER_GUID) == 0 || Conf[ConfSection]["GUID"].compare(NewGuid) == 0) {
|
||||
auto NewCont = SDL_GameControllerOpen(i);
|
||||
|
||||
// We failed to load the controller. Go to next.
|
||||
if (NewCont == nullptr) {
|
||||
SPDLOG_ERROR("SDL Controller failed to open: ({})", SDL_GetError());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (SDL_GameControllerHasSensor(NewCont, SDL_SENSOR_GYRO))
|
||||
{
|
||||
SDL_GameControllerSetSensorEnabled(NewCont, SDL_SENSOR_GYRO, SDL_TRUE);
|
||||
}
|
||||
|
||||
guid = NewGuid;
|
||||
Cont = NewCont;
|
||||
|
||||
std::string BindingConfSection = GetBindingConfSection();
|
||||
std::string PadConfSection = *GetPadConfSection();
|
||||
std::shared_ptr<ConfigFile> config = GlobalCtx2::GetInstance()->GetConfig();
|
||||
|
||||
if (!config->has(BindingConfSection)) {
|
||||
CreateDefaultBinding();
|
||||
}
|
||||
|
||||
if (!config->has(PadConfSection)) {
|
||||
CreateDefaultPadConf();
|
||||
}
|
||||
|
||||
LoadBinding();
|
||||
LoadAxisThresholds();
|
||||
// Update per-controller settings in ImGui menu after opening controller.
|
||||
Game::LoadPadSettings();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
// We failed to load the controller. Go to next.
|
||||
if (NewCont == nullptr) {
|
||||
SPDLOG_ERROR("SDL Controller failed to open: ({})", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return Cont != nullptr;
|
||||
if (SDL_GameControllerHasSensor(NewCont, SDL_SENSOR_GYRO)) {
|
||||
SDL_GameControllerSetSensorEnabled(NewCont, SDL_SENSOR_GYRO, SDL_TRUE);
|
||||
supportsGyro = true;
|
||||
}
|
||||
|
||||
char GuidBuf[33];
|
||||
SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(physicalSlot), GuidBuf, sizeof(GuidBuf));
|
||||
GUID = std::string(GuidBuf);
|
||||
Cont = NewCont;
|
||||
wCamX = 0;
|
||||
wCamY = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SDLController::Close() {
|
||||
@ -108,31 +41,14 @@ namespace Ship {
|
||||
SDL_GameControllerClose(Cont);
|
||||
}
|
||||
Cont = nullptr;
|
||||
guid = "";
|
||||
ButtonMapping.clear();
|
||||
ThresholdMapping.clear();
|
||||
dwPressedButtons = 0;
|
||||
wStickX = 0;
|
||||
wStickY = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SDLController::LoadAxisThresholds() {
|
||||
std::string ConfSection = GetBindingConfSection();
|
||||
std::shared_ptr<ConfigFile> pConf = GlobalCtx2::GetInstance()->GetConfig();
|
||||
ConfigFile& Conf = *pConf.get();
|
||||
|
||||
ThresholdMapping[SDL_CONTROLLER_AXIS_LEFTX] = Ship::stoi(Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_LEFTX) + "_threshold"]);
|
||||
ThresholdMapping[SDL_CONTROLLER_AXIS_LEFTY] = Ship::stoi(Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_LEFTY) + "_threshold"]);
|
||||
ThresholdMapping[SDL_CONTROLLER_AXIS_RIGHTX] = Ship::stoi(Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_RIGHTX) + "_threshold"]);
|
||||
ThresholdMapping[SDL_CONTROLLER_AXIS_RIGHTY] = Ship::stoi(Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_RIGHTY) + "_threshold"]);
|
||||
ThresholdMapping[SDL_CONTROLLER_AXIS_TRIGGERLEFT] = Ship::stoi(Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_TRIGGERLEFT) + "_threshold"]);
|
||||
ThresholdMapping[SDL_CONTROLLER_AXIS_TRIGGERRIGHT] = Ship::stoi(Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_TRIGGERRIGHT) + "_threshold"]);
|
||||
}
|
||||
|
||||
|
||||
void SDLController::NormalizeStickAxis(int16_t wAxisValueX, int16_t wAxisValueY, int16_t wAxisThreshold, bool isRightStick) {
|
||||
void SDLController::NormalizeStickAxis(int16_t wAxisValueX, int16_t wAxisValueY, int16_t wAxisThreshold, bool isRightStick, float sensitivity) {
|
||||
//scale {-32768 ... +32767} to {-84 ... +84}
|
||||
auto ax = wAxisValueX * 85.0 / 32767.0;
|
||||
auto ay = wAxisValueY * 85.0 / 32767.0;
|
||||
@ -166,18 +82,56 @@ namespace Ship {
|
||||
if (!isRightStick) {
|
||||
wStickX = +ax;
|
||||
wStickY = -ay;
|
||||
}
|
||||
else {
|
||||
//SOHTODO KIRITO: Camera Sensitivity
|
||||
wCamX = +ax * 15.0f;
|
||||
wCamY = -ay * 15.0f;
|
||||
} else {
|
||||
wCamX = +ax * sensitivity;
|
||||
wCamY = -ay * sensitivity;
|
||||
}
|
||||
}
|
||||
|
||||
void SDLController::ReadFromSource() {
|
||||
std::string ConfSection = GetBindingConfSection();
|
||||
std::shared_ptr<ConfigFile> pConf = GlobalCtx2::GetInstance()->GetConfig();
|
||||
ConfigFile& Conf = *pConf.get();
|
||||
|
||||
int32_t SDLController::ReadRawPress() {
|
||||
SDL_GameControllerUpdate();
|
||||
|
||||
for (int32_t i = SDL_CONTROLLER_BUTTON_A; i < SDL_CONTROLLER_BUTTON_MAX; i++) {
|
||||
if (SDL_GameControllerGetButton(Cont, static_cast<SDL_GameControllerButton>(i))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++) {
|
||||
const auto Axis = static_cast<SDL_GameControllerAxis>(i);
|
||||
const auto AxisValue = SDL_GameControllerGetAxis(Cont, Axis) / 32767;
|
||||
|
||||
if(AxisValue < 0) {
|
||||
return -(Axis + AXIS_SCANCODE_BIT);
|
||||
}
|
||||
|
||||
if (AxisValue > 0) {
|
||||
return (Axis + AXIS_SCANCODE_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
ControllerThresholds SDLAxisToThreshold( uint32_t axis ){
|
||||
switch(axis){
|
||||
case SDL_CONTROLLER_AXIS_LEFTX:
|
||||
case SDL_CONTROLLER_AXIS_LEFTY:
|
||||
return LEFT_STICK;
|
||||
case SDL_CONTROLLER_AXIS_RIGHTX:
|
||||
case SDL_CONTROLLER_AXIS_RIGHTY:
|
||||
return RIGHT_STICK;
|
||||
case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
|
||||
return LEFT_TRIGGER;
|
||||
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
|
||||
return RIGHT_TRIGGER;
|
||||
default: return DRIFT_X;
|
||||
}
|
||||
}
|
||||
|
||||
void SDLController::ReadFromSource(int32_t slot) {
|
||||
DeviceProfile& profile = profiles[slot];
|
||||
|
||||
SDL_GameControllerUpdate();
|
||||
|
||||
@ -194,21 +148,14 @@ namespace Ship {
|
||||
}
|
||||
}
|
||||
|
||||
auto cameraX = SDL_GameControllerGetAxis(Cont, SDL_CONTROLLER_AXIS_RIGHTX);
|
||||
auto cameraY = SDL_GameControllerGetAxis(Cont, SDL_CONTROLLER_AXIS_RIGHTY);
|
||||
NormalizeStickAxis(cameraX, cameraY, ThresholdMapping[SDL_CONTROLLER_AXIS_LEFTX], true);
|
||||
|
||||
if (SDL_GameControllerHasSensor(Cont, SDL_SENSOR_GYRO))
|
||||
{
|
||||
size_t contNumber = GetControllerNumber();
|
||||
if (supportsGyro && profile.UseGyro) {
|
||||
|
||||
float gyroData[3];
|
||||
SDL_GameControllerGetSensorData(Cont, SDL_SENSOR_GYRO, gyroData, 3);
|
||||
|
||||
const char* contName = SDL_GameControllerName(Cont);
|
||||
float gyro_drift_x = CVar_GetFloat(StringHelper::Sprintf("gCont%i_GyroDriftX", contNumber).c_str(), 0.0f);
|
||||
float gyro_drift_y = CVar_GetFloat(StringHelper::Sprintf("gCont%i_GyroDriftY", contNumber).c_str(), 0.0f);
|
||||
const float gyro_sensitivity = CVar_GetFloat(StringHelper::Sprintf("gCont%i_GyroSensitivity", contNumber).c_str(), 1.0f);
|
||||
float gyro_drift_x = profile.GyroThresholds[DRIFT_X] / 100.0f;
|
||||
float gyro_drift_y = profile.GyroThresholds[DRIFT_Y] / 100.0f;
|
||||
const float gyro_sensitivity = profile.GyroThresholds[SENSITIVITY] / 100.0f;
|
||||
|
||||
if (gyro_drift_x == 0) {
|
||||
gyro_drift_x = gyroData[0];
|
||||
@ -218,8 +165,8 @@ namespace Ship {
|
||||
gyro_drift_y = gyroData[1];
|
||||
}
|
||||
|
||||
CVar_SetFloat(StringHelper::Sprintf("gCont%i_GyroDriftX", contNumber).c_str(), gyro_drift_x);
|
||||
CVar_SetFloat(StringHelper::Sprintf("gCont%i_GyroDriftY", contNumber).c_str(), gyro_drift_y);
|
||||
profile.GyroThresholds[DRIFT_X] = (int) gyro_drift_x * 100;
|
||||
profile.GyroThresholds[DRIFT_Y] = (int) gyro_drift_y * 100;
|
||||
|
||||
wGyroX = gyroData[0] - gyro_drift_x;
|
||||
wGyroY = gyroData[1] - gyro_drift_y;
|
||||
@ -229,28 +176,32 @@ namespace Ship {
|
||||
}
|
||||
|
||||
for (int32_t i = SDL_CONTROLLER_BUTTON_A; i < SDL_CONTROLLER_BUTTON_MAX; i++) {
|
||||
if (ButtonMapping.contains(i)) {
|
||||
if (SDL_GameControllerGetButton(Cont, (SDL_GameControllerButton)i)) {
|
||||
dwPressedButtons |= ButtonMapping[i];
|
||||
if (profile.Mappings.contains(i)) {
|
||||
if (SDL_GameControllerGetButton(Cont, static_cast<SDL_GameControllerButton>(i))) {
|
||||
dwPressedButtons[slot] |= profile.Mappings[i];
|
||||
}
|
||||
else {
|
||||
dwPressedButtons &= ~ButtonMapping[i];
|
||||
dwPressedButtons[slot] &= ~profile.Mappings[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SDL_GameControllerAxis StickAxisX = SDL_CONTROLLER_AXIS_INVALID;
|
||||
SDL_GameControllerAxis StickAxisY = SDL_CONTROLLER_AXIS_INVALID;
|
||||
int32_t StickDeadzone = 0;
|
||||
SDL_GameControllerAxis LStickAxisX = SDL_CONTROLLER_AXIS_INVALID;
|
||||
SDL_GameControllerAxis LStickAxisY = SDL_CONTROLLER_AXIS_INVALID;
|
||||
int32_t LStickDeadzone = 0;
|
||||
|
||||
SDL_GameControllerAxis RStickAxisX = SDL_CONTROLLER_AXIS_INVALID;
|
||||
SDL_GameControllerAxis RStickAxisY = SDL_CONTROLLER_AXIS_INVALID;
|
||||
int32_t RStickDeadzone = 0;
|
||||
|
||||
for (int32_t i = SDL_CONTROLLER_AXIS_LEFTX; i < SDL_CONTROLLER_AXIS_MAX; i++) {
|
||||
auto Axis = (SDL_GameControllerAxis)i;
|
||||
auto PosScancode = i + AXIS_SCANCODE_BIT;
|
||||
auto NegScancode = -PosScancode;
|
||||
auto AxisThreshold = ThresholdMapping[i];
|
||||
auto PosButton = ButtonMapping[PosScancode];
|
||||
auto NegButton = ButtonMapping[NegScancode];
|
||||
auto AxisValue = SDL_GameControllerGetAxis(Cont, Axis);
|
||||
const auto Axis = static_cast<SDL_GameControllerAxis>(i);
|
||||
const auto PosScancode = i + AXIS_SCANCODE_BIT;
|
||||
const auto NegScancode = -PosScancode;
|
||||
const auto AxisThreshold = profile.Thresholds[SDLAxisToThreshold(i)];
|
||||
const auto PosButton = profile.Mappings[PosScancode];
|
||||
const auto NegButton = profile.Mappings[NegScancode];
|
||||
const auto AxisValue = SDL_GameControllerGetAxis(Cont, Axis);
|
||||
|
||||
#ifdef TARGET_WEB
|
||||
// Firefox has a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1606562
|
||||
@ -263,94 +214,176 @@ namespace Ship {
|
||||
}
|
||||
#endif
|
||||
|
||||
// If the axis is NOT mapped to the control stick.
|
||||
if (!(
|
||||
PosButton == BTN_STICKLEFT || PosButton == BTN_STICKRIGHT ||
|
||||
PosButton == BTN_STICKUP || PosButton == BTN_STICKDOWN ||
|
||||
NegButton == BTN_STICKLEFT || NegButton == BTN_STICKRIGHT ||
|
||||
NegButton == BTN_STICKUP || NegButton == BTN_STICKDOWN)) {
|
||||
if (AxisValue > AxisThreshold) {
|
||||
dwPressedButtons |= PosButton;
|
||||
dwPressedButtons &= ~NegButton;
|
||||
|
||||
if (AxisValue > 0x1E00) {
|
||||
dwPressedButtons[slot] |= PosButton;
|
||||
dwPressedButtons[slot] &= ~NegButton;
|
||||
}
|
||||
else if (AxisValue < -AxisThreshold) {
|
||||
dwPressedButtons &= ~PosButton;
|
||||
dwPressedButtons |= NegButton;
|
||||
else if (AxisValue < -0x1E00) {
|
||||
dwPressedButtons[slot] &= ~PosButton;
|
||||
dwPressedButtons[slot] |= NegButton;
|
||||
}
|
||||
else {
|
||||
dwPressedButtons &= ~PosButton;
|
||||
dwPressedButtons &= ~NegButton;
|
||||
dwPressedButtons[slot] &= ~PosButton;
|
||||
dwPressedButtons[slot] &= ~NegButton;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (PosButton == BTN_STICKLEFT || PosButton == BTN_STICKRIGHT) {
|
||||
if (StickAxisX != SDL_CONTROLLER_AXIS_INVALID && StickAxisX != Axis) {
|
||||
SPDLOG_TRACE("Invalid PosStickX configured. Neg was {} and Pos is {}", StickAxisX, Axis);
|
||||
if (LStickAxisX != SDL_CONTROLLER_AXIS_INVALID && LStickAxisX != Axis) {
|
||||
SPDLOG_TRACE("Invalid PosStickX configured. Neg was {} and Pos is {}", LStickAxisX, Axis);
|
||||
}
|
||||
|
||||
if (StickDeadzone != 0 && StickDeadzone != AxisThreshold) {
|
||||
SPDLOG_TRACE("Invalid Deadzone configured. Up/Down was {} and Left/Right is {}", StickDeadzone, AxisThreshold);
|
||||
if (LStickDeadzone != 0 && LStickDeadzone != AxisThreshold) {
|
||||
SPDLOG_TRACE("Invalid Deadzone configured. Up/Down was {} and Left/Right is {}", LStickDeadzone, AxisThreshold);
|
||||
}
|
||||
|
||||
StickDeadzone = AxisThreshold;
|
||||
StickAxisX = Axis;
|
||||
LStickDeadzone = AxisThreshold;
|
||||
LStickAxisX = Axis;
|
||||
}
|
||||
|
||||
if (PosButton == BTN_STICKUP || PosButton == BTN_STICKDOWN) {
|
||||
if (StickAxisY != SDL_CONTROLLER_AXIS_INVALID && StickAxisY != Axis) {
|
||||
SPDLOG_TRACE("Invalid PosStickY configured. Neg was {} and Pos is {}", StickAxisY, Axis);
|
||||
if (LStickAxisY != SDL_CONTROLLER_AXIS_INVALID && LStickAxisY != Axis) {
|
||||
SPDLOG_TRACE("Invalid PosStickY configured. Neg was {} and Pos is {}", LStickAxisY, Axis);
|
||||
}
|
||||
|
||||
if (StickDeadzone != 0 && StickDeadzone != AxisThreshold) {
|
||||
SPDLOG_TRACE("Invalid Deadzone configured. Left/Right was {} and Up/Down is {}", StickDeadzone, AxisThreshold);
|
||||
if (LStickDeadzone != 0 && LStickDeadzone != AxisThreshold) {
|
||||
SPDLOG_TRACE("Invalid Deadzone configured. Left/Right was {} and Up/Down is {}", LStickDeadzone, AxisThreshold);
|
||||
}
|
||||
|
||||
StickDeadzone = AxisThreshold;
|
||||
StickAxisY = Axis;
|
||||
LStickDeadzone = AxisThreshold;
|
||||
LStickAxisY = Axis;
|
||||
}
|
||||
|
||||
if (NegButton == BTN_STICKLEFT || NegButton == BTN_STICKRIGHT) {
|
||||
if (StickAxisX != SDL_CONTROLLER_AXIS_INVALID && StickAxisX != Axis) {
|
||||
SPDLOG_TRACE("Invalid NegStickX configured. Pos was {} and Neg is {}", StickAxisX, Axis);
|
||||
if (LStickAxisX != SDL_CONTROLLER_AXIS_INVALID && LStickAxisX != Axis) {
|
||||
SPDLOG_TRACE("Invalid NegStickX configured. Pos was {} and Neg is {}", LStickAxisX, Axis);
|
||||
}
|
||||
|
||||
if (StickDeadzone != 0 && StickDeadzone != AxisThreshold) {
|
||||
SPDLOG_TRACE("Invalid Deadzone configured. Left/Right was {} and Up/Down is {}", StickDeadzone, AxisThreshold);
|
||||
if (LStickDeadzone != 0 && LStickDeadzone != AxisThreshold) {
|
||||
SPDLOG_TRACE("Invalid Deadzone configured. Left/Right was {} and Up/Down is {}", LStickDeadzone, AxisThreshold);
|
||||
}
|
||||
|
||||
StickDeadzone = AxisThreshold;
|
||||
StickAxisX = Axis;
|
||||
LStickDeadzone = AxisThreshold;
|
||||
LStickAxisX = Axis;
|
||||
}
|
||||
|
||||
if (NegButton == BTN_STICKUP || NegButton == BTN_STICKDOWN) {
|
||||
if (StickAxisY != SDL_CONTROLLER_AXIS_INVALID && StickAxisY != Axis) {
|
||||
SPDLOG_TRACE("Invalid NegStickY configured. Pos was {} and Neg is {}", StickAxisY, Axis);
|
||||
if (LStickAxisY != SDL_CONTROLLER_AXIS_INVALID && LStickAxisY != Axis) {
|
||||
SPDLOG_TRACE("Invalid NegStickY configured. Pos was {} and Neg is {}", LStickAxisY, Axis);
|
||||
}
|
||||
|
||||
if (StickDeadzone != 0 && StickDeadzone != AxisThreshold) {
|
||||
SPDLOG_TRACE("Invalid Deadzone misconfigured. Left/Right was {} and Up/Down is {}", StickDeadzone, AxisThreshold);
|
||||
if (LStickDeadzone != 0 && LStickDeadzone != AxisThreshold) {
|
||||
SPDLOG_TRACE("Invalid Deadzone misconfigured. Left/Right was {} and Up/Down is {}", LStickDeadzone, AxisThreshold);
|
||||
}
|
||||
|
||||
StickDeadzone = AxisThreshold;
|
||||
StickAxisY = Axis;
|
||||
LStickDeadzone = AxisThreshold;
|
||||
LStickAxisY = Axis;
|
||||
}
|
||||
}
|
||||
|
||||
if (StickAxisX != SDL_CONTROLLER_AXIS_INVALID && StickAxisY != SDL_CONTROLLER_AXIS_INVALID) {
|
||||
auto AxisValueX = SDL_GameControllerGetAxis(Cont, StickAxisX);
|
||||
auto AxisValueY = SDL_GameControllerGetAxis(Cont, StickAxisY);
|
||||
NormalizeStickAxis(AxisValueX, AxisValueY, StickDeadzone, false);
|
||||
if (LStickAxisX != SDL_CONTROLLER_AXIS_INVALID && LStickAxisY != SDL_CONTROLLER_AXIS_INVALID) {
|
||||
const auto AxisValueX = SDL_GameControllerGetAxis(Cont, LStickAxisX);
|
||||
const auto AxisValueY = SDL_GameControllerGetAxis(Cont, LStickAxisY);
|
||||
NormalizeStickAxis(AxisValueX, AxisValueY, LStickDeadzone, false, profile.Thresholds[SENSITIVITY]);
|
||||
}
|
||||
|
||||
// Right Stick
|
||||
// If the axis is NOT mapped to the control stick.
|
||||
if (!(
|
||||
PosButton == BTN_VSTICKLEFT || PosButton == BTN_VSTICKRIGHT ||
|
||||
PosButton == BTN_VSTICKUP || PosButton == BTN_VSTICKDOWN ||
|
||||
NegButton == BTN_VSTICKLEFT || NegButton == BTN_VSTICKRIGHT ||
|
||||
NegButton == BTN_VSTICKUP || NegButton == BTN_VSTICKDOWN)) {
|
||||
|
||||
if (AxisValue > 0x1E00) {
|
||||
dwPressedButtons[slot] |= PosButton;
|
||||
dwPressedButtons[slot] &= ~NegButton;
|
||||
}
|
||||
else if (AxisValue < -0x1E00) {
|
||||
dwPressedButtons[slot] &= ~PosButton;
|
||||
dwPressedButtons[slot] |= NegButton;
|
||||
}
|
||||
else {
|
||||
dwPressedButtons[slot] &= ~PosButton;
|
||||
dwPressedButtons[slot] &= ~NegButton;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (PosButton == BTN_VSTICKLEFT || PosButton == BTN_VSTICKRIGHT) {
|
||||
if (RStickAxisX != SDL_CONTROLLER_AXIS_INVALID && RStickAxisX != Axis) {
|
||||
SPDLOG_TRACE("Invalid PosStickX configured. Neg was {} and Pos is {}", RStickAxisX, Axis);
|
||||
}
|
||||
|
||||
if (RStickDeadzone != 0 && RStickDeadzone != AxisThreshold) {
|
||||
SPDLOG_TRACE("Invalid Deadzone configured. Up/Down was {} and Left/Right is {}", RStickDeadzone, AxisThreshold);
|
||||
}
|
||||
|
||||
RStickDeadzone = AxisThreshold;
|
||||
RStickAxisX = Axis;
|
||||
}
|
||||
|
||||
if (PosButton == BTN_VSTICKUP || PosButton == BTN_VSTICKDOWN) {
|
||||
if (RStickAxisY != SDL_CONTROLLER_AXIS_INVALID && RStickAxisY != Axis) {
|
||||
SPDLOG_TRACE("Invalid PosStickY configured. Neg was {} and Pos is {}", RStickAxisY, Axis);
|
||||
}
|
||||
|
||||
if (RStickDeadzone != 0 && RStickDeadzone != AxisThreshold) {
|
||||
SPDLOG_TRACE("Invalid Deadzone configured. Left/Right was {} and Up/Down is {}", RStickDeadzone, AxisThreshold);
|
||||
}
|
||||
|
||||
RStickDeadzone = AxisThreshold;
|
||||
RStickAxisY = Axis;
|
||||
}
|
||||
|
||||
if (NegButton == BTN_VSTICKLEFT || NegButton == BTN_VSTICKRIGHT) {
|
||||
if (RStickAxisX != SDL_CONTROLLER_AXIS_INVALID && RStickAxisX != Axis) {
|
||||
SPDLOG_TRACE("Invalid NegStickX configured. Pos was {} and Neg is {}", RStickAxisX, Axis);
|
||||
}
|
||||
|
||||
if (RStickDeadzone != 0 && RStickDeadzone != AxisThreshold) {
|
||||
SPDLOG_TRACE("Invalid Deadzone configured. Left/Right was {} and Up/Down is {}", RStickDeadzone, AxisThreshold);
|
||||
}
|
||||
|
||||
RStickDeadzone = AxisThreshold;
|
||||
RStickAxisX = Axis;
|
||||
}
|
||||
|
||||
if (NegButton == BTN_VSTICKUP || NegButton == BTN_VSTICKDOWN) {
|
||||
if (RStickAxisY != SDL_CONTROLLER_AXIS_INVALID && RStickAxisY != Axis) {
|
||||
SPDLOG_TRACE("Invalid NegStickY configured. Pos was {} and Neg is {}", RStickAxisY, Axis);
|
||||
}
|
||||
|
||||
if (RStickDeadzone != 0 && RStickDeadzone != AxisThreshold) {
|
||||
SPDLOG_TRACE("Invalid Deadzone misconfigured. Left/Right was {} and Up/Down is {}", RStickDeadzone, AxisThreshold);
|
||||
}
|
||||
|
||||
RStickDeadzone = AxisThreshold;
|
||||
RStickAxisY = Axis;
|
||||
}
|
||||
}
|
||||
|
||||
if (RStickAxisX != SDL_CONTROLLER_AXIS_INVALID && RStickAxisY != SDL_CONTROLLER_AXIS_INVALID) {
|
||||
const auto AxisValueX = SDL_GameControllerGetAxis(Cont, RStickAxisX);
|
||||
const auto AxisValueY = SDL_GameControllerGetAxis(Cont, RStickAxisY);
|
||||
NormalizeStickAxis(AxisValueX, AxisValueY, RStickDeadzone, true, profile.Thresholds[SENSITIVITY]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SDLController::WriteToSource(ControllerCallback* controller)
|
||||
void SDLController::WriteToSource(int32_t slot, ControllerCallback* controller)
|
||||
{
|
||||
if (CanRumble()) {
|
||||
if (CanRumble() && profiles[slot].UseRumble) {
|
||||
if (controller->rumble > 0) {
|
||||
float rumble_strength = CVar_GetFloat(StringHelper::Sprintf("gCont%i_RumbleStrength", GetControllerNumber()).c_str(), 1.0f);
|
||||
float rumble_strength = profiles[slot].RumbleStrength;
|
||||
SDL_GameControllerRumble(Cont, 0xFFFF * rumble_strength, 0xFFFF * rumble_strength, 0);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
SDL_GameControllerRumble(Cont, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
@ -373,74 +406,71 @@ namespace Ship {
|
||||
}
|
||||
}
|
||||
|
||||
void SDLController::CreateDefaultBinding() {
|
||||
std::string ConfSection = GetBindingConfSection();
|
||||
std::shared_ptr<ConfigFile> pConf = GlobalCtx2::GetInstance()->GetConfig();
|
||||
const char* AxisNames[] = {
|
||||
"Left Stick X",
|
||||
"Left Stick Y",
|
||||
"Right Stick X",
|
||||
"Right Stick Y",
|
||||
"Left Trigger",
|
||||
"Right Trigger",
|
||||
"Start Button"
|
||||
};
|
||||
|
||||
ConfigFile& Conf = *pConf.get();
|
||||
char buffer[50];
|
||||
const char* SDLController::GetButtonName(int slot, int n64Button) {
|
||||
std::map<int32_t, int32_t>& Mappings = profiles[slot].Mappings;
|
||||
const auto find = std::find_if(Mappings.begin(), Mappings.end(), [n64Button](const std::pair<int32_t, int32_t>& pair) {
|
||||
return pair.second == n64Button;
|
||||
});
|
||||
|
||||
Conf[ConfSection][STR(BTN_CRIGHT)] = std::to_string(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
|
||||
Conf[ConfSection][STR(BTN_CLEFT)] = std::to_string(SDL_CONTROLLER_BUTTON_Y);
|
||||
Conf[ConfSection][STR(BTN_CDOWN)] = std::to_string(SDL_CONTROLLER_BUTTON_X);
|
||||
Conf[ConfSection][STR(BTN_CUP)] = std::to_string(SDL_CONTROLLER_BUTTON_RIGHTSTICK);
|
||||
//Conf[ConfSection][STR(BTN_CRIGHT + "_2")] = std::to_string(SDL_CONTROLLER_BUTTON_X);
|
||||
//Conf[ConfSection][STR(BTN_CLEFT + "_2")] = std::to_string(SDL_CONTROLLER_BUTTON_Y);
|
||||
//Conf[ConfSection][STR(BTN_CDOWN + "_2")] = std::to_string(SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
|
||||
//Conf[ConfSection][STR(BTN_CUP + "_2")] = std::to_string(SDL_CONTROLLER_BUTTON_RIGHTSTICK);
|
||||
Conf[ConfSection][STR(BTN_R)] = std::to_string((SDL_CONTROLLER_AXIS_TRIGGERRIGHT + AXIS_SCANCODE_BIT));
|
||||
Conf[ConfSection][STR(BTN_L)] = std::to_string(SDL_CONTROLLER_BUTTON_LEFTSHOULDER);
|
||||
Conf[ConfSection][STR(BTN_DRIGHT)] = std::to_string(SDL_CONTROLLER_BUTTON_DPAD_RIGHT);
|
||||
Conf[ConfSection][STR(BTN_DLEFT)] = std::to_string(SDL_CONTROLLER_BUTTON_DPAD_LEFT);
|
||||
Conf[ConfSection][STR(BTN_DDOWN)] = std::to_string(SDL_CONTROLLER_BUTTON_DPAD_DOWN);
|
||||
Conf[ConfSection][STR(BTN_DUP)] = std::to_string(SDL_CONTROLLER_BUTTON_DPAD_UP);
|
||||
Conf[ConfSection][STR(BTN_START)] = std::to_string(SDL_CONTROLLER_BUTTON_START);
|
||||
Conf[ConfSection][STR(BTN_Z)] = std::to_string((SDL_CONTROLLER_AXIS_TRIGGERLEFT + AXIS_SCANCODE_BIT));
|
||||
Conf[ConfSection][STR(BTN_B)] = std::to_string(SDL_CONTROLLER_BUTTON_B);
|
||||
Conf[ConfSection][STR(BTN_A)] = std::to_string(SDL_CONTROLLER_BUTTON_A);
|
||||
Conf[ConfSection][STR(BTN_STICKRIGHT)] = std::to_string((SDL_CONTROLLER_AXIS_LEFTX + AXIS_SCANCODE_BIT));
|
||||
Conf[ConfSection][STR(BTN_STICKLEFT)] = std::to_string(-(SDL_CONTROLLER_AXIS_LEFTX + AXIS_SCANCODE_BIT));
|
||||
Conf[ConfSection][STR(BTN_STICKDOWN)] = std::to_string((SDL_CONTROLLER_AXIS_LEFTY + AXIS_SCANCODE_BIT));
|
||||
Conf[ConfSection][STR(BTN_STICKUP)] = std::to_string(-(SDL_CONTROLLER_AXIS_LEFTY + AXIS_SCANCODE_BIT));
|
||||
if (find == Mappings.end()) return "Unknown";
|
||||
|
||||
Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_LEFTX) + "_threshold"] = std::to_string(16.0);
|
||||
Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_LEFTY) + "_threshold"] = std::to_string(16.0);
|
||||
Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_RIGHTX) + "_threshold"] = std::to_string(0x4000);
|
||||
Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_RIGHTY) + "_threshold"] = std::to_string(0x4000);
|
||||
Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_TRIGGERLEFT) + "_threshold"] = std::to_string(0x1E00);
|
||||
Conf[ConfSection][STR(SDL_CONTROLLER_AXIS_TRIGGERRIGHT) + "_threshold"] = std::to_string(0x1E00);
|
||||
int btn = abs(find->first);
|
||||
|
||||
Conf.Save();
|
||||
}
|
||||
if(btn >= AXIS_SCANCODE_BIT) {
|
||||
btn -= AXIS_SCANCODE_BIT;
|
||||
|
||||
void SDLController::CreateDefaultPadConf() {
|
||||
std::string ConfSection = *GetPadConfSection();
|
||||
std::shared_ptr<ConfigFile> pConf = GlobalCtx2::GetInstance()->GetConfig();
|
||||
ConfigFile& Conf = *pConf.get();
|
||||
|
||||
Conf.Save();
|
||||
}
|
||||
|
||||
void SDLController::SetButtonMapping(const std::string& szButtonName, int32_t dwScancode) {
|
||||
if (guid.compare(INVALID_SDL_CONTROLLER_GUID)) {
|
||||
return;
|
||||
snprintf(buffer, sizeof(buffer), "%s%s", AxisNames[btn], find->first > 0 ? "+" : "-");
|
||||
return buffer;
|
||||
}
|
||||
|
||||
Controller::SetButtonMapping(szButtonName, dwScancode);
|
||||
snprintf(buffer, sizeof(buffer), "Button %d", btn);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
std::string SDLController::GetControllerType() {
|
||||
return "SDL";
|
||||
}
|
||||
|
||||
std::string SDLController::GetConfSection() {
|
||||
return GetControllerType() + " CONTROLLER " + std::to_string(GetControllerNumber() + 1);
|
||||
const char* SDLController::GetControllerName() {
|
||||
return SDL_GameControllerNameForIndex(physicalSlot);
|
||||
}
|
||||
|
||||
std::string SDLController::GetBindingConfSection() {
|
||||
return GetControllerType() + " CONTROLLER BINDING " + guid;
|
||||
}
|
||||
void SDLController::CreateDefaultBinding(int32_t slot) {
|
||||
DeviceProfile& profile = profiles[slot];
|
||||
profile.Mappings.clear();
|
||||
|
||||
std::optional<std::string> SDLController::GetPadConfSection() {
|
||||
return GetControllerType() + " CONTROLLER PAD " + guid;
|
||||
profile.UseRumble = true;
|
||||
profile.RumbleStrength = 1.0f;
|
||||
profile.UseGyro = false;
|
||||
profile.Mappings[ SDL_CONTROLLER_AXIS_RIGHTX | AXIS_SCANCODE_BIT] = BTN_CRIGHT;
|
||||
profile.Mappings[-(SDL_CONTROLLER_AXIS_RIGHTX | AXIS_SCANCODE_BIT)] = BTN_CLEFT;
|
||||
profile.Mappings[ SDL_CONTROLLER_AXIS_RIGHTY | AXIS_SCANCODE_BIT] = BTN_CDOWN;
|
||||
profile.Mappings[-(SDL_CONTROLLER_AXIS_RIGHTY | AXIS_SCANCODE_BIT)] = BTN_CUP;
|
||||
profile.Mappings[SDL_CONTROLLER_AXIS_TRIGGERRIGHT + AXIS_SCANCODE_BIT] = BTN_R;
|
||||
profile.Mappings[SDL_CONTROLLER_BUTTON_LEFTSHOULDER] = BTN_L;
|
||||
profile.Mappings[SDL_CONTROLLER_BUTTON_DPAD_RIGHT] = BTN_DRIGHT;
|
||||
profile.Mappings[SDL_CONTROLLER_BUTTON_DPAD_LEFT] = BTN_DLEFT;
|
||||
profile.Mappings[SDL_CONTROLLER_BUTTON_DPAD_DOWN] = BTN_DDOWN;
|
||||
profile.Mappings[SDL_CONTROLLER_BUTTON_DPAD_UP] = BTN_DUP;
|
||||
profile.Mappings[SDL_CONTROLLER_BUTTON_START] = BTN_START;
|
||||
profile.Mappings[SDL_CONTROLLER_AXIS_TRIGGERLEFT + AXIS_SCANCODE_BIT] = BTN_Z;
|
||||
profile.Mappings[SDL_CONTROLLER_BUTTON_B] = BTN_B;
|
||||
profile.Mappings[SDL_CONTROLLER_BUTTON_A] = BTN_A;
|
||||
profile.Mappings[(SDL_CONTROLLER_AXIS_LEFTX + AXIS_SCANCODE_BIT)] = BTN_STICKRIGHT;
|
||||
profile.Mappings[-(SDL_CONTROLLER_AXIS_LEFTX + AXIS_SCANCODE_BIT)] = BTN_STICKLEFT;
|
||||
profile.Mappings[SDL_CONTROLLER_AXIS_LEFTY + AXIS_SCANCODE_BIT] = BTN_STICKDOWN;
|
||||
profile.Mappings[-(SDL_CONTROLLER_AXIS_LEFTY + AXIS_SCANCODE_BIT)] = BTN_STICKUP;
|
||||
profile.Thresholds[LEFT_STICK] = 16.0;
|
||||
profile.Thresholds[RIGHT_STICK] = 16.0;
|
||||
profile.Thresholds[LEFT_TRIGGER] = 0x1E00;
|
||||
profile.Thresholds[RIGHT_TRIGGER] = 0x1E00;
|
||||
profile.Thresholds[SENSITIVITY] = 16.0;
|
||||
}
|
||||
}
|
||||
|
@ -6,46 +6,35 @@
|
||||
#include <SDL2/SDL.h>
|
||||
#endif
|
||||
|
||||
#define INVALID_SDL_CONTROLLER_GUID (std::string("00000000000000000000000000000000"))
|
||||
|
||||
namespace Ship {
|
||||
class SDLController : public Controller {
|
||||
public:
|
||||
SDLController(int32_t dwControllerNumber);
|
||||
~SDLController();
|
||||
|
||||
void ReadFromSource();
|
||||
void WriteToSource(ControllerCallback* controller);
|
||||
bool Connected() const { return Cont != nullptr; }
|
||||
bool CanRumble() const {
|
||||
SDLController(int slot) : Controller(), Cont(nullptr), physicalSlot(slot) { }
|
||||
void ReadFromSource(int32_t slot) override;
|
||||
const char* GetControllerName() override;
|
||||
const char* GetButtonName(int slot, int n64Button) override;
|
||||
void WriteToSource(int32_t slot, ControllerCallback* controller) override;
|
||||
bool Connected() const override { return Cont != nullptr; }
|
||||
bool CanGyro() const override { return supportsGyro; }
|
||||
bool CanRumble() const override {
|
||||
#if SDL_COMPILEDVERSION >= SDL_VERSIONNUM(2,0,18)
|
||||
return SDL_GameControllerHasRumble(Cont);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string GetGuid() { return guid; };
|
||||
|
||||
bool HasPadConf() const { return true; }
|
||||
std::optional<std::string> GetPadConfSection();
|
||||
bool Open();
|
||||
void ClearRawPress() override {}
|
||||
int32_t ReadRawPress() override;
|
||||
|
||||
protected:
|
||||
std::string GetControllerType();
|
||||
void SetButtonMapping(const std::string& szButtonName, int32_t dwScancode);
|
||||
std::string GetConfSection();
|
||||
std::string GetBindingConfSection();
|
||||
void CreateDefaultBinding();
|
||||
void CreateDefaultPadConf();
|
||||
static bool IsGuidInUse(const std::string& guid);
|
||||
void CreateDefaultBinding(int32_t slot) override;
|
||||
|
||||
private:
|
||||
SDL_GameController* Cont;
|
||||
std::string guid;
|
||||
std::map<int32_t, int16_t> ThresholdMapping;
|
||||
|
||||
void LoadAxisThresholds();
|
||||
void NormalizeStickAxis(int16_t wAxisValueX, int16_t wAxisValueY, int16_t wAxisThreshold, bool isRightStick);
|
||||
bool Open();
|
||||
int physicalSlot;
|
||||
bool supportsGyro;
|
||||
void NormalizeStickAxis(int16_t wAxisValueX, int16_t wAxisValueY, int16_t wAxisThreshold, bool isRightStick, float sensitivity);
|
||||
bool Close();
|
||||
};
|
||||
}
|
||||
|
@ -101,6 +101,10 @@
|
||||
#define BTN_STICKRIGHT 0x20000
|
||||
#define BTN_STICKDOWN 0x40000
|
||||
#define BTN_STICKUP 0x80000
|
||||
#define BTN_VSTICKUP 0x100000
|
||||
#define BTN_VSTICKDOWN 0x200000
|
||||
#define BTN_VSTICKLEFT 0x400000
|
||||
#define BTN_VSTICKRIGHT 0x800000
|
||||
|
||||
typedef struct {
|
||||
/* 0x00 */ int32_t ram[15];
|
||||
|
@ -38,8 +38,7 @@ extern "C" {
|
||||
uint8_t __enableGameInput = 1;
|
||||
|
||||
int32_t osContInit(OSMesgQueue* mq, uint8_t* controllerBits, OSContStatus* status) {
|
||||
std::shared_ptr<Ship::ConfigFile> pConf = Ship::GlobalCtx2::GetInstance()->GetConfig();
|
||||
Ship::ConfigFile& Conf = *pConf.get();
|
||||
*controllerBits = 0;
|
||||
|
||||
if (SDL_Init(SDL_INIT_GAMECONTROLLER) != 0) {
|
||||
SPDLOG_ERROR("Failed to initialize SDL game controllers ({})", SDL_GetError());
|
||||
@ -54,43 +53,7 @@ extern "C" {
|
||||
SPDLOG_ERROR("Failed add SDL game controller mappings from \"{}\" ({})", controllerDb, SDL_GetError());
|
||||
}
|
||||
|
||||
// TODO: This for loop is debug. Burn it with fire.
|
||||
for (int i = 0; i < SDL_NumJoysticks(); i++) {
|
||||
if (SDL_IsGameController(i)) {
|
||||
// Get the GUID from SDL
|
||||
char buf[33];
|
||||
SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(i), buf, sizeof(buf));
|
||||
auto guid = std::string(buf);
|
||||
auto name = std::string(SDL_GameControllerNameForIndex(i));
|
||||
|
||||
SPDLOG_INFO("Found Controller \"{}\" with ID \"{}\"", name, guid);
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < __osMaxControllers; i++) {
|
||||
std::string ControllerType = Conf["CONTROLLERS"]["CONTROLLER " + std::to_string(i+1)];
|
||||
mINI::INIStringUtil::toLower(ControllerType);
|
||||
|
||||
if (ControllerType == "auto") {
|
||||
Ship::Window::Controllers[i].push_back(std::make_shared<Ship::KeyboardController>(i));
|
||||
Ship::Window::Controllers[i].push_back(std::make_shared<Ship::SDLController>(i));
|
||||
} else if (ControllerType == "keyboard") {
|
||||
Ship::Window::Controllers[i].push_back(std::make_shared<Ship::KeyboardController>(i));
|
||||
} else if (ControllerType == "usb") {
|
||||
Ship::Window::Controllers[i].push_back(std::make_shared<Ship::SDLController>(i));
|
||||
} else if (ControllerType == "unplugged") {
|
||||
// Do nothing for unplugged controllers
|
||||
} else {
|
||||
SPDLOG_ERROR("Invalid Controller Type: {}", ControllerType);
|
||||
}
|
||||
}
|
||||
|
||||
*controllerBits = 0;
|
||||
for (size_t i = 0; i < __osMaxControllers; i++) {
|
||||
if (Ship::Window::Controllers[i].size() > 0) {
|
||||
*controllerBits |= 1 << i;
|
||||
}
|
||||
}
|
||||
Ship::Window::ControllerApi->Init(controllerBits);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -103,17 +66,14 @@ extern "C" {
|
||||
pad->button = 0;
|
||||
pad->stick_x = 0;
|
||||
pad->stick_y = 0;
|
||||
pad->cam_x = 0;
|
||||
pad->cam_y = 0;
|
||||
pad->err_no = 0;
|
||||
pad->gyro_x = 0;
|
||||
pad->gyro_y = 0;
|
||||
|
||||
if (__enableGameInput)
|
||||
{
|
||||
for (size_t i = 0; i < __osMaxControllers; i++) {
|
||||
for (size_t j = 0; j < Ship::Window::Controllers[i].size(); j++) {
|
||||
Ship::Window::Controllers[i][j]->Read(&pad[i]);
|
||||
}
|
||||
}
|
||||
if (__enableGameInput) {
|
||||
Ship::Window::ControllerApi->WriteToPad(pad);
|
||||
}
|
||||
|
||||
ModInternal::ExecuteHooks<ModInternal::ControllerRead>(pad);
|
||||
@ -129,15 +89,10 @@ extern "C" {
|
||||
|
||||
if (hashStr != nullptr) {
|
||||
auto res = std::static_pointer_cast<Ship::Array>(Ship::GlobalCtx2::GetInstance()->GetResourceManager()->LoadResource(hashStr->c_str()));
|
||||
return (Vtx*)res->vertices.data();
|
||||
}
|
||||
|
||||
//if (res != nullptr)
|
||||
return (Vtx*)res->vertices.data();
|
||||
//else
|
||||
//return (Vtx*)Ship::GlobalCtx2::GetInstance()->GetResourceManager()->LoadFile(hashStr)->buffer.get();
|
||||
}
|
||||
else {
|
||||
return nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int32_t* ResourceMgr_LoadMtxByCRC(uint64_t crc) {
|
||||
@ -146,9 +101,9 @@ extern "C" {
|
||||
if (hashStr != nullptr) {
|
||||
auto res = std::static_pointer_cast<Ship::Matrix>(Ship::GlobalCtx2::GetInstance()->GetResourceManager()->LoadResource(hashStr->c_str()));
|
||||
return (int32_t*)res->mtx.data();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Gfx* ResourceMgr_LoadGfxByCRC(uint64_t crc) {
|
||||
@ -233,7 +188,7 @@ extern GfxWindowManagerAPI gfx_sdl;
|
||||
void SetWindowManager(GfxWindowManagerAPI** WmApi, GfxRenderingAPI** RenderingApi, const std::string& gfx_backend);
|
||||
|
||||
namespace Ship {
|
||||
std::map<size_t, std::vector<std::shared_ptr<Controller>>> Window::Controllers;
|
||||
|
||||
int32_t Window::lastScancode;
|
||||
|
||||
Window::Window(std::shared_ptr<GlobalCtx2> Context) : Context(Context), APlayer(nullptr) {
|
||||
@ -248,26 +203,49 @@ namespace Ship {
|
||||
SPDLOG_INFO("destruct window");
|
||||
}
|
||||
|
||||
void Window::CreateDefaults() {
|
||||
const std::shared_ptr<Mercury> pConf = GlobalCtx2::GetInstance()->GetConfig();
|
||||
if (pConf->isNewInstance) {
|
||||
pConf->setInt("Window.Width", 640);
|
||||
pConf->setInt("Window.Height", 480);
|
||||
pConf->setBool("Window.Options", false);
|
||||
pConf->setString("Window.GfxBackend", "");
|
||||
|
||||
pConf->setBool("Window.Fullscreen.Enabled", false);
|
||||
pConf->setInt("Window.Fullscreen.Width", 640);
|
||||
pConf->setInt("Window.Fullscreen.Height", 480);
|
||||
|
||||
pConf->setString("Game.SaveName", "");
|
||||
pConf->setString("Game.Main Archive", "");
|
||||
pConf->setString("Game.Patches Archive", "");
|
||||
|
||||
pConf->setInt("Shortcuts.Fullscreen", 0x044);
|
||||
pConf->setInt("Shortcuts.Console", 0x029);
|
||||
pConf->save();
|
||||
}
|
||||
}
|
||||
|
||||
void Window::Init() {
|
||||
std::shared_ptr<ConfigFile> pConf = GlobalCtx2::GetInstance()->GetConfig();
|
||||
ConfigFile& Conf = *pConf.get();
|
||||
std::shared_ptr<Mercury> pConf = GlobalCtx2::GetInstance()->GetConfig();
|
||||
|
||||
CreateDefaults();
|
||||
|
||||
SetAudioPlayer();
|
||||
bIsFullscreen = Ship::stob(Conf["WINDOW"]["FULLSCREEN"]);
|
||||
if (bIsFullscreen) {
|
||||
dwWidth = Ship::stoi(Conf["WINDOW"]["FULLSCREEN WIDTH"], 1920);
|
||||
dwHeight = Ship::stoi(Conf["WINDOW"]["FULLSCREEN HEIGHT"], 1080);
|
||||
} else {
|
||||
dwWidth = Ship::stoi(Conf["WINDOW"]["WINDOW WIDTH"], 640);
|
||||
dwHeight = Ship::stoi(Conf["WINDOW"]["WINDOW HEIGHT"], 480);
|
||||
}
|
||||
dwMenubar = Ship::stoi(Conf["WINDOW"]["menubar"], 0);
|
||||
const std::string& gfx_backend = Conf["WINDOW"]["GFX BACKEND"];
|
||||
bIsFullscreen = pConf->getBool("Window.Fullscreen.Enabled", false);
|
||||
|
||||
dwWidth = pConf->getInt("Window.Fullscreen.Width", bIsFullscreen ? 1920 : 640);
|
||||
dwHeight = pConf->getInt("Window.Fullscreen.Height", bIsFullscreen ? 1080 : 480);
|
||||
dwMenubar = pConf->getBool("Window.Options", false);
|
||||
const std::string& gfx_backend = pConf->getString("Window.GfxBackend");
|
||||
SetWindowManager(&WmApi, &RenderingApi, gfx_backend);
|
||||
|
||||
gfx_init(WmApi, RenderingApi, GetContext()->GetName().c_str(), bIsFullscreen, dwWidth, dwHeight);
|
||||
WmApi->set_fullscreen_changed_callback(Window::OnFullscreenChanged);
|
||||
WmApi->set_keyboard_callbacks(Window::KeyDown, Window::KeyUp, Window::AllKeysUp);
|
||||
WmApi->set_fullscreen_changed_callback(OnFullscreenChanged);
|
||||
WmApi->set_keyboard_callbacks(KeyDown, KeyUp, AllKeysUp);
|
||||
|
||||
ModInternal::RegisterHook<ModInternal::ExitGame>([]() {
|
||||
ControllerApi->SaveControllerSettings();
|
||||
});
|
||||
}
|
||||
|
||||
void Window::StartFrame() {
|
||||
@ -318,30 +296,26 @@ namespace Ship {
|
||||
void Window::MainLoop(void (*MainFunction)(void)) {
|
||||
WmApi->main_loop(MainFunction);
|
||||
}
|
||||
|
||||
bool Window::KeyUp(int32_t dwScancode) {
|
||||
std::shared_ptr<ConfigFile> pConf = GlobalCtx2::GetInstance()->GetConfig();
|
||||
ConfigFile& Conf = *pConf.get();
|
||||
std::shared_ptr<Mercury> pConf = GlobalCtx2::GetInstance()->GetConfig();
|
||||
|
||||
if (dwScancode == Ship::stoi(Conf["KEYBOARD SHORTCUTS"]["KEY_FULLSCREEN"])) {
|
||||
if (dwScancode == pConf->getInt("Shortcuts.Fullscreen", 0x044)) {
|
||||
GlobalCtx2::GetInstance()->GetWindow()->ToggleFullscreen();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// OTRTODO: Rig with Kirito's console?
|
||||
//if (dwScancode == Ship::stoi(Conf["KEYBOARD SHORTCUTS"]["KEY_CONSOLE"])) {
|
||||
// ToggleConsole();
|
||||
//}
|
||||
|
||||
lastScancode = -1;
|
||||
|
||||
bool bIsProcessed = false;
|
||||
for (size_t i = 0; i < __osMaxControllers; i++) {
|
||||
for (size_t j = 0; j < Controllers[i].size(); j++) {
|
||||
KeyboardController* pad = dynamic_cast<KeyboardController*>(Ship::Window::Controllers[i][j].get());
|
||||
if (pad != nullptr) {
|
||||
if (pad->ReleaseButton(dwScancode)) {
|
||||
bIsProcessed = true;
|
||||
}
|
||||
}
|
||||
const auto pad = dynamic_cast<KeyboardController*>(ControllerApi->physicalDevices[ControllerApi->physicalDevices.size() - 2].get());
|
||||
if (pad != nullptr) {
|
||||
if (pad->ReleaseButton(dwScancode)) {
|
||||
bIsProcessed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -350,14 +324,11 @@ namespace Ship {
|
||||
|
||||
bool Window::KeyDown(int32_t dwScancode) {
|
||||
bool bIsProcessed = false;
|
||||
for (size_t i = 0; i < __osMaxControllers; i++) {
|
||||
for (size_t j = 0; j < Controllers[i].size(); j++) {
|
||||
KeyboardController* pad = dynamic_cast<KeyboardController*>(Ship::Window::Controllers[i][j].get());
|
||||
if (pad != nullptr) {
|
||||
if (pad->PressButton(dwScancode)) {
|
||||
bIsProcessed = true;
|
||||
}
|
||||
}
|
||||
|
||||
const auto pad = dynamic_cast<KeyboardController*>(ControllerApi->physicalDevices[ControllerApi->physicalDevices.size() - 2].get());
|
||||
if (pad != nullptr) {
|
||||
if (pad->PressButton(dwScancode)) {
|
||||
bIsProcessed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -368,21 +339,17 @@ namespace Ship {
|
||||
|
||||
|
||||
void Window::AllKeysUp(void) {
|
||||
for (size_t i = 0; i < __osMaxControllers; i++) {
|
||||
for (size_t j = 0; j < Controllers[i].size(); j++) {
|
||||
KeyboardController* pad = dynamic_cast<KeyboardController*>(Ship::Window::Controllers[i][j].get());
|
||||
if (pad != nullptr) {
|
||||
pad->ReleaseAllButtons();
|
||||
}
|
||||
}
|
||||
const auto pad = dynamic_cast<KeyboardController*>(ControllerApi->physicalDevices[ControllerApi->physicalDevices.size() - 2].get());
|
||||
if (pad != nullptr) {
|
||||
pad->ReleaseAllButtons();
|
||||
}
|
||||
}
|
||||
|
||||
void Window::OnFullscreenChanged(bool bIsFullscreen) {
|
||||
std::shared_ptr<ConfigFile> pConf = GlobalCtx2::GetInstance()->GetConfig();
|
||||
ConfigFile& Conf = *pConf.get();
|
||||
std::shared_ptr<Mercury> pConf = GlobalCtx2::GetInstance()->GetConfig();
|
||||
|
||||
GlobalCtx2::GetInstance()->GetWindow()->bIsFullscreen = bIsFullscreen;
|
||||
Conf["WINDOW"]["FULLSCREEN"] = std::to_string(bIsFullscreen);
|
||||
pConf->setBool("Window.Fullscreen.Enabled", bIsFullscreen);
|
||||
GlobalCtx2::GetInstance()->GetWindow()->ShowCursor(!bIsFullscreen);
|
||||
}
|
||||
|
||||
|
@ -5,17 +5,22 @@
|
||||
#include "UltraController.h"
|
||||
#include "Controller.h"
|
||||
#include "GlobalCtx2.h"
|
||||
#include "ControlDeck.h"
|
||||
#include <string>
|
||||
|
||||
#include "Lib/Fast3D/gfx_window_manager_api.h"
|
||||
|
||||
namespace Ship {
|
||||
class AudioPlayer;
|
||||
|
||||
class Window {
|
||||
public:
|
||||
static std::map<size_t, std::vector<std::shared_ptr<Controller>>> Controllers;
|
||||
static int32_t lastScancode;
|
||||
inline static ControlDeck* ControllerApi = new ControlDeck;
|
||||
|
||||
Window(std::shared_ptr<GlobalCtx2> Context);
|
||||
~Window();
|
||||
void CreateDefaults();
|
||||
void MainLoop(void (*MainFunction)(void));
|
||||
void Init();
|
||||
void StartFrame();
|
||||
@ -31,9 +36,11 @@ namespace Ship {
|
||||
bool IsFullscreen() { return bIsFullscreen; }
|
||||
uint32_t GetCurrentWidth();
|
||||
uint32_t GetCurrentHeight();
|
||||
ControlDeck* GetControlDeck() { return ControllerApi; };
|
||||
uint32_t dwMenubar;
|
||||
std::shared_ptr<GlobalCtx2> GetContext() { return Context.lock(); }
|
||||
std::shared_ptr<AudioPlayer> GetAudioPlayer() { return APlayer; }
|
||||
const char* GetKeyName(int scancode) { return WmApi->get_key_name(scancode); }
|
||||
|
||||
protected:
|
||||
private:
|
||||
@ -46,11 +53,10 @@ namespace Ship {
|
||||
std::weak_ptr<GlobalCtx2> Context;
|
||||
std::shared_ptr<AudioPlayer> APlayer;
|
||||
|
||||
GfxWindowManagerAPI* WmApi;
|
||||
GfxRenderingAPI* RenderingApi;
|
||||
GfxWindowManagerAPI* WmApi;
|
||||
bool bIsFullscreen;
|
||||
uint32_t dwWidth;
|
||||
uint32_t dwHeight;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -38,38 +38,38 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Testing|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Testing|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
@ -256,13 +256,16 @@
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Audio.cpp" />
|
||||
<ClCompile Include="Blob.cpp" />
|
||||
<ClCompile Include="ControlDeck.cpp" />
|
||||
<ClCompile Include="Cvar.cpp" />
|
||||
<ClCompile Include="Environment.cpp" />
|
||||
<ClCompile Include="Factories\AudioFactory.cpp" />
|
||||
<ClCompile Include="InputEditor.cpp" />
|
||||
<ClCompile Include="GameOverlay.cpp" />
|
||||
<ClCompile Include="GameSettings.cpp" />
|
||||
<ClCompile Include="Lib\ImGui\backends\imgui_impl_dx11.cpp" />
|
||||
<ClCompile Include="Lib\ImGui\backends\imgui_impl_win32.cpp" />
|
||||
<ClCompile Include="Lib\Mercury\Mercury.cpp" />
|
||||
<ClCompile Include="luslog.cpp" />
|
||||
<ClCompile Include="mixer.c" />
|
||||
<ClCompile Include="ModManager.cpp" />
|
||||
@ -279,7 +282,6 @@
|
||||
<ClCompile Include="Factories\TextureFactory.cpp" />
|
||||
<ClCompile Include="Factories\VtxFactory.cpp" />
|
||||
<ClCompile Include="Array.cpp" />
|
||||
<ClCompile Include="ConfigFile.cpp" />
|
||||
<ClCompile Include="Controller.cpp" />
|
||||
<ClCompile Include="Hooks.cpp" />
|
||||
<ClCompile Include="ImGuiImpl.cpp" />
|
||||
@ -342,13 +344,18 @@
|
||||
<ClCompile Include="SDLController.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Lib\Mercury\Mercury.h" />
|
||||
<ClInclude Include="Lib\nlohmann\json.hpp" />
|
||||
<ClInclude Include="abi.h" />
|
||||
<ClInclude Include="Audio.h" />
|
||||
<ClInclude Include="AudioPlayer.h" />
|
||||
<ClInclude Include="Blob.h" />
|
||||
<ClInclude Include="ControlDeck.h" />
|
||||
<ClInclude Include="Cvar.h" />
|
||||
<ClInclude Include="DisconnectedController.h" />
|
||||
<ClInclude Include="Environment.h" />
|
||||
<ClInclude Include="Factories\AudioFactory.h" />
|
||||
<ClInclude Include="InputEditor.h" />
|
||||
<ClInclude Include="GameOverlay.h" />
|
||||
<ClInclude Include="GameSettings.h" />
|
||||
<ClInclude Include="GameVersions.h" />
|
||||
@ -404,7 +411,6 @@
|
||||
<ClInclude Include="Vertex.h" />
|
||||
<ClInclude Include="stox.h" />
|
||||
<ClInclude Include="Lib\mINI\src\mini\ini.h" />
|
||||
<ClInclude Include="ConfigFile.h" />
|
||||
<ClInclude Include="Controller.h" />
|
||||
<ClInclude Include="KeyboardController.h" />
|
||||
<ClInclude Include="Factories\CollisionHeaderFactory.h" />
|
||||
|
@ -31,9 +31,6 @@
|
||||
<Filter Include="Source Files\Globals">
|
||||
<UniqueIdentifier>{c0f07350-c627-444e-9f66-23e19407ad9a}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Config">
|
||||
<UniqueIdentifier>{9cf4833f-e90c-4a9d-8747-d47cde657beb}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Resources\Files">
|
||||
<UniqueIdentifier>{2aa34c3b-6148-480f-a4fc-19c4e0f8c822}</UniqueIdentifier>
|
||||
</Filter>
|
||||
@ -94,6 +91,15 @@
|
||||
<Filter Include="Source Files\Lib\dr_libs">
|
||||
<UniqueIdentifier>{db6e02cc-fc4c-4138-8219-1d281ad93ec2}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Lib\nlohmann">
|
||||
<UniqueIdentifier>{2be7c90f-ba21-455d-8a11-6f99452be15c}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Lib\Mercury">
|
||||
<UniqueIdentifier>{7e415dd2-403b-4d4d-b4f2-3e311f91db19}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\Controller\InputEditor">
|
||||
<UniqueIdentifier>{010dc29b-d1f6-4793-a4e7-4156aa4fcdd6}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Factories\MaterialFactory.cpp">
|
||||
@ -165,9 +171,6 @@
|
||||
<ClCompile Include="MemoryPack.cpp">
|
||||
<Filter>Source Files\Controller\Attachment</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ConfigFile.cpp">
|
||||
<Filter>Source Files\Config</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CollisionHeader.cpp">
|
||||
<Filter>Source Files\Resources\Files</Filter>
|
||||
</ClCompile>
|
||||
@ -354,6 +357,15 @@
|
||||
<ClCompile Include="Factories\AudioFactory.cpp">
|
||||
<Filter>Source Files\Resources\Factories</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="InputEditor.cpp">
|
||||
<Filter>Source Files\Controller\InputEditor</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ControlDeck.cpp">
|
||||
<Filter>Source Files\Controller</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Lib\Mercury\Mercury.cpp">
|
||||
<Filter>Source Files\Lib\Mercury</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Lib\tinyxml2\tinyxml2.h">
|
||||
@ -386,9 +398,6 @@
|
||||
<ClInclude Include="MemoryPack.h">
|
||||
<Filter>Source Files\Controller\Attachment</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ConfigFile.h">
|
||||
<Filter>Source Files\Config</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ResourceMgr.h">
|
||||
<Filter>Source Files\Resources</Filter>
|
||||
</ClInclude>
|
||||
@ -659,5 +668,20 @@
|
||||
<ClInclude Include="Lib\dr_libs\wav.h">
|
||||
<Filter>Source Files\Lib\dr_libs</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="InputEditor.h">
|
||||
<Filter>Source Files\Controller\InputEditor</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ControlDeck.h">
|
||||
<Filter>Source Files\Controller</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DisconnectedController.h">
|
||||
<Filter>Source Files\Controller</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Lib\nlohmann\json.hpp">
|
||||
<Filter>Source Files\Lib\nlohmann</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Lib\Mercury\Mercury.h">
|
||||
<Filter>Source Files\Lib\Mercury</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -559,6 +559,7 @@ void ActorOverlayTable_Cleanup(void);
|
||||
u16 DynaSSNodeList_GetNextNodeIdx(DynaSSNodeList*);
|
||||
void func_80038A28(CollisionPoly* poly, f32 tx, f32 ty, f32 tz, MtxF* dest);
|
||||
f32 CollisionPoly_GetPointDistanceFromPlane(CollisionPoly* poly, Vec3f* point);
|
||||
CollisionHeader* BgCheck_GetCollisionHeader(CollisionContext* colCtx, s32 bgId);
|
||||
void CollisionPoly_GetVerticesByBgId(CollisionPoly* poly, s32 bgId, CollisionContext* colCtx, Vec3f* dest);
|
||||
s32 BgCheck_CheckStaticCeiling(StaticLookup* lookup, u16 xpFlags, CollisionContext* colCtx, f32* outY, Vec3f* pos,
|
||||
f32 checkHeight, CollisionPoly** outPoly);
|
||||
|
@ -143,7 +143,7 @@
|
||||
<ClCompile>
|
||||
<WarningLevel>TurnOffAllWarnings</WarningLevel>
|
||||
<SDLCheck>false</SDLCheck>
|
||||
<PreprocessorDefinitions>INCLUDE_GAME_PRINTF;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;ENABLE_DX11;%(PreprocessorDefinitions)GLEW_STATIC </PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;ENABLE_DX11;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
@ -1038,7 +1038,6 @@
|
||||
<ClInclude Include="soh\Enhancements\debugger\debugSaveEditor.h" />
|
||||
<ClInclude Include="soh\Enhancements\debugger\ImGuiHelpers.h" />
|
||||
<ClInclude Include="soh\gameconsole.h" />
|
||||
<ClInclude Include="soh\Lib\nlohmann\json.hpp" />
|
||||
<ClInclude Include="soh\OTRAudio.h" />
|
||||
<ClInclude Include="soh\OTRGlobals.h" />
|
||||
<ClInclude Include="soh\SaveManager.h" />
|
||||
|
@ -82,12 +82,6 @@
|
||||
<Filter Include="Source Files\soh\Enhancements\debugger">
|
||||
<UniqueIdentifier>{04fc1c52-49ff-48e2-ae23-2c00867374f8}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\soh\Lib">
|
||||
<UniqueIdentifier>{dbcf07c4-80b1-4c88-ac54-2bbdd8f53ee4}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files\soh\Lib\nlohmann">
|
||||
<UniqueIdentifier>{9c880c8e-492b-48f6-b230-1fd269ea74b1}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\boot\assert.c">
|
||||
@ -3947,9 +3941,6 @@
|
||||
<ClInclude Include="soh\OTRAudio.h">
|
||||
<Filter>Source Files\soh</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="soh\Lib\nlohmann\json.hpp">
|
||||
<Filter>Source Files\soh\Lib\nlohmann</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="soh\SaveManager.h">
|
||||
<Filter>Source Files\soh</Filter>
|
||||
</ClInclude>
|
||||
|
@ -20,7 +20,7 @@ extern BootCommandFunc BootCommands_Command_LoadFileSelect(char** argv, s32 argc
|
||||
static BootCommand sCommands[] = { { "--skiplogo", BootCommands_Command_SkipLogo },
|
||||
{ "--loadfileselect", BootCommands_Command_LoadFileSelect } };
|
||||
|
||||
void BootCommands_Init()
|
||||
void BootCommands_Init()
|
||||
{
|
||||
CVar_RegisterS32("gDisableLOD", 0);
|
||||
CVar_RegisterS32("gDebugEnabled", 0);
|
||||
@ -30,7 +30,6 @@ void BootCommands_Init()
|
||||
CVar_RegisterS32("gHoverFishing", 0);
|
||||
CVar_RegisterS32("gN64WeirdFrames", 0);
|
||||
CVar_RegisterS32("gBombchusOOB", 0);
|
||||
CVar_RegisterS32("gRumbleEnabled", 0);
|
||||
CVar_RegisterS32("gUniformLR", 0);
|
||||
CVar_RegisterS32("gTwoHandedIdle", 0);
|
||||
CVar_RegisterS32("gDekuNutUpgradeFix", 0);
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <Utils/StringHelper.h>
|
||||
#include <Utils/File.h>
|
||||
|
||||
#include "Window.h"
|
||||
#include "Lib/ImGui/imgui_internal.h"
|
||||
#undef PATH_HACK
|
||||
#undef Path
|
||||
@ -315,7 +316,7 @@ static bool SaveStateHandler(const std::vector<std::string>& args) {
|
||||
unsigned int slot = OTRGlobals::Instance->gSaveStateMgr->GetCurrentSlot();
|
||||
const SaveStateReturn rtn = OTRGlobals::Instance->gSaveStateMgr->AddRequest({ slot, RequestType::SAVE });
|
||||
|
||||
switch (rtn) {
|
||||
switch (rtn) {
|
||||
case SaveStateReturn::SUCCESS:
|
||||
INFO("[SOH] Saved state to slot %u", slot);
|
||||
return CMD_SUCCESS;
|
||||
@ -329,7 +330,7 @@ static bool SaveStateHandler(const std::vector<std::string>& args) {
|
||||
static bool LoadStateHandler(const std::vector<std::string>& args) {
|
||||
unsigned int slot = OTRGlobals::Instance->gSaveStateMgr->GetCurrentSlot();
|
||||
const SaveStateReturn rtn = OTRGlobals::Instance->gSaveStateMgr->AddRequest({ slot, RequestType::LOAD });
|
||||
|
||||
|
||||
switch (rtn) {
|
||||
case SaveStateReturn::SUCCESS:
|
||||
INFO("[SOH] Loaded state from slot %u", slot);
|
||||
@ -342,7 +343,7 @@ static bool LoadStateHandler(const std::vector<std::string>& args) {
|
||||
return CMD_FAILED;
|
||||
case SaveStateReturn::FAIL_WRONG_GAMESTATE:
|
||||
ERROR("[SOH] Can not load a state outside of \"GamePlay\"");
|
||||
return CMD_FAILED;
|
||||
return CMD_FAILED;
|
||||
}
|
||||
|
||||
}
|
||||
@ -360,7 +361,7 @@ static bool StateSlotSelectHandler(const std::vector<std::string>& args) {
|
||||
ERROR("[SOH] SaveState slot value must be a number.");
|
||||
return CMD_FAILED;
|
||||
}
|
||||
|
||||
|
||||
if (slot < 0) {
|
||||
ERROR("[SOH] Invalid slot passed. Slot must be between 0 and 2");
|
||||
return CMD_FAILED;
|
||||
@ -498,8 +499,7 @@ template <typename Numeric> bool is_number(const std::string& s) {
|
||||
return ((std::istringstream(s) >> n >> std::ws).eof());
|
||||
}
|
||||
|
||||
void DebugConsole_LoadCVars()
|
||||
{
|
||||
void DebugConsole_LoadLegacyCVars() {
|
||||
auto cvarsConfig = Ship::GlobalCtx2::GetPathRelativeToAppDirectory("cvars.cfg");
|
||||
if (File::Exists(cvarsConfig)) {
|
||||
const auto lines = File::ReadAllLines(cvarsConfig);
|
||||
@ -520,23 +520,58 @@ void DebugConsole_LoadCVars()
|
||||
CVar_SetS32(cfg[0].c_str(), std::stoi(cfg[1]));
|
||||
}
|
||||
}
|
||||
|
||||
fs::remove("cvars.cfg");
|
||||
}
|
||||
}
|
||||
|
||||
void DebugConsole_LoadCVars() {
|
||||
|
||||
std::shared_ptr<Mercury> pConf = Ship::GlobalCtx2::GetInstance()->GetConfig();
|
||||
pConf->reload();
|
||||
|
||||
for (const auto& item : pConf->rjson["CVars"].items()) {
|
||||
auto value = item.value();
|
||||
switch (value.type()) {
|
||||
case nlohmann::detail::value_t::array:
|
||||
break;
|
||||
case nlohmann::detail::value_t::string:
|
||||
CVar_SetString(item.key().c_str(), value.get<std::string>().c_str());
|
||||
break;
|
||||
case nlohmann::detail::value_t::boolean:
|
||||
CVar_SetS32(item.key().c_str(), value.get<bool>());
|
||||
break;
|
||||
case nlohmann::detail::value_t::number_unsigned:
|
||||
case nlohmann::detail::value_t::number_integer:
|
||||
CVar_SetS32(item.key().c_str(), value.get<int>());
|
||||
break;
|
||||
case nlohmann::detail::value_t::number_float:
|
||||
CVar_SetFloat(item.key().c_str(), value.get<float>());
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
if (item.key() == "gOpenMenuBar") {
|
||||
int bp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
DebugConsole_LoadLegacyCVars();
|
||||
}
|
||||
|
||||
void DebugConsole_SaveCVars()
|
||||
{
|
||||
std::string output;
|
||||
std::shared_ptr<Mercury> pConf = Ship::GlobalCtx2::GetInstance()->GetConfig();
|
||||
|
||||
for (const auto &cvar : cvars) {
|
||||
if (cvar.second->type == CVAR_TYPE_STRING)
|
||||
output += StringHelper::Sprintf("%s = \"%s\"\n", cvar.first.c_str(), cvar.second->value.valueStr);
|
||||
const std::string key = StringHelper::Sprintf("CVars.%s", cvar.first.c_str());
|
||||
|
||||
if (cvar.second->type == CVAR_TYPE_STRING && cvar.second->value.valueStr != nullptr)
|
||||
pConf->setString(key, std::string(cvar.second->value.valueStr));
|
||||
else if (cvar.second->type == CVAR_TYPE_S32)
|
||||
output += StringHelper::Sprintf("%s = %i\n", cvar.first.c_str(), cvar.second->value.valueS32);
|
||||
pConf->setInt(key, cvar.second->value.valueS32);
|
||||
else if (cvar.second->type == CVAR_TYPE_FLOAT)
|
||||
output += StringHelper::Sprintf("%s = %f\n", cvar.first.c_str(), cvar.second->value.valueFloat);
|
||||
pConf->setFloat(key, cvar.second->value.valueFloat);
|
||||
}
|
||||
|
||||
|
||||
auto cvarsConfig = Ship::GlobalCtx2::GetPathRelativeToAppDirectory("cvars.cfg");
|
||||
File::WriteAllText(cvarsConfig, output);
|
||||
pConf->save();
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include "utils.hpp"
|
||||
#include "shops.hpp"
|
||||
#include "hints.hpp"
|
||||
#include "soh/Lib/nlohmann/json.hpp"
|
||||
#include "Lib/nlohmann/json.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "randomizer.h"
|
||||
#include "soh/Lib/nlohmann/json.hpp"
|
||||
#include "Lib/nlohmann/json.hpp"
|
||||
#include <fstream>
|
||||
#include <variables.h>
|
||||
#include <macros.h>
|
||||
|
@ -1,5 +1,4 @@
|
||||
#ifndef RANDOMIZER_H
|
||||
#define RANDOMIZER_H
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
@ -54,5 +53,3 @@ void Rando_Init(void);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -1153,34 +1153,27 @@ extern "C" s32* ResourceMgr_LoadCSByName(const char* path)
|
||||
return (s32*)res->commands.data();
|
||||
}
|
||||
|
||||
std::filesystem::path GetSaveFile(Ship::ConfigFile& Conf) {
|
||||
std::string fileName = Conf.get("SAVE").get("Save Filename");
|
||||
|
||||
if (fileName.empty()) {
|
||||
Conf["SAVE"]["Save Filename"] = Ship::GlobalCtx2::GetPathRelativeToAppDirectory("oot_save.sav");
|
||||
Conf.Save();
|
||||
}
|
||||
std::filesystem::path GetSaveFile(std::shared_ptr<Mercury> Conf) {
|
||||
const std::string fileName = Conf->getString("Game.SaveName", Ship::GlobalCtx2::GetPathRelativeToAppDirectory("oot_save.sav"));
|
||||
std::filesystem::path saveFile = std::filesystem::absolute(fileName);
|
||||
|
||||
if (!std::filesystem::exists(saveFile.parent_path())) {
|
||||
std::filesystem::create_directories(saveFile.parent_path());
|
||||
if (!exists(saveFile.parent_path())) {
|
||||
create_directories(saveFile.parent_path());
|
||||
}
|
||||
|
||||
return saveFile;
|
||||
}
|
||||
|
||||
std::filesystem::path GetSaveFile() {
|
||||
std::shared_ptr<Ship::ConfigFile> pConf = OTRGlobals::Instance->context->GetConfig();
|
||||
Ship::ConfigFile& Conf = *pConf.get();
|
||||
const std::shared_ptr<Mercury> pConf = OTRGlobals::Instance->context->GetConfig();
|
||||
|
||||
return GetSaveFile(Conf);
|
||||
return GetSaveFile(pConf);
|
||||
}
|
||||
|
||||
void OTRGlobals::CheckSaveFile(size_t sramSize) {
|
||||
std::shared_ptr<Ship::ConfigFile> pConf = context->GetConfig();
|
||||
Ship::ConfigFile& Conf = *pConf.get();
|
||||
void OTRGlobals::CheckSaveFile(size_t sramSize) const {
|
||||
const std::shared_ptr<Mercury> pConf = Instance->context->GetConfig();
|
||||
|
||||
std::filesystem::path savePath = GetSaveFile(Conf);
|
||||
std::filesystem::path savePath = GetSaveFile(pConf);
|
||||
std::fstream saveFile(savePath, std::fstream::in | std::fstream::out | std::fstream::binary);
|
||||
if (saveFile.fail()) {
|
||||
saveFile.open(savePath, std::fstream::in | std::fstream::out | std::fstream::binary | std::fstream::app);
|
||||
@ -1199,25 +1192,6 @@ extern "C" void Ctx_WriteSaveFile(uintptr_t addr, void* dramAddr, size_t size) {
|
||||
OTRGlobals::Instance->context->WriteSaveFile(GetSaveFile(), addr, dramAddr, size);
|
||||
}
|
||||
|
||||
/* Remember to free after use of value */
|
||||
extern "C" char* Config_getValue(char* category, char* key) {
|
||||
std::shared_ptr<Ship::ConfigFile> pConf = OTRGlobals::Instance->context->GetConfig();
|
||||
Ship::ConfigFile& Conf = *pConf.get();
|
||||
|
||||
std::string data = Conf.get(std::string(category)).get(std::string(key));
|
||||
char* retval = (char*)malloc(data.length()+1);
|
||||
strcpy(retval, data.c_str());
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
extern "C" bool Config_setValue(char* category, char* key, char* value) {
|
||||
std::shared_ptr<Ship::ConfigFile> pConf = OTRGlobals::Instance->context->GetConfig();
|
||||
Ship::ConfigFile& Conf = *pConf.get();
|
||||
Conf[std::string(category)][std::string(key)] = std::string(value);
|
||||
return Conf.Save();
|
||||
}
|
||||
|
||||
std::wstring StringToU16(const std::string& s) {
|
||||
std::vector<unsigned long> result;
|
||||
size_t i = 0;
|
||||
@ -1319,11 +1293,10 @@ extern "C" uint32_t OTRGetCurrentHeight() {
|
||||
}
|
||||
|
||||
extern "C" void OTRControllerCallback(ControllerCallback* controller) {
|
||||
auto controllers = OTRGlobals::Instance->context->GetWindow()->Controllers;
|
||||
for (size_t i = 0; i < controllers.size(); i++) {
|
||||
for (int j = 0; j < controllers[i].size(); j++) {
|
||||
OTRGlobals::Instance->context->GetWindow()->Controllers[i][j]->WriteToSource(controller);
|
||||
}
|
||||
const auto controllers = Ship::Window::ControllerApi->virtualDevices;
|
||||
|
||||
for (int i = 0; i < controllers.size(); ++i) {
|
||||
Ship::Window::ControllerApi->physicalDevices[controllers[i]]->WriteToSource(i, controller);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1377,11 +1350,11 @@ extern "C" void AudioPlayer_Play(const uint8_t* buf, uint32_t len) {
|
||||
}
|
||||
|
||||
extern "C" int Controller_ShouldRumble(size_t i) {
|
||||
for (const auto& controller : Ship::Window::Controllers.at(i))
|
||||
{
|
||||
float rumble_strength = CVar_GetFloat(StringHelper::Sprintf("gCont%i_RumbleStrength", i).c_str(), 1.0f);
|
||||
|
||||
if (controller->CanRumble() && rumble_strength > 0.001f) {
|
||||
const auto controllers = Ship::Window::ControllerApi->virtualDevices;
|
||||
|
||||
for (const auto virtual_entry : controllers) {
|
||||
if (Ship::Window::ControllerApi->physicalDevices[virtual_entry]->CanRumble()) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ public:
|
||||
~OTRGlobals();
|
||||
|
||||
private:
|
||||
void CheckSaveFile(size_t sramSize);
|
||||
void CheckSaveFile(size_t sramSize) const;
|
||||
};
|
||||
#endif
|
||||
|
||||
@ -61,8 +61,6 @@ SoundFontSample* ResourceMgr_LoadAudioSample(const char* path);
|
||||
CollisionHeader* ResourceMgr_LoadColByName(const char* path);
|
||||
void Ctx_ReadSaveFile(uintptr_t addr, void* dramAddr, size_t size);
|
||||
void Ctx_WriteSaveFile(uintptr_t addr, void* dramAddr, size_t size);
|
||||
char* Config_getValue(char* category, char* key);
|
||||
bool Config_setValue(char* category, char* key, char* value);
|
||||
|
||||
uint64_t GetPerfCounter();
|
||||
struct SkeletonHeader* ResourceMgr_LoadSkeletonByName(const char* path);
|
||||
|
@ -269,7 +269,7 @@ void PadMgr_ProcessInputs(PadMgr* padMgr) {
|
||||
input->press.stick_y += (s8)(input->cur.stick_y - input->prev.stick_y);
|
||||
}
|
||||
|
||||
controllerCallback.rumble = CVar_GetS32("gRumbleEnabled", 0) && (padMgr->rumbleEnable[0] > 0);
|
||||
controllerCallback.rumble = (padMgr->rumbleEnable[0] > 0);
|
||||
|
||||
if (HealthMeter_IsCritical()) {
|
||||
controllerCallback.ledColor = 0;
|
||||
|
@ -1481,7 +1481,7 @@ s32 Camera_Free(Camera* camera) {
|
||||
|
||||
camBgChk.pos = camera->eye;
|
||||
|
||||
float maxRadius = 160.0f;
|
||||
float maxRadius = 150.0f;
|
||||
if (Camera_BGCheckInfo(camera, &at, &camBgChk)) {
|
||||
VecSph collSphere;
|
||||
OLib_Vec3fDiffToVecSphGeo(&collSphere, &at, &camBgChk.pos);
|
||||
|
@ -89,6 +89,7 @@ void TransitionWipe_Draw(void* thisx, Gfx** gfxP) {
|
||||
TransitionWipe* this = (TransitionWipe*)thisx;
|
||||
s32 pad[4];
|
||||
Gfx* tex;
|
||||
Gfx* wipeDl = sWipeDList;
|
||||
|
||||
modelView = this->modelView[this->frame];
|
||||
|
||||
|
@ -419,7 +419,7 @@ void FileChoose_UpdateMainMenu(GameState* thisx) {
|
||||
(!fileSelectSpoilerFileLoaded &&
|
||||
SpoilerFileExists(CVar_GetString("gSpoilerLog", "")))) {
|
||||
if (CVar_GetS32("gNewFileDropped", 0) != 0) {
|
||||
CVar_SetString("gSpoilerLog", CVar_GetString("gDroppedFile", ""));
|
||||
CVar_SetString("gSpoilerLog", CVar_GetString("gDroppedFile", "None"));
|
||||
}
|
||||
bool silent = true;
|
||||
if ((CVar_GetS32("gNewFileDropped", 0) != 0) ||
|
||||
@ -1894,7 +1894,7 @@ void FileChoose_Main(GameState* thisx) {
|
||||
};
|
||||
FileChooseContext* this = (FileChooseContext*)thisx;
|
||||
Input* input = &this->state.input[0];
|
||||
|
||||
|
||||
if (CVar_GetS32("gTimeFlowFileSelect", 0) != 0) {
|
||||
gSaveContext.skyboxTime += 0x10;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user