diff --git a/bin/custom_textures.c b/bin/custom_textures.c index 8136a1fcb..7ec6e78bc 100644 --- a/bin/custom_textures.c +++ b/bin/custom_textures.c @@ -31,6 +31,14 @@ ALIGNED8 const Texture texture_selectionbox_forward_icon[] = { #include "textures/segment2/custom_selectionbox_forward_icon.rgba16.inc.c" }; +ALIGNED8 const Texture texture_selectionbox_up_icon[] = { +#include "textures/segment2/custom_selectionbox_up_icon.rgba16.inc.c" +}; + +ALIGNED8 const Texture texture_selectionbox_down_icon[] = { +#include "textures/segment2/custom_selectionbox_down_icon.rgba16.inc.c" +}; + ALIGNED8 const Texture texture_coopdx_logo[] = { #include "textures/segment2/custom_coopdx_logo.rgba32.inc.c" }; diff --git a/lang/Czech.ini b/lang/Czech.ini index ddf7ae547..e3288b251 100644 --- a/lang/Czech.ini +++ b/lang/Czech.ini @@ -431,6 +431,11 @@ REFRESHING = "Obnovování..." ENTER_PASSWORD = "Zadejte heslo soukromé hry:" SEARCH = "Hledat" NO_LOBBIES_FOUND = "Nebyly nalezeny žádné hry." +SORT_BY = "Seřadit podle" +NONE = "Žádné" +NAME = "Jméno" +GAMEMODE = "Herní mód" +PLAYERS = "Hráči" [CHANGELOG] CHANGELOG_TITLE = "ZÁZNAM ZMĚN" diff --git a/lang/Dutch.ini b/lang/Dutch.ini index a06530f8b..0e2e49de5 100644 --- a/lang/Dutch.ini +++ b/lang/Dutch.ini @@ -431,6 +431,11 @@ REFRESHING = "herladen..." ENTER_PASSWORD = "Typ het wachtwoord van de privé lobby:" SEARCH = "Zoek" NO_LOBBIES_FOUND = "Er zijn geen lobby's gevonden." +SORT_BY = "Sorteer op" +NONE = "Geen" +NAME = "Naam" +GAMEMODE = "Spelmodus" +PLAYERS = "Spelers" [CHANGELOG] CHANGELOG_TITLE = "WIJZIGINGENLOGBOEK" diff --git a/lang/English.ini b/lang/English.ini index 14ec1d462..3aeb58d31 100644 --- a/lang/English.ini +++ b/lang/English.ini @@ -431,6 +431,11 @@ REFRESHING = "Refreshing..." ENTER_PASSWORD = "Enter the private lobby's password:" SEARCH = "Search" NO_LOBBIES_FOUND = "No lobbies were found." +SORT_BY = "Sort By" +NONE = "None" +NAME = "Name" +GAMEMODE = "Gamemode" +PLAYERS = "Players" [CHANGELOG] CHANGELOG_TITLE = "CHANGELOG" diff --git a/lang/French.ini b/lang/French.ini index b0dfe3be2..08c5bef6d 100644 --- a/lang/French.ini +++ b/lang/French.ini @@ -431,6 +431,11 @@ REFRESHING = "Actualisation..." ENTER_PASSWORD = "Entrez le mot de passe de la partie:" SEARCH = "Rechercher" NO_LOBBIES_FOUND = "Aucune partie n'a été trouvée." +SORT_BY = "Trier par" +NONE = "Aucun" +NAME = "Pseudo" +GAMEMODE = "Mode de jeu" +PLAYERS = "Joueurs" [CHANGELOG] CHANGELOG_TITLE = "MODIFICATIONS" diff --git a/lang/German.ini b/lang/German.ini index 46d0bf440..7d09ce1ff 100644 --- a/lang/German.ini +++ b/lang/German.ini @@ -431,6 +431,11 @@ REFRESHING = "Aktualisiere..." ENTER_PASSWORD = "Gebe das Lobby-Passwort ein:" SEARCH = "Suchen" NO_LOBBIES_FOUND = "Keine Lobbys gefunden." +SORT_BY = "Sortieren nach" +NONE = "Keine" +NAME = "Name" +GAMEMODE = "Spielmodus" +PLAYERS = "Spieler" [CHANGELOG] CHANGELOG_TITLE = "ÄNDERUNGSPROTOKOLL" diff --git a/lang/Italian.ini b/lang/Italian.ini index e0827a2f6..4fc917652 100644 --- a/lang/Italian.ini +++ b/lang/Italian.ini @@ -429,6 +429,11 @@ REFRESHING = "Ricaricando..." ENTER_PASSWORD = "Scrivi la password della stanza privata:" SEARCH = "Cerca" NO_LOBBIES_FOUND = "Non è stata trovata alcuna stanza." +SORT_BY = "Ordina per" +NONE = "Nulla" +NAME = "Nome" +GAMEMODE = "Modalità di gioco" +PLAYERS = "Giocatori" [CHANGELOG] CHANGELOG_TITLE = "REGISTRO DELLE MODIFICHE" diff --git a/lang/Japanese.ini b/lang/Japanese.ini index 322c449e8..727c28cf0 100644 --- a/lang/Japanese.ini +++ b/lang/Japanese.ini @@ -434,6 +434,11 @@ ENTER_PASSWORD = "部屋のパスワードを入力してください:" SEARCH = "検索" NONE_FOUND = "部屋が見つかりませんでした" NO_LOBBIES_FOUND = "ロビーは見つからなかった。" +SORT_BY = "並べ替え" +NONE = "なし" +NAME = "名前" +GAMEMODE = "ゲームモード" +PLAYERS = "プレイヤー数" [CHANGELOG] CHANGELOG_TITLE = "CHANGELOG" diff --git a/lang/Portuguese.ini b/lang/Portuguese.ini index e935499a6..42c240758 100644 --- a/lang/Portuguese.ini +++ b/lang/Portuguese.ini @@ -412,7 +412,7 @@ MUTE_FOCUS_LOSS = "Silenciar quando a janela estiver desfocada" [LANGUAGE] LANGUAGE = "IDIOMA" -Czech = "Tcheco (Čeština)" +Czech = "Tcheco (Čeština)" Dutch = "Holandês (Nederlands)" French = "Francês (Français)" German = "Alemão (Deutsch)" @@ -431,6 +431,11 @@ REFRESHING = "Recarregando..." ENTER_PASSWORD = "Digite a senha da sala privada:" SEARCH = "Pesquisar" NO_LOBBIES_FOUND = "Nenhuma sala encontrada." +SORT_BY = "Sortieren nach" +NONE = "Keine" +NAME = "Name" +GAMEMODE = "Spielmodus" +PLAYERS = "Spieler" [CHANGELOG] CHANGELOG_TITLE = "ALTERAÇÕES" diff --git a/lang/Russian.ini b/lang/Russian.ini index 52861a4cf..d26da9921 100644 --- a/lang/Russian.ini +++ b/lang/Russian.ini @@ -430,6 +430,11 @@ REFRESHING = "Обновление..." ENTER_PASSWORD = "Введите пароль закрытой группы:" SEARCH = "Поиск" NO_LOBBIES_FOUND = "Группы не найдены." +SORT_BY = "Сортировка" +NONE = "Нет" +NAME = "По имени" +GAMEMODE = "По игровому режиму" +PLAYERS = "По игрокам" [CHANGELOG] CHANGELOG_TITLE = "ЖУРНАЛ ИЗМЕНЕНИЙ" diff --git a/lang/Spanish.ini b/lang/Spanish.ini index f083c8ba2..d8fd35902 100644 --- a/lang/Spanish.ini +++ b/lang/Spanish.ini @@ -431,6 +431,11 @@ REFRESHING = "Refrescando..." ENTER_PASSWORD = "Escribe la contraseña de la partida privada:" SEARCH = "Buscar" NO_LOBBIES_FOUND = "No se han encontrado partidas." +SORT_BY = "Ordenar por" +NONE = "Nada" +NAME = "Nombre" +GAMEMODE = "Modo de juego" +PLAYERS = "Jugadores" [CHANGELOG] CHANGELOG_TITLE = "REGISTRO DE CAMBIOS" diff --git a/src/pc/configfile.c b/src/pc/configfile.c index f2267a7db..8f23905c1 100644 --- a/src/pc/configfile.c +++ b/src/pc/configfile.c @@ -174,6 +174,8 @@ unsigned int configHostSaveSlot = 1; char configJoinIp[MAX_CONFIG_STRING] = ""; unsigned int configJoinPort = DEFAULT_PORT; unsigned int configNetworkSystem = 0; +unsigned int configCoopNetSortSelected = 0; +bool configCoopNetSortInverted = false; unsigned int configPlayerInteraction = 1; unsigned int configPlayerKnockbackStrength = 25; unsigned int configStayInLevelAfterStar = 0; @@ -333,6 +335,8 @@ static const struct ConfigOption options[] = { {.name = "coop_join_ip", .type = CONFIG_TYPE_STRING, .stringValue = (char*)&configJoinIp, .maxStringLength = MAX_CONFIG_STRING}, {.name = "coop_join_port", .type = CONFIG_TYPE_UINT, .uintValue = &configJoinPort}, {.name = "coop_network_system", .type = CONFIG_TYPE_UINT, .uintValue = &configNetworkSystem}, + {.name = "coop_coop_net_sort_selected", .type = CONFIG_TYPE_UINT, .uintValue = &configCoopNetSortSelected}, + {.name = "coop_coop_net_sort_inverted", .type = CONFIG_TYPE_BOOL, .uintValue = &configCoopNetSortInverted}, {.name = "coop_player_interaction", .type = CONFIG_TYPE_UINT, .uintValue = &configPlayerInteraction}, {.name = "coop_player_knockback_strength", .type = CONFIG_TYPE_UINT, .uintValue = &configPlayerKnockbackStrength}, {.name = "coop_stay_in_level_after_star", .type = CONFIG_TYPE_UINT, .uintValue = &configStayInLevelAfterStar}, diff --git a/src/pc/configfile.h b/src/pc/configfile.h index d537970e4..8374097cb 100644 --- a/src/pc/configfile.h +++ b/src/pc/configfile.h @@ -139,6 +139,8 @@ extern unsigned int configHostSaveSlot; extern char configJoinIp[MAX_CONFIG_STRING]; extern unsigned int configJoinPort; extern unsigned int configNetworkSystem; +extern unsigned int configCoopNetSortSelected; +extern bool configCoopNetSortInverted; extern unsigned int configPlayerInteraction; extern unsigned int configPlayerKnockbackStrength; extern unsigned int configStayInLevelAfterStar; diff --git a/src/pc/djui/djui_panel_join_lobbies.c b/src/pc/djui/djui_panel_join_lobbies.c index 91b700f71..f753dc424 100644 --- a/src/pc/djui/djui_panel_join_lobbies.c +++ b/src/pc/djui/djui_panel_join_lobbies.c @@ -4,6 +4,7 @@ #include "djui_panel.h" #include "djui_panel_menu.h" #include "djui_panel_join_message.h" +#include "djui_panel_join_lobbies.h" #include "djui_lobby_entry.h" #include "djui_panel_rules.h" #include "pc/network/network.h" @@ -18,13 +19,85 @@ #define DJUI_DESC_PANEL_WIDTH (410.0f + (16 * 2.0f)) +extern ALIGNED8 u8 texture_selectionbox_up_icon[]; +extern ALIGNED8 u8 texture_selectionbox_down_icon[]; + +static struct LobbySortType sLobbySorting[] = { + { + "NONE", + LOBBY_SORTING_NONE, + }, + { + "NAME", + LOBBY_SORTING_NAME, + }, + { + "GAMEMODE", + LOBBY_SORTING_GAMEMODE, + }, + { + "PLAYERS", + LOBBY_SORTING_PLAYERS, + }, +}; +static const int numSortOptions = sizeof(sLobbySorting) / sizeof(sLobbySorting[0]); + +static struct CoopnetLobby** sCoopnetLobbies = NULL; +static unsigned int sCoopnetLobbyCount = 0; + static struct DjuiPaginated* sLobbyPaginated = NULL; static struct DjuiFlowLayout* sLobbyLayout = NULL; static struct DjuiButton* sRefreshButton = NULL; static struct DjuiThreePanel* sDescriptionPanel = NULL; static struct DjuiText* sTooltip = NULL; +static struct DjuiSelectionbox* sSelectionbox = NULL; +static struct DjuiImage* sSortInvertImage = NULL; +static unsigned int sSavedLobbyStartCount = 0; + static char* sPassword = NULL; +static void free_coopnet_lobbies() { + for (unsigned int i = 0; i < sCoopnetLobbyCount; i++) + { + struct CoopnetLobby* lobby = sCoopnetLobbies[i]; + if (!lobby) { continue; } + free(lobby->playerText); + free(lobby->hostName); + free(lobby->mode); + free(lobby->description); + free(lobby); + } + + free(sCoopnetLobbies); + sCoopnetLobbies = NULL; + sCoopnetLobbyCount = 0; +} + +static int sort_coopnet_lobby_comp(const void* a, const void* b) { + const struct CoopnetLobby* lobbyA = *(const struct CoopnetLobby**)a; + const struct CoopnetLobby* lobbyB = *(const struct CoopnetLobby**)b; + + int retValue = 0; + enum LobbySorting sortBy = sLobbySorting[configCoopNetSortSelected].sortType; + if (sortBy == LOBBY_SORTING_NAME) { + retValue = strcmp(lobbyA->hostName, lobbyB->hostName); + } else if (sortBy == LOBBY_SORTING_GAMEMODE) { + retValue = strcmp(lobbyA->mode, lobbyB->mode); + } else if (sortBy == LOBBY_SORTING_PLAYERS) { + retValue = lobbyB->playerCount - lobbyA->playerCount; + } else if (sortBy == LOBBY_SORTING_NONE) { + retValue = lobbyA->lobbyId > lobbyB->lobbyId ? 1 : -1; + } + + retValue *= configCoopNetSortInverted ? -1 : 1; + if (lobbyA->disabled != lobbyB->disabled) { + retValue = lobbyA->disabled ? 1 : -1; + } else if (retValue == 0) { + retValue = lobbyA->lobbyId > lobbyB->lobbyId ? 1 : -1; + } + return retValue; +} + static void djui_panel_join_lobby_description_create(void) { struct DjuiThreePanel* panel = djui_three_panel_create(&gDjuiRoot->base, 0, 1200, 0); struct DjuiThreePanelTheme theme = gDjuiThemes[configDjuiTheme]->threePanels; @@ -56,6 +129,7 @@ static void djui_panel_join_lobby_description_create(void) { sDescriptionPanel = panel; } + static void djui_lobby_on_hover(struct DjuiBase* base) { struct DjuiLobbyEntry* entry = (struct DjuiLobbyEntry*)base; djui_text_set_text(sTooltip, entry->description); @@ -79,6 +153,22 @@ void djui_panel_join_lobby(struct DjuiBase* caller) { djui_panel_join_message_create(caller); } +static void djui_panel_join_on_sorting_change(UNUSED struct DjuiBase* base) { + qsort(sCoopnetLobbies, sCoopnetLobbyCount, sizeof(sCoopnetLobbies[0]), sort_coopnet_lobby_comp); + djui_base_destroy_children(&sLobbyLayout->base); + for (unsigned int i = 0; i < sCoopnetLobbyCount; i++) { + struct CoopnetLobby* lobby = sCoopnetLobbies[i]; + struct DjuiLobbyEntry* entry = djui_lobby_entry_create(&sLobbyLayout->base, lobby->hostName, lobby->mode, lobby->playerText, lobby->description, lobby->disabled, djui_panel_join_lobby, djui_lobby_on_hover, djui_lobby_on_hover_end); + entry->base.tag = (s64)lobby->lobbyId; + } +} + +static void djui_panel_join_invert_sort(UNUSED struct DjuiBase* caller) { + configCoopNetSortInverted = !configCoopNetSortInverted; + sSortInvertImage->textureInfo.texture = configCoopNetSortInverted ? texture_selectionbox_up_icon : texture_selectionbox_down_icon; + djui_panel_join_on_sorting_change(NULL); +} + void djui_panel_join_query(uint64_t aLobbyId, UNUSED uint64_t aOwnerId, uint16_t aConnections, uint16_t aMaxConnections, UNUSED int64_t aTimestamp, UNUSED const char* aGame, const char* aVersion, const char* aHostName, const char* aMode, const char* aDescription) { if (!sLobbyLayout) { return; } if (!sLobbyPaginated) { return; } @@ -97,16 +187,47 @@ void djui_panel_join_query(uint64_t aLobbyId, UNUSED uint64_t aOwnerId, uint16_t snprintf(mode, COOPNET_MAX_MODE_LEN, "\\#ff0000\\[%s]", aVersion); } - struct DjuiBase* layoutBase = &sLobbyLayout->base; - struct DjuiLobbyEntry* entry = djui_lobby_entry_create(layoutBase, (char*)aHostName, (char*)mode, playerText, (char*)aDescription, disabled, djui_panel_join_lobby, djui_lobby_on_hover, djui_lobby_on_hover_end); - entry->base.tag = (s64)aLobbyId; - djui_paginated_update_page_buttons(sLobbyPaginated); + struct CoopnetLobby* lobby = malloc(sizeof(struct CoopnetLobby)); + + if (!lobby) { + LOG_ERROR("Failed to allocate memory to lobby!"); + return; + } + + lobby->lobbyId = aLobbyId; + lobby->playerCount = aConnections; + lobby->playerText = strdup(playerText); + lobby->hostName = strdup(aHostName); + lobby->mode = strdup(mode); + lobby->description = strdup(aDescription); + lobby->disabled = disabled; + + struct CoopnetLobby** lobbies = realloc(sCoopnetLobbies, (sCoopnetLobbyCount + 1) * sizeof(struct CoopnetLobby*)); + if (!lobbies) { + LOG_ERROR("Failed to reallocate memory to lobbies!"); + return; + } + sCoopnetLobbies = lobbies; + sCoopnetLobbies[sCoopnetLobbyCount] = lobby; + sCoopnetLobbyCount++; } void djui_panel_join_query_finish(void) { if (!sLobbyLayout) { return; } if (!sLobbyPaginated) { return; } if (!sRefreshButton) { return; } + + qsort(sCoopnetLobbies, sCoopnetLobbyCount, sizeof(sCoopnetLobbies[0]), sort_coopnet_lobby_comp); + + djui_base_destroy_children(&sLobbyLayout->base); + djui_base_set_enabled(&sLobbyLayout->base, true); + struct DjuiBase* layoutBase = &sLobbyLayout->base; + for (unsigned int i = 0; i < sCoopnetLobbyCount; i++) { + struct CoopnetLobby* lobby = sCoopnetLobbies[i]; + struct DjuiLobbyEntry* entry = djui_lobby_entry_create(layoutBase, lobby->hostName, lobby->mode, lobby->playerText, lobby->description, lobby->disabled, djui_panel_join_lobby, djui_lobby_on_hover, djui_lobby_on_hover_end); + entry->base.tag = (s64)lobby->lobbyId; + } + djui_text_set_text(sRefreshButton->text, DLANG(LOBBIES, REFRESH)); djui_base_set_enabled(&sRefreshButton->base, true); @@ -117,7 +238,9 @@ void djui_panel_join_query_finish(void) { djui_text_set_alignment(text, DJUI_HALIGN_CENTER, DJUI_VALIGN_CENTER); djui_text_set_drop_shadow(text, 64, 64, 64, 100); } + sLobbyPaginated->startIndex = sSavedLobbyStartCount; djui_paginated_update_page_buttons(sLobbyPaginated); + djui_panel_join_on_sorting_change(NULL); } void djui_panel_join_lobbies_on_destroy(UNUSED struct DjuiBase* caller) { @@ -126,6 +249,8 @@ void djui_panel_join_lobbies_on_destroy(UNUSED struct DjuiBase* caller) { sRefreshButton = NULL; sLobbyLayout = NULL; sLobbyPaginated = NULL; + sSavedLobbyStartCount = 0; + free_coopnet_lobbies(); if (sDescriptionPanel != NULL) { djui_base_destroy(&sDescriptionPanel->base); @@ -134,10 +259,18 @@ void djui_panel_join_lobbies_on_destroy(UNUSED struct DjuiBase* caller) { } void djui_panel_join_lobbies_refresh(UNUSED struct DjuiBase* caller) { + sSavedLobbyStartCount = sLobbyPaginated->startIndex; + djui_base_set_enabled(&sLobbyLayout->base, false); djui_base_destroy_children(&sLobbyLayout->base); + for (unsigned int i = 0; i < sCoopnetLobbyCount; i++) { + struct CoopnetLobby* lobby = sCoopnetLobbies[i]; + struct DjuiLobbyEntry* entry = djui_lobby_entry_create(&sLobbyLayout->base, lobby->hostName, lobby->mode, lobby->playerText, lobby->description, true, djui_panel_join_lobby, djui_lobby_on_hover, djui_lobby_on_hover_end); + entry->base.tag = (s64)lobby->lobbyId; + } djui_text_set_text(sRefreshButton->text, DLANG(LOBBIES, REFRESHING)); djui_base_set_enabled(&sRefreshButton->base, false); djui_paginated_update_page_buttons(sLobbyPaginated); + free_coopnet_lobbies(); ns_coopnet_query(djui_panel_join_query, djui_panel_join_query_finish, sPassword); } @@ -146,6 +279,9 @@ void djui_panel_join_lobbies_value_changed(UNUSED struct DjuiBase* caller) { } void djui_panel_join_lobbies_create(struct DjuiBase* caller, const char* password) { + if (configCoopNetSortSelected > numSortOptions) { + configCoopNetSortSelected = numSortOptions; + } if (sPassword) { free(sPassword); sPassword = NULL; } sPassword = strdup(password); bool private = (strlen(password) > 0); @@ -162,6 +298,27 @@ void djui_panel_join_lobbies_create(struct DjuiBase* caller, const char* passwor true); struct DjuiBase* body = djui_three_panel_get_body(panel); { + char* sortChoices[sizeof(sLobbySorting)]; + for (int i = 0; i < numSortOptions; i++) { + sortChoices[i] = djui_language_get("LOBBIES", sLobbySorting[i].langKey); + } + struct DjuiFlowLayout* flowLayout = djui_flow_layout_create(body); + djui_base_set_color(&flowLayout->base, 0, 0, 0, 0); + djui_base_set_size_type(&flowLayout->base, DJUI_SVT_RELATIVE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&flowLayout->base, 1.0f, 32.0f); + sSelectionbox = djui_selectionbox_create(&flowLayout->base, DLANG(LOBBIES, SORT_BY), sortChoices, numSortOptions, &configCoopNetSortSelected, djui_panel_join_on_sorting_change); + djui_base_set_size(&sSelectionbox->base, 0.925, 32); + djui_base_set_size(&sSelectionbox->rect->base, 0.55, 1); + struct DjuiButton* button = djui_button_create(&flowLayout->base, "", DJUI_BUTTON_STYLE_NORMAL, djui_panel_join_invert_sort); + djui_base_set_alignment(&button->base, DJUI_HALIGN_RIGHT, DJUI_VALIGN_BOTTOM); + djui_base_set_size_type(&button->base, DJUI_SVT_ABSOLUTE, DJUI_SVT_ABSOLUTE); + djui_base_set_size(&button->base, 32, 32); + sSortInvertImage = djui_image_create(&button->base, configCoopNetSortInverted ? texture_selectionbox_up_icon : texture_selectionbox_down_icon, 16, 16, G_IM_FMT_RGBA, G_IM_SIZ_16b); + djui_base_set_size(&sSortInvertImage->base, 16, 16); + djui_base_set_alignment(&sSortInvertImage->base, DJUI_HALIGN_CENTER, DJUI_VALIGN_CENTER); + djui_flow_layout_set_margin(flowLayout, 16); + djui_flow_layout_set_flow_direction(flowLayout, DJUI_FLOW_DIR_RIGHT); + sLobbyPaginated = djui_paginated_create(body, 10); sLobbyLayout = sLobbyPaginated->layout; djui_flow_layout_set_margin(sLobbyLayout, 4); diff --git a/src/pc/djui/djui_panel_join_lobbies.h b/src/pc/djui/djui_panel_join_lobbies.h index cda56f95e..2e9921b08 100644 --- a/src/pc/djui/djui_panel_join_lobbies.h +++ b/src/pc/djui/djui_panel_join_lobbies.h @@ -1,3 +1,26 @@ #pragma once +enum LobbySorting { + LOBBY_SORTING_NONE, + LOBBY_SORTING_NAME, + LOBBY_SORTING_GAMEMODE, + LOBBY_SORTING_PLAYERS, + LOBBY_SORTING_COUNT, +}; + +struct LobbySortType { + const char* langKey; + enum LobbySorting sortType; +}; + +struct CoopnetLobby { + uint64_t lobbyId; + int playerCount; + char* hostName; + char* mode; + char* playerText; + char* description; + bool disabled; +}; + void djui_panel_join_lobbies_create(struct DjuiBase* caller, const char* password); diff --git a/src/pc/mods/mods.c b/src/pc/mods/mods.c index 84b8fb497..604e9c820 100644 --- a/src/pc/mods/mods.c +++ b/src/pc/mods/mods.c @@ -29,12 +29,24 @@ struct LocalEnabledPath { struct LocalEnabledPath* sLocalEnabledPaths = NULL; void mods_get_main_mod_name(char* destination, u32 maxSize) { + struct Mod* selectedRomhack = NULL; struct Mod* picked = NULL; size_t pickedSize = 0; for (u16 i = 0; i < gLocalMods.entryCount; i++) { struct Mod* mod = gLocalMods.entries[i]; if (!mod->enabled) { continue; } + // always make gamemodes the main mod + if ((mod->category && strcmp(mod->category, "gamemode") == 0) + || (mod->incompatible && strcmp(mod->incompatible, "gamemode") == 0)) { + picked = mod; + break; + } + // prioritize romhacks + if ((mod->category && strcmp(mod->category, "romhack") == 0) + || (mod->incompatible && strcmp(mod->incompatible, "romhack") == 0)) { + selectedRomhack = mod; + } size_t size = mod_get_lua_size(mod); if (size > pickedSize) { picked = mod; @@ -42,6 +54,7 @@ void mods_get_main_mod_name(char* destination, u32 maxSize) { } } + if (selectedRomhack) { picked = selectedRomhack; } snprintf(destination, maxSize, "%s", picked ? picked->name : "Super Mario 64"); } diff --git a/textures/segment2/custom_selectionbox_down_icon.rgba16.png b/textures/segment2/custom_selectionbox_down_icon.rgba16.png new file mode 100644 index 000000000..ac6be8ddb Binary files /dev/null and b/textures/segment2/custom_selectionbox_down_icon.rgba16.png differ diff --git a/textures/segment2/custom_selectionbox_up_icon.rgba16.png b/textures/segment2/custom_selectionbox_up_icon.rgba16.png new file mode 100644 index 000000000..6bc626944 Binary files /dev/null and b/textures/segment2/custom_selectionbox_up_icon.rgba16.png differ