Add in-game control binding menu

Originally on the testing branch
This commit is contained in:
Vinícius R. Miguel 2020-05-15 15:38:35 -03:00
parent fd74e20373
commit 4a13fd3380
17 changed files with 770 additions and 406 deletions

View File

@ -448,8 +448,8 @@ endif
# Check for better camera option # Check for better camera option
ifeq ($(BETTERCAMERA),1) ifeq ($(BETTERCAMERA),1)
CC_CHECK += -DBETTERCAMERA CC_CHECK += -DBETTERCAMERA -DEXT_OPTIONS_MENU
CFLAGS += -DBETTERCAMERA CFLAGS += -DBETTERCAMERA -DEXT_OPTIONS_MENU
endif endif
# Check for no drawing distance option # Check for no drawing distance option
@ -588,11 +588,13 @@ ifeq ($(VERSION),eu)
$(BUILD_DIR)/src/menu/file_select.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o $(BUILD_DIR)/src/menu/file_select.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o
$(BUILD_DIR)/src/menu/star_select.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o $(BUILD_DIR)/src/menu/star_select.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o
$(BUILD_DIR)/src/game/ingame_menu.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o $(BUILD_DIR)/src/game/ingame_menu.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o
$(BUILD_DIR)/src/game/options_menu.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o
O_FILES += $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o O_FILES += $(BUILD_DIR)/bin/eu/translation_en.o $(BUILD_DIR)/bin/eu/translation_de.o $(BUILD_DIR)/bin/eu/translation_fr.o
else else
$(BUILD_DIR)/src/menu/file_select.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/src/menu/file_select.o: $(BUILD_DIR)/include/text_strings.h
$(BUILD_DIR)/src/menu/star_select.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/src/menu/star_select.o: $(BUILD_DIR)/include/text_strings.h
$(BUILD_DIR)/src/game/ingame_menu.o: $(BUILD_DIR)/include/text_strings.h $(BUILD_DIR)/src/game/ingame_menu.o: $(BUILD_DIR)/include/text_strings.h
$(BUILD_DIR)/src/game/options_menu.o: $(BUILD_DIR)/include/text_strings.h
endif endif
################################################################ ################################################################

View File

@ -3,20 +3,39 @@
#include "text_menu_strings.h" #include "text_menu_strings.h"
#define NC_CAMX _("Camera X Sensitivity") #define TEXT_OPT_CAMX _("Camera X Sensitivity")
#define NC_CAMY _("Camera Y Sensitivity") #define TEXT_OPT_CAMY _("Camera Y Sensitivity")
#define NC_INVERTX _("Invert X Axis") #define TEXT_OPT_INVERTX _("Invert X Axis")
#define NC_INVERTY _("Invert Y Axis") #define TEXT_OPT_INVERTY _("Invert Y Axis")
#define NC_CAMC _("Camera Centre Aggression") #define TEXT_OPT_CAMC _("Camera Centre Aggression")
#define NC_CAMP _("Camera Pan Level") #define TEXT_OPT_CAMP _("Camera Pan Level")
#define NC_ENABLED _("Enabled") #define TEXT_OPT_ENABLED _("Enabled")
#define NC_DISABLED _("Disabled") #define TEXT_OPT_DISABLED _("Disabled")
#define NC_BUTTON _("[R]: Options") #define TEXT_OPT_BUTTON1 _("[R]: Options")
#define NC_BUTTON2 _("[R]: Return") #define TEXT_OPT_BUTTON2 _("[R]: Return")
#define NC_OPTION _("OPTIONS") #define TEXT_OPT_OPTIONS _("OPTIONS")
#define NC_HIGHLIGHT _("O") #define TEXT_OPT_CAMERA _("CAMERA")
#define NC_ANALOGUE _("Analogue Camera") #define TEXT_OPT_CONTROLS _("CONTROLS")
#define NC_MOUSE _("Mouse Look") #define TEXT_OPT_HIGHLIGHT _("O")
#define TEXT_OPT_ANALOGUE _("Analogue Camera")
#define TEXT_OPT_MOUSE _("Mouse Look")
#define TEXT_OPT_UNBOUND _("NONE")
#define TEXT_OPT_PRESSKEY _("...")
#define TEXT_BIND_A _("A Button")
#define TEXT_BIND_B _("B Button")
#define TEXT_BIND_START _("Start Button")
#define TEXT_BIND_L _("L Trigger")
#define TEXT_BIND_R _("R Trigger")
#define TEXT_BIND_Z _("Z Trigger")
#define TEXT_BIND_C_UP _("C-Up")
#define TEXT_BIND_C_DOWN _("C-Down")
#define TEXT_BIND_C_LEFT _("C-Left")
#define TEXT_BIND_C_RIGHT _("C-Right")
#define TEXT_BIND_UP _("Stick Up")
#define TEXT_BIND_DOWN _("Stick Down")
#define TEXT_BIND_LEFT _("Stick Left")
#define TEXT_BIND_RIGHT _("Stick Right")
/** /**
* Global Symbols * Global Symbols

View File

@ -26,14 +26,9 @@ enum newcam_flagvalues
}; };
extern void newcam_display_options(void);
extern void newcam_check_pause_buttons(void);
extern void newcam_init_settings(void); extern void newcam_init_settings(void);
extern void newcam_save_settings(void);
extern void newcam_render_option_text(void);
extern void newcam_diagnostics(void); extern void newcam_diagnostics(void);
extern u8 newcam_option_open;
extern u8 newcam_sensitivityX; //How quick the camera works. extern u8 newcam_sensitivityX; //How quick the camera works.
extern u8 newcam_sensitivityY; extern u8 newcam_sensitivityY;

View File

@ -105,17 +105,6 @@ u16 newcam_mode;
u16 newcam_intendedmode = 0; // which camera mode the camera's going to try to be in when not forced into another. u16 newcam_intendedmode = 0; // which camera mode the camera's going to try to be in when not forced into another.
u16 newcam_modeflags; u16 newcam_modeflags;
u8 newcam_option_open = 0;
s8 newcam_option_selection = 0;
f32 newcam_option_timer = 0;
u8 newcam_option_index = 0;
u8 newcam_option_scroll = 0;
u8 newcam_option_scroll_last = 0;
u8 newcam_total = 8; //How many options there are in newcam_uptions.
u8 newcam_options[][64] = {{NC_ANALOGUE}, {NC_MOUSE}, {NC_CAMX}, {NC_CAMY}, {NC_INVERTX}, {NC_INVERTY}, {NC_CAMC}, {NC_CAMP}};
u8 newcam_flags[][64] = {{NC_DISABLED}, {NC_ENABLED}};
u8 newcam_strings[][64] = {{NC_BUTTON}, {NC_BUTTON2}, {NC_OPTION}, {NC_HIGHLIGHT}};
extern int mouse_x; extern int mouse_x;
extern int mouse_y; extern int mouse_y;
@ -168,18 +157,6 @@ void newcam_init_settings(void)
newcam_analogue = (u8)configEnableCamera; newcam_analogue = (u8)configEnableCamera;
} }
void newcam_save_settings(void)
{
configCameraXSens = newcam_sensitivityX;
configCameraYSens = newcam_sensitivityY;
configCameraAggr = newcam_aggression;
configCameraPan = newcam_panlevel;
configCameraInvertX = newcam_invertX != 0;
configCameraInvertY = newcam_invertY != 0;
configEnableCamera = newcam_analogue != 0;
configCameraMouse = newcam_mouse != 0;
}
/** Mathematic calculations. This stuffs so basic even *I* understand it lol /** Mathematic calculations. This stuffs so basic even *I* understand it lol
Basically, it just returns a position based on angle */ Basically, it just returns a position based on angle */
static s16 lengthdir_x(f32 length, s16 dir) static s16 lengthdir_x(f32 length, s16 dir)
@ -670,234 +647,10 @@ void newcam_loop(struct Camera *c)
newcam_position_cam(); newcam_position_cam();
newcam_find_fixed(); newcam_find_fixed();
if (gMarioObject) if (gMarioObject)
newcam_apply_values(c); newcam_apply_values(c);
//Just some visual information on the values of the camera. utilises ifdef because it's better at runtime. //Just some visual information on the values of the camera. utilises ifdef because it's better at runtime.
#ifdef NEWCAM_DEBUG #ifdef NEWCAM_DEBUG
newcam_diagnostics(); newcam_diagnostics();
#endif // NEWCAM_DEBUG #endif // NEWCAM_DEBUG
} }
//Displays a box.
void newcam_display_box(s16 x1, s16 y1, s16 x2, s16 y2, u8 r, u8 g, u8 b)
{
gDPPipeSync(gDisplayListHead++);
gDPSetRenderMode(gDisplayListHead++, G_RM_OPA_SURF, G_RM_OPA_SURF2);
gDPSetCycleType(gDisplayListHead++, G_CYC_FILL);
gDPSetFillColor(gDisplayListHead++, GPACK_RGBA5551(r, g, b, 255));
gDPFillRectangle(gDisplayListHead++, x1, y1, x2 - 1, y2 - 1);
gDPPipeSync(gDisplayListHead++);
gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE);
}
//I actually took the time to redo this, properly. Lmao. Please don't bully me over this anymore :(
void newcam_change_setting(u8 toggle)
{
switch (newcam_option_selection)
{
case 0:
newcam_analogue ^= 1;
break;
case 1:
newcam_mouse ^= 1;
break;
case 2:
newcam_sensitivityX = newcam_clamp(newcam_sensitivityX + toggle, 10, 250);
break;
case 3:
newcam_sensitivityY = newcam_clamp(newcam_sensitivityY + toggle, 10, 250);
break;
case 4:
newcam_invertX ^= 1;
break;
case 5:
newcam_invertY ^= 1;
break;
case 6:
newcam_aggression = newcam_clamp(newcam_aggression + toggle, 0, 100);
break;
case 7:
newcam_panlevel = newcam_clamp(newcam_panlevel + toggle, 0, 100);
break;
}
}
void newcam_text(s16 x, s16 y, u8 str[], u8 col)
{
u8 textX;
textX = get_str_x_pos_from_center(x,str,10.0f);
gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255);
print_generic_string(textX+1,y-1,str);
if (col != 0)
{
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
}
else
{
gDPSetEnvColor(gDisplayListHead++, 255, 32, 32, 255);
}
print_generic_string(textX,y,str);
}
//Options menu
void newcam_display_options()
{
u8 i = 0;
u8 newstring[32];
s16 scroll;
s16 scrollpos;
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin);
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
print_hud_lut_string(HUD_LUT_GLOBAL, 118, 40, newcam_strings[2]);
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end);
if (newcam_total>4)
{
newcam_display_box(272,90,280,208,0x80,0x80,0x80);
scrollpos = (54)*((f32)newcam_option_scroll/(newcam_total-4));
newcam_display_box(272,90+scrollpos,280,154+scrollpos,0xFF,0xFF,0xFF);
}
gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 80, SCREEN_WIDTH, SCREEN_HEIGHT);
for (i = 0; i < newcam_total; i++)
{
scroll = 140-(32*i)+(newcam_option_scroll*32);
if (scroll <= 140 && scroll > 32)
{
newcam_text(160,scroll,newcam_options[i],newcam_option_selection-i);
switch (i)
{
case 0:
newcam_text(160,scroll-12,newcam_flags[newcam_analogue],newcam_option_selection-i);
break;
case 1:
newcam_text(160,scroll-12,newcam_flags[newcam_mouse],newcam_option_selection-i);
break;
case 2:
int_to_str(newcam_sensitivityX,newstring);
newcam_text(160,scroll-12,newstring,newcam_option_selection-i);
break;
case 3:
int_to_str(newcam_sensitivityY,newstring);
newcam_text(160,scroll-12,newstring,newcam_option_selection-i);
break;
case 4:
newcam_text(160,scroll-12,newcam_flags[newcam_invertX],newcam_option_selection-i);
break;
case 5:
newcam_text(160,scroll-12,newcam_flags[newcam_invertY],newcam_option_selection-i);
break;
case 6:
int_to_str(newcam_aggression,newstring);
newcam_text(160,scroll-12,newstring,newcam_option_selection-i);
break;
case 7:
int_to_str(newcam_panlevel,newstring);
newcam_text(160,scroll-12,newstring,newcam_option_selection-i);
break;
}
}
}
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin);
print_hud_lut_string(HUD_LUT_GLOBAL, 80, 90+(32*(newcam_option_selection-newcam_option_scroll)), newcam_strings[3]);
print_hud_lut_string(HUD_LUT_GLOBAL, 224, 90+(32*(newcam_option_selection-newcam_option_scroll)), newcam_strings[3]);
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end);
}
//This has been separated for interesting reasons. Don't question it.
void newcam_render_option_text(void)
{
gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
newcam_text(278,212,newcam_strings[newcam_option_open],1);
gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
}
void newcam_check_pause_buttons()
{
if (gPlayer1Controller->buttonPressed & R_TRIG)
{
if (newcam_option_open == 0)
{
#ifndef nosound
play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs);
#endif
newcam_option_open = 1;
}
else
{
#ifndef nosound
play_sound(SOUND_MENU_MARIO_CASTLE_WARP2, gDefaultSoundArgs);
#endif
newcam_option_open = 0;
newcam_save_settings();
}
}
if (newcam_option_open)
{
if (ABS(gPlayer1Controller->stickY) > 60)
{
newcam_option_timer -= 1;
if (newcam_option_timer <= 0)
{
switch (newcam_option_index)
{
case 0: newcam_option_index++; newcam_option_timer += 10; break;
default: newcam_option_timer += 3; break;
}
#ifndef nosound
play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs);
#endif
if (gPlayer1Controller->stickY >= 60)
{
newcam_option_selection--;
if (newcam_option_selection < 0)
newcam_option_selection = newcam_total-1;
}
else
{
newcam_option_selection++;
if (newcam_option_selection >= newcam_total)
newcam_option_selection = 0;
}
}
}
else
if (ABS(gPlayer1Controller->stickX) > 60)
{
newcam_option_timer -= 1;
if (newcam_option_timer <= 0)
{
switch (newcam_option_index)
{
case 0: newcam_option_index++; newcam_option_timer += 10; break;
default: newcam_option_timer += 3; break;
}
#ifndef nosound
play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs);
#endif
if (gPlayer1Controller->stickX >= 60)
newcam_change_setting(1);
else
newcam_change_setting(-1);
}
}
else
{
newcam_option_timer = 0;
newcam_option_index = 0;
}
while (newcam_option_scroll - newcam_option_selection < -3 && newcam_option_selection > newcam_option_scroll)
newcam_option_scroll +=1;
while (newcam_option_scroll + newcam_option_selection > 0 && newcam_option_selection < newcam_option_scroll)
newcam_option_scroll -=1;
}
}

View File

@ -22,6 +22,9 @@
#ifdef BETTERCAMERA #ifdef BETTERCAMERA
#include "bettercamera.h" #include "bettercamera.h"
#endif #endif
#ifdef EXT_OPTIONS_MENU
#include "options_menu.h"
#endif
extern Gfx *gDisplayListHead; extern Gfx *gDisplayListHead;
extern s16 gCurrCourseNum; extern s16 gCurrCourseNum;
@ -2629,9 +2632,8 @@ s16 render_pause_courses_and_castle(void) {
#ifdef VERSION_EU #ifdef VERSION_EU
gInGameLanguage = eu_get_language(); gInGameLanguage = eu_get_language();
#endif #endif
#ifdef BETTERCAMERA #ifdef EXT_OPTIONS_MENU
if (newcam_option_open == 0) if (optmenu_open == 0) {
{
#endif #endif
switch (gDialogBoxState) { switch (gDialogBoxState) {
case DIALOG_STATE_OPENING: case DIALOG_STATE_OPENING:
@ -2708,15 +2710,13 @@ s16 render_pause_courses_and_castle(void) {
if (gDialogTextAlpha < 250) { if (gDialogTextAlpha < 250) {
gDialogTextAlpha += 25; gDialogTextAlpha += 25;
} }
#ifdef BETTERCAMERA #ifdef EXT_OPTIONS_MENU
} } else {
else
{
shade_screen(); shade_screen();
newcam_display_options(); optmenu_draw();
} }
newcam_check_pause_buttons(); optmenu_check_buttons();
newcam_render_option_text(); optmenu_draw_prompt();
#endif #endif
return 0; return 0;

View File

@ -179,7 +179,7 @@ u8 unused3[4];
u8 unused4[2]; u8 unused4[2];
// For configfile intro skipping // For configfile intro skipping
extern unsigned int configSkipIntro; //extern unsigned int configSkipIntro;
void basic_update(s16 *arg); void basic_update(s16 *arg);
@ -1217,7 +1217,7 @@ s32 init_level(void) {
if (gMarioState->action != ACT_UNINITIALIZED) { if (gMarioState->action != ACT_UNINITIALIZED) {
if (save_file_exists(gCurrSaveFileNum - 1)) { if (save_file_exists(gCurrSaveFileNum - 1)) {
set_mario_action(gMarioState, ACT_IDLE, 0); set_mario_action(gMarioState, ACT_IDLE, 0);
} else if (gCLIOpts.SkipIntro == 0 && configSkipIntro == 0) { } else if (gCLIOpts.SkipIntro == 0) {
set_mario_action(gMarioState, ACT_INTRO_CUTSCENE, 0); set_mario_action(gMarioState, ACT_INTRO_CUTSCENE, 0);
val4 = 1; val4 = 1;
} }

463
src/game/options_menu.c Normal file
View File

@ -0,0 +1,463 @@
#ifdef EXT_OPTIONS_MENU
#include "sm64.h"
#include "include/text_strings.h"
#include "engine/math_util.h"
#include "audio/external.h"
#include "game/camera.h"
#include "game/level_update.h"
#include "game/print.h"
#include "game/segment2.h"
#include "game/save_file.h"
#include "game/bettercamera.h"
#include "game/mario_misc.h"
#include "game/game_init.h"
#include "game/ingame_menu.h"
#include "game/options_menu.h"
#include "pc/configfile.h"
#include "pc/controller/controller_api.h"
#include <stdbool.h>
u8 optmenu_open = 0;
static u8 optmenu_binding = 0;
static u8 optmenu_bind_idx = 0;
// How to add stuff:
// strings: add them to include/text_strings.h.in
// and to menuStr[] / opts*Str[]
// options: add them to the relevant options list
// menus: add a new submenu definition and a new
// option to the optsMain list
static const u8 toggleStr[][64] = {
{ TEXT_OPT_DISABLED },
{ TEXT_OPT_ENABLED },
};
static const u8 menuStr[][64] = {
{ TEXT_OPT_HIGHLIGHT },
{ TEXT_OPT_BUTTON1 },
{ TEXT_OPT_BUTTON2 },
{ TEXT_OPT_OPTIONS },
{ TEXT_OPT_CAMERA },
{ TEXT_OPT_CONTROLS },
};
static const u8 optsCameraStr[][64] = {
{ TEXT_OPT_CAMX },
{ TEXT_OPT_CAMY },
{ TEXT_OPT_INVERTX },
{ TEXT_OPT_INVERTY },
{ TEXT_OPT_CAMC },
{ TEXT_OPT_CAMP },
{ TEXT_OPT_ANALOGUE },
{ TEXT_OPT_MOUSE },
};
static const u8 bindStr[][64] = {
{ TEXT_OPT_UNBOUND },
{ TEXT_OPT_PRESSKEY },
{ TEXT_BIND_A },
{ TEXT_BIND_B },
{ TEXT_BIND_START },
{ TEXT_BIND_L },
{ TEXT_BIND_R },
{ TEXT_BIND_Z },
{ TEXT_BIND_C_UP },
{ TEXT_BIND_C_DOWN },
{ TEXT_BIND_C_LEFT },
{ TEXT_BIND_C_RIGHT },
{ TEXT_BIND_UP },
{ TEXT_BIND_DOWN },
{ TEXT_BIND_LEFT },
{ TEXT_BIND_RIGHT },
};
enum OptType {
OPT_INVALID = 0,
OPT_TOGGLE,
OPT_CHOICE,
OPT_SCROLL,
OPT_SUBMENU,
OPT_BIND,
};
struct SubMenu;
struct Option {
enum OptType type;
const u8 *label;
union {
u32 *uval;
bool *bval;
};
union {
struct {
const u8 **choices;
u32 numChoices;
};
struct {
u32 scrMin;
u32 scrMax;
u32 scrStep;
};
struct SubMenu *nextMenu;
};
};
struct SubMenu {
struct SubMenu *prev; // this is set at runtime to avoid needless complication
const u8 *label;
struct Option *opts;
s32 numOpts;
s32 select;
s32 scroll;
};
/* submenu option lists */
static struct Option optsCamera[] = {
{ .type = OPT_TOGGLE, .label = optsCameraStr[6], .bval = &configEnableCamera, },
{ .type = OPT_TOGGLE, .label = optsCameraStr[7], .bval = &configCameraMouse, },
{ .type = OPT_TOGGLE, .label = optsCameraStr[2], .bval = &configCameraInvertX, },
{ .type = OPT_TOGGLE, .label = optsCameraStr[3], .bval = &configCameraInvertY, },
{ .type = OPT_SCROLL, .label = optsCameraStr[0], .uval = &configCameraXSens, .scrMin = 10, .scrMax = 250, .scrStep = 1 },
{ .type = OPT_SCROLL, .label = optsCameraStr[1], .uval = &configCameraYSens, .scrMin = 10, .scrMax = 250, .scrStep = 1 },
{ .type = OPT_SCROLL, .label = optsCameraStr[4], .uval = &configCameraAggr, .scrMin = 0, .scrMax = 100, .scrStep = 1 },
{ .type = OPT_SCROLL, .label = optsCameraStr[5], .uval = &configCameraPan, .scrMin = 0, .scrMax = 100, .scrStep = 1 },
};
static struct Option optsControls[] = {
{ .type = OPT_BIND, .label = bindStr[2], .uval = configKeyA, },
{ .type = OPT_BIND, .label = bindStr[3], .uval = configKeyB, },
{ .type = OPT_BIND, .label = bindStr[4], .uval = configKeyStart, },
{ .type = OPT_BIND, .label = bindStr[5], .uval = configKeyL, },
{ .type = OPT_BIND, .label = bindStr[6], .uval = configKeyR, },
{ .type = OPT_BIND, .label = bindStr[7], .uval = configKeyZ, },
{ .type = OPT_BIND, .label = bindStr[8], .uval = configKeyCUp, },
{ .type = OPT_BIND, .label = bindStr[9], .uval = configKeyCDown, },
{ .type = OPT_BIND, .label = bindStr[10], .uval = configKeyCLeft, },
{ .type = OPT_BIND, .label = bindStr[11], .uval = configKeyCRight, },
{ .type = OPT_BIND, .label = bindStr[12], .uval = configKeyStickUp, },
{ .type = OPT_BIND, .label = bindStr[13], .uval = configKeyStickDown, },
{ .type = OPT_BIND, .label = bindStr[14], .uval = configKeyStickLeft, },
{ .type = OPT_BIND, .label = bindStr[15], .uval = configKeyStickRight, },
};
/* submenu definitions */
static struct SubMenu menuCamera = {
.label = menuStr[4],
.opts = optsCamera,
.numOpts = sizeof(optsCamera) / sizeof(optsCamera[0]),
};
static struct SubMenu menuControls = {
.label = menuStr[5],
.opts = optsControls,
.numOpts = sizeof(optsControls) / sizeof(optsControls[0]),
};
/* main options menu definition */
static struct Option optsMain[] = {
{ .type = OPT_SUBMENU, .label = menuStr[4], .nextMenu = &menuCamera, },
{ .type = OPT_SUBMENU, .label = menuStr[5], .nextMenu = &menuControls, },
};
static struct SubMenu menuMain = {
.label = menuStr[3],
.opts = optsMain,
.numOpts = sizeof(optsMain) / sizeof(optsMain[0]),
};
/* implementation */
static s32 optmenu_option_timer = 0;
static u8 optmenu_hold_count = 0;
static struct SubMenu *currentMenu = &menuMain;
static inline s32 wrap_add(s32 a, const s32 b, const s32 min, const s32 max) {
a += b;
if (a < min) a = max - (min - a) + 1;
else if (a > max) a = min + (a - max) - 1;
return a;
}
static void uint_to_hex(u32 num, u8 *dst) {
u8 places = 4;
while (places--) {
const u32 digit = num & 0xF;
dst[places] = digit;
num >>= 4;
}
dst[4] = DIALOG_CHAR_TERMINATOR;
}
//Displays a box.
static void optmenu_draw_box(s16 x1, s16 y1, s16 x2, s16 y2, u8 r, u8 g, u8 b) {
gDPPipeSync(gDisplayListHead++);
gDPSetRenderMode(gDisplayListHead++, G_RM_OPA_SURF, G_RM_OPA_SURF2);
gDPSetCycleType(gDisplayListHead++, G_CYC_FILL);
gDPSetFillColor(gDisplayListHead++, GPACK_RGBA5551(r, g, b, 255));
gDPFillRectangle(gDisplayListHead++, x1, y1, x2 - 1, y2 - 1);
gDPPipeSync(gDisplayListHead++);
gDPSetCycleType(gDisplayListHead++, G_CYC_1CYCLE);
}
static void optmenu_draw_text(s16 x, s16 y, const u8 *str, u8 col) {
const u8 textX = get_str_x_pos_from_center(x, (u8*)str, 10.0f);
gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255);
print_generic_string(textX+1, y-1, str);
if (col == 0) {
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
} else {
gDPSetEnvColor(gDisplayListHead++, 255, 32, 32, 255);
}
print_generic_string(textX, y, str);
}
static void optmenu_draw_opt(const struct Option *opt, s16 x, s16 y, u8 sel) {
u8 buf[32] = { 0 };
if (opt->type == OPT_SUBMENU)
y -= 6;
optmenu_draw_text(x, y, opt->label, sel);
switch (opt->type) {
case OPT_TOGGLE:
optmenu_draw_text(x, y-13, toggleStr[(int)*opt->bval], sel);
break;
case OPT_CHOICE:
optmenu_draw_text(x, y-13, opt->choices[*opt->uval], sel);
break;
case OPT_SCROLL:
int_to_str(*opt->uval, buf);
optmenu_draw_text(x, y-13, buf, sel);
break;
case OPT_BIND:
x = 112;
for (u8 i = 0; i < MAX_BINDS; ++i, x += 48) {
const u8 white = (sel && (optmenu_bind_idx == i));
// TODO: button names
if (opt->uval[i] == VK_INVALID) {
if (optmenu_binding && white)
optmenu_draw_text(x, y-13, bindStr[1], 1);
else
optmenu_draw_text(x, y-13, bindStr[0], white);
} else {
uint_to_hex(opt->uval[i], buf);
optmenu_draw_text(x, y-13, buf, white);
}
}
break;
default: break;
};
}
static void optmenu_opt_change(struct Option *opt, s32 val) {
switch (opt->type) {
case OPT_TOGGLE:
*opt->bval = !*opt->bval;
break;
case OPT_CHOICE:
*opt->uval = wrap_add(*opt->uval, val, 0, opt->numChoices - 1);
break;
case OPT_SCROLL:
*opt->uval = wrap_add(*opt->uval, val, opt->scrMin, opt->scrMax);
break;
case OPT_SUBMENU:
opt->nextMenu->prev = currentMenu;
currentMenu = opt->nextMenu;
break;
case OPT_BIND:
if (val == 0xFF) {
// clear the bind
opt->uval[optmenu_bind_idx] = VK_INVALID;
} else if (val == 0) {
opt->uval[optmenu_bind_idx] = VK_INVALID;
optmenu_binding = 1;
controller_get_raw_key(); // clear the last key, which is probably A
} else {
optmenu_bind_idx = wrap_add(optmenu_bind_idx, val, 0, MAX_BINDS - 1);
}
break;
default: break;
};
}
static inline s16 get_hudstr_centered_x(const s16 sx, const u8 *str) {
const u8 *chr = str;
s16 len = 0;
while (*chr != GLOBAR_CHAR_TERMINATOR) ++chr, ++len;
return sx - len * 6; // stride is 12
}
//Options menu
void optmenu_draw(void) {
s16 scroll;
s16 scrollpos;
const s16 labelX = get_hudstr_centered_x(160, currentMenu->label);
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin);
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
print_hud_lut_string(HUD_LUT_GLOBAL, labelX, 40, currentMenu->label);
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end);
if (currentMenu->numOpts > 4) {
optmenu_draw_box(272, 90, 280, 208, 0x80, 0x80, 0x80);
scrollpos = 54 * ((f32)currentMenu->scroll / (currentMenu->numOpts-4));
optmenu_draw_box(272, 90+scrollpos, 280, 154+scrollpos, 0xFF, 0xFF, 0xFF);
}
gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 80, SCREEN_WIDTH, SCREEN_HEIGHT);
for (u8 i = 0; i < currentMenu->numOpts; i++) {
scroll = 140 - 32 * i + currentMenu->scroll * 32;
// FIXME: just start from the first visible option bruh
if (scroll <= 140 && scroll > 32)
optmenu_draw_opt(&currentMenu->opts[i], 160, scroll, (currentMenu->select == i));
}
gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin);
print_hud_lut_string(HUD_LUT_GLOBAL, 80, 90 + (32 * (currentMenu->select - currentMenu->scroll)), menuStr[0]);
print_hud_lut_string(HUD_LUT_GLOBAL, 224, 90 + (32 * (currentMenu->select - currentMenu->scroll)), menuStr[0]);
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end);
}
//This has been separated for interesting reasons. Don't question it.
void optmenu_draw_prompt(void) {
gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
optmenu_draw_text(278, 212, menuStr[1 + optmenu_open], 0);
gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
}
void optmenu_toggle(void) {
if (optmenu_open == 0) {
#ifndef nosound
play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs);
#endif
currentMenu = &menuMain;
optmenu_open = 1;
} else {
#ifndef nosound
play_sound(SOUND_MENU_MARIO_CASTLE_WARP2, gDefaultSoundArgs);
#endif
optmenu_open = 0;
newcam_init_settings(); // load bettercam settings from config vars
controller_reconfigure(); // rebind using new config values
configfile_save(CONFIG_FILE);
}
}
void optmenu_check_buttons(void) {
if (optmenu_binding) {
u32 key = controller_get_raw_key();
if (key != VK_INVALID) {
#ifndef nosound
play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs);
#endif
currentMenu->opts[currentMenu->select].uval[optmenu_bind_idx] = key;
optmenu_binding = 0;
optmenu_option_timer = 12;
}
return;
}
if (gPlayer1Controller->buttonPressed & R_TRIG)
optmenu_toggle();
if (!optmenu_open) return;
u8 allowInput = 0;
optmenu_option_timer--;
if (optmenu_option_timer <= 0) {
if (optmenu_hold_count == 0) {
optmenu_hold_count++;
optmenu_option_timer = 10;
} else {
optmenu_option_timer = 3;
}
allowInput = 1;
}
if (ABS(gPlayer1Controller->stickY) > 60) {
if (allowInput) {
#ifndef nosound
play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs);
#endif
if (gPlayer1Controller->stickY >= 60) {
currentMenu->select--;
if (currentMenu->select < 0)
currentMenu->select = currentMenu->numOpts-1;
} else {
currentMenu->select++;
if (currentMenu->select >= currentMenu->numOpts)
currentMenu->select = 0;
}
if (currentMenu->select < currentMenu->scroll)
currentMenu->scroll = currentMenu->select;
else if (currentMenu->select > currentMenu->scroll + 3)
currentMenu->scroll = currentMenu->select - 3;
}
} else if (ABS(gPlayer1Controller->stickX) > 60) {
if (allowInput) {
#ifndef nosound
play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs);
#endif
if (gPlayer1Controller->stickX >= 60)
optmenu_opt_change(&currentMenu->opts[currentMenu->select], 1);
else
optmenu_opt_change(&currentMenu->opts[currentMenu->select], -1);
}
} else if (gPlayer1Controller->buttonPressed & A_BUTTON) {
if (allowInput) {
#ifndef nosound
play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs);
#endif
optmenu_opt_change(&currentMenu->opts[currentMenu->select], 0);
}
} else if (gPlayer1Controller->buttonPressed & B_BUTTON) {
if (allowInput) {
if (currentMenu->prev) {
#ifndef nosound
play_sound(SOUND_MENU_CHANGE_SELECT, gDefaultSoundArgs);
#endif
currentMenu = currentMenu->prev;
} else {
// can't go back, exit the menu altogether
optmenu_toggle();
}
}
} else if (gPlayer1Controller->buttonPressed & Z_TRIG) {
// HACK: clear binds with Z
if (allowInput && currentMenu->opts[currentMenu->select].type == OPT_BIND)
optmenu_opt_change(&currentMenu->opts[currentMenu->select], 0xFF);
} else if (gPlayer1Controller->buttonPressed & START_BUTTON) {
if (allowInput) optmenu_toggle();
} else {
optmenu_hold_count = 0;
optmenu_option_timer = 0;
}
}
#endif // EXT_OPTIONS_MENU

11
src/game/options_menu.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef OPTIONS_MENU_H
#define OPTIONS_MENU_H
void optmenu_toggle(void);
void optmenu_draw(void);
void optmenu_draw_prompt(void);
void optmenu_check_buttons(void);
extern u8 optmenu_open;
#endif // OPTIONS_MENU_H

View File

@ -7,6 +7,7 @@
#include <ctype.h> #include <ctype.h>
#include "configfile.h" #include "configfile.h"
#include "controller/controller_api.h"
#define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0])) #define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0]))
@ -14,6 +15,7 @@ enum ConfigOptionType {
CONFIG_TYPE_BOOL, CONFIG_TYPE_BOOL,
CONFIG_TYPE_UINT, CONFIG_TYPE_UINT,
CONFIG_TYPE_FLOAT, CONFIG_TYPE_FLOAT,
CONFIG_TYPE_BIND,
}; };
struct ConfigOption { struct ConfigOption {
@ -29,35 +31,24 @@ struct ConfigOption {
/* /*
*Config options and default values *Config options and default values
*/ */
bool configFullscreen = false;
// Keyboard mappings (scancode values) bool configFullscreen = false;
unsigned int configKeyA = 0x26; // Keyboard mappings (VK_ values, by default keyboard/gamepad/mouse)
unsigned int configKeyB = 0x33; unsigned int configKeyA[MAX_BINDS] = { 0x0026, 0x1000, 0x1103 };
unsigned int configKeyStart = 0x39; unsigned int configKeyB[MAX_BINDS] = { 0x0033, 0x1002, 0x1101 };
unsigned int configKeyL = 0x34; unsigned int configKeyStart[MAX_BINDS] = { 0x0039, 0x1006, VK_INVALID };
unsigned int configKeyR = 0x36; unsigned int configKeyL[MAX_BINDS] = { 0x0034, 0x1007, 0x1104 };
unsigned int configKeyZ = 0x25; unsigned int configKeyR[MAX_BINDS] = { 0x0036, 0x100A, 0x1105 };
unsigned int configKeyCUp = 0x148; unsigned int configKeyZ[MAX_BINDS] = { 0x0025, 0x1009, 0x1102 };
unsigned int configKeyCDown = 0x150; unsigned int configKeyCUp[MAX_BINDS] = { 0x0148, VK_INVALID, VK_INVALID };
unsigned int configKeyCLeft = 0x14B; unsigned int configKeyCDown[MAX_BINDS] = { 0x0150, VK_INVALID, VK_INVALID };
unsigned int configKeyCRight = 0x14D; unsigned int configKeyCLeft[MAX_BINDS] = { 0x014B, VK_INVALID, VK_INVALID };
unsigned int configKeyStickUp = 0x11; unsigned int configKeyCRight[MAX_BINDS] = { 0x014D, VK_INVALID, VK_INVALID };
unsigned int configKeyStickDown = 0x1F; unsigned int configKeyStickUp[MAX_BINDS] = { 0x0011, VK_INVALID, VK_INVALID };
unsigned int configKeyStickLeft = 0x1E; unsigned int configKeyStickDown[MAX_BINDS] = { 0x001F, VK_INVALID, VK_INVALID };
unsigned int configKeyStickRight = 0x20; unsigned int configKeyStickLeft[MAX_BINDS] = { 0x001E, VK_INVALID, VK_INVALID };
// Gamepad mappings (SDL_GameControllerButton values) unsigned int configKeyStickRight[MAX_BINDS] = { 0x0020, VK_INVALID, VK_INVALID };
unsigned int configJoyA = 0;
unsigned int configJoyB = 2;
unsigned int configJoyStart = 6;
unsigned int configJoyL = 7;
unsigned int configJoyR = 10;
unsigned int configJoyZ = 9;
// Mouse button mappings (0 for none, 1 for left, 2 for middle, 3 for right)
unsigned int configMouseA = 3;
unsigned int configMouseB = 1;
unsigned int configMouseL = 4;
unsigned int configMouseR = 5;
unsigned int configMouseZ = 2;
#ifdef BETTERCAMERA #ifdef BETTERCAMERA
// BetterCamera settings // BetterCamera settings
unsigned int configCameraXSens = 50; unsigned int configCameraXSens = 50;
@ -69,35 +60,23 @@ bool configCameraInvertY = false;
bool configEnableCamera = false; bool configEnableCamera = false;
bool configCameraMouse = false; bool configCameraMouse = false;
#endif #endif
unsigned int configSkipIntro = 0;
static const struct ConfigOption options[] = { static const struct ConfigOption options[] = {
{.name = "fullscreen", .type = CONFIG_TYPE_BOOL, .boolValue = &configFullscreen}, {.name = "fullscreen", .type = CONFIG_TYPE_BOOL, .boolValue = &configFullscreen},
{.name = "key_a", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyA}, {.name = "key_a", .type = CONFIG_TYPE_BIND, .uintValue = configKeyA},
{.name = "key_b", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyB}, {.name = "key_b", .type = CONFIG_TYPE_BIND, .uintValue = configKeyB},
{.name = "key_start", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStart}, {.name = "key_start", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStart},
{.name = "key_l", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyL}, {.name = "key_l", .type = CONFIG_TYPE_BIND, .uintValue = configKeyL},
{.name = "key_r", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyR}, {.name = "key_r", .type = CONFIG_TYPE_BIND, .uintValue = configKeyR},
{.name = "key_z", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyZ}, {.name = "key_z", .type = CONFIG_TYPE_BIND, .uintValue = configKeyZ},
{.name = "key_cup", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCUp}, {.name = "key_cup", .type = CONFIG_TYPE_BIND, .uintValue = configKeyCUp},
{.name = "key_cdown", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCDown}, {.name = "key_cdown", .type = CONFIG_TYPE_BIND, .uintValue = configKeyCDown},
{.name = "key_cleft", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCLeft}, {.name = "key_cleft", .type = CONFIG_TYPE_BIND, .uintValue = configKeyCLeft},
{.name = "key_cright", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyCRight}, {.name = "key_cright", .type = CONFIG_TYPE_BIND, .uintValue = configKeyCRight},
{.name = "key_stickup", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickUp}, {.name = "key_stickup", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickUp},
{.name = "key_stickdown", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickDown}, {.name = "key_stickdown", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickDown},
{.name = "key_stickleft", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickLeft}, {.name = "key_stickleft", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickLeft},
{.name = "key_stickright", .type = CONFIG_TYPE_UINT, .uintValue = &configKeyStickRight}, {.name = "key_stickright", .type = CONFIG_TYPE_BIND, .uintValue = configKeyStickRight},
{.name = "joy_a", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyA},
{.name = "joy_b", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyB},
{.name = "joy_start", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyStart},
{.name = "joy_l", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyL},
{.name = "joy_r", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyR},
{.name = "joy_z", .type = CONFIG_TYPE_UINT, .uintValue = &configJoyZ},
{.name = "mouse_a", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseA},
{.name = "mouse_b", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseB},
{.name = "mouse_l", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseL},
{.name = "mouse_r", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseR},
{.name = "mouse_z", .type = CONFIG_TYPE_UINT, .uintValue = &configMouseZ},
#ifdef BETTERCAMERA #ifdef BETTERCAMERA
{.name = "bettercam_enable", .type = CONFIG_TYPE_BOOL, .boolValue = &configEnableCamera}, {.name = "bettercam_enable", .type = CONFIG_TYPE_BOOL, .boolValue = &configEnableCamera},
{.name = "bettercam_mouse_look", .type = CONFIG_TYPE_BOOL, .boolValue = &configCameraMouse}, {.name = "bettercam_mouse_look", .type = CONFIG_TYPE_BOOL, .boolValue = &configCameraMouse},
@ -108,7 +87,7 @@ static const struct ConfigOption options[] = {
{.name = "bettercam_aggression", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraAggr}, {.name = "bettercam_aggression", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraAggr},
{.name = "bettercam_pan_level", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraPan}, {.name = "bettercam_pan_level", .type = CONFIG_TYPE_UINT, .uintValue = &configCameraPan},
#endif #endif
{.name = "skip_intro", .type = CONFIG_TYPE_UINT, .uintValue = &configSkipIntro}, //{.name = "skip_intro", .type = CONFIG_TYPE_UINT, .uintValue = &configSkipIntro}, // Add this back!
}; };
// Reads an entire line from a file (excluding the newline character) and returns an allocated string // Reads an entire line from a file (excluding the newline character) and returns an allocated string
@ -209,9 +188,13 @@ void configfile_load(const char *filename) {
while (isspace(*p)) while (isspace(*p))
p++; p++;
if (!*p || *p == '#') // comment or empty line
continue;
numTokens = tokenize_string(p, 2, tokens); numTokens = tokenize_string(p, 2, tokens);
if (numTokens != 0) { if (numTokens != 0) {
if (numTokens == 2) { if (numTokens >= 2) {
const struct ConfigOption *option = NULL; const struct ConfigOption *option = NULL;
for (unsigned int i = 0; i < ARRAY_LEN(options); i++) { for (unsigned int i = 0; i < ARRAY_LEN(options); i++) {
@ -233,6 +216,10 @@ void configfile_load(const char *filename) {
case CONFIG_TYPE_UINT: case CONFIG_TYPE_UINT:
sscanf(tokens[1], "%u", option->uintValue); sscanf(tokens[1], "%u", option->uintValue);
break; break;
case CONFIG_TYPE_BIND:
for (int i = 0; i < MAX_BINDS && i < numTokens - 1; ++i)
sscanf(tokens[i + 1], "%x", option->uintValue + i);
break;
case CONFIG_TYPE_FLOAT: case CONFIG_TYPE_FLOAT:
sscanf(tokens[1], "%f", option->floatValue); sscanf(tokens[1], "%f", option->floatValue);
break; break;
@ -275,6 +262,12 @@ void configfile_save(const char *filename) {
case CONFIG_TYPE_FLOAT: case CONFIG_TYPE_FLOAT:
fprintf(file, "%s %f\n", option->name, *option->floatValue); fprintf(file, "%s %f\n", option->name, *option->floatValue);
break; break;
case CONFIG_TYPE_BIND:
fprintf(file, "%s ", option->name);
for (int i = 0; i < MAX_BINDS; ++i)
fprintf(file, "%04x ", option->uintValue[i]);
fprintf(file, "\n");
break;
default: default:
assert(0); // unknown type assert(0); // unknown type
} }

View File

@ -4,34 +4,23 @@
#include <stdbool.h> #include <stdbool.h>
#define CONFIG_FILE "sm64config.txt" #define CONFIG_FILE "sm64config.txt"
#define MAX_BINDS 3
extern bool configFullscreen; extern bool configFullscreen;
extern unsigned int configKeyA; extern unsigned int configKeyA[];
extern unsigned int configKeyB; extern unsigned int configKeyB[];
extern unsigned int configKeyStart; extern unsigned int configKeyStart[];
extern unsigned int configKeyL; extern unsigned int configKeyL[];
extern unsigned int configKeyR; extern unsigned int configKeyR[];
extern unsigned int configKeyZ; extern unsigned int configKeyZ[];
extern unsigned int configKeyCUp; extern unsigned int configKeyCUp[];
extern unsigned int configKeyCDown; extern unsigned int configKeyCDown[];
extern unsigned int configKeyCLeft; extern unsigned int configKeyCLeft[];
extern unsigned int configKeyCRight; extern unsigned int configKeyCRight[];
extern unsigned int configKeyStickUp; extern unsigned int configKeyStickUp[];
extern unsigned int configKeyStickDown; extern unsigned int configKeyStickDown[];
extern unsigned int configKeyStickLeft; extern unsigned int configKeyStickLeft[];
extern unsigned int configKeyStickRight; extern unsigned int configKeyStickRight[];
extern unsigned int configJoyA;
extern unsigned int configJoyB;
extern unsigned int configJoyStart;
extern unsigned int configJoyL;
extern unsigned int configJoyR;
extern unsigned int configJoyZ;
extern unsigned int configMouseA;
extern unsigned int configMouseB;
extern unsigned int configMouseStart;
extern unsigned int configMouseL;
extern unsigned int configMouseR;
extern unsigned int configMouseZ;
#ifdef BETTERCAMERA #ifdef BETTERCAMERA
extern unsigned int configCameraXSens; extern unsigned int configCameraXSens;
extern unsigned int configCameraYSens; extern unsigned int configCameraYSens;

View File

@ -2,15 +2,21 @@
#define CONTROLLER_API #define CONTROLLER_API
#define DEADZONE 4960 #define DEADZONE 4960
#define VK_INVALID 0xFFFF
// Analog camera movement by Pathétique (github.com/vrmiguel), y0shin and Mors #define VK_SIZE 0x1000
// Contribute or communicate bugs at github.com/vrmiguel/sm64-analog-camera
#include <ultra64.h> #include <ultra64.h>
struct ControllerAPI { struct ControllerAPI {
void (*init)(void); const u32 vkbase; // base number in the virtual keyspace (e.g. keyboard is 0x0000-0x1000)
void (*read)(OSContPad *pad); void (*init)(void); // call once, also calls reconfig()
void (*read)(OSContPad *pad); // read controller and update N64 pad values
u32 (*rawkey)(void); // returns last pressed virtual key or VK_INVALID if none
void (*reconfig)(void); // (optional) call when bindings have changed
}; };
// used for binding keys
u32 controller_get_raw_key(void);
void controller_reconfigure(void);
#endif #endif

View File

@ -56,3 +56,18 @@ void osContGetReadData(OSContPad *pad) {
controller_implementations[i]->read(pad); controller_implementations[i]->read(pad);
} }
} }
u32 controller_get_raw_key(void) {
for (size_t i = 0; i < sizeof(controller_implementations) / sizeof(struct ControllerAPI *); i++) {
u32 vk = controller_implementations[i]->rawkey();
if (vk != VK_INVALID) return vk + controller_implementations[i]->vkbase;
}
return VK_INVALID;
}
void controller_reconfigure(void) {
for (size_t i = 0; i < sizeof(controller_implementations) / sizeof(struct ControllerAPI *); i++) {
if (controller_implementations[i]->reconfig)
controller_implementations[i]->reconfig();
}
}

View File

@ -8,14 +8,19 @@
#endif #endif
#include "../configfile.h" #include "../configfile.h"
#include "controller_keyboard.h"
static int keyboard_buttons_down; static int keyboard_buttons_down;
static int keyboard_mapping[14][2]; #define MAX_KEYBINDS 64
static int keyboard_mapping[MAX_KEYBINDS][2];
static int num_keybinds = 0;
static u32 keyboard_lastkey = VK_INVALID;
static int keyboard_map_scancode(int scancode) { static int keyboard_map_scancode(int scancode) {
int ret = 0; int ret = 0;
for (size_t i = 0; i < sizeof(keyboard_mapping) / sizeof(keyboard_mapping[0]); i++) { for (int i = 0; i < num_keybinds; i++) {
if (keyboard_mapping[i][0] == scancode) { if (keyboard_mapping[i][0] == scancode) {
ret |= keyboard_mapping[i][1]; ret |= keyboard_mapping[i][1];
} }
@ -26,12 +31,15 @@ static int keyboard_map_scancode(int scancode) {
bool keyboard_on_key_down(int scancode) { bool keyboard_on_key_down(int scancode) {
int mapped = keyboard_map_scancode(scancode); int mapped = keyboard_map_scancode(scancode);
keyboard_buttons_down |= mapped; keyboard_buttons_down |= mapped;
keyboard_lastkey = scancode;
return mapped != 0; return mapped != 0;
} }
bool keyboard_on_key_up(int scancode) { bool keyboard_on_key_up(int scancode) {
int mapped = keyboard_map_scancode(scancode); int mapped = keyboard_map_scancode(scancode);
keyboard_buttons_down &= ~mapped; keyboard_buttons_down &= ~mapped;
if (keyboard_lastkey == (u32) scancode)
keyboard_lastkey = VK_INVALID;
return mapped != 0; return mapped != 0;
} }
@ -39,28 +47,38 @@ void keyboard_on_all_keys_up(void) {
keyboard_buttons_down = 0; keyboard_buttons_down = 0;
} }
static void set_keyboard_mapping(int index, int mask, int scancode) { static void keyboard_add_binds(int mask, unsigned int *scancode) {
keyboard_mapping[index][0] = scancode; for (int i = 0; i < MAX_BINDS && num_keybinds < MAX_KEYBINDS; ++i) {
keyboard_mapping[index][1] = mask; if (scancode[i] < VK_BASE_KEYBOARD + VK_SIZE) {
keyboard_mapping[num_keybinds][0] = scancode[i];
keyboard_mapping[num_keybinds][1] = mask;
num_keybinds++;
}
}
}
static void keyboard_bindkeys(void) {
bzero(keyboard_mapping, sizeof(keyboard_mapping));
num_keybinds = 0;
keyboard_add_binds(0x80000, configKeyStickUp);
keyboard_add_binds(0x10000, configKeyStickLeft);
keyboard_add_binds(0x40000, configKeyStickDown);
keyboard_add_binds(0x20000, configKeyStickRight);
keyboard_add_binds(A_BUTTON, configKeyA);
keyboard_add_binds(B_BUTTON, configKeyB);
keyboard_add_binds(Z_TRIG, configKeyZ);
keyboard_add_binds(U_CBUTTONS, configKeyCUp);
keyboard_add_binds(L_CBUTTONS, configKeyCLeft);
keyboard_add_binds(D_CBUTTONS, configKeyCDown);
keyboard_add_binds(R_CBUTTONS, configKeyCRight);
keyboard_add_binds(L_TRIG, configKeyL);
keyboard_add_binds(R_TRIG, configKeyR);
keyboard_add_binds(START_BUTTON, configKeyStart);
} }
static void keyboard_init(void) { static void keyboard_init(void) {
int i = 0; keyboard_bindkeys();
set_keyboard_mapping(i++, 0x80000, configKeyStickUp);
set_keyboard_mapping(i++, 0x10000, configKeyStickLeft);
set_keyboard_mapping(i++, 0x40000, configKeyStickDown);
set_keyboard_mapping(i++, 0x20000, configKeyStickRight);
set_keyboard_mapping(i++, A_BUTTON, configKeyA);
set_keyboard_mapping(i++, B_BUTTON, configKeyB);
set_keyboard_mapping(i++, Z_TRIG, configKeyZ);
set_keyboard_mapping(i++, U_CBUTTONS, configKeyCUp);
set_keyboard_mapping(i++, L_CBUTTONS, configKeyCLeft);
set_keyboard_mapping(i++, D_CBUTTONS, configKeyCDown);
set_keyboard_mapping(i++, R_CBUTTONS, configKeyCRight);
set_keyboard_mapping(i++, L_TRIG, configKeyL);
set_keyboard_mapping(i++, R_TRIG, configKeyR);
set_keyboard_mapping(i++, START_BUTTON, configKeyStart);
#ifdef TARGET_WEB #ifdef TARGET_WEB
controller_emscripten_keyboard_init(); controller_emscripten_keyboard_init();
@ -83,7 +101,16 @@ static void keyboard_read(OSContPad *pad) {
} }
} }
static u32 keyboard_rawkey(void) {
const u32 ret = keyboard_lastkey;
keyboard_lastkey = VK_INVALID;
return ret;
}
struct ControllerAPI controller_keyboard = { struct ControllerAPI controller_keyboard = {
VK_BASE_KEYBOARD,
keyboard_init, keyboard_init,
keyboard_read keyboard_read,
keyboard_rawkey,
keyboard_bindkeys,
}; };

View File

@ -4,6 +4,8 @@
#include <stdbool.h> #include <stdbool.h>
#include "controller_api.h" #include "controller_api.h"
# define VK_BASE_KEYBOARD 0x0000
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif

View File

@ -23,7 +23,14 @@ static void tas_read(OSContPad *pad) {
} }
} }
static u32 tas_rawkey(void) {
return VK_INVALID;
}
struct ControllerAPI controller_recorded_tas = { struct ControllerAPI controller_recorded_tas = {
VK_INVALID,
tas_init, tas_init,
tas_read tas_read,
tas_rawkey,
NULL, // no rebinding
}; };

View File

@ -11,9 +11,15 @@
#include <ultra64.h> #include <ultra64.h>
#include "controller_api.h" #include "controller_api.h"
#include "controller_sdl.h"
#include "../configfile.h" #include "../configfile.h"
// mouse buttons are also in the controller namespace (why), just offset 0x100
#define VK_OFS_SDL_MOUSE 0x0100
#define VK_BASE_SDL_MOUSE (VK_BASE_SDL_GAMEPAD + VK_OFS_SDL_MOUSE)
#define MAX_JOYBINDS 32
#define MAX_MOUSEBUTTONS 8 // arbitrary
extern int16_t rightx; extern int16_t rightx;
extern int16_t righty; extern int16_t righty;
@ -27,6 +33,51 @@ extern u8 newcam_mouse;
static bool init_ok; static bool init_ok;
static SDL_GameController *sdl_cntrl; static SDL_GameController *sdl_cntrl;
static u32 num_joy_binds = 0;
static u32 num_mouse_binds = 0;
static u32 joy_binds[MAX_JOYBINDS][2];
static u32 mouse_binds[MAX_JOYBINDS][2];
static bool joy_buttons[SDL_CONTROLLER_BUTTON_MAX ] = { false };
static u32 mouse_buttons = 0;
static u32 last_mouse = VK_INVALID;
static u32 last_joybutton = VK_INVALID;
static inline void controller_add_binds(const u32 mask, const u32 *btns) {
for (u32 i = 0; i < MAX_BINDS; ++i) {
if (btns[i] >= VK_BASE_SDL_GAMEPAD && btns[i] <= VK_BASE_SDL_GAMEPAD + VK_SIZE) {
if (btns[i] >= VK_BASE_SDL_MOUSE && num_joy_binds < MAX_JOYBINDS) {
mouse_binds[num_mouse_binds][0] = btns[i] - VK_BASE_SDL_MOUSE;
mouse_binds[num_mouse_binds][1] = mask;
++num_mouse_binds;
} else if (num_mouse_binds < MAX_JOYBINDS) {
joy_binds[num_joy_binds][0] = btns[i] - VK_BASE_SDL_GAMEPAD;
joy_binds[num_joy_binds][1] = mask;
++num_joy_binds;
}
}
}
}
static void controller_sdl_bind(void) {
bzero(joy_binds, sizeof(joy_binds));
bzero(mouse_binds, sizeof(mouse_binds));
num_joy_binds = 0;
num_mouse_binds = 0;
controller_add_binds(A_BUTTON, configKeyA);
controller_add_binds(B_BUTTON, configKeyB);
controller_add_binds(Z_TRIG, configKeyZ);
controller_add_binds(U_CBUTTONS, configKeyCUp);
controller_add_binds(L_CBUTTONS, configKeyCLeft);
controller_add_binds(D_CBUTTONS, configKeyCDown);
controller_add_binds(R_CBUTTONS, configKeyCRight);
controller_add_binds(L_TRIG, configKeyL);
controller_add_binds(R_TRIG, configKeyR);
controller_add_binds(START_BUTTON, configKeyStart);
}
static void controller_sdl_init(void) { static void controller_sdl_init(void) {
if (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_EVENTS) != 0) { if (SDL_Init(SDL_INIT_GAMECONTROLLER | SDL_INIT_EVENTS) != 0) {
fprintf(stderr, "SDL init error: %s\n", SDL_GetError()); fprintf(stderr, "SDL init error: %s\n", SDL_GetError());
@ -39,6 +90,8 @@ static void controller_sdl_init(void) {
SDL_GetRelativeMouseState(&mouse_x, &mouse_y); SDL_GetRelativeMouseState(&mouse_x, &mouse_y);
#endif #endif
controller_sdl_bind();
init_ok = true; init_ok = true;
} }
@ -53,13 +106,16 @@ static void controller_sdl_read(OSContPad *pad) {
else else
SDL_SetRelativeMouseMode(SDL_FALSE); SDL_SetRelativeMouseMode(SDL_FALSE);
const u32 mbuttons = SDL_GetRelativeMouseState(&mouse_x, &mouse_y); u32 mouse = SDL_GetRelativeMouseState(&mouse_x, &mouse_y);
for (u32 i = 0; i < num_mouse_binds; ++i)
if (mouse & SDL_BUTTON(mouse_binds[i][0]))
pad->button |= mouse_binds[i][1];
// remember buttons that changed from 0 to 1
last_mouse = (mouse_buttons ^ mouse) & mouse;
mouse_buttons = mouse;
if (configMouseA && (mbuttons & SDL_BUTTON(configMouseA))) pad->button |= A_BUTTON;
if (configMouseB && (mbuttons & SDL_BUTTON(configMouseB))) pad->button |= B_BUTTON;
if (configMouseL && (mbuttons & SDL_BUTTON(configMouseL))) pad->button |= L_TRIG;
if (configMouseR && (mbuttons & SDL_BUTTON(configMouseR))) pad->button |= R_TRIG;
if (configMouseZ && (mbuttons & SDL_BUTTON(configMouseZ))) pad->button |= Z_TRIG;
#endif #endif
SDL_GameControllerUpdate(); SDL_GameControllerUpdate();
@ -82,12 +138,16 @@ static void controller_sdl_read(OSContPad *pad) {
} }
} }
if (SDL_GameControllerGetButton(sdl_cntrl, configJoyStart)) pad->button |= START_BUTTON; for (u32 i = 0; i < SDL_CONTROLLER_BUTTON_MAX; ++i) {
if (SDL_GameControllerGetButton(sdl_cntrl, configJoyZ)) pad->button |= Z_TRIG; const bool new = SDL_GameControllerGetButton(sdl_cntrl, i);
if (SDL_GameControllerGetButton(sdl_cntrl, configJoyL)) pad->button |= L_TRIG; const bool pressed = !joy_buttons[i] && new;
if (SDL_GameControllerGetButton(sdl_cntrl, configJoyR)) pad->button |= R_TRIG; joy_buttons[i] = new;
if (SDL_GameControllerGetButton(sdl_cntrl, configJoyA)) pad->button |= A_BUTTON; if (pressed) last_joybutton = i;
if (SDL_GameControllerGetButton(sdl_cntrl, configJoyB)) pad->button |= B_BUTTON; }
for (u32 i = 0; i < num_joy_binds; ++i)
if (joy_buttons[joy_binds[i][0]])
pad->button |= joy_binds[i][1];
int16_t leftx = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_LEFTX); int16_t leftx = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_LEFTX);
int16_t lefty = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_LEFTY); int16_t lefty = SDL_GameControllerGetAxis(sdl_cntrl, SDL_CONTROLLER_AXIS_LEFTY);
@ -127,7 +187,27 @@ static void controller_sdl_read(OSContPad *pad) {
} }
} }
static u32 controller_sdl_rawkey(void) {
if (last_joybutton != VK_INVALID) {
const u32 ret = last_joybutton;
last_joybutton = VK_INVALID;
return ret;
}
for (int i = 0; i < MAX_MOUSEBUTTONS; ++i) {
if (last_mouse & SDL_BUTTON(i)) {
const u32 ret = VK_OFS_SDL_MOUSE + i;
last_mouse = 0;
return ret;
}
}
return VK_INVALID;
}
struct ControllerAPI controller_sdl = { struct ControllerAPI controller_sdl = {
VK_BASE_SDL_GAMEPAD,
controller_sdl_init, controller_sdl_init,
controller_sdl_read controller_sdl_read,
controller_sdl_rawkey,
controller_sdl_bind,
}; };

View File

@ -3,6 +3,8 @@
#include "controller_api.h" #include "controller_api.h"
#define VK_BASE_SDL_GAMEPAD 0x1000
extern struct ControllerAPI controller_sdl; extern struct ControllerAPI controller_sdl;
#endif #endif