ui: merge save slot dialog via libtrx

This commit is contained in:
Marcin Kurczewski 2025-04-20 00:14:15 +02:00
parent db94669ae4
commit 7f574c9448
15 changed files with 75 additions and 254 deletions

View file

@ -1,25 +1,33 @@
#include "game/ui/dialogs/save_slot.h"
#include "game/screen.h"
#include "global/vars.h"
#include <libtrx/game/game_string.h>
#include <libtrx/game/input.h>
#include <libtrx/game/savegame.h>
#include <libtrx/game/ui/common.h>
#include <libtrx/game/ui/elements/anchor.h>
#include <libtrx/game/ui/elements/hide.h>
#include <libtrx/game/ui/elements/label.h>
#include <libtrx/game/ui/elements/modal.h>
#include <libtrx/game/ui/elements/offset.h>
#include <libtrx/game/ui/elements/requester.h>
#include <libtrx/game/ui/elements/resize.h>
#include <libtrx/game/ui/elements/spacer.h>
#include <libtrx/game/ui/elements/stack.h>
#include <libtrx/memory.h>
#include "game/game_string.h"
#include "game/input.h"
#include "game/inventory.h"
#include "game/savegame.h"
#include "game/scaler.h"
#include "game/ui/common.h"
#include "game/ui/elements/anchor.h"
#include "game/ui/elements/hide.h"
#include "game/ui/elements/label.h"
#include "game/ui/elements/modal.h"
#include "game/ui/elements/offset.h"
#include "game/ui/elements/requester.h"
#include "game/ui/elements/resize.h"
#include "game/ui/elements/spacer.h"
#include "game/ui/elements/stack.h"
#include "game/viewport.h"
#include "memory.h"
#include "utils.h"
#include <stdio.h>
// TODO: consolidate this variable
#if TR_VERSION == 1
extern int32_t g_InvMode;
#else
extern int32_t g_Inv_Mode;
#endif
typedef struct UI_SAVE_SLOT_DIALOG_STATE {
UI_SAVE_SLOT_DIALOG_TYPE type;
UI_REQUESTER_STATE req;
@ -34,15 +42,20 @@ static void M_EmptySlot(const UI_SAVE_SLOT_DIALOG_STATE *s, int32_t slot_idx);
static int32_t M_GetVisibleRows(void)
{
const int32_t res_h = Screen_GetResHeightDownscaled(RSR_TEXT);
if (res_h <= 240) {
return 5;
} else if (res_h <= 384) {
return 7;
} else if (res_h <= 480) {
if (TR_VERSION == 2) {
return 10;
} else {
return 12;
const int32_t res_h =
Scaler_CalcInverse(Viewport_GetHeight(), SCALER_TARGET_TEXT);
if (res_h <= 240) {
return 5;
} else if (res_h <= 384) {
return 7;
} else if (res_h <= 480) {
return 10;
} else {
return 12;
}
}
}
@ -55,8 +68,10 @@ static bool M_ShowDetails(
if (!UI_Requester_IsRowSelected(&s->req, slot_idx)) {
return false;
}
const SAVEGAME_INFO *const info = Savegame_GetSavegameInfo(slot_idx);
return info != nullptr && info->level_title != nullptr;
if (TR_VERSION == 2) {
return false;
}
return !Savegame_IsSlotFree(slot_idx);
}
static void M_NonEmptySlot(
@ -74,10 +89,19 @@ static void M_NonEmptySlot(
UI_BeginHide(true);
UI_Label("\\{button right}");
UI_EndHide();
UI_BeginStack(UI_STACK_HORIZONTAL);
if (TR_VERSION == 1) {
UI_BeginStack(UI_STACK_HORIZONTAL);
}
} else {
UI_BeginAnchor(0.5f, 0.5f);
UI_BeginStack(UI_STACK_HORIZONTAL);
if (TR_VERSION == 1) {
UI_BeginAnchor(0.5f, 0.5f);
UI_BeginStack(UI_STACK_HORIZONTAL);
} else {
UI_BeginStackEx((UI_STACK_SETTINGS) {
.orientation = UI_STACK_HORIZONTAL,
.align = { .h = UI_STACK_H_ALIGN_DISTRIBUTE },
});
}
}
// Level title with the save counter
@ -90,14 +114,18 @@ static void M_NonEmptySlot(
}
if (show_details) {
UI_EndStack();
if (TR_VERSION == 1) {
UI_EndStack();
}
UI_BeginOffset(0.0f, -1.0f);
UI_Label("\\{button right}");
UI_EndOffset();
UI_EndStack();
} else {
UI_EndStack();
UI_EndAnchor();
if (TR_VERSION == 1) {
UI_EndAnchor();
}
}
}
@ -121,7 +149,8 @@ UI_SAVE_SLOT_DIALOG_STATE *UI_SaveSlotDialog_Init(
UI_Requester_Init(
&s->req, M_GetVisibleRows(), Savegame_GetSlotCount(), true);
s->req.row_pad = 2.0f;
s->req.show_arrows = true;
s->req.row_spacing = TR_VERSION == 1 ? 2.0f : 3.0f;
s->req.show_arrows = TR_VERSION == 1;
s->req.reserve_space = true;
s->req.sel_row = save_slot;
CLAMP(s->req.sel_row, 0, s->req.max_rows);
@ -165,7 +194,12 @@ UI_SAVE_SLOT_DIALOG_CHOICE UI_SaveSlotDialog_Control(
void UI_SaveSlotDialog(const UI_SAVE_SLOT_DIALOG_STATE *const s)
{
UI_BeginModal(0.5f, g_InvMode == INV_TITLE_MODE ? 0.72f : 0.55f);
#if TR_VERSION == 1
const float modal_y = g_InvMode == INV_TITLE_MODE ? 0.72f : 0.55f;
#else
const float modal_y = g_Inv_Mode == INV_TITLE_MODE ? 0.8f : 0.65f;
#endif
UI_BeginModal(0.5f, modal_y);
UI_BeginResize(300.0f, -1.0f);
const char *const title = (s->type == UI_SAVE_SLOT_DIALOG_SAVE_GAME)

View file

@ -148,3 +148,4 @@ GS_DEFINE(DETAIL_RENDER_MODE, "Render mode")
GS_DEFINE(DETAIL_UI_TEXT_SCALE, "UI text scale")
GS_DEFINE(DETAIL_UI_BAR_SCALE, "UI bar scale")
GS_DEFINE(PAGINATION_NAV, "%d / %d")
GS_DEFINE(MISC_EMPTY_SLOT_FMT, "- EMPTY SLOT -")

View file

@ -7,6 +7,7 @@
#include "./ui/dialogs/new_game.h"
#include "./ui/dialogs/pause.h"
#include "./ui/dialogs/photo_mode.h"
#include "./ui/dialogs/save_slot.h"
#include "./ui/dialogs/stats.h"
#include "./ui/elements/anchor.h"
#include "./ui/elements/fade.h"

View file

@ -2,7 +2,7 @@
#pragma once
#include <libtrx/game/ui/common.h>
#include "../common.h"
typedef enum {
UI_SAVE_SLOT_DIALOG_LOAD_GAME,

View file

@ -210,6 +210,7 @@ sources = [
'game/ui/dialogs/new_game.c',
'game/ui/dialogs/pause.c',
'game/ui/dialogs/photo_mode.c',
'game/ui/dialogs/save_slot.c',
'game/ui/dialogs/stats.c',
'game/ui/elements/anchor.c',
'game/ui/elements/fade.c',

View file

@ -4,9 +4,9 @@
void GameString_Init(void)
{
#include "game_string.def"
#include <libtrx/game/game_string.def>
// force order
#include "game_string.def"
}
void GameString_Shutdown(void)

View file

@ -33,7 +33,6 @@ GS_DEFINE(STATS_BONUS_STATISTICS, "Bonus Statistics")
GS_DEFINE(STATS_AMMO, "AMMO HITS/USED")
GS_DEFINE(STATS_DISTANCE_TRAVELLED, "DISTANCE TRAVELLED")
GS_DEFINE(STATS_MEDIPACKS_USED, "HEALTH PACKS USED")
GS_DEFINE(MISC_EMPTY_SLOT_FMT, "- EMPTY SLOT %d -")
GS_DEFINE(OSD_FLY_MODE_ON, "Fly mode enabled")
GS_DEFINE(OSD_FLY_MODE_OFF, "Fly mode disabled")
GS_DEFINE(OSD_GIVE_ITEM_ALL_KEYS, "Surprise! Every key item Lara needs is now in her backpack.")
@ -50,3 +49,4 @@ GS_DEFINE(OSD_DOOR_CLOSE, "Close Sesame!")
GS_DEFINE(OSD_DOOR_OPEN_FAIL, "No doors in Lara's proximity")
GS_DEFINE(ITEM_EXAMINE_ROLE, "\\{button empty} %s: Examine")
GS_DEFINE(ITEM_USE_ROLE, "\\{button empty} %s: Use")
GS_DEFINE(MISC_EMPTY_SLOT_FMT, "- EMPTY SLOT %d -")

View file

@ -9,7 +9,6 @@
#include "game/screen.h"
#include "game/sound.h"
#include "game/text.h"
#include "game/ui/dialogs/save_slot.h"
#include "game/ui/dialogs/select_level.h"
#include "global/const.h"
#include "global/vars.h"

View file

@ -262,7 +262,6 @@ sources = [
'game/stats/common.c',
'game/text.c',
'game/ui/common.c',
'game/ui/dialogs/save_slot.c',
'game/ui/dialogs/select_level.c',
'game/ui/dialogs/stats.c',
'game/viewport.c',

View file

@ -29,7 +29,6 @@ GS_DEFINE(STATS_ASSAULT_NO_TIMES_SET, "No Times Set")
GS_DEFINE(STATS_ASSAULT_FINISH, "Finish")
GS_DEFINE(PASSPORT_EXIT_DEMO, "Exit Demo")
GS_DEFINE(MISC_NONE, "None")
GS_DEFINE(MISC_EMPTY_SLOT_FMT, "- EMPTY SLOT -")
GS_DEFINE(OSD_BILINEAR_FILTER_ON, "Bilinear filter: on")
GS_DEFINE(OSD_BILINEAR_FILTER_OFF, "Bilinear filter: off")
GS_DEFINE(OSD_WIREFRAME_MODE_ON, "Wireframe mode: on")

View file

@ -9,7 +9,6 @@
#include "game/savegame.h"
#include "game/sound.h"
#include "game/text.h"
#include "game/ui/dialogs/save_slot.h"
#include "global/vars.h"
#include <libtrx/config.h>

View file

@ -1,177 +0,0 @@
#include "game/ui/dialogs/save_slot.h"
#include "game/inventory_ring/vars.h"
#include "global/vars.h"
#include <libtrx/game/game_string.h>
#include <libtrx/game/input.h>
#include <libtrx/game/savegame.h>
#include <libtrx/game/scaler.h>
#include <libtrx/game/ui/common.h>
#include <libtrx/game/ui/elements/anchor.h>
#include <libtrx/game/ui/elements/hide.h>
#include <libtrx/game/ui/elements/label.h>
#include <libtrx/game/ui/elements/modal.h>
#include <libtrx/game/ui/elements/offset.h>
#include <libtrx/game/ui/elements/requester.h>
#include <libtrx/game/ui/elements/resize.h>
#include <libtrx/game/ui/elements/spacer.h>
#include <libtrx/game/ui/elements/stack.h>
#include <libtrx/memory.h>
#include <stdio.h>
typedef struct UI_SAVE_SLOT_DIALOG_STATE {
UI_SAVE_SLOT_DIALOG_TYPE type;
UI_REQUESTER_STATE req;
} UI_SAVE_SLOT_DIALOG_STATE;
static int32_t M_GetVisibleRows(void);
static bool M_ShowDetails(const UI_SAVE_SLOT_DIALOG_STATE *s, int32_t slot_idx);
static void M_NonEmptySlot(
const UI_SAVE_SLOT_DIALOG_STATE *s, int32_t slot_idx,
const SAVEGAME_INFO *info);
static void M_EmptySlot(const UI_SAVE_SLOT_DIALOG_STATE *s, int32_t slot_idx);
static int32_t M_GetVisibleRows(void)
{
return 10;
}
static bool M_ShowDetails(
const UI_SAVE_SLOT_DIALOG_STATE *const s, const int32_t slot_idx)
{
return false;
}
static void M_NonEmptySlot(
const UI_SAVE_SLOT_DIALOG_STATE *const s, const int32_t slot_idx,
const SAVEGAME_INFO *const info)
{
const bool show_details = M_ShowDetails(s, slot_idx);
if (show_details) {
UI_BeginStackEx((UI_STACK_SETTINGS) {
.orientation = UI_STACK_HORIZONTAL,
.align = { .h = UI_STACK_H_ALIGN_DISTRIBUTE },
});
// Balance both sides so that the row text appears centered
UI_BeginHide(true);
UI_Label("\\{button right}");
UI_EndHide();
} else {
UI_BeginStackEx((UI_STACK_SETTINGS) {
.orientation = UI_STACK_HORIZONTAL,
.align = { .h = UI_STACK_H_ALIGN_DISTRIBUTE },
});
}
// Level title with the save counter
UI_Label(info->level_title);
if (info->counter > 0) {
UI_Spacer(8.0f, 0.0f);
char buf[16];
sprintf(buf, "%d", info->counter);
UI_Label(buf);
}
if (show_details) {
UI_BeginOffset(0.0f, -1.0f);
UI_Label("\\{button right}");
UI_EndOffset();
UI_EndStack();
} else {
UI_EndStack();
}
}
static void M_EmptySlot(
const UI_SAVE_SLOT_DIALOG_STATE *const s, const int32_t slot_idx)
{
char buf[16];
sprintf(buf, GS(MISC_EMPTY_SLOT_FMT), slot_idx + 1);
UI_BeginAnchor(0.5f, 0.5f);
UI_Label(buf);
UI_EndAnchor();
}
UI_SAVE_SLOT_DIALOG_STATE *UI_SaveSlotDialog_Init(
const UI_SAVE_SLOT_DIALOG_TYPE type, const int32_t save_slot)
{
UI_SAVE_SLOT_DIALOG_STATE *const s =
Memory_Alloc(sizeof(UI_SAVE_SLOT_DIALOG_STATE));
s->type = type;
UI_Requester_Init(
&s->req, M_GetVisibleRows(), Savegame_GetSlotCount(), true);
s->req.row_pad = 2.0f;
s->req.row_spacing = 2.0f;
s->req.show_arrows = false;
s->req.reserve_space = true;
s->req.sel_row = save_slot;
CLAMP(s->req.sel_row, 0, s->req.max_rows);
return s;
}
void UI_SaveSlotDialog_Free(UI_SAVE_SLOT_DIALOG_STATE *const s)
{
UI_Requester_Free(&s->req);
}
UI_SAVE_SLOT_DIALOG_CHOICE UI_SaveSlotDialog_Control(
UI_SAVE_SLOT_DIALOG_STATE *const s)
{
UI_Requester_SetVisibleRows(&s->req, M_GetVisibleRows());
const int32_t sel_row = UI_Requester_GetCurrentRow(&s->req);
if (M_ShowDetails(s, sel_row) && g_InputDB.menu_right) {
return (UI_SAVE_SLOT_DIALOG_CHOICE) {
.action = UI_SAVE_SLOT_DIALOG_DETAILS,
.slot_num = sel_row,
};
}
const int32_t choice = UI_Requester_Control(&s->req);
if (choice == UI_REQUESTER_CANCEL) {
return (UI_SAVE_SLOT_DIALOG_CHOICE) {
.action = UI_SAVE_SLOT_DIALOG_CANCEL,
};
} else if (
choice != UI_REQUESTER_NO_CHOICE
&& (s->type == UI_SAVE_SLOT_DIALOG_SAVE_GAME
|| !Savegame_IsSlotFree(choice))) {
return (UI_SAVE_SLOT_DIALOG_CHOICE) {
.action = UI_SAVE_SLOT_DIALOG_CONFIRM,
.slot_num = sel_row,
};
}
return (UI_SAVE_SLOT_DIALOG_CHOICE) {
.action = UI_SAVE_SLOT_DIALOG_NO_CHOICE,
};
}
void UI_SaveSlotDialog(const UI_SAVE_SLOT_DIALOG_STATE *const s)
{
UI_BeginModal(0.5f, g_Inv_Mode == INV_TITLE_MODE ? 0.8f : 0.63f);
UI_BeginResize(300.0f, -1.0f);
const char *const title = (s->type == UI_SAVE_SLOT_DIALOG_SAVE_GAME)
? GS(PASSPORT_SAVE_GAME)
: GS(PASSPORT_LOAD_GAME);
UI_BeginRequester(&s->req, title);
const int32_t first = UI_Requester_GetFirstRow(&s->req);
const int32_t last = UI_Requester_GetLastRow(&s->req);
for (int32_t i = first; i < last; ++i) {
UI_BeginRequesterRow(&s->req, i);
const SAVEGAME_INFO *const info = Savegame_GetSavegameInfo(i);
if (info != nullptr && info->level_title != nullptr) {
M_NonEmptySlot(s, i, info);
} else {
M_EmptySlot(s, i);
}
UI_EndRequesterRow(&s->req, i);
}
UI_EndRequester(&s->req);
UI_EndResize();
UI_EndModal();
}

View file

@ -1,34 +0,0 @@
// UI dialog for selecting a save slot (load or save game)
#pragma once
#include <libtrx/game/ui/common.h>
typedef enum {
UI_SAVE_SLOT_DIALOG_LOAD_GAME,
UI_SAVE_SLOT_DIALOG_SAVE_GAME,
} UI_SAVE_SLOT_DIALOG_TYPE;
typedef enum {
UI_SAVE_SLOT_DIALOG_NO_CHOICE,
UI_SAVE_SLOT_DIALOG_CANCEL,
UI_SAVE_SLOT_DIALOG_CONFIRM,
UI_SAVE_SLOT_DIALOG_DETAILS,
} UI_SAVE_SLOT_DIALOG_ACTION;
typedef struct {
UI_SAVE_SLOT_DIALOG_ACTION action;
int32_t slot_num;
} UI_SAVE_SLOT_DIALOG_CHOICE;
typedef struct UI_SAVE_SLOT_DIALOG_STATE UI_SAVE_SLOT_DIALOG_STATE;
// state functions
struct UI_SAVE_SLOT_DIALOG_STATE *UI_SaveSlotDialog_Init(
UI_SAVE_SLOT_DIALOG_TYPE type, int32_t save_slot);
void UI_SaveSlotDialog_Free(struct UI_SAVE_SLOT_DIALOG_STATE *s);
UI_SAVE_SLOT_DIALOG_CHOICE UI_SaveSlotDialog_Control(
struct UI_SAVE_SLOT_DIALOG_STATE *s);
// draw functions
void UI_SaveSlotDialog(const struct UI_SAVE_SLOT_DIALOG_STATE *s);

View file

@ -270,7 +270,6 @@ sources = [
'game/text.c',
'game/ui/common.c',
'game/ui/dialogs/graphic_settings.c',
'game/ui/dialogs/save_slot.c',
'game/ui/dialogs/stats.c',
'game/viewport.c',
'global/enum_map.c',

View file

@ -68,8 +68,8 @@ def postprocess_game_strings(
def process(game: int) -> None:
GAME_STRING_DEF_PATHS = [
PROJECT_PATHS[game].src_dir / "game/game_string.def",
SHARED_INCLUDE_DIR / "game/game_string.def",
PROJECT_PATHS[game].src_dir / "game/game_string.def",
]
OBJECT_NAMES_DEF_PATH = (
SHARED_INCLUDE_DIR / f"game/objects/names_tr{game}.def"