mirror of
https://github.com/LostArtefacts/TRX.git
synced 2025-04-28 12:47:58 +03:00
ui2: port pause exit dialog
Some checks are pending
Run code linters / Run code linters (push) Waiting to run
Some checks are pending
Run code linters / Run code linters (push) Waiting to run
This commit is contained in:
parent
eaf3a2bfc5
commit
03b0b443bb
8 changed files with 196 additions and 68 deletions
|
@ -26,6 +26,7 @@
|
|||
- fixed sprites missing the fog effect (regression from 4.9)
|
||||
- improved bubble appearance (#2672)
|
||||
- improved rendering performance
|
||||
- improved pause exit dialog - it can now be canceled with escape
|
||||
- removed the pretty pixels options (it's now always enabled, #2258)
|
||||
|
||||
## [4.9](https://github.com/LostArtefacts/TRX/compare/tr1-4.8.3...tr1-4.9) - 2025-03-31
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
- fixed a crash if an image was missing
|
||||
- fixed a crash on level load if an animation has no frames (#2746, regression from 0.8)
|
||||
- fixed a crash in custom levels with large rooms (#2749)
|
||||
- improved pause exit dialog - it can now be canceled with escape
|
||||
- removed the need to specify in the game flow levels that have no secrets (secrets will be automatically counted) (#1582)
|
||||
- removed the hard-coded end-level behaviour of the bird guardian for custom levels (#1583)
|
||||
|
||||
|
|
|
@ -11,8 +11,7 @@
|
|||
#include "game/shell.h"
|
||||
#include "game/sound.h"
|
||||
#include "game/text.h"
|
||||
#include "game/ui/common.h"
|
||||
#include "game/ui/widgets/requester.h"
|
||||
#include "game/ui2.h"
|
||||
#include "memory.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
@ -23,14 +22,15 @@ typedef enum {
|
|||
STATE_FADE_IN,
|
||||
STATE_WAIT,
|
||||
STATE_ASK,
|
||||
STATE_CONFIRM,
|
||||
STATE_FADE_OUT,
|
||||
} STATE;
|
||||
|
||||
typedef struct {
|
||||
STATE state;
|
||||
bool is_ui_ready;
|
||||
UI_WIDGET *ui;
|
||||
struct {
|
||||
bool is_ready;
|
||||
UI2_PAUSE_STATE state;
|
||||
} ui;
|
||||
TEXTSTRING *mode_text;
|
||||
GF_ACTION action;
|
||||
FADER back_fader;
|
||||
|
@ -43,8 +43,6 @@ static void M_ReturnToGame(M_PRIV *p);
|
|||
static void M_ExitToTitle(M_PRIV *p);
|
||||
static void M_CreateText(M_PRIV *p);
|
||||
static void M_RemoveText(M_PRIV *p);
|
||||
static int32_t M_DisplayRequester(
|
||||
M_PRIV *p, const char *header, const char *option1, const char *option2);
|
||||
|
||||
static PHASE_CONTROL M_Start(PHASE *phase);
|
||||
static void M_End(PHASE *phase);
|
||||
|
@ -60,10 +58,7 @@ static void M_FadeIn(M_PRIV *const p)
|
|||
static void M_FadeOut(M_PRIV *const p)
|
||||
{
|
||||
M_RemoveText(p);
|
||||
if (p->ui != nullptr) {
|
||||
p->ui->free(p->ui);
|
||||
p->ui = nullptr;
|
||||
}
|
||||
p->ui.is_ready = false;
|
||||
if (p->action == GF_NOOP) {
|
||||
Fader_Init(&p->back_fader, FADER_ANY, FADER_TRANSPARENT, FADE_TIME);
|
||||
} else {
|
||||
|
@ -108,42 +103,13 @@ static void M_RemoveText(M_PRIV *const p)
|
|||
p->mode_text = nullptr;
|
||||
}
|
||||
|
||||
static int32_t M_DisplayRequester(
|
||||
M_PRIV *const p, const char *header, const char *option1,
|
||||
const char *option2)
|
||||
{
|
||||
if (!p->is_ui_ready) {
|
||||
if (p->ui == nullptr) {
|
||||
p->ui = UI_Requester_Create((UI_REQUESTER_SETTINGS) {
|
||||
.is_selectable = true,
|
||||
.width = 160,
|
||||
.visible_rows = 2,
|
||||
});
|
||||
}
|
||||
UI_Requester_ClearRows(p->ui);
|
||||
UI_Requester_SetTitle(p->ui, header);
|
||||
UI_Requester_AddRowC(p->ui, option1, nullptr);
|
||||
UI_Requester_AddRowC(p->ui, option2, nullptr);
|
||||
p->ui->set_position(
|
||||
p->ui, (UI_GetCanvasWidth() - p->ui->get_width(p->ui)) / 2,
|
||||
(UI_GetCanvasHeight() - p->ui->get_height(p->ui)) - 50);
|
||||
p->is_ui_ready = true;
|
||||
}
|
||||
|
||||
const int32_t choice = UI_Requester_GetSelectedRow(p->ui);
|
||||
if (choice >= 0) {
|
||||
p->is_ui_ready = false;
|
||||
}
|
||||
return choice;
|
||||
}
|
||||
|
||||
static PHASE_CONTROL M_Start(PHASE *const phase)
|
||||
{
|
||||
M_PRIV *const p = phase->priv;
|
||||
|
||||
p->ui.is_ready = false;
|
||||
UI2_Pause_Init(&p->ui.state);
|
||||
M_PauseGame(p);
|
||||
|
||||
p->is_ui_ready = false;
|
||||
return (PHASE_CONTROL) { .action = PHASE_ACTION_CONTINUE };
|
||||
}
|
||||
|
||||
|
@ -151,10 +117,7 @@ static void M_End(PHASE *const phase)
|
|||
{
|
||||
M_PRIV *const p = phase->priv;
|
||||
M_RemoveText(p);
|
||||
if (p->ui != nullptr) {
|
||||
p->ui->free(p->ui);
|
||||
p->ui = nullptr;
|
||||
}
|
||||
UI2_Pause_Free(&p->ui.state);
|
||||
}
|
||||
|
||||
static PHASE_CONTROL M_Control(PHASE *const phase, int32_t const num_frames)
|
||||
|
@ -164,8 +127,8 @@ static PHASE_CONTROL M_Control(PHASE *const phase, int32_t const num_frames)
|
|||
Input_Update();
|
||||
Shell_ProcessInput();
|
||||
|
||||
if (p->ui != nullptr) {
|
||||
p->ui->control(p->ui);
|
||||
if (p->ui.is_ready) {
|
||||
UI2_Pause_Control(&p->ui.state);
|
||||
}
|
||||
|
||||
switch (p->state) {
|
||||
|
@ -190,30 +153,23 @@ static PHASE_CONTROL M_Control(PHASE *const phase, int32_t const num_frames)
|
|||
break;
|
||||
|
||||
case STATE_ASK: {
|
||||
const int32_t choice = M_DisplayRequester(
|
||||
p, GS(PAUSE_EXIT_TO_TITLE), GS(PAUSE_CONTINUE), GS(PAUSE_QUIT));
|
||||
if (choice == 0) {
|
||||
const UI2_PAUSE_EXIT_CHOICE choice = UI2_Pause_Control(&p->ui.state);
|
||||
switch (choice) {
|
||||
case UI2_PAUSE_RESUME_PAUSE:
|
||||
p->state = STATE_WAIT;
|
||||
return (PHASE_CONTROL) { .action = PHASE_ACTION_NO_WAIT };
|
||||
case UI2_PAUSE_EXIT_TO_GAME:
|
||||
M_ReturnToGame(p);
|
||||
return (PHASE_CONTROL) { .action = PHASE_ACTION_NO_WAIT };
|
||||
} else if (choice == 1) {
|
||||
p->state = STATE_CONFIRM;
|
||||
case UI2_PAUSE_EXIT_TO_TITLE:
|
||||
M_ExitToTitle(p);
|
||||
return (PHASE_CONTROL) { .action = PHASE_ACTION_NO_WAIT };
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case STATE_CONFIRM: {
|
||||
const int32_t choice = M_DisplayRequester(
|
||||
p, GS(PAUSE_ARE_YOU_SURE), GS(PAUSE_YES), GS(PAUSE_NO));
|
||||
if (choice == 0) {
|
||||
M_ExitToTitle(p);
|
||||
return (PHASE_CONTROL) { .action = PHASE_ACTION_NO_WAIT };
|
||||
} else if (choice == 1) {
|
||||
M_ReturnToGame(p);
|
||||
return (PHASE_CONTROL) { .action = PHASE_ACTION_NO_WAIT };
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_FADE_OUT:
|
||||
if (!Fader_IsActive(&p->back_fader)) {
|
||||
return (PHASE_CONTROL) {
|
||||
|
@ -223,7 +179,6 @@ static PHASE_CONTROL M_Control(PHASE *const phase, int32_t const num_frames)
|
|||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (PHASE_CONTROL) { .action = PHASE_ACTION_CONTINUE };
|
||||
}
|
||||
|
@ -237,8 +192,8 @@ static void M_Draw(PHASE *const phase)
|
|||
Interpolation_Enable();
|
||||
Fader_Draw(&p->back_fader);
|
||||
|
||||
if (p->ui != nullptr) {
|
||||
p->ui->draw(p->ui);
|
||||
if (p->state == STATE_ASK) {
|
||||
UI2_Pause(&p->ui.state);
|
||||
}
|
||||
Output_DrawPolyList();
|
||||
}
|
||||
|
|
72
src/libtrx/game/ui2/dialogs/pause.c
Normal file
72
src/libtrx/game/ui2/dialogs/pause.c
Normal file
|
@ -0,0 +1,72 @@
|
|||
#include "game/ui2/dialogs/pause.h"
|
||||
|
||||
#include "game/game_string.h"
|
||||
#include "game/ui2/elements/anchor.h"
|
||||
#include "game/ui2/elements/frame.h"
|
||||
#include "game/ui2/elements/label.h"
|
||||
#include "game/ui2/elements/modal.h"
|
||||
#include "game/ui2/elements/pad.h"
|
||||
#include "game/ui2/elements/requester.h"
|
||||
|
||||
static const GAME_STRING_ID m_Options[2][2] = {
|
||||
{ GS_ID(PAUSE_CONTINUE), GS_ID(PAUSE_QUIT) },
|
||||
{ GS_ID(PAUSE_YES), GS_ID(PAUSE_NO) },
|
||||
};
|
||||
|
||||
void UI2_Pause_Init(UI2_PAUSE_STATE *const s)
|
||||
{
|
||||
s->phase = 0;
|
||||
UI2_Requester_Init(&s->req, 2, 2, true);
|
||||
}
|
||||
|
||||
void UI2_Pause_Free(UI2_PAUSE_STATE *const s)
|
||||
{
|
||||
UI2_Requester_Free(&s->req);
|
||||
}
|
||||
|
||||
UI2_PAUSE_EXIT_CHOICE UI2_Pause_Control(UI2_PAUSE_STATE *const s)
|
||||
{
|
||||
const int32_t choice = UI2_Requester_Control(&s->req);
|
||||
if (s->phase == 0) {
|
||||
if (choice == UI2_REQUESTER_CANCEL) {
|
||||
return UI2_PAUSE_RESUME_PAUSE;
|
||||
} else if (choice == 0) {
|
||||
return UI2_PAUSE_EXIT_TO_GAME;
|
||||
} else if (choice == 1) {
|
||||
s->phase = 1;
|
||||
UI2_Requester_Free(&s->req);
|
||||
UI2_Requester_Init(&s->req, 2, 2, true);
|
||||
}
|
||||
} else {
|
||||
if (choice == UI2_REQUESTER_CANCEL) {
|
||||
s->phase = 0;
|
||||
} else if (choice == 0) {
|
||||
return UI2_PAUSE_EXIT_TO_TITLE;
|
||||
} else if (choice == 1) {
|
||||
return UI2_PAUSE_EXIT_TO_GAME;
|
||||
}
|
||||
}
|
||||
return UI2_PAUSE_NOOP;
|
||||
}
|
||||
|
||||
void UI2_Pause(UI2_PAUSE_STATE *const s)
|
||||
{
|
||||
UI2_BeginModal(0.5f, 1.0f);
|
||||
UI2_BeginPad(50.0f, 50.0f);
|
||||
UI2_BeginRequester(
|
||||
&s->req, 0.5f, 1.0f,
|
||||
s->phase == 0 ? GS(PAUSE_EXIT_TO_TITLE) : GS(PAUSE_ARE_YOU_SURE));
|
||||
|
||||
for (int32_t i = UI2_Requester_GetFirstRow(&s->req);
|
||||
i < UI2_Requester_GetLastRow(&s->req); i++) {
|
||||
UI2_BeginRequesterRow(&s->req, i);
|
||||
UI2_BeginAnchor(0.5f, 0.5f);
|
||||
UI2_Label(GameString_Get(m_Options[s->phase][i]));
|
||||
UI2_EndAnchor();
|
||||
UI2_EndRequesterRow(&s->req, i);
|
||||
}
|
||||
|
||||
UI2_EndRequester(&s->req);
|
||||
UI2_EndPad();
|
||||
UI2_EndModal();
|
||||
}
|
71
src/libtrx/game/ui2/elements/pause.c
Normal file
71
src/libtrx/game/ui2/elements/pause.c
Normal file
|
@ -0,0 +1,71 @@
|
|||
#include "game/game_string.h"
|
||||
#include "game/ui2/dialogs/pause.h"
|
||||
#include "game/ui2/elements/anchor.h"
|
||||
#include "game/ui2/elements/frame.h"
|
||||
#include "game/ui2/elements/label.h"
|
||||
#include "game/ui2/elements/modal.h"
|
||||
#include "game/ui2/elements/pad.h"
|
||||
#include "game/ui2/elements/requester.h"
|
||||
|
||||
static const GAME_STRING_ID m_Options[2][2] = {
|
||||
{ GS_ID(PAUSE_CONTINUE), GS_ID(PAUSE_QUIT) },
|
||||
{ GS_ID(PAUSE_YES), GS_ID(PAUSE_NO) },
|
||||
};
|
||||
|
||||
void UI2_Pause_Init(UI2_PAUSE_STATE *const s)
|
||||
{
|
||||
s->phase = 0;
|
||||
UI2_Requester_Init(&s->req, 2, 2, true);
|
||||
}
|
||||
|
||||
void UI2_Pause_Free(UI2_PAUSE_STATE *const s)
|
||||
{
|
||||
UI2_Requester_Free(&s->req);
|
||||
}
|
||||
|
||||
UI2_PAUSE_EXIT_CHOICE UI2_Pause_Control(UI2_PAUSE_STATE *const s)
|
||||
{
|
||||
const int32_t choice = UI2_Requester_Control(&s->req);
|
||||
if (s->phase == 0) {
|
||||
if (choice == UI2_REQUESTER_CANCEL) {
|
||||
return UI2_PAUSE_RESUME_PAUSE;
|
||||
} else if (choice == 0) {
|
||||
return UI2_PAUSE_EXIT_TO_GAME;
|
||||
} else if (choice == 1) {
|
||||
s->phase = 1;
|
||||
UI2_Requester_Free(&s->req);
|
||||
UI2_Requester_Init(&s->req, 2, 2, true);
|
||||
}
|
||||
} else {
|
||||
if (choice == UI2_REQUESTER_CANCEL) {
|
||||
s->phase = 0;
|
||||
} else if (choice == 0) {
|
||||
return UI2_PAUSE_EXIT_TO_TITLE;
|
||||
} else if (choice == 1) {
|
||||
return UI2_PAUSE_EXIT_TO_GAME;
|
||||
}
|
||||
}
|
||||
return UI2_PAUSE_NOOP;
|
||||
}
|
||||
|
||||
void UI2_Pause(UI2_PAUSE_STATE *const s)
|
||||
{
|
||||
UI2_BeginModal(0.5f, 1.0f);
|
||||
UI2_BeginPad(50.0f, 50.0f);
|
||||
UI2_BeginRequester(
|
||||
&s->req, 0.5f, 1.0f,
|
||||
s->phase == 0 ? GS(PAUSE_EXIT_TO_TITLE) : GS(PAUSE_ARE_YOU_SURE));
|
||||
|
||||
for (int32_t i = UI2_Requester_GetFirstRow(&s->req);
|
||||
i < UI2_Requester_GetLastRow(&s->req); i++) {
|
||||
UI2_BeginRequesterRow(&s->req, i);
|
||||
UI2_BeginAnchor(0.5f, 0.5f);
|
||||
UI2_Label(GameString_Get(m_Options[s->phase][i]));
|
||||
UI2_EndAnchor();
|
||||
UI2_EndRequesterRow(&s->req, i);
|
||||
}
|
||||
|
||||
UI2_EndRequester(&s->req);
|
||||
UI2_EndPad();
|
||||
UI2_EndModal();
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
#include "./ui2/common.h"
|
||||
#include "./ui2/dialogs/examine_item.h"
|
||||
#include "./ui2/dialogs/new_game.h"
|
||||
#include "./ui2/dialogs/pause.h"
|
||||
#include "./ui2/dialogs/photo_mode.h"
|
||||
#include "./ui2/elements/anchor.h"
|
||||
#include "./ui2/elements/flash.h"
|
||||
|
|
26
src/libtrx/include/libtrx/game/ui2/dialogs/pause.h
Normal file
26
src/libtrx/include/libtrx/game/ui2/dialogs/pause.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
// A pause exit confirmation dialog.
|
||||
|
||||
#include "../common.h"
|
||||
#include "../elements/requester.h"
|
||||
|
||||
typedef struct {
|
||||
int32_t phase;
|
||||
UI2_REQUESTER_STATE req;
|
||||
} UI2_PAUSE_STATE;
|
||||
|
||||
typedef enum {
|
||||
UI2_PAUSE_NOOP,
|
||||
UI2_PAUSE_RESUME_PAUSE,
|
||||
UI2_PAUSE_EXIT_TO_GAME,
|
||||
UI2_PAUSE_EXIT_TO_TITLE,
|
||||
} UI2_PAUSE_EXIT_CHOICE;
|
||||
|
||||
// state functions
|
||||
void UI2_Pause_Init(UI2_PAUSE_STATE *s);
|
||||
UI2_PAUSE_EXIT_CHOICE UI2_Pause_Control(UI2_PAUSE_STATE *s);
|
||||
void UI2_Pause_Free(UI2_PAUSE_STATE *s);
|
||||
|
||||
// draw functions
|
||||
void UI2_Pause(UI2_PAUSE_STATE *s);
|
|
@ -211,6 +211,7 @@ sources = [
|
|||
'game/ui2/common.c',
|
||||
'game/ui2/dialogs/examine_item.c',
|
||||
'game/ui2/dialogs/new_game.c',
|
||||
'game/ui2/dialogs/pause.c',
|
||||
'game/ui2/dialogs/photo_mode.c',
|
||||
'game/ui2/elements/anchor.c',
|
||||
'game/ui2/elements/flash.c',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue