Implemented rumble, added control options menu with rumble strength and targeting mode

This commit is contained in:
Mr-Wiseguy
2024-03-04 02:13:12 -05:00
parent 9ef243bc05
commit 57475d058b
12 changed files with 189 additions and 26 deletions
+24 -11
View File
@@ -23,6 +23,17 @@ constexpr auto rr_default = RT64::UserConfiguration::RefreshRate::Di
constexpr int rr_manual_default = 60;
constexpr bool developer_mode_default = false;
template <typename T>
void from_or_default(const json& j, const std::string& key, T& out, T default_value) {
auto find_it = j.find(key);
if (find_it != j.end()) {
find_it->get_to(out);
}
else {
out = default_value;
}
}
namespace ultramodern {
void to_json(json& j, const GraphicsConfig& config) {
j = json{
@@ -36,17 +47,6 @@ namespace ultramodern {
};
}
template <typename T>
void from_or_default(const json& j, const std::string& key, T& out, T default_value) {
auto find_it = j.find(key);
if (find_it != j.end()) {
find_it->get_to(out);
}
else {
out = default_value;
}
}
void from_json(const json& j, GraphicsConfig& config) {
from_or_default(j, "res_option", config.res_option, res_default);
from_or_default(j, "wm_option", config.wm_option, wm_default);
@@ -171,6 +171,11 @@ void add_input_bindings(nlohmann::json& out, recomp::GameInput input, recomp::In
void save_controls_config(const std::filesystem::path& path) {
nlohmann::json config_json{};
config_json["options"] = {};
recomp::to_json(config_json["options"]["targeting_mode"], recomp::get_targeting_mode());
config_json["options"]["rumble_strength"] = recomp::get_rumble_strength();
config_json["keyboard"] = {};
config_json["controller"] = {};
@@ -221,6 +226,14 @@ void load_controls_config(const std::filesystem::path& path) {
nlohmann::json config_json{};
config_file >> config_json;
recomp::TargetingMode targeting_mode;
from_or_default(config_json["options"], "targeting_mode", targeting_mode, recomp::TargetingMode::Switch);
recomp::set_targeting_mode(targeting_mode);
int rumble_strength;
from_or_default(config_json["options"], "rumble_strength", rumble_strength, 25);
recomp::set_rumble_strength(rumble_strength);
if (!load_input_device_from_json(config_json, recomp::InputDevice::Keyboard, "keyboard")) {
assign_all_mappings(recomp::InputDevice::Keyboard, recomp::default_n64_keyboard_mappings);
+8
View File
@@ -345,6 +345,14 @@ void recomp::poll_inputs() {
#endif
}
void recomp::set_rumble(bool on) {
uint16_t rumble_strength = recomp::get_rumble_strength() * 0xFFFF / 100;
uint32_t duration = 1000000; // Dummy duration value that lasts long enough to matter as the game will reset rumble on its own.
for (const auto& controller : InputState.cur_controllers) {
SDL_GameControllerRumble(controller, 0, on ? rumble_strength : 0, duration);
}
}
bool controller_button_state(int32_t input_id) {
if (input_id >= 0 && input_id < SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_MAX) {
SDL_GameControllerButton button = (SDL_GameControllerButton)input_id;
+4
View File
@@ -62,3 +62,7 @@ extern "C" void recomp_get_aspect_ratio(uint8_t* rdram, recomp_context* ctx) {
return;
}
}
extern "C" void recomp_get_targeting_mode(uint8_t* rdram, recomp_context* ctx) {
_return(ctx, static_cast<int>(recomp::get_targeting_mode()));
}
+1
View File
@@ -253,6 +253,7 @@ int main(int argc, char** argv) {
ultramodern::input_callbacks_t input_callbacks{
.poll_input = recomp::poll_inputs,
.get_input = recomp::get_n64_input,
.set_rumble = recomp::set_rumble,
};
recomp::start({}, audio_callbacks, input_callbacks, gfx_callbacks);
+33 -4
View File
@@ -75,7 +75,7 @@ extern "C" void osContGetQuery_recomp(uint8_t * rdram, recomp_context * ctx) {
// Mark controller 0 as present
MEM_H(0, status) = 0x0005; // type: CONT_TYPE_NORMAL (from joybus)
MEM_B(2, status) = 0x00; // status: 0 (from joybus)
MEM_B(2, status) = 0x01; // status: 0x01 (from joybus, indicates that a pak is plugged into the controller)
MEM_B(3, status) = 0x00; // errno: 0 (from libultra)
// Mark controllers 1-3 as not connected
@@ -91,17 +91,46 @@ extern "C" void osContSetCh_recomp(uint8_t* rdram, recomp_context* ctx) {
}
extern "C" void __osMotorAccess_recomp(uint8_t* rdram, recomp_context* ctx) {
PTR(void) pfs = _arg<0, PTR(void)>(rdram, ctx);
s32 flag = _arg<1, s32>(rdram, ctx);
s32 channel = MEM_W(8, pfs);
// Only respect accesses to controller 0.
if (channel == 0) {
input_callbacks.set_rumble(flag);
}
_return<s32>(ctx, 0);
}
extern "C" void osMotorInit_recomp(uint8_t* rdram, recomp_context* ctx) {
;
PTR(void) pfs = _arg<1, PTR(void)>(rdram, ctx);
s32 channel = _arg<2, s32>(rdram, ctx);
MEM_W(8, pfs) = channel;
_return<s32>(ctx, 0);
}
extern "C" void osMotorStart_recomp(uint8_t* rdram, recomp_context* ctx) {
;
PTR(void) pfs = _arg<0, PTR(void)>(rdram, ctx);
s32 channel = MEM_W(8, pfs);
// Only respect accesses to controller 0.
if (channel == 0) {
input_callbacks.set_rumble(true);
}
_return<s32>(ctx, 0);
}
extern "C" void osMotorStop_recomp(uint8_t* rdram, recomp_context* ctx) {
;
PTR(void) pfs = _arg<0, PTR(void)>(rdram, ctx);
s32 channel = MEM_W(8, pfs);
// Only respect accesses to controller 0.
if (channel == 0) {
input_callbacks.set_rumble(false);
}
_return<s32>(ctx, 0);
}
+43
View File
@@ -9,6 +9,7 @@
ultramodern::GraphicsConfig new_options;
Rml::DataModelHandle graphics_model_handle;
Rml::DataModelHandle controls_model_handle;
Rml::DataModelHandle control_options_model_handle;
// True if controller config menu is open, false if keyboard config menu is open, undefined otherwise
bool configuring_controller = false;
@@ -70,6 +71,35 @@ void close_config_menu() {
}
}
struct ControlOptionsContext {
int rumble_strength = 50; // 0 to 100
recomp::TargetingMode targeting_mode = recomp::TargetingMode::Switch;
};
ControlOptionsContext control_options_context;
int recomp::get_rumble_strength() {
return control_options_context.rumble_strength;
}
void recomp::set_rumble_strength(int strength) {
control_options_context.rumble_strength = strength;
if (control_options_model_handle) {
control_options_model_handle.DirtyVariable("rumble_strength");
}
}
recomp::TargetingMode recomp::get_targeting_mode() {
return control_options_context.targeting_mode;
}
void recomp::set_targeting_mode(recomp::TargetingMode mode) {
control_options_context.targeting_mode = mode;
if (control_options_model_handle) {
control_options_model_handle.DirtyVariable("targeting_mode");
}
}
struct DebugContext {
Rml::DataModelHandle model_handle;
std::vector<std::string> area_names;
@@ -345,6 +375,18 @@ public:
controls_model_handle = constructor.GetModelHandle();
}
void make_control_options_bindings(Rml::Context* context) {
Rml::DataModelConstructor constructor = context->CreateDataModel("control_options_model");
if (!constructor) {
throw std::runtime_error("Failed to make RmlUi data model for the control options menu");
}
constructor.Bind("rumble_strength", &control_options_context.rumble_strength);
bind_option(constructor, "targeting_mode", &control_options_context.targeting_mode);
control_options_model_handle = constructor.GetModelHandle();
}
void make_debug_bindings(Rml::Context* context) {
Rml::DataModelConstructor constructor = context->CreateDataModel("debug_model");
if (!constructor) {
@@ -370,6 +412,7 @@ public:
void make_bindings(Rml::Context* context) override {
make_graphics_bindings(context);
make_controls_bindings(context);
make_control_options_bindings(context);
make_debug_bindings(context);
}
};