diff --git a/Makefile b/Makefile index cebba9f..9a9c417 100644 --- a/Makefile +++ b/Makefile @@ -257,7 +257,7 @@ build/src/menu/landing_menu.o: build/assets/materials/ui.h build/src/audio/clips build/src/menu/options_menu.o: build/assets/materials/ui.h -build/src/menu/joystick_options.o: build/assets/materials/ui.h +build/src/menu/joystick_options.o: build/assets/materials/ui.h build/src/audio/clips.h build/src/menu/controls.o: build/assets/materials/ui.h build/src/audio/clips.h diff --git a/src/menu/joystick_options.c b/src/menu/joystick_options.c index 7e3b973..617602e 100644 --- a/src/menu/joystick_options.c +++ b/src/menu/joystick_options.c @@ -2,20 +2,71 @@ #include "../controls/controller.h" #include "../font/dejavusans.h" +#include "../audio/soundplayer.h" +#include "../savefile/savefile.h" #include "../build/assets/materials/ui.h" +#include "../build/src/audio/clips.h" #define JOYSTICK_Y 54 #define JOYSTICK_WIDTH 252 #define JOYSTICK_HEIGHT 124 +#define SCROLL_TICKS 9 +#define SCROLL_INTERVALS (SCROLL_TICKS - 1) + #define JOYSTICK_X ((SCREEN_WD - JOYSTICK_WIDTH) / 2) void joystickOptionsInit(struct JoystickOptions* joystickOptions) { - joystickOptions->selectedItem = 0; + joystickOptions->selectedItem = JoystickOptionInvert; joystickOptions->invertControls = menuBuildCheckbox(&gDejaVuSansFont, "Invert Camera", JOYSTICK_X + 8, JOYSTICK_Y + 8); - joystickOptions->lookSensitivity = menuBuildSlider(JOYSTICK_X + 8, JOYSTICK_Y + 20, 80, 8, 13); + + joystickOptions->lookSensitivityText = menuBuildText(&gDejaVuSansFont, "Look Sensitivity", JOYSTICK_X + 8, JOYSTICK_Y + 28); + joystickOptions->lookSensitivity = menuBuildSlider(JOYSTICK_X + 120, JOYSTICK_Y + 28, 120, SCROLL_TICKS); + + joystickOptions->lookAccelerationText = menuBuildText(&gDejaVuSansFont, "Look Acceleration", JOYSTICK_X + 8, JOYSTICK_Y + 48); + joystickOptions->lookAcceleration = menuBuildSlider(JOYSTICK_X + 120, JOYSTICK_Y + 48, 120, SCROLL_TICKS); + + joystickOptions->invertControls.checked = (gSaveData.controls.flags & ControlSaveFlagsInvert) != 0; + joystickOptions->lookSensitivity.value = (float)gSaveData.controls.sensitivity / 0xFFFF; + joystickOptions->lookAcceleration.value = (float)gSaveData.controls.acceleration / 0xFFFF; +} + +#define FULL_SCROLL_TIME 2.0f +#define SCROLL_MULTIPLIER (int)(0xFFFF * FIXED_DELTA_TIME / (80 * FULL_SCROLL_TIME)) + +#define SCROLL_CHUNK_SIZE (0x10000 / SCROLL_INTERVALS) + +void joystickOptionsHandleSlider(unsigned short* settingValue, float* sliderValue) { + OSContPad* pad = controllersGetControllerData(0); + + int newValue = (int)*settingValue + pad->stick_x * SCROLL_MULTIPLIER; + + if (controllerGetButtonDown(0, A_BUTTON | R_JPAD)) { + if (newValue >= 0xFFFF && controllerGetButtonDown(0, A_BUTTON)) { + newValue = 0; + } else { + newValue = newValue + SCROLL_CHUNK_SIZE; + newValue = newValue - (newValue % SCROLL_CHUNK_SIZE); + } + } + + if (controllerGetButtonDown(0, L_JPAD)) { + newValue = newValue - 1; + newValue = newValue - (newValue % SCROLL_CHUNK_SIZE); + } + + if (newValue < 0) { + newValue = 0; + } + + if (newValue > 0xFFFF) { + newValue = 0xFFFF; + } + + *settingValue = newValue; + *sliderValue = (float)newValue / 0xFFFF; } enum MenuDirection joystickOptionsUpdate(struct JoystickOptions* joystickOptions) { @@ -25,6 +76,44 @@ enum MenuDirection joystickOptionsUpdate(struct JoystickOptions* joystickOptions return MenuDirectionUp; } + if (controllerDir & ControllerDirectionDown) { + ++joystickOptions->selectedItem; + + if (joystickOptions->selectedItem == JoystickOptionCount) { + joystickOptions->selectedItem = 0; + } + } + + if (controllerDir & ControllerDirectionUp) { + if (joystickOptions->selectedItem == 0) { + joystickOptions->selectedItem = JoystickOptionCount - 1; + } else { + --joystickOptions->selectedItem; + } + } + + switch (joystickOptions->selectedItem) { + case JoystickOptionInvert: + if (controllerGetButtonDown(0, A_BUTTON)) { + joystickOptions->invertControls.checked = !joystickOptions->invertControls.checked; + soundPlayerPlay(SOUNDS_BUTTONCLICKRELEASE, 1.0f, 0.5f, NULL, NULL); + + if (joystickOptions->invertControls.checked) { + gSaveData.controls.flags |= ControlSaveFlagsInvert; + } else { + gSaveData.controls.flags &= ~ControlSaveFlagsInvert; + } + } + + break; + case JoystickOptionSensitivity: + joystickOptionsHandleSlider(&gSaveData.controls.sensitivity, &joystickOptions->lookSensitivity.value); + return MenuDirectionStay; + case JoystickOptionAcceleration: + joystickOptionsHandleSlider(&gSaveData.controls.acceleration, &joystickOptions->lookAcceleration.value); + return MenuDirectionStay; + } + if (controllerDir & ControllerDirectionLeft) { return MenuDirectionLeft; } @@ -40,13 +129,28 @@ void joystickOptionsRender(struct JoystickOptions* joystickOptions, struct Rende gSPDisplayList(renderState->dl++, ui_material_list[SOLID_ENV_INDEX]); gSPDisplayList(renderState->dl++, joystickOptions->invertControls.outline); + renderState->dl = menuCheckboxRender(&joystickOptions->invertControls, renderState->dl); gSPDisplayList(renderState->dl++, joystickOptions->lookSensitivity.back); + renderState->dl = menuSliderRender(&joystickOptions->lookSensitivity, renderState->dl); + + gSPDisplayList(renderState->dl++, joystickOptions->lookAcceleration.back); + renderState->dl = menuSliderRender(&joystickOptions->lookAcceleration, renderState->dl); gSPDisplayList(renderState->dl++, ui_material_revert_list[SOLID_ENV_INDEX]); gSPDisplayList(renderState->dl++, ui_material_list[DEJAVU_SANS_INDEX]); + gDPPipeSync(renderState->dl++); + menuSetRenderColor(renderState, joystickOptions->selectedItem == JoystickOptionInvert, &gSelectionGray, &gColorWhite); gSPDisplayList(renderState->dl++, joystickOptions->invertControls.text); + gDPPipeSync(renderState->dl++); + menuSetRenderColor(renderState, joystickOptions->selectedItem == JoystickOptionSensitivity, &gSelectionGray, &gColorWhite); + gSPDisplayList(renderState->dl++, joystickOptions->lookSensitivityText); + + gDPPipeSync(renderState->dl++); + menuSetRenderColor(renderState, joystickOptions->selectedItem == JoystickOptionAcceleration, &gSelectionGray, &gColorWhite); + gSPDisplayList(renderState->dl++, joystickOptions->lookAccelerationText); + gSPDisplayList(renderState->dl++, ui_material_revert_list[DEJAVU_SANS_INDEX]); } \ No newline at end of file diff --git a/src/menu/joystick_options.h b/src/menu/joystick_options.h index 7a1cbc8..5b9b153 100644 --- a/src/menu/joystick_options.h +++ b/src/menu/joystick_options.h @@ -4,9 +4,20 @@ #include "./menu.h" #include "../graphics/graphics.h" +enum JoystickOption { + JoystickOptionInvert, + JoystickOptionSensitivity, + JoystickOptionAcceleration, + + JoystickOptionCount, +}; + struct JoystickOptions { struct MenuCheckbox invertControls; struct MenuSlider lookSensitivity; + struct MenuSlider lookAcceleration; + Gfx* lookSensitivityText; + Gfx* lookAccelerationText; short selectedItem; }; diff --git a/src/menu/menu.c b/src/menu/menu.c index 4f845be..9f971da 100644 --- a/src/menu/menu.c +++ b/src/menu/menu.c @@ -197,36 +197,90 @@ struct MenuCheckbox menuBuildCheckbox(struct Font* font, char* message, int x, i return result; } -#define SLIDER_CENTER_HEIGHT 4 +Gfx* menuCheckboxRender(struct MenuCheckbox* checkbox, Gfx* dl) { + if (!checkbox->checked) { + return dl; + } -struct MenuSlider menuBuildSlider(int x, int y, int w, int h, int tickCount) { + gDPPipeSync(dl++); + gDPSetEnvColor(dl++, 255, 255, 255, 255); + gDPFillRectangle( + dl++, + checkbox->x + 3, + checkbox->y + 3, + checkbox->x + 8, + checkbox->y + 8 + ); + return dl; +} + + +#define SLIDER_TRACK_HEIGHT 4 +#define SLIDER_HEIGHT 12 +#define SLIDER_WIDTH 6 +#define TICK_Y 11 +#define TICK_HEIGHT 3 + +struct MenuSlider menuBuildSlider(int x, int y, int w, int tickCount) { struct MenuSlider result; result.x = x; result.y = y; result.w = w; - result.h = h; - result.back = malloc(sizeof(Gfx) * 12 + tickCount + 2); + result.back = malloc(sizeof(Gfx) * (12 + tickCount)); Gfx* dl = result.back; int sliderX = x; - int sliderY = y + (h >> 1) - (SLIDER_CENTER_HEIGHT >> 1); + int sliderY = y + (SLIDER_HEIGHT / 2) - (SLIDER_TRACK_HEIGHT / 2); gDPPipeSync(dl++); - gDPSetEnvColor(dl++, 93, 96, 97, 255); + gDPSetEnvColor(dl++, 25, 25, 25, 255); gDPFillRectangle( dl++, sliderX, sliderY, sliderX + w, - sliderY + SLIDER_CENTER_HEIGHT + sliderY + SLIDER_TRACK_HEIGHT ); - dl = menuRenderOutline(sliderX, sliderY, w, SLIDER_CENTER_HEIGHT, 1, dl); + + int tickMin = x + (SLIDER_WIDTH / 2); + int tickWidth = w - SLIDER_WIDTH; + for (int i = 0; i < tickCount; ++i) { + int tickX = (i * tickWidth) / (tickCount - 1) + tickMin; + gDPFillRectangle( + dl++, + tickX, + y + TICK_Y, + tickX + 1, + y + TICK_Y + TICK_HEIGHT + ); + } + + dl = menuRenderOutline(sliderX, sliderY, w, SLIDER_TRACK_HEIGHT, 1, dl); + gSPEndDisplayList(dl++); result.value = 0; return result; +} + +Gfx* menuSliderRender(struct MenuSlider* slider, Gfx* dl) { + gDPPipeSync(dl++); + gDPSetEnvColor(dl++, 93, 96, 97, 255); + + int sliderPos = (slider->w - SLIDER_WIDTH) * slider->value + slider->x + (SLIDER_WIDTH / 2); + + gDPFillRectangle( + dl++, + sliderPos - (SLIDER_WIDTH / 2), + slider->y, + sliderPos + (SLIDER_WIDTH / 2), + slider->y + SLIDER_HEIGHT + ); + dl = menuRenderOutline(sliderPos - (SLIDER_WIDTH / 2), slider->y, SLIDER_WIDTH, SLIDER_HEIGHT, 0, dl); + + return dl; } \ No newline at end of file diff --git a/src/menu/menu.h b/src/menu/menu.h index 7ff9dfe..a13a47e 100644 --- a/src/menu/menu.h +++ b/src/menu/menu.h @@ -25,7 +25,7 @@ struct MenuSlider { Gfx* back; float value; short x, y; - short w, h; + short w; }; enum MenuDirection { @@ -55,6 +55,9 @@ struct MenuButton menuBuildButton(struct Font* font, char* message, int x, int y void menuSetRenderColor(struct RenderState* renderState, int isSelected, struct Coloru8* selected, struct Coloru8* defaultColor); struct MenuCheckbox menuBuildCheckbox(struct Font* font, char* message, int x, int y); -struct MenuSlider menuBuildSlider(int x, int y, int w, int h, int tickCount); +Gfx* menuCheckboxRender(struct MenuCheckbox* checkbox, Gfx* dl); + +struct MenuSlider menuBuildSlider(int x, int y, int w, int tickCount); +Gfx* menuSliderRender(struct MenuSlider* slider, Gfx* dl); #endif \ No newline at end of file diff --git a/src/player/player.c b/src/player/player.c index c675281..6bf3bb3 100644 --- a/src/player/player.c +++ b/src/player/player.c @@ -186,9 +186,11 @@ void playerInit(struct Player* player, struct Location* startLocation, struct Ve #define PLAYER_STOP_ACCEL (5.875f) #define PLAYER_SLIDE_ACCEL (40.0f) -#define ROTATE_RATE (M_PI * 2.0f) -#define ROTATE_RATE_DELTA (M_PI * 0.125f) -#define ROTATE_RATE_STOP_DELTA (M_PI * 0.25f) +#define MIN_ROTATE_RATE (M_PI * 0.5f) +#define MAX_ROTATE_RATE (M_PI * 3.5f) + +#define MIN_ROTATE_RATE_DELTA (M_PI * 0.06125f) +#define MAX_ROTATE_RATE_DELTA MAX_ROTATE_RATE #define JUMP_IMPULSE 2.7f @@ -718,18 +720,26 @@ void playerUpdate(struct Player* player, struct Transform* cameraTransform) { } struct Vector2 lookInput = controllerDirectionGet(ControllerActionRotate); - float targetYaw = -lookInput.x * ROTATE_RATE; - float targetPitch = lookInput.y * ROTATE_RATE; + float rotateRate = mathfLerp(MIN_ROTATE_RATE, MAX_ROTATE_RATE, (float)gSaveData.controls.sensitivity / 0xFFFF); + float targetYaw = -lookInput.x * rotateRate; + float targetPitch = lookInput.y * rotateRate; + + if (gSaveData.controls.flags & ControlSaveFlagsInvert) { + targetYaw = -targetYaw; + targetPitch = -targetPitch; + } + + float rotateRateDelta = mathfLerp(MIN_ROTATE_RATE_DELTA, MAX_ROTATE_RATE_DELTA, (float)gSaveData.controls.acceleration / 0xFFFF); player->yawVelocity = mathfMoveTowards( player->yawVelocity, targetYaw, - player->yawVelocity * targetYaw > 0.0f ? ROTATE_RATE_DELTA : ROTATE_RATE_STOP_DELTA + rotateRateDelta ); player->pitchVelocity = mathfMoveTowards( player->pitchVelocity, targetPitch, - player->pitchVelocity * targetPitch > 0.0f ? ROTATE_RATE_DELTA : ROTATE_RATE_STOP_DELTA + rotateRateDelta ); struct Vector3 lookingForward; diff --git a/src/savefile/savefile.c b/src/savefile/savefile.c index f239943..6f67359 100755 --- a/src/savefile/savefile.c +++ b/src/savefile/savefile.c @@ -90,6 +90,9 @@ void savefileNew() { } controllerSetDefaultSource(); + gSaveData.controls.flags = 0; + gSaveData.controls.sensitivity = 0x7FFF; + gSaveData.controls.acceleration = 0x7FFF; gSaveData.audio.soundVolume = 0xFF; gSaveData.audio.musicVolume = 0xFF; diff --git a/src/savefile/savefile.h b/src/savefile/savefile.h index 440d5b2..de8b4d2 100755 --- a/src/savefile/savefile.h +++ b/src/savefile/savefile.h @@ -20,7 +20,7 @@ #define SCREEN_SHOT_SRAM(slotIndex) (((slotIndex) + 1) * SAVE_SLOT_SIZE + MAX_CHECKPOINT_SIZE + SRAM_START_ADDR) -#define SAVEFILE_HEADER 0xDEAD +#define SAVEFILE_HEADER 0xDEA0 // first save slot is always reserved for auto save #define MAX_SAVE_SLOTS ((int)(SRAM_SIZE / SAVE_SLOT_SIZE) - 1) @@ -33,8 +33,15 @@ struct SaveHeader { unsigned char nextTestSubject; }; +enum ControlSaveFlags { + ControlSaveFlagsInvert, +}; + struct ControlSaveState { unsigned char controllerSettings[2][ControllerActionSourceCount]; + unsigned short flags; + unsigned short sensitivity; + unsigned short acceleration; }; struct AudioSettingsSaveState {