mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Options menu background + sounds
This commit is contained in:
parent
c377ea1186
commit
2e875eb3f0
4 changed files with 125 additions and 7 deletions
23
src/k_menu.h
23
src/k_menu.h
|
|
@ -126,6 +126,7 @@ typedef struct menu_s
|
|||
menuitem_t *menuitems; // menu items
|
||||
|
||||
INT16 x, y; // x, y of menu
|
||||
INT16 extra1, extra2; // Can be whatever really! Options menu uses extra1 for bg colour.
|
||||
|
||||
INT16 transitionID; // only transition if IDs match
|
||||
INT16 transitionTics; // tics for transitions out
|
||||
|
|
@ -200,6 +201,19 @@ extern menu_t PLAY_BattleGamemodesDef;
|
|||
extern menuitem_t OPTIONS_Main[];
|
||||
extern menu_t OPTIONS_MainDef;
|
||||
|
||||
// We'll need this since we're gonna have to dynamically enable and disable options depending on which state we're in.
|
||||
typedef enum
|
||||
{
|
||||
mopt_controls = 0,
|
||||
mopt_video,
|
||||
mopt_sound,
|
||||
mopt_hud,
|
||||
mopt_gameplay,
|
||||
mopt_server,
|
||||
mopt_data,
|
||||
mopt_manual,
|
||||
} mopt_e;
|
||||
|
||||
extern menuitem_t OPTIONS_Video[];
|
||||
extern menu_t OPTIONS_VideoDef;
|
||||
|
||||
|
|
@ -530,12 +544,19 @@ extern struct optionsmenu_s {
|
|||
modedesc_t modedescs[MAXMODEDESCS];
|
||||
|
||||
UINT8 erasecontext;
|
||||
|
||||
// background:
|
||||
INT16 currcolour;
|
||||
INT16 lastcolour;
|
||||
tic_t fade;
|
||||
} optionsmenu;
|
||||
|
||||
void M_InitOptions(INT32 choice); // necessary for multiplayer since there's some options we won't want to access
|
||||
void M_OptionsTick(void);
|
||||
boolean M_OptionsInputs(INT32 ch);
|
||||
boolean M_OptionsQuit(void); // resets buttons when you quit the options.
|
||||
void M_OptionsChangeBGColour(INT16 newcolour); // changes the background colour for options
|
||||
|
||||
void M_HandleItemToggles(INT32 choice); // For item toggling
|
||||
void M_EraseData(INT32 choice); // For data erasing
|
||||
|
||||
|
|
@ -667,6 +688,7 @@ void M_DrawAddons(void);
|
|||
0,\
|
||||
source,\
|
||||
0, 0,\
|
||||
0, 0, \
|
||||
1, 10,\
|
||||
M_DrawKartGamemodeMenu,\
|
||||
NULL,\
|
||||
|
|
@ -681,6 +703,7 @@ void M_DrawAddons(void);
|
|||
0,\
|
||||
source,\
|
||||
0, 0,\
|
||||
0, 0, \
|
||||
1, 10,\
|
||||
M_DrawImageDef,\
|
||||
NULL,\
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ menu_t PLAY_CharSelectDef = {
|
|||
PLAY_CharSelect,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
M_DrawCharacterSelect,
|
||||
M_CharacterSelectTick,
|
||||
M_CharacterSelectQuit,
|
||||
|
|
@ -123,6 +124,7 @@ menu_t PLAY_CupSelectDef = {
|
|||
0,
|
||||
PLAY_CupSelect,
|
||||
0, 0,
|
||||
0, 0,
|
||||
2, 10,
|
||||
M_DrawCupSelect,
|
||||
M_CupSelectTick,
|
||||
|
|
@ -141,6 +143,7 @@ menu_t PLAY_LevelSelectDef = {
|
|||
0,
|
||||
PLAY_LevelSelect,
|
||||
0, 0,
|
||||
0, 0,
|
||||
2, 10,
|
||||
M_DrawLevelSelect,
|
||||
M_LevelSelectTick,
|
||||
|
|
@ -162,6 +165,7 @@ menu_t PLAY_TimeAttackDef = {
|
|||
0,
|
||||
PLAY_TimeAttack,
|
||||
0, 0,
|
||||
0, 0,
|
||||
2, 10,
|
||||
M_DrawTimeAttack,
|
||||
NULL,
|
||||
|
|
@ -204,6 +208,7 @@ menu_t PLAY_MP_OptSelectDef = {
|
|||
0,
|
||||
PLAY_MP_OptSelect,
|
||||
0, 0,
|
||||
0, 0,
|
||||
-1, 1,
|
||||
M_DrawMPOptSelect,
|
||||
M_MPOptSelectTick,
|
||||
|
|
@ -239,6 +244,7 @@ menu_t PLAY_MP_HostDef = {
|
|||
0,
|
||||
PLAY_MP_Host,
|
||||
0, 0,
|
||||
0, 0,
|
||||
-1, 1, // 1 frame transition.... This is really just because I don't want the black fade when we press esc, hehe
|
||||
M_DrawMPHost,
|
||||
M_MPOptSelectTick, // This handles the unfolding options
|
||||
|
|
@ -274,6 +280,7 @@ menu_t PLAY_MP_JoinIPDef = {
|
|||
0,
|
||||
PLAY_MP_JoinIP,
|
||||
0, 0,
|
||||
0, 0,
|
||||
-1, 1, // 1 frame transition.... This is really just because I don't want the black fade when we press esc, hehe
|
||||
M_DrawMPJoinIP,
|
||||
M_MPOptSelectTick, // This handles the unfolding options
|
||||
|
|
@ -294,6 +301,7 @@ menu_t PLAY_MP_RoomSelectDef = {
|
|||
PLAY_MP_RoomSelect,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
M_DrawMPRoomSelect,
|
||||
M_MPRoomSelectTick,
|
||||
NULL,
|
||||
|
|
@ -304,7 +312,7 @@ menu_t PLAY_MP_RoomSelectDef = {
|
|||
menuitem_t OPTIONS_Main[] =
|
||||
{
|
||||
|
||||
{IT_STRING | IT_SUBMENU, "Control Setup", "Remap keys & buttons to your likings.",
|
||||
{IT_STRING | IT_TRANSTEXT, "Profile Setup", "Remap keys & buttons to your likings.",
|
||||
NULL, NULL, 0, 0},
|
||||
|
||||
{IT_STRING | IT_SUBMENU, "Video Options", "Change video settings such as the resolution.",
|
||||
|
|
@ -329,12 +337,14 @@ menuitem_t OPTIONS_Main[] =
|
|||
NULL, M_Manual, 0, 0},
|
||||
};
|
||||
|
||||
// For options menu, the 'extra1' field will determine the background colour to use for... the background! (What a concept!)
|
||||
menu_t OPTIONS_MainDef = {
|
||||
sizeof (OPTIONS_Main) / sizeof (menuitem_t),
|
||||
&MainDef,
|
||||
0,
|
||||
OPTIONS_Main,
|
||||
0, 0,
|
||||
SKINCOLOR_SLATE, 0,
|
||||
2, 10,
|
||||
M_DrawOptions,
|
||||
M_OptionsTick,
|
||||
|
|
@ -395,6 +405,7 @@ menu_t OPTIONS_VideoDef = {
|
|||
0,
|
||||
OPTIONS_Video,
|
||||
32, 80,
|
||||
SKINCOLOR_PLAGUE, 0,
|
||||
2, 10,
|
||||
M_DrawGenericOptions,
|
||||
M_OptionsTick,
|
||||
|
|
@ -415,6 +426,7 @@ menu_t OPTIONS_VideoModesDef = {
|
|||
0,
|
||||
OPTIONS_VideoModes,
|
||||
48, 80,
|
||||
SKINCOLOR_PLAGUE, 0,
|
||||
2, 10,
|
||||
M_DrawVideoModes,
|
||||
M_OptionsTick,
|
||||
|
|
@ -472,6 +484,7 @@ menu_t OPTIONS_VideoOGLDef = {
|
|||
0,
|
||||
OPTIONS_VideoOGL,
|
||||
32, 80,
|
||||
SKINCOLOR_PLAGUE, 0,
|
||||
2, 10,
|
||||
M_DrawGenericOptions,
|
||||
M_OptionsTick,
|
||||
|
|
@ -534,6 +547,7 @@ menu_t OPTIONS_SoundDef = {
|
|||
0,
|
||||
OPTIONS_Sound,
|
||||
48, 80,
|
||||
SKINCOLOR_THUNDER, 0,
|
||||
2, 10,
|
||||
M_DrawGenericOptions,
|
||||
M_OptionsTick,
|
||||
|
|
@ -588,6 +602,7 @@ menu_t OPTIONS_HUDDef = {
|
|||
0,
|
||||
OPTIONS_HUD,
|
||||
48, 80,
|
||||
SKINCOLOR_SUNSLAM, 0,
|
||||
2, 10,
|
||||
M_DrawGenericOptions,
|
||||
M_OptionsTick,
|
||||
|
|
@ -636,6 +651,7 @@ menu_t OPTIONS_HUDOnlineDef = {
|
|||
0,
|
||||
OPTIONS_HUDOnline,
|
||||
48, 80,
|
||||
SKINCOLOR_SUNSLAM, 0,
|
||||
2, 10,
|
||||
M_DrawGenericOptions,
|
||||
M_OptionsTick,
|
||||
|
|
@ -688,6 +704,7 @@ menu_t OPTIONS_GameplayDef = {
|
|||
0,
|
||||
OPTIONS_Gameplay,
|
||||
48, 80,
|
||||
SKINCOLOR_SCARLET, 0,
|
||||
2, 10,
|
||||
M_DrawGenericOptions,
|
||||
M_OptionsTick,
|
||||
|
|
@ -732,6 +749,7 @@ menu_t OPTIONS_GameplayItemsDef = {
|
|||
0,
|
||||
OPTIONS_GameplayItems,
|
||||
0, 75,
|
||||
SKINCOLOR_SCARLET, 0,
|
||||
2, 10,
|
||||
M_DrawItemToggles,
|
||||
M_OptionsTick,
|
||||
|
|
@ -795,6 +813,7 @@ menu_t OPTIONS_ServerDef = {
|
|||
0,
|
||||
OPTIONS_Server,
|
||||
48, 70, // This menu here is slightly higher because there's a lot of options...
|
||||
SKINCOLOR_VIOLET, 0,
|
||||
2, 10,
|
||||
M_DrawGenericOptions,
|
||||
M_OptionsTick,
|
||||
|
|
@ -852,6 +871,7 @@ menu_t OPTIONS_ServerAdvancedDef = {
|
|||
0,
|
||||
OPTIONS_ServerAdvanced,
|
||||
48, 70, // This menu here is slightly higher because there's a lot of options...
|
||||
SKINCOLOR_VIOLET, 0,
|
||||
2, 10,
|
||||
M_DrawGenericOptions,
|
||||
M_OptionsTick,
|
||||
|
|
@ -892,6 +912,7 @@ menu_t OPTIONS_DataDef = {
|
|||
0,
|
||||
OPTIONS_Data,
|
||||
48, 80,
|
||||
SKINCOLOR_BLUEBERRY, 0,
|
||||
2, 10,
|
||||
M_DrawGenericOptions,
|
||||
M_OptionsTick,
|
||||
|
|
@ -937,6 +958,7 @@ menu_t OPTIONS_DataAddonDef = {
|
|||
0,
|
||||
OPTIONS_DataAddon,
|
||||
48, 80,
|
||||
SKINCOLOR_BLUEBERRY, 0,
|
||||
2, 10,
|
||||
M_DrawGenericOptions,
|
||||
M_OptionsTick,
|
||||
|
|
@ -976,6 +998,7 @@ menu_t OPTIONS_DataScreenshotDef = {
|
|||
0,
|
||||
OPTIONS_DataScreenshot,
|
||||
48, 80,
|
||||
SKINCOLOR_BLUEBERRY, 0,
|
||||
2, 10,
|
||||
M_DrawGenericOptions,
|
||||
M_OptionsTick,
|
||||
|
|
@ -999,6 +1022,7 @@ menu_t OPTIONS_DataReplayDef = {
|
|||
0,
|
||||
OPTIONS_DataReplay,
|
||||
48, 80,
|
||||
SKINCOLOR_BLUEBERRY, 0,
|
||||
2, 10,
|
||||
M_DrawGenericOptions,
|
||||
M_OptionsTick,
|
||||
|
|
@ -1036,6 +1060,7 @@ menu_t OPTIONS_DataDiscordDef = {
|
|||
0,
|
||||
OPTIONS_DataDiscord,
|
||||
48, 80,
|
||||
SKINCOLOR_BLUEBERRY, 0,
|
||||
2, 10,
|
||||
M_DrawGenericOptions,
|
||||
M_OptionsTick,
|
||||
|
|
@ -1068,6 +1093,7 @@ menu_t OPTIONS_DataEraseDef = {
|
|||
0,
|
||||
OPTIONS_DataErase,
|
||||
48, 80,
|
||||
SKINCOLOR_BLUEBERRY, 0,
|
||||
2, 10,
|
||||
M_DrawGenericOptions,
|
||||
M_OptionsTick,
|
||||
|
|
@ -1127,6 +1153,7 @@ menu_t PAUSE_MainDef = {
|
|||
0,
|
||||
PAUSE_Main,
|
||||
0, 0,
|
||||
0, 0,
|
||||
1, 10, // For transition with some menus!
|
||||
M_DrawPause,
|
||||
M_PauseTick,
|
||||
|
|
@ -1177,6 +1204,7 @@ menu_t PAUSE_PlaybackMenuDef = {
|
|||
PAUSE_PlaybackMenu,
|
||||
BASEVIDWIDTH/2 - 88, 2,
|
||||
0, 0,
|
||||
0, 0,
|
||||
M_DrawPlaybackMenu,
|
||||
NULL,
|
||||
NULL,
|
||||
|
|
@ -1220,6 +1248,7 @@ menu_t MISC_AddonsDef = {
|
|||
MISC_AddonsMenu,
|
||||
50, 28,
|
||||
0, 0,
|
||||
0, 0,
|
||||
M_DrawAddons,
|
||||
NULL,
|
||||
NULL,
|
||||
|
|
|
|||
|
|
@ -1652,10 +1652,34 @@ void M_DrawMPRoomSelect(void)
|
|||
|
||||
// OPTIONS MENU
|
||||
|
||||
// Draws the cogs and also the options background!
|
||||
static void M_DrawOptionsCogs(void)
|
||||
{
|
||||
patch_t *back[3] = {W_CachePatchName("OPT_BAK1", PU_CACHE), W_CachePatchName("OPT_BAK2", PU_CACHE), W_CachePatchName("OPT_BAK3", PU_CACHE)};
|
||||
V_DrawFixedPatch(0, 0, FRACUNIT, 0, back[(optionsmenu.ticker/10) %3], NULL);
|
||||
// the background isn't drawn outside of being in the main menu state.
|
||||
if (gamestate == GS_MENU)
|
||||
{
|
||||
patch_t *back[3] = {W_CachePatchName("OPT_BG1", PU_CACHE), W_CachePatchName("OPT_BG2", PU_CACHE), W_CachePatchName("OPT_BG3", PU_CACHE)};
|
||||
INT32 tflag = 0;
|
||||
UINT8 *c;
|
||||
UINT8 *c2; // colormap for the one we're changing
|
||||
|
||||
if (optionsmenu.fade)
|
||||
{
|
||||
c2 = R_GetTranslationColormap(TC_DEFAULT, optionsmenu.lastcolour, GTC_CACHE);
|
||||
V_DrawFixedPatch(0, 0, FRACUNIT, 0, back[(optionsmenu.ticker/10) %3], c2);
|
||||
|
||||
// prepare fade flag:
|
||||
tflag = min(V_90TRANS, (optionsmenu.fade)<<V_ALPHASHIFT);
|
||||
|
||||
}
|
||||
c = R_GetTranslationColormap(TC_DEFAULT, optionsmenu.currcolour, GTC_CACHE);
|
||||
V_DrawFixedPatch(0, 0, FRACUNIT, tflag, back[(optionsmenu.ticker/10) %3], c);
|
||||
}
|
||||
else
|
||||
{
|
||||
patch_t *back_pause[3] = {W_CachePatchName("OPT_BAK1", PU_CACHE), W_CachePatchName("OPT_BAK2", PU_CACHE), W_CachePatchName("OPT_BAK3", PU_CACHE)};
|
||||
V_DrawFixedPatch(0, 0, FRACUNIT, V_MODULATE, back_pause[(optionsmenu.ticker/10) %3], NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void M_DrawOptionsMovingButton(void)
|
||||
|
|
@ -1682,16 +1706,20 @@ void M_DrawOptions(void)
|
|||
{
|
||||
INT32 py = y - (itemOn*48);
|
||||
INT32 px = x - menutransition.tics*64;
|
||||
INT32 tflag = 0;
|
||||
|
||||
if (i == itemOn)
|
||||
c = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE);
|
||||
else
|
||||
c = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_BLACK, GTC_CACHE);
|
||||
|
||||
if (currentMenu->menuitems[i].status & IT_TRANSTEXT)
|
||||
tflag = V_TRANSLUCENT;
|
||||
|
||||
if (!(menutransition.tics && i == itemOn))
|
||||
{
|
||||
V_DrawFixedPatch(px*FRACUNIT, py*FRACUNIT, FRACUNIT, 0, buttback, c);
|
||||
V_DrawCenteredGamemodeString(px-3, py - 16, V_ALLOWLOWERCASE, (i == itemOn ? c : NULL), currentMenu->menuitems[i].text);
|
||||
V_DrawCenteredGamemodeString(px-3, py - 16, V_ALLOWLOWERCASE|tflag, (i == itemOn ? c : NULL), currentMenu->menuitems[i].text);
|
||||
}
|
||||
|
||||
y += 48;
|
||||
|
|
@ -2076,7 +2104,7 @@ void M_DrawItemToggles(void)
|
|||
if (shitsfree)
|
||||
shitsfree--;
|
||||
|
||||
V_DrawCenteredString(BASEVIDWIDTH/2, currentMenu->y, highlightflags, va("* %s *", currentMenu->menuitems[itemOn].text));
|
||||
V_DrawCenteredString(BASEVIDWIDTH/2 + menutransition.tics*48, currentMenu->y, highlightflags, va("* %s *", currentMenu->menuitems[itemOn].text));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1663,6 +1663,7 @@ menu_t MessageDef =
|
|||
0, // lastOn, flags (TO HACK)
|
||||
MessageMenu, // menuitem_t ->
|
||||
0, 0, // x, y (TO HACK)
|
||||
0, 0, // extra1, extra2
|
||||
0, 0, // transition tics
|
||||
M_DrawMessageMenu, // drawing routine ->
|
||||
NULL, // ticker routine
|
||||
|
|
@ -2993,7 +2994,15 @@ void M_InitOptions(INT32 choice)
|
|||
{
|
||||
(void)choice;
|
||||
|
||||
// @TODO: Change options when you do them from a netgame.
|
||||
OPTIONS_MainDef.menuitems[mopt_gameplay].status = IT_STRING | IT_SUBMENU;
|
||||
OPTIONS_MainDef.menuitems[mopt_server].status = IT_STRING | IT_SUBMENU;
|
||||
|
||||
// disable gameplay & server options if you aren't an admin in netgames. (GS_MENU check maybe unecessary but let's not take any chances)
|
||||
if (netgame && gamestate != GS_MENU && !IsPlayerAdmin(consoleplayer))
|
||||
{
|
||||
OPTIONS_MainDef.menuitems[mopt_gameplay].status = IT_STRING | IT_TRANSTEXT;
|
||||
OPTIONS_MainDef.menuitems[mopt_server].status = IT_STRING | IT_TRANSTEXT;
|
||||
}
|
||||
|
||||
optionsmenu.ticker = 0;
|
||||
optionsmenu.offset = 0;
|
||||
|
|
@ -3003,6 +3012,11 @@ void M_InitOptions(INT32 choice)
|
|||
optionsmenu.toptx = 0;
|
||||
optionsmenu.topty = 0;
|
||||
|
||||
// BG setup:
|
||||
optionsmenu.currcolour = OPTIONS_MainDef.extra1;
|
||||
optionsmenu.lastcolour = 0;
|
||||
optionsmenu.fade = 0;
|
||||
|
||||
// So that pause doesn't go to the main menu...
|
||||
OPTIONS_MainDef.prevMenu = currentMenu;
|
||||
|
||||
|
|
@ -3015,6 +3029,14 @@ void M_InitOptions(INT32 choice)
|
|||
M_SetupNextMenu(&OPTIONS_MainDef, false);
|
||||
}
|
||||
|
||||
// Prepares changing the colour of the background
|
||||
void M_OptionsChangeBGColour(INT16 newcolour)
|
||||
{
|
||||
optionsmenu.fade = 10;
|
||||
optionsmenu.lastcolour = optionsmenu.currcolour;
|
||||
optionsmenu.currcolour = newcolour;
|
||||
}
|
||||
|
||||
boolean M_OptionsQuit(void)
|
||||
{
|
||||
optionsmenu.toptx = 140-1;
|
||||
|
|
@ -3037,7 +3059,7 @@ void M_OptionsTick(void)
|
|||
optionsmenu.opty = optionsmenu.topty; // Avoid awkward 1 px errors.
|
||||
}
|
||||
|
||||
// Garbage:
|
||||
// Move the button for cool animations
|
||||
if (currentMenu == &OPTIONS_MainDef)
|
||||
{
|
||||
M_OptionsQuit(); // ...So now this is used here.
|
||||
|
|
@ -3048,6 +3070,14 @@ void M_OptionsTick(void)
|
|||
optionsmenu.topty = 50;
|
||||
}
|
||||
|
||||
// Handle the background stuff:
|
||||
if (optionsmenu.fade)
|
||||
optionsmenu.fade--;
|
||||
|
||||
// change the colour if we aren't matching the current menu colour
|
||||
if (optionsmenu.currcolour != currentMenu->extra1)
|
||||
M_OptionsChangeBGColour(currentMenu->extra1);
|
||||
|
||||
}
|
||||
|
||||
boolean M_OptionsInputs(INT32 ch)
|
||||
|
|
@ -3059,6 +3089,7 @@ boolean M_OptionsInputs(INT32 ch)
|
|||
{
|
||||
optionsmenu.offset += 48;
|
||||
M_NextOpt();
|
||||
S_StartSound(NULL, sfx_menu1);
|
||||
|
||||
if (itemOn == 0)
|
||||
optionsmenu.offset -= currentMenu->numitems*48;
|
||||
|
|
@ -3069,6 +3100,7 @@ boolean M_OptionsInputs(INT32 ch)
|
|||
{
|
||||
optionsmenu.offset -= 48;
|
||||
M_PrevOpt();
|
||||
S_StartSound(NULL, sfx_menu1);
|
||||
|
||||
if (itemOn == currentMenu->numitems-1)
|
||||
optionsmenu.offset += currentMenu->numitems*48;
|
||||
|
|
@ -3077,6 +3109,10 @@ boolean M_OptionsInputs(INT32 ch)
|
|||
}
|
||||
case KEY_ENTER:
|
||||
{
|
||||
|
||||
if (currentMenu->menuitems[itemOn].status & IT_TRANSTEXT)
|
||||
return true; // No.
|
||||
|
||||
optionsmenu.optx = 140;
|
||||
optionsmenu.opty = 70; // Default position for the currently selected option.
|
||||
|
||||
|
|
@ -3450,6 +3486,7 @@ boolean M_PauseInputs(INT32 ch)
|
|||
case KEY_UPARROW:
|
||||
{
|
||||
pausemenu.offset -= 50; // Each item is spaced by 50 px
|
||||
S_StartSound(NULL, sfx_menu1);
|
||||
M_PrevOpt();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -3457,6 +3494,7 @@ boolean M_PauseInputs(INT32 ch)
|
|||
case KEY_DOWNARROW:
|
||||
{
|
||||
pausemenu.offset += 50; // Each item is spaced by 50 px
|
||||
S_StartSound(NULL, sfx_menu1);
|
||||
M_NextOpt();
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue