Battle stuff
Build Dusk / build-linux (push) Successful in 7m5s
Build Dusk / build-psp (push) Successful in 1m33s
Build Dusk / build-knulli (push) Successful in 4m0s
Build Dusk / build-gamecube (push) Successful in 3m31s
Build Dusk / build-gamecube-iso (push) Successful in 3m39s
Build Dusk / build-wii (push) Successful in 3m5s
Build Dusk / build-wii-iso (push) Successful in 3m25s

This commit is contained in:
2026-07-08 07:55:15 -05:00
parent 0bd2491ab7
commit a73f55beb0
22 changed files with 589 additions and 144 deletions
+5 -1
View File
@@ -27,12 +27,16 @@ msgid "ui.settings.tabs.audio"
msgstr "Audio"
msgid "ui.settings.input.deadzone"
msgstr "Zona muerta"
msgstr "Deadzone"
#: src/dusk/ui/frame/settings/uisettingsgeneral.c
msgid "ui.settings.general.language"
msgstr "Idioma"
#: src/dusk/ui/frame/settings/uisettingsgeneral.c
msgid "ui.settings.general.language_detail"
msgstr "Se aplica después de reiniciar la aplicación."
#: src/dusk/ui/frame/settings/uisettings.c
msgid "ui.settings.apply"
msgstr "Aplicar"
+4
View File
@@ -33,6 +33,10 @@ msgstr "デッドゾーン"
msgid "ui.settings.general.language"
msgstr "言語"
#: src/dusk/ui/frame/settings/uisettingsgeneral.c
msgid "ui.settings.general.language_detail"
msgstr "アプリケーションを再起動すると適用されます。"
#: src/dusk/ui/frame/settings/uisettings.c
msgid "ui.settings.apply"
msgstr "適用"
+1
View File
@@ -11,6 +11,7 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
)
# Subdirs
add_subdirectory(battle)
add_subdirectory(cutscene)
add_subdirectory(entity)
add_subdirectory(overworld)
+12
View File
@@ -0,0 +1,12 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
battle.c
battlefighter.c
party.c
)
+49
View File
@@ -0,0 +1,49 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "battle.h"
#include "util/memory.h"
battle_t BATTLE;
void battleInit(void) {
memoryZero(&BATTLE, sizeof(battle_t));
for(uint8_t i = 0; i < BATTLE_FIGHTER_COUNT_MAX; i++) {
BATTLE.fighters[i].id = i;
}
}
uint8_t battleGetAvailableFighter(void) {
for(uint8_t i = 0; i < BATTLE_FIGHTER_COUNT_MAX; i++) {
if(BATTLE.fighters[i].status == BATTLE_FIGHTER_STATUS_NULL) return i;
}
return 0xFF;
}
battlefighter_t *battleAddFighter(
const battlefighterteam_t team,
const battlefightercontroller_t controller,
const battlefighterstats_t stats,
const uint16_t healthMax,
const uint16_t mpMax
) {
const uint8_t index = battleGetAvailableFighter();
if(index == 0xFF) return NULL;
battlefighter_t *fighter = &BATTLE.fighters[index];
battleFighterInit(fighter, team, controller, stats, healthMax, mpMax);
return fighter;
}
void battleStart(void) {
BATTLE.active = true;
}
void battleDispose(void) {
battleInit();
}
+61
View File
@@ -0,0 +1,61 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "battlefighter.h"
#define BATTLE_FIGHTER_COUNT_MAX 8
typedef struct {
bool_t active;
battlefighter_t fighters[BATTLE_FIGHTER_COUNT_MAX];
} battle_t;
extern battle_t BATTLE;
/**
* Initializes the battle system. Marks it as inactive with no fighters.
*/
void battleInit(void);
/**
* Gets an available (unused) fighter slot index.
*
* @return The index of an available fighter slot, or 0xFF if none are
* available.
*/
uint8_t battleGetAvailableFighter(void);
/**
* Adds a fighter to the battle in the next available slot.
*
* @param team The team the fighter belongs to.
* @param controller Who makes decisions for the fighter.
* @param stats The fighter's base combat stats.
* @param healthMax The fighter's maximum health.
* @param mpMax The fighter's maximum mp.
* @return Pointer to the newly added fighter, or NULL if the battle is
* already full.
*/
battlefighter_t *battleAddFighter(
const battlefighterteam_t team,
const battlefightercontroller_t controller,
const battlefighterstats_t stats,
const uint16_t healthMax,
const uint16_t mpMax
);
/**
* Starts a battle, marking it active. Any fighters already added via
* battleAddFighter remain in place.
*/
void battleStart(void);
/**
* Disposes of the battle, clearing all fighters and marking it inactive.
*/
void battleDispose(void);
+43
View File
@@ -0,0 +1,43 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "battlefighter.h"
#include "assert/assert.h"
#include "util/memory.h"
void battleFighterInit(
battlefighter_t *fighter,
const battlefighterteam_t team,
const battlefightercontroller_t controller,
const battlefighterstats_t stats,
const uint16_t healthMax,
const uint16_t mpMax
) {
assertNotNull(fighter, "Fighter pointer cannot be NULL");
assertTrue(team < BATTLE_FIGHTER_TEAM_COUNT, "Invalid fighter team");
assertTrue(
controller < BATTLE_FIGHTER_CONTROLLER_COUNT,
"Invalid fighter controller"
);
const uint8_t id = fighter->id;
memoryZero(fighter, sizeof(battlefighter_t));
fighter->id = id;
fighter->status = BATTLE_FIGHTER_STATUS_NORMAL;
fighter->team = team;
fighter->controller = controller;
fighter->stats = stats;
fighter->healthMax = healthMax;
fighter->health = healthMax;
fighter->mpMax = mpMax;
fighter->mp = mpMax;
}
bool_t battleFighterIsAlive(const battlefighter_t *fighter) {
assertNotNull(fighter, "Fighter pointer cannot be NULL");
return fighter->status == BATTLE_FIGHTER_STATUS_NORMAL;
}
+87
View File
@@ -0,0 +1,87 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
// An empty status means the slot in BATTLE.fighters is unused.
typedef enum {
BATTLE_FIGHTER_STATUS_NULL,
BATTLE_FIGHTER_STATUS_NORMAL,
BATTLE_FIGHTER_STATUS_DEAD,
BATTLE_FIGHTER_STATUS_COUNT
} battlefighterstatus_t;
typedef enum {
BATTLE_FIGHTER_TEAM_ALLY,
BATTLE_FIGHTER_TEAM_ENEMY,
BATTLE_FIGHTER_TEAM_COUNT
} battlefighterteam_t;
typedef enum {
BATTLE_FIGHTER_CONTROLLER_PLAYER,
BATTLE_FIGHTER_CONTROLLER_AI,
BATTLE_FIGHTER_CONTROLLER_COUNT
} battlefightercontroller_t;
// Base combat stats, kept separate from the resource pools (health/mp) on
// battlefighter_t so that equipment/buffs can later modify them without
// touching current health/mp state.
typedef struct {
uint16_t attack;
uint16_t defense;
uint16_t magic;
uint16_t speed;
uint16_t luck;
} battlefighterstats_t;
typedef struct {
uint8_t id;
battlefighterstatus_t status;
battlefighterteam_t team;
battlefightercontroller_t controller;
uint16_t health;
uint16_t healthMax;
uint16_t mp;
uint16_t mpMax;
battlefighterstats_t stats;
} battlefighter_t;
/**
* Initializes a battle fighter in place, filling health/mp to their maximum
* values and setting its status to normal.
*
* @param fighter Pointer to the fighter to initialize.
* @param team The team the fighter belongs to.
* @param controller Who makes decisions for the fighter.
* @param stats The fighter's base combat stats.
* @param healthMax The fighter's maximum health.
* @param mpMax The fighter's maximum mp.
*/
void battleFighterInit(
battlefighter_t *fighter,
const battlefighterteam_t team,
const battlefightercontroller_t controller,
const battlefighterstats_t stats,
const uint16_t healthMax,
const uint16_t mpMax
);
/**
* Returns true if the fighter is in a state where it can still act (i.e.
* is not dead).
*
* @param fighter Pointer to the fighter to check.
* @returns True if the fighter can act.
*/
bool_t battleFighterIsAlive(const battlefighter_t *fighter);
+71
View File
@@ -0,0 +1,71 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "party.h"
#include "assert/assert.h"
#include "util/memory.h"
party_t PARTY;
void partyInit(void) {
memoryZero(&PARTY, sizeof(party_t));
for(uint8_t i = 0; i < PARTY_MEMBER_COUNT_MAX; i++) {
PARTY.members[i].id = i;
}
for(uint8_t i = 0; i < PARTY_ACTIVE_SIZE_MAX; i++) {
PARTY.order[i] = PARTY_ORDER_EMPTY;
}
}
uint8_t partyGetAvailableMember(void) {
for(uint8_t i = 0; i < PARTY_MEMBER_COUNT_MAX; i++) {
if(PARTY.members[i].status == BATTLE_FIGHTER_STATUS_NULL) return i;
}
return 0xFF;
}
battlefighter_t *partyAddMember(
const battlefighterstats_t stats,
const uint16_t healthMax,
const uint16_t mpMax
) {
const uint8_t index = partyGetAvailableMember();
if(index == 0xFF) return NULL;
battlefighter_t *member = &PARTY.members[index];
battleFighterInit(
member, BATTLE_FIGHTER_TEAM_ALLY, BATTLE_FIGHTER_CONTROLLER_PLAYER,
stats, healthMax, mpMax
);
for(uint8_t i = 0; i < PARTY_ACTIVE_SIZE_MAX; i++) {
if(PARTY.order[i] != PARTY_ORDER_EMPTY) continue;
PARTY.order[i] = index;
break;
}
return member;
}
battlefighter_t *partyGetOrderMember(const uint8_t slot) {
assertTrue(slot < PARTY_ACTIVE_SIZE_MAX, "Invalid party order slot");
const uint8_t index = PARTY.order[slot];
if(index == PARTY_ORDER_EMPTY) return NULL;
return &PARTY.members[index];
}
void partySetOrder(const uint8_t slot, const uint8_t memberIndex) {
assertTrue(slot < PARTY_ACTIVE_SIZE_MAX, "Invalid party order slot");
assertTrue(
memberIndex == PARTY_ORDER_EMPTY || memberIndex < PARTY_MEMBER_COUNT_MAX,
"Invalid party member index"
);
PARTY.order[slot] = memberIndex;
}
+73
View File
@@ -0,0 +1,73 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "battlefighter.h"
#define PARTY_MEMBER_COUNT_MAX 4
#define PARTY_ACTIVE_SIZE_MAX 3
#define PARTY_ORDER_EMPTY 0xFF
typedef struct {
battlefighter_t members[PARTY_MEMBER_COUNT_MAX];
// Maps an active battle slot to the roster member filling it, or
// PARTY_ORDER_EMPTY if the slot is unfilled. Only the first
// PARTY_ACTIVE_SIZE_MAX of the PARTY_MEMBER_COUNT_MAX roster members
// can be in the active lineup at once.
uint8_t order[PARTY_ACTIVE_SIZE_MAX];
} party_t;
extern party_t PARTY;
/**
* Initializes the party system with an empty roster and order.
*/
void partyInit(void);
/**
* Gets an available (unused) party member slot index.
*
* @return The index of an available slot, or 0xFF if the party is full.
*/
uint8_t partyGetAvailableMember(void);
/**
* Adds a member to the party roster in the next available slot. Party
* members are always allies controlled by the player. If there is a
* free active order slot, the new member is placed into it.
*
* @param stats The member's base combat stats.
* @param healthMax The member's maximum health.
* @param mpMax The member's maximum mp.
* @return Pointer to the newly added party member, or NULL if the party is
* already full.
*/
battlefighter_t *partyAddMember(
const battlefighterstats_t stats,
const uint16_t healthMax,
const uint16_t mpMax
);
/**
* Gets the roster member currently occupying an active order slot.
*
* @param slot The active order slot to query.
* @return Pointer to the member in that slot, or NULL if the slot is
* empty.
*/
battlefighter_t *partyGetOrderMember(const uint8_t slot);
/**
* Assigns a roster member to an active order slot, replacing whatever
* was there. Use PARTY_ORDER_EMPTY to clear a slot.
*
* @param slot The active order slot to assign.
* @param memberIndex The roster member index to place there, or
* PARTY_ORDER_EMPTY to clear the slot.
*/
void partySetOrder(const uint8_t slot, const uint8_t memberIndex);
+2
View File
@@ -14,6 +14,7 @@
#include "rpg/cutscene/cutscenesystem.h"
#include "rpg/cutscene/scene/testcutscene.h"
#include "rpg/item/backpack.h"
#include "rpg/battle/party.h"
#include "time/time.h"
#include "rpgcamera.h"
#include "util/memory.h"
@@ -30,6 +31,7 @@ errorret_t rpgInit(void) {
memoryZero(MAP_AREAS, sizeof(MAP_AREAS));
backpackInit();
partyInit();
cutsceneSystemInit();
errorChain(mapInit());
+37 -24
View File
@@ -26,7 +26,7 @@ uisettingstabdef_t UI_SETTINGS_TAB_DEFS[UI_SETTINGS_TAB_COUNT] = {
{
.localeId = "ui.settings.tabs.general",
.init = uiSettingsGeneralInit,
.menu = &UI_SETTINGS_GENERAL.menu,
.menu = (uimenu_t *)&UI_SETTINGS.data,
.load = uiSettingsGeneralLoad,
.apply = uiSettingsGeneralApply,
.hasChanges = uiSettingsGeneralHasChanges
@@ -35,7 +35,7 @@ uisettingstabdef_t UI_SETTINGS_TAB_DEFS[UI_SETTINGS_TAB_COUNT] = {
{
.localeId = "ui.settings.tabs.input",
.init = uiSettingsInputInit,
.menu = &UI_SETTINGS_INPUT.menu,
.menu = (uimenu_t *)&UI_SETTINGS.data,
.load = uiSettingsInputLoad,
.apply = uiSettingsInputApply,
.hasChanges = uiSettingsInputHasChanges
@@ -44,7 +44,7 @@ uisettingstabdef_t UI_SETTINGS_TAB_DEFS[UI_SETTINGS_TAB_COUNT] = {
{
.localeId = "ui.settings.tabs.display",
.init = uiSettingsDisplayInit,
.menu = &UI_SETTINGS_DISPLAY.menu,
.menu = (uimenu_t *)&UI_SETTINGS.data,
.load = uiSettingsDisplayLoad,
.apply = uiSettingsDisplayApply,
.hasChanges = uiSettingsDisplayHasChanges
@@ -53,7 +53,7 @@ uisettingstabdef_t UI_SETTINGS_TAB_DEFS[UI_SETTINGS_TAB_COUNT] = {
{
.localeId = "ui.settings.tabs.audio",
.init = uiSettingsAudioInit,
.menu = &UI_SETTINGS_AUDIO.menu,
.menu = (uimenu_t *)&UI_SETTINGS.data,
.load = uiSettingsAudioLoad,
.apply = uiSettingsAudioApply,
.hasChanges = uiSettingsAudioHasChanges
@@ -67,6 +67,27 @@ static uisettingstabdef_t UI_SETTINGS_TOP_DEF = {
};
static uisettingstabdef_t *UI_SETTINGS_PENDING_DISCARD = NULL;
static uisettingstabdef_t *UI_SETTINGS_ACTIVE_TAB = NULL;
errorret_t uiSettingsActivateTab(uisettingstabdef_t *def) {
if(def == UI_SETTINGS_ACTIVE_TAB) errorOk();
errorChain(def->init(&UI_SETTINGS.data));
def->menu->user = def;
def->load();
UI_SETTINGS_ACTIVE_TAB = def;
errorOk();
}
void uiSettingsTabChanged(
const uimenu_t *menu,
const uint8_t index,
const uimenuitem_t *item
) {
if(index >= UI_SETTINGS_TAB_COUNT) return;
errorCatch(uiSettingsActivateTab(&UI_SETTINGS_TAB_DEFS[index]));
}
void uiSettingsTabSelected(
const uimenu_t *menu,
@@ -74,11 +95,15 @@ void uiSettingsTabSelected(
const uimenuitem_t *item
) {
if(index >= UI_SETTINGS_TAB_COUNT) return;
uiMenuOpen(UI_SETTINGS_TAB_DEFS[index].menu);
uisettingstabdef_t *def = &UI_SETTINGS_TAB_DEFS[index];
errorCatch(uiSettingsActivateTab(def));
uiMenuOpen(def->menu);
}
errorret_t uiSettingsInit(void) {
memoryZero(&UI_SETTINGS, sizeof(uisettings_t));
UI_SETTINGS_ACTIVE_TAB = NULL;
for(uint8_t i = 0; i < UI_SETTINGS_TAB_COUNT; i++) {
errorChain(assetLocaleGetString(
@@ -92,7 +117,7 @@ errorret_t uiSettingsInit(void) {
MENU_BEGIN(
&UI_SETTINGS.tabsMenu, UI_SETTINGS.tabs, uiSettingsTabSelected,
uiSettingsDiscardMenuClosed, NULL
uiSettingsDiscardMenuClosed, uiSettingsTabChanged
);
for(uint8_t i = 0; i < UI_SETTINGS_TAB_COUNT; i++) {
MENU_TAB(UI_SETTINGS.tabLabels[i]);
@@ -109,10 +134,7 @@ errorret_t uiSettingsInit(void) {
UI_SETTINGS_APPLY_LABEL_MAX
));
for(uint8_t i = 0; i < UI_SETTINGS_TAB_COUNT; i++) {
errorChain(UI_SETTINGS_TAB_DEFS[i].init());
UI_SETTINGS_TAB_DEFS[i].menu->user = &UI_SETTINGS_TAB_DEFS[i];
}
errorChain(uiSettingsActivateTab(&UI_SETTINGS_TAB_DEFS[0]));
errorOk();
}
@@ -146,12 +168,10 @@ errorret_t uiSettingsDraw(void) {
const float_t pageY = contentY + tabsRowHeight + UI_FRAME_PADDING_Y;
const float_t pageHeight = contentHeight - tabsRowHeight - UI_FRAME_PADDING_Y;
for(uint8_t i = 0; i < UI_SETTINGS_TAB_COUNT; i++) {
if(!uiTabIsActive(&UI_SETTINGS.tabs[i].tab)) continue;
if(UI_SETTINGS_ACTIVE_TAB != NULL) {
errorChain(uiMenuDraw(
UI_SETTINGS_TAB_DEFS[i].menu, contentX, pageY, contentWidth, pageHeight
UI_SETTINGS_ACTIVE_TAB->menu, contentX, pageY, contentWidth, pageHeight
));
break;
}
errorChain(spriteBatchFlush());
@@ -185,22 +205,15 @@ errorret_t uiSettingsDispose(void) {
}
void uiSettingsLoad(void) {
for(uint8_t i = 0; i < UI_SETTINGS_TAB_COUNT; i++) {
UI_SETTINGS_TAB_DEFS[i].load();
}
if(UI_SETTINGS_ACTIVE_TAB != NULL) UI_SETTINGS_ACTIVE_TAB->load();
}
void uiSettingsApply(void) {
for(uint8_t i = 0; i < UI_SETTINGS_TAB_COUNT; i++) {
UI_SETTINGS_TAB_DEFS[i].apply();
}
if(UI_SETTINGS_ACTIVE_TAB != NULL) UI_SETTINGS_ACTIVE_TAB->apply();
}
bool_t uiSettingsHasChanges(void) {
for(uint8_t i = 0; i < UI_SETTINGS_TAB_COUNT; i++) {
if(UI_SETTINGS_TAB_DEFS[i].hasChanges()) return true;
}
return false;
return UI_SETTINGS_ACTIVE_TAB != NULL && UI_SETTINGS_ACTIVE_TAB->hasChanges();
}
void uiSettingsDiscardMenuClosed(const uimenu_t *menu) {
+10 -8
View File
@@ -8,27 +8,29 @@
#pragma once
#include "error/error.h"
#include "ui/widget/uimenu.h"
#include "uisettingsdata.h"
#define UI_SETTINGS_TAB_COUNT 4
#define UI_SETTINGS_TAB_LABEL_MAX 32
#define UI_SETTINGS_APPLY_LABEL_MAX 32
typedef struct {
uimenu_t tabsMenu;
uimenuitem_t tabs[UI_SETTINGS_TAB_COUNT];
char_t tabLabels[UI_SETTINGS_TAB_COUNT][UI_SETTINGS_TAB_LABEL_MAX];
char_t applyLabel[UI_SETTINGS_APPLY_LABEL_MAX];
} uisettings_t;
typedef struct {
const char_t *localeId;
errorret_t (*init)(void);
errorret_t (*init)(uisettingsdata_t *data);
uimenu_t *menu;
void (*load)(void);
void (*apply)(void);
bool_t (*hasChanges)(void);
} uisettingstabdef_t;
typedef struct {
uimenu_t tabsMenu;
uimenuitem_t tabs[UI_SETTINGS_TAB_COUNT];
char_t tabLabels[UI_SETTINGS_TAB_COUNT][UI_SETTINGS_TAB_LABEL_MAX];
char_t applyLabel[UI_SETTINGS_APPLY_LABEL_MAX];
uisettingsdata_t data;
} uisettings_t;
extern uisettings_t UI_SETTINGS;
extern uisettingstabdef_t UI_SETTINGS_TAB_DEFS[UI_SETTINGS_TAB_COUNT];
+7 -7
View File
@@ -6,11 +6,10 @@
*/
#include "uisettingsaudio.h"
#include "uisettings.h"
#include "assert/assert.h"
#include "util/memory.h"
uisettingsaudio_t UI_SETTINGS_AUDIO;
void uiSettingsAudioSelected(
const uimenu_t *menu,
const uint8_t index,
@@ -20,17 +19,18 @@ void uiSettingsAudioSelected(
uiSettingsAudioApply();
}
errorret_t uiSettingsAudioInit(void) {
memoryZero(&UI_SETTINGS_AUDIO, sizeof(uisettingsaudio_t));
errorret_t uiSettingsAudioInit(uisettingsdata_t *data) {
uisettingsaudio_t *audio = &data->audio;
memoryZero(audio, sizeof(uisettingsaudio_t));
MENU_BEGIN(
&UI_SETTINGS_AUDIO.menu, UI_SETTINGS_AUDIO.items,
&audio->menu, audio->items,
uiSettingsAudioSelected, uiSettingsDiscardMenuClosed, NULL
);
MENU_LABEL("No audio settings yet");
MENU_BUTTON(UI_SETTINGS.applyLabel);
MENU_END(UI_SETTINGS_AUDIO.items, 1);
MENU_END(audio->items, 1);
errorOk();
}
@@ -39,7 +39,7 @@ void uiSettingsAudioLoad(void) {
}
void uiSettingsAudioApply(void) {
uiMenuClose(&UI_SETTINGS_AUDIO.menu);
uiMenuClose(&UI_SETTINGS.data.audio.menu);
}
bool_t uiSettingsAudioHasChanges(void) {
+5 -14
View File
@@ -7,25 +7,16 @@
#pragma once
#include "error/error.h"
#include "ui/widget/uimenu.h"
#include "uisettings.h"
#define UI_SETTINGS_AUDIO_ITEM_COUNT 2
#define UI_SETTINGS_AUDIO_INDEX_APPLY 1
typedef struct {
uimenu_t menu;
uimenuitem_t items[UI_SETTINGS_AUDIO_ITEM_COUNT];
} uisettingsaudio_t;
extern uisettingsaudio_t UI_SETTINGS_AUDIO;
#include "uisettingsdata.h"
/**
* Initializes the audio settings page.
* Initializes the audio settings page into the shared settings data
* union.
*
* @param data The shared settings data union to initialize into.
* @return Any error that occurs.
*/
errorret_t uiSettingsAudioInit(void);
errorret_t uiSettingsAudioInit(uisettingsdata_t *data);
/**
* Loads the page's widgets from the current engine audio settings.
@@ -0,0 +1,67 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#include "ui/widget/uimenu.h"
#define UI_SETTINGS_GENERAL_ITEM_COUNT 3
#define UI_SETTINGS_GENERAL_INDEX_LANGUAGE 0
#define UI_SETTINGS_GENERAL_INDEX_LANGUAGE_DETAIL 1
#define UI_SETTINGS_GENERAL_INDEX_APPLY 2
#define UI_SETTINGS_GENERAL_LOCALE_COUNT 3
#define UI_SETTINGS_GENERAL_LABEL_MAX 32
#define UI_SETTINGS_GENERAL_INFO_MAX 64
typedef struct {
uimenu_t menu;
uimenuitem_t items[UI_SETTINGS_GENERAL_ITEM_COUNT];
const char_t *localeNames[UI_SETTINGS_GENERAL_LOCALE_COUNT];
char_t languageLabel[UI_SETTINGS_GENERAL_LABEL_MAX];
char_t labelInfo[UI_SETTINGS_GENERAL_INFO_MAX];
} uisettingsgeneral_t;
#define UI_SETTINGS_INPUT_ITEM_COUNT 2
#define UI_SETTINGS_INPUT_LABEL_MAX 32
#define UI_SETTINGS_INPUT_INDEX_DEADZONE 0
#define UI_SETTINGS_INPUT_INDEX_APPLY 1
typedef struct {
uimenu_t menu;
uimenuitem_t items[UI_SETTINGS_INPUT_ITEM_COUNT];
char_t deadzoneLabel[UI_SETTINGS_INPUT_LABEL_MAX];
} uisettingsinput_t;
#define UI_SETTINGS_DISPLAY_ITEM_COUNT 2
#define UI_SETTINGS_DISPLAY_INDEX_APPLY 1
typedef struct {
uimenu_t menu;
uimenuitem_t items[UI_SETTINGS_DISPLAY_ITEM_COUNT];
} uisettingsdisplay_t;
#define UI_SETTINGS_AUDIO_ITEM_COUNT 2
#define UI_SETTINGS_AUDIO_INDEX_APPLY 1
typedef struct {
uimenu_t menu;
uimenuitem_t items[UI_SETTINGS_AUDIO_ITEM_COUNT];
} uisettingsaudio_t;
/**
* Shared, overlapping storage for every settings tab's page data. Only
* one member is ever live at a time -- the active tab's Init writes
* into it, overwriting whatever the previously active tab left there.
* This trades simultaneous access to every tab for a memory footprint
* of only the single largest page.
*/
typedef union {
uisettingsgeneral_t general;
uisettingsinput_t input;
uisettingsdisplay_t display;
uisettingsaudio_t audio;
} uisettingsdata_t;
@@ -6,11 +6,10 @@
*/
#include "uisettingsdisplay.h"
#include "uisettings.h"
#include "assert/assert.h"
#include "util/memory.h"
uisettingsdisplay_t UI_SETTINGS_DISPLAY;
void uiSettingsDisplaySelected(
const uimenu_t *menu,
const uint8_t index,
@@ -20,17 +19,18 @@ void uiSettingsDisplaySelected(
uiSettingsDisplayApply();
}
errorret_t uiSettingsDisplayInit(void) {
memoryZero(&UI_SETTINGS_DISPLAY, sizeof(uisettingsdisplay_t));
errorret_t uiSettingsDisplayInit(uisettingsdata_t *data) {
uisettingsdisplay_t *display = &data->display;
memoryZero(display, sizeof(uisettingsdisplay_t));
MENU_BEGIN(
&UI_SETTINGS_DISPLAY.menu, UI_SETTINGS_DISPLAY.items,
&display->menu, display->items,
uiSettingsDisplaySelected, uiSettingsDiscardMenuClosed, NULL
);
MENU_LABEL("No display settings yet");
MENU_BUTTON(UI_SETTINGS.applyLabel);
MENU_END(UI_SETTINGS_DISPLAY.items, 1);
MENU_END(display->items, 1);
errorOk();
}
@@ -39,7 +39,7 @@ void uiSettingsDisplayLoad(void) {
}
void uiSettingsDisplayApply(void) {
uiMenuClose(&UI_SETTINGS_DISPLAY.menu);
uiMenuClose(&UI_SETTINGS.data.display.menu);
}
bool_t uiSettingsDisplayHasChanges(void) {
+5 -14
View File
@@ -7,25 +7,16 @@
#pragma once
#include "error/error.h"
#include "ui/widget/uimenu.h"
#include "uisettings.h"
#define UI_SETTINGS_DISPLAY_ITEM_COUNT 2
#define UI_SETTINGS_DISPLAY_INDEX_APPLY 1
typedef struct {
uimenu_t menu;
uimenuitem_t items[UI_SETTINGS_DISPLAY_ITEM_COUNT];
} uisettingsdisplay_t;
extern uisettingsdisplay_t UI_SETTINGS_DISPLAY;
#include "uisettingsdata.h"
/**
* Initializes the display settings page.
* Initializes the display settings page into the shared settings data
* union.
*
* @param data The shared settings data union to initialize into.
* @return Any error that occurs.
*/
errorret_t uiSettingsDisplayInit(void);
errorret_t uiSettingsDisplayInit(uisettingsdata_t *data);
/**
* Loads the page's widgets from the current engine display settings.
+21 -16
View File
@@ -6,6 +6,7 @@
*/
#include "uisettingsgeneral.h"
#include "uisettings.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/string.h"
@@ -21,8 +22,6 @@ static const localeinfo_t *const UI_SETTINGS_GENERAL_LOCALES[
&LOCALE_ES_MX
};
uisettingsgeneral_t UI_SETTINGS_GENERAL;
void uiSettingsGeneralSelected(
const uimenu_t *menu,
const uint8_t index,
@@ -38,46 +37,49 @@ void uiSettingsGeneralSelected(
}
}
errorret_t uiSettingsGeneralInit(void) {
memoryZero(&UI_SETTINGS_GENERAL, sizeof(uisettingsgeneral_t));
errorret_t uiSettingsGeneralInit(uisettingsdata_t *data) {
uisettingsgeneral_t *general = &data->general;
memoryZero(general, sizeof(uisettingsgeneral_t));
for(uint8_t i = 0; i < UI_SETTINGS_GENERAL_LOCALE_COUNT; i++) {
UI_SETTINGS_GENERAL.localeNames[i] = UI_SETTINGS_GENERAL_LOCALES[i]->name;
general->localeNames[i] = UI_SETTINGS_GENERAL_LOCALES[i]->name;
}
errorChain(assetLocaleGetString(
&LOCALE.entry->data.locale,
"ui.settings.general.language",
0,
UI_SETTINGS_GENERAL.languageLabel,
general->languageLabel,
UI_SETTINGS_GENERAL_LABEL_MAX
));
errorChain(assetLocaleGetString(
&LOCALE.entry->data.locale,
"ui.settings.general.language_detail",
0,
UI_SETTINGS_GENERAL.labelInfo,
general->labelInfo,
UI_SETTINGS_GENERAL_INFO_MAX
));
MENU_BEGIN(
&UI_SETTINGS_GENERAL.menu, UI_SETTINGS_GENERAL.items,
&general->menu, general->items,
uiSettingsGeneralSelected, uiSettingsDiscardMenuClosed, NULL
);
MENU_DROPDOWN(
UI_SETTINGS_GENERAL.languageLabel, UI_SETTINGS_GENERAL.localeNames,
general->languageLabel, general->localeNames,
UI_SETTINGS_GENERAL_LOCALE_COUNT, 0
);
MENU_LABEL(UI_SETTINGS_GENERAL.labelInfo);
MENU_LABEL(general->labelInfo);
MENU_BUTTON(UI_SETTINGS.applyLabel);
MENU_END(UI_SETTINGS_GENERAL.items, 1);
MENU_END(general->items, 1);
errorOk();
}
void uiSettingsGeneralLoad(void) {
uisettingsgeneral_t *general = &UI_SETTINGS.data.general;
uint8_t index = 0;
for(uint8_t i = 0; i < UI_SETTINGS_GENERAL_LOCALE_COUNT; i++) {
if(stringCompare(LOCALE.locale->file, UI_SETTINGS_GENERAL_LOCALES[i]->file) != 0) {
@@ -88,23 +90,26 @@ void uiSettingsGeneralLoad(void) {
}
uiDropdownSetSelectedIndex(
&UI_SETTINGS_GENERAL.items[UI_SETTINGS_GENERAL_INDEX_LANGUAGE].dropdown,
index
&general->items[UI_SETTINGS_GENERAL_INDEX_LANGUAGE].dropdown, index
);
}
void uiSettingsGeneralApply(void) {
uisettingsgeneral_t *general = &UI_SETTINGS.data.general;
uint8_t index = uiDropdownGetSelectedIndex(
&UI_SETTINGS_GENERAL.items[UI_SETTINGS_GENERAL_INDEX_LANGUAGE].dropdown
&general->items[UI_SETTINGS_GENERAL_INDEX_LANGUAGE].dropdown
);
errorCatch(localeManagerSetLocale(UI_SETTINGS_GENERAL_LOCALES[index]));
uiMenuClose(&UI_SETTINGS_GENERAL.menu);
uiMenuClose(&general->menu);
}
bool_t uiSettingsGeneralHasChanges(void) {
uisettingsgeneral_t *general = &UI_SETTINGS.data.general;
uint8_t index = uiDropdownGetSelectedIndex(
&UI_SETTINGS_GENERAL.items[UI_SETTINGS_GENERAL_INDEX_LANGUAGE].dropdown
&general->items[UI_SETTINGS_GENERAL_INDEX_LANGUAGE].dropdown
);
return stringCompare(
LOCALE.locale->file, UI_SETTINGS_GENERAL_LOCALES[index]->file
+5 -24
View File
@@ -7,35 +7,16 @@
#pragma once
#include "error/error.h"
#include "ui/widget/uimenu.h"
#include "uisettings.h"
#define UI_SETTINGS_GENERAL_ITEM_COUNT 3
#define UI_SETTINGS_GENERAL_INDEX_LANGUAGE 0
#define UI_SETTINGS_GENERAL_INDEX_LANGUAGE_DETAIL 1
#define UI_SETTINGS_GENERAL_INDEX_APPLY 2
#define UI_SETTINGS_GENERAL_LOCALE_COUNT 3
#define UI_SETTINGS_GENERAL_LABEL_MAX 32
#define UI_SETTINGS_GENERAL_INFO_MAX 64
typedef struct {
uimenu_t menu;
uimenuitem_t items[UI_SETTINGS_GENERAL_ITEM_COUNT];
const char_t *localeNames[UI_SETTINGS_GENERAL_LOCALE_COUNT];
char_t languageLabel[UI_SETTINGS_GENERAL_LABEL_MAX];
char_t labelInfo[UI_SETTINGS_GENERAL_INFO_MAX];
} uisettingsgeneral_t;
extern uisettingsgeneral_t UI_SETTINGS_GENERAL;
#include "uisettingsdata.h"
/**
* Initializes the general settings page.
* Initializes the general settings page into the shared settings data
* union.
*
* @param data The shared settings data union to initialize into.
* @return Any error that occurs.
*/
errorret_t uiSettingsGeneralInit(void);
errorret_t uiSettingsGeneralInit(uisettingsdata_t *data);
/**
* Loads the page's widgets from the current engine general settings.
+12 -12
View File
@@ -6,14 +6,13 @@
*/
#include "uisettingsinput.h"
#include "uisettings.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "locale/localemanager.h"
#include "asset/loader/locale/assetlocaleloader.h"
#include "input/input.h"
uisettingsinput_t UI_SETTINGS_INPUT;
void uiSettingsInputSelected(
const uimenu_t *menu,
const uint8_t index,
@@ -23,11 +22,12 @@ void uiSettingsInputSelected(
uiSettingsInputApply();
}
errorret_t uiSettingsInputInit(void) {
memoryZero(&UI_SETTINGS_INPUT, sizeof(uisettingsinput_t));
errorret_t uiSettingsInputInit(uisettingsdata_t *data) {
uisettingsinput_t *input = &data->input;
memoryZero(input, sizeof(uisettingsinput_t));
MENU_BEGIN(
&UI_SETTINGS_INPUT.menu, UI_SETTINGS_INPUT.items,
&input->menu, input->items,
uiSettingsInputSelected, uiSettingsDiscardMenuClosed, NULL
);
#ifdef DUSK_INPUT_GAMEPAD
@@ -35,18 +35,18 @@ errorret_t uiSettingsInputInit(void) {
&LOCALE.entry->data.locale,
"ui.settings.input.deadzone",
0,
UI_SETTINGS_INPUT.deadzoneLabel,
input->deadzoneLabel,
UI_SETTINGS_INPUT_LABEL_MAX
));
MENU_SLIDER_FLOAT(
UI_SETTINGS_INPUT.deadzoneLabel, INPUT_DEADZONE_DEFAULT, 0.0f, 1.0f, 0.05f
input->deadzoneLabel, INPUT_DEADZONE_DEFAULT, 0.0f, 1.0f, 0.05f
);
#else
MENU_LABEL("No input settings yet");
#endif
MENU_BUTTON(UI_SETTINGS.applyLabel);
MENU_END(UI_SETTINGS_INPUT.items, 1);
MENU_END(input->items, 1);
errorOk();
}
@@ -54,7 +54,7 @@ errorret_t uiSettingsInputInit(void) {
void uiSettingsInputLoad(void) {
#ifdef DUSK_INPUT_GAMEPAD
uiSliderSetFloat(
&UI_SETTINGS_INPUT.items[UI_SETTINGS_INPUT_INDEX_DEADZONE].slider,
&UI_SETTINGS.data.input.items[UI_SETTINGS_INPUT_INDEX_DEADZONE].slider,
INPUT.deadzone
);
#endif
@@ -63,16 +63,16 @@ void uiSettingsInputLoad(void) {
void uiSettingsInputApply(void) {
#ifdef DUSK_INPUT_GAMEPAD
INPUT.deadzone = uiSliderGetFloat(
&UI_SETTINGS_INPUT.items[UI_SETTINGS_INPUT_INDEX_DEADZONE].slider
&UI_SETTINGS.data.input.items[UI_SETTINGS_INPUT_INDEX_DEADZONE].slider
);
#endif
uiMenuClose(&UI_SETTINGS_INPUT.menu);
uiMenuClose(&UI_SETTINGS.data.input.menu);
}
bool_t uiSettingsInputHasChanges(void) {
#ifdef DUSK_INPUT_GAMEPAD
if(uiSliderGetFloat(
&UI_SETTINGS_INPUT.items[UI_SETTINGS_INPUT_INDEX_DEADZONE].slider
&UI_SETTINGS.data.input.items[UI_SETTINGS_INPUT_INDEX_DEADZONE].slider
) != INPUT.deadzone) return true;
#endif
+5 -17
View File
@@ -7,28 +7,16 @@
#pragma once
#include "error/error.h"
#include "ui/widget/uimenu.h"
#include "uisettings.h"
#define UI_SETTINGS_INPUT_ITEM_COUNT 2
#define UI_SETTINGS_INPUT_LABEL_MAX 32
#define UI_SETTINGS_INPUT_INDEX_DEADZONE 0
#define UI_SETTINGS_INPUT_INDEX_APPLY 1
typedef struct {
uimenu_t menu;
uimenuitem_t items[UI_SETTINGS_INPUT_ITEM_COUNT];
char_t deadzoneLabel[UI_SETTINGS_INPUT_LABEL_MAX];
} uisettingsinput_t;
extern uisettingsinput_t UI_SETTINGS_INPUT;
#include "uisettingsdata.h"
/**
* Initializes the input settings page.
* Initializes the input settings page into the shared settings data
* union.
*
* @param data The shared settings data union to initialize into.
* @return Any error that occurs.
*/
errorret_t uiSettingsInputInit(void);
errorret_t uiSettingsInputInit(uisettingsdata_t *data);
/**
* Loads the page's widgets from the current engine input settings.