mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
- Change M_DrawAddons to draw relatively, so that the height can be changed without consequence. - Add an option to adjust the spacing of Addons Menu entries (currently unused, but could be explored later). - Moved Search (still not yet functional) further up the menu.
4417 lines
123 KiB
C
4417 lines
123 KiB
C
/// \file k_menudraw.c
|
|
/// \brief SRB2Kart's menu drawer functions
|
|
|
|
#ifdef __GNUC__
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include "k_menu.h"
|
|
|
|
#include "doomdef.h"
|
|
#include "d_main.h"
|
|
#include "d_netcmd.h"
|
|
#include "console.h"
|
|
#include "r_local.h"
|
|
#include "hu_stuff.h"
|
|
#include "g_game.h"
|
|
#include "g_input.h"
|
|
#include "m_argv.h"
|
|
|
|
// Data.
|
|
#include "sounds.h"
|
|
#include "s_sound.h"
|
|
#include "i_system.h"
|
|
|
|
// Addfile
|
|
#include "filesrch.h"
|
|
|
|
#include "v_video.h"
|
|
#include "i_video.h"
|
|
#include "keys.h"
|
|
#include "z_zone.h"
|
|
#include "w_wad.h"
|
|
#include "p_local.h"
|
|
#include "p_setup.h"
|
|
#include "f_finale.h"
|
|
|
|
#ifdef HWRENDER
|
|
#include "hardware/hw_main.h"
|
|
#endif
|
|
|
|
#include "d_net.h"
|
|
#include "mserv.h"
|
|
#include "m_misc.h"
|
|
#include "m_anigif.h"
|
|
#include "byteptr.h"
|
|
#include "st_stuff.h"
|
|
#include "i_sound.h"
|
|
#include "k_kart.h"
|
|
#include "k_hud.h"
|
|
#include "k_follower.h"
|
|
#include "d_player.h" // KITEM_ constants
|
|
#include "doomstat.h" // MAXSPLITSCREENPLAYERS
|
|
|
|
#include "i_joy.h" // for joystick menu controls
|
|
|
|
// Condition Sets
|
|
#include "m_cond.h"
|
|
|
|
// And just some randomness for the exits.
|
|
#include "m_random.h"
|
|
|
|
#if defined(HAVE_SDL)
|
|
#include "SDL.h"
|
|
#if SDL_VERSION_ATLEAST(2,0,0)
|
|
#include "sdl/sdlmain.h" // JOYSTICK_HOTPLUG
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef PC_DOS
|
|
#include <stdio.h> // for snprintf
|
|
int snprintf(char *str, size_t n, const char *fmt, ...);
|
|
//int vsnprintf(char *str, size_t n, const char *fmt, va_list ap);
|
|
#endif
|
|
|
|
#define SKULLXOFF -32
|
|
#define LINEHEIGHT 16
|
|
#define STRINGHEIGHT 8
|
|
#define FONTBHEIGHT 20
|
|
#define SMALLLINEHEIGHT 8
|
|
#define SLIDER_RANGE 10
|
|
#define SLIDER_WIDTH (8*SLIDER_RANGE+6)
|
|
#define SERVERS_PER_PAGE 11
|
|
|
|
|
|
// horizontally centered text
|
|
static void M_CentreText(INT32 xoffs, INT32 y, const char *string)
|
|
{
|
|
INT32 x;
|
|
//added : 02-02-98 : centre on 320, because V_DrawString centers on vid.width...
|
|
x = ((BASEVIDWIDTH - V_StringWidth(string, V_OLDSPACING))>>1) + xoffs;
|
|
V_DrawString(x,y,V_OLDSPACING,string);
|
|
}
|
|
|
|
|
|
// A smaller 'Thermo', with range given as percents (0-100)
|
|
static void M_DrawSlider(INT32 x, INT32 y, const consvar_t *cv, boolean ontop)
|
|
{
|
|
INT32 i;
|
|
INT32 range;
|
|
patch_t *p;
|
|
|
|
for (i = 0; cv->PossibleValue[i+1].strvalue; i++);
|
|
|
|
x = BASEVIDWIDTH - x - SLIDER_WIDTH;
|
|
|
|
if (ontop)
|
|
{
|
|
V_DrawCharacter(x - 16 - (skullAnimCounter/5), y,
|
|
'\x1C' | highlightflags, false); // left arrow
|
|
V_DrawCharacter(x+(SLIDER_RANGE*8) + 8 + (skullAnimCounter/5), y,
|
|
'\x1D' | highlightflags, false); // right arrow
|
|
}
|
|
|
|
if ((range = atoi(cv->defaultvalue)) != cv->value)
|
|
{
|
|
range = ((range - cv->PossibleValue[0].value) * 100 /
|
|
(cv->PossibleValue[1].value - cv->PossibleValue[0].value));
|
|
|
|
if (range < 0)
|
|
range = 0;
|
|
if (range > 100)
|
|
range = 100;
|
|
|
|
// draw the default
|
|
p = W_CachePatchName("M_SLIDEC", PU_CACHE);
|
|
V_DrawScaledPatch(x - 4 + (((SLIDER_RANGE)*8 + 4)*range)/100, y, 0, p);
|
|
}
|
|
|
|
V_DrawScaledPatch(x - 8, y, 0, W_CachePatchName("M_SLIDEL", PU_CACHE));
|
|
|
|
p = W_CachePatchName("M_SLIDEM", PU_CACHE);
|
|
for (i = 0; i < SLIDER_RANGE; i++)
|
|
V_DrawScaledPatch (x+i*8, y, 0,p);
|
|
|
|
p = W_CachePatchName("M_SLIDER", PU_CACHE);
|
|
V_DrawScaledPatch(x+SLIDER_RANGE*8, y, 0, p);
|
|
|
|
range = ((cv->value - cv->PossibleValue[0].value) * 100 /
|
|
(cv->PossibleValue[1].value - cv->PossibleValue[0].value));
|
|
|
|
if (range < 0)
|
|
range = 0;
|
|
if (range > 100)
|
|
range = 100;
|
|
|
|
// draw the slider cursor
|
|
p = W_CachePatchName("M_SLIDEC", PU_CACHE);
|
|
V_DrawScaledPatch(x - 4 + (((SLIDER_RANGE)*8 + 4)*range)/100, y, 0, p);
|
|
}
|
|
|
|
|
|
static patch_t *addonsp[NUM_EXT+5];
|
|
|
|
static fixed_t bgText1Scroll = 0;
|
|
static fixed_t bgText2Scroll = 0;
|
|
static fixed_t bgImageScroll = 0;
|
|
static char bgImageName[9];
|
|
|
|
#define MENUBG_TEXTSCROLL 6
|
|
#define MENUBG_IMAGESCROLL 32
|
|
|
|
void M_UpdateMenuBGImage(boolean forceReset)
|
|
{
|
|
char oldName[9];
|
|
|
|
memcpy(oldName, bgImageName, 9);
|
|
|
|
if (currentMenu->menuitems[itemOn].patch)
|
|
{
|
|
sprintf(bgImageName, "%s", currentMenu->menuitems[itemOn].patch);
|
|
}
|
|
else
|
|
{
|
|
sprintf(bgImageName, "MENUI000");
|
|
}
|
|
|
|
if (forceReset == false && strcmp(bgImageName, oldName))
|
|
{
|
|
bgImageScroll = (BASEVIDWIDTH / 2)*FRACUNIT;
|
|
}
|
|
}
|
|
|
|
void M_DrawMenuBackground(void)
|
|
{
|
|
patch_t *text1 = W_CachePatchName("MENUBGT1", PU_CACHE);
|
|
patch_t *text2 = W_CachePatchName("MENUBGT2", PU_CACHE);
|
|
|
|
fixed_t text1loop = SHORT(text1->height)*FRACUNIT;
|
|
fixed_t text2loop = SHORT(text2->width)*FRACUNIT;
|
|
|
|
V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUBG4", PU_CACHE), NULL);
|
|
|
|
V_DrawFixedPatch(-bgImageScroll, 0, FRACUNIT, 0, W_CachePatchName("MENUBG1", PU_CACHE), NULL);
|
|
V_DrawFixedPatch(-bgImageScroll, 0, FRACUNIT, 0, W_CachePatchName(bgImageName, PU_CACHE), NULL);
|
|
|
|
V_DrawFixedPatch(0, (BASEVIDHEIGHT + 16) * FRACUNIT, FRACUNIT, V_SUBTRACT, W_CachePatchName("MENUBG2", PU_CACHE), NULL);
|
|
|
|
V_DrawFixedPatch(8 * FRACUNIT, -bgText1Scroll,
|
|
FRACUNIT, V_SUBTRACT, text1, NULL);
|
|
V_DrawFixedPatch(8 * FRACUNIT, -bgText1Scroll + text1loop,
|
|
FRACUNIT, V_SUBTRACT, text1, NULL);
|
|
|
|
bgText1Scroll += (MENUBG_TEXTSCROLL*renderdeltatics);
|
|
while (bgText1Scroll > text1loop)
|
|
bgText1Scroll -= text1loop;
|
|
|
|
V_DrawFixedPatch(-bgText2Scroll, (BASEVIDHEIGHT-8) * FRACUNIT,
|
|
FRACUNIT, V_ADD, text2, NULL);
|
|
V_DrawFixedPatch(-bgText2Scroll + text2loop, (BASEVIDHEIGHT-8) * FRACUNIT,
|
|
FRACUNIT, V_ADD, text2, NULL);
|
|
|
|
bgText2Scroll += (MENUBG_TEXTSCROLL*renderdeltatics);
|
|
while (bgText2Scroll > text2loop)
|
|
bgText2Scroll -= text2loop;
|
|
|
|
if (bgImageScroll > 0)
|
|
{
|
|
bgImageScroll -= (MENUBG_IMAGESCROLL*renderdeltatics);
|
|
if (bgImageScroll < 0)
|
|
{
|
|
bgImageScroll = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void M_DrawMenuParty(void)
|
|
{
|
|
const INT32 PLATTER_WIDTH = 19;
|
|
const INT32 PLATTER_STAGGER = 6;
|
|
const INT32 PLATTER_OFFSET = (PLATTER_WIDTH - PLATTER_STAGGER);
|
|
|
|
patch_t *small = W_CachePatchName("MENUPLRA", PU_CACHE);
|
|
patch_t *large = W_CachePatchName("MENUPLRB", PU_CACHE);
|
|
|
|
INT32 x, y;
|
|
INT32 skin;
|
|
UINT16 color;
|
|
UINT8 *colormap;
|
|
|
|
if (setup_numplayers == 0 || currentMenu == &PLAY_CharSelectDef)
|
|
{
|
|
return;
|
|
}
|
|
|
|
x = 2;
|
|
y = BASEVIDHEIGHT - small->height - 2;
|
|
|
|
switch (setup_numplayers)
|
|
{
|
|
case 1:
|
|
{
|
|
x -= 8;
|
|
V_DrawScaledPatch(x, y, 0, small);
|
|
|
|
skin = R_SkinAvailable(cv_skin[0].string);
|
|
color = cv_playercolor[0].value;
|
|
colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
|
|
|
|
V_DrawMappedPatch(x + 22, y + 8, 0, faceprefix[skin][FACE_MINIMAP], colormap);
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
x -= 8;
|
|
V_DrawScaledPatch(x, y, 0, small);
|
|
V_DrawScaledPatch(x + PLATTER_OFFSET, y - PLATTER_STAGGER, 0, small);
|
|
|
|
skin = R_SkinAvailable(cv_skin[1].string);
|
|
color = cv_playercolor[1].value;
|
|
colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
|
|
|
|
V_DrawMappedPatch(x + PLATTER_OFFSET + 22, y - PLATTER_STAGGER + 8, 0, faceprefix[skin][FACE_MINIMAP], colormap);
|
|
|
|
skin = R_SkinAvailable(cv_skin[0].string);
|
|
color = cv_playercolor[0].value;
|
|
colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
|
|
|
|
V_DrawMappedPatch(x + 22, y + 8, 0, faceprefix[skin][FACE_MINIMAP], colormap);
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
V_DrawScaledPatch(x, y, 0, large);
|
|
V_DrawScaledPatch(x + PLATTER_OFFSET, y - PLATTER_STAGGER, 0, small);
|
|
|
|
skin = R_SkinAvailable(cv_skin[1].string);
|
|
color = cv_playercolor[1].value;
|
|
colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
|
|
|
|
V_DrawMappedPatch(x + PLATTER_OFFSET + 22, y - PLATTER_STAGGER + 8, 0, faceprefix[skin][FACE_MINIMAP], colormap);
|
|
|
|
skin = R_SkinAvailable(cv_skin[0].string);
|
|
color = cv_playercolor[0].value;
|
|
colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
|
|
|
|
V_DrawMappedPatch(x + 12, y - 2, 0, faceprefix[skin][FACE_MINIMAP], colormap);
|
|
|
|
skin = R_SkinAvailable(cv_skin[2].string);
|
|
color = cv_playercolor[2].value;
|
|
colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
|
|
|
|
V_DrawMappedPatch(x + 22, y + 8, 0, faceprefix[skin][FACE_MINIMAP], colormap);
|
|
break;
|
|
}
|
|
case 4:
|
|
{
|
|
V_DrawScaledPatch(x, y, 0, large);
|
|
V_DrawScaledPatch(x + PLATTER_OFFSET, y - PLATTER_STAGGER, 0, large);
|
|
|
|
skin = R_SkinAvailable(cv_skin[1].string);
|
|
color = cv_playercolor[1].value;
|
|
colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
|
|
|
|
V_DrawMappedPatch(x + PLATTER_OFFSET + 12, y - PLATTER_STAGGER - 2, 0, faceprefix[skin][FACE_MINIMAP], colormap);
|
|
|
|
skin = R_SkinAvailable(cv_skin[0].string);
|
|
color = cv_playercolor[0].value;
|
|
colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
|
|
|
|
V_DrawMappedPatch(x + 12, y - 2, 0, faceprefix[skin][FACE_MINIMAP], colormap);
|
|
|
|
skin = R_SkinAvailable(cv_skin[3].string);
|
|
color = cv_playercolor[3].value;
|
|
colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
|
|
|
|
V_DrawMappedPatch(x + PLATTER_OFFSET + 22, y - PLATTER_STAGGER + 8, 0, faceprefix[skin][FACE_MINIMAP], colormap);
|
|
|
|
skin = R_SkinAvailable(cv_skin[2].string);
|
|
color = cv_playercolor[2].value;
|
|
colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
|
|
|
|
V_DrawMappedPatch(x + 22, y + 8, 0, faceprefix[skin][FACE_MINIMAP], colormap);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
x += PLATTER_WIDTH;
|
|
y += small->height;
|
|
V_DrawScaledPatch(x + 16, y - 12, 0, W_CachePatchName(va("OPPRNK0%d", setup_numplayers % 10), PU_CACHE));
|
|
}
|
|
|
|
void M_DrawMenuForeground(void)
|
|
{
|
|
if (gamestate == GS_MENU)
|
|
{
|
|
M_DrawMenuParty();
|
|
}
|
|
|
|
// draw non-green resolution border
|
|
if ((vid.width % BASEVIDWIDTH != 0) || (vid.height % BASEVIDHEIGHT != 0))
|
|
{
|
|
V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("WEIRDRES", PU_CACHE), NULL);
|
|
}
|
|
}
|
|
|
|
// Draws the typing submenu
|
|
static void M_DrawMenuTyping(void)
|
|
{
|
|
|
|
INT32 i, j;
|
|
|
|
INT32 x = 60;
|
|
INT32 y = 100 + (9-menutyping.menutypingfade)*8;
|
|
INT32 tflag = (9 - menutyping.menutypingfade)<<V_ALPHASHIFT;
|
|
|
|
consvar_t *cv = currentMenu->menuitems[itemOn].itemaction.cvar;
|
|
|
|
char buf[8]; // We write there to use drawstring for convenience.
|
|
|
|
V_DrawFadeScreen(31, menutyping.menutypingfade);
|
|
|
|
// Draw the string we're editing at the top.
|
|
V_DrawString(x, y-48 + 12, V_ALLOWLOWERCASE|tflag, cv->string);
|
|
if (skullAnimCounter < 4)
|
|
V_DrawCharacter(x + V_StringWidth(cv->string, 0), y - 35, '_' | 0x80, false);
|
|
|
|
// Some contextual stuff
|
|
if (menutyping.keyboardtyping)
|
|
V_DrawThinString(10, 175, V_ALLOWLOWERCASE|tflag|V_GRAYMAP, "Type using your keyboard. Press Enter to confirm & exit.\nUse your controller or any directional input to use the Virtual Keyboard.\n");
|
|
else
|
|
V_DrawThinString(10, 175, V_ALLOWLOWERCASE|tflag|V_GRAYMAP, "Type using the Virtual Keyboard. Use the \'OK\' button to confirm & exit.\nPress any keyboard key not bound to a control to use it.");
|
|
|
|
|
|
// Now the keyboard itself
|
|
for (i=0; i < 5; i++)
|
|
{
|
|
for (j=0; j < 13; j++)
|
|
{
|
|
INT32 mflag = 0;
|
|
INT16 c = virtualKeyboard[i][j];
|
|
if (menutyping.keyboardshift ^ menutyping.keyboardcapslock)
|
|
c = shift_virtualKeyboard[i][j];
|
|
|
|
|
|
if (c == KEY_BACKSPACE)
|
|
strcpy(buf, "DEL");
|
|
|
|
else if (c == KEY_RSHIFT)
|
|
strcpy(buf, "SHIFT");
|
|
|
|
else if (c == KEY_CAPSLOCK)
|
|
strcpy(buf, "CAPS");
|
|
|
|
else if (c == KEY_ENTER)
|
|
strcpy(buf, "OK");
|
|
|
|
else if (c == KEY_SPACE)
|
|
strcpy(buf, "SPACE");
|
|
|
|
else
|
|
{
|
|
buf[0] = c;
|
|
buf[1] = '\0';
|
|
}
|
|
|
|
// highlight:
|
|
if (menutyping.keyboardx == j && menutyping.keyboardy == i && !menutyping.keyboardtyping)
|
|
mflag |= highlightflags;
|
|
else if (menutyping.keyboardtyping)
|
|
mflag |= V_TRANSLUCENT; // grey it out if we can't use it.
|
|
|
|
V_DrawString(x, y, V_ALLOWLOWERCASE|tflag|mflag, buf);
|
|
|
|
x += V_StringWidth(buf, 0)+8;
|
|
}
|
|
x = 60;
|
|
y += 12;
|
|
}
|
|
}
|
|
|
|
// Draw the message popup submenu
|
|
static void M_DrawMenuMessage(void)
|
|
{
|
|
|
|
INT32 y = menumessage.y + (9-menumessage.fadetimer)*20;
|
|
size_t i, start = 0;
|
|
INT16 max;
|
|
char string[MAXMENUMESSAGE];
|
|
INT32 mlines;
|
|
const char *msg = menumessage.message;
|
|
|
|
mlines = menumessage.m>>8;
|
|
max = (INT16)((UINT8)(menumessage.m & 0xFF)*8);
|
|
|
|
V_DrawFadeScreen(31, menumessage.fadetimer);
|
|
M_DrawTextBox(menumessage.x, y - 8, (max+7)>>3, mlines);
|
|
|
|
while (*(msg+start))
|
|
{
|
|
size_t len = strlen(msg+start);
|
|
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
if (*(msg+start+i) == '\n')
|
|
{
|
|
memset(string, 0, MAXMENUMESSAGE);
|
|
if (i >= MAXMENUMESSAGE)
|
|
{
|
|
CONS_Printf("M_DrawMenuMessage: too long segment in %s\n", msg);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
strncpy(string,msg+start, i);
|
|
string[i] = '\0';
|
|
start += i;
|
|
i = (size_t)-1; //added : 07-02-98 : damned!
|
|
start++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == strlen(msg+start))
|
|
{
|
|
if (i >= MAXMENUMESSAGE)
|
|
{
|
|
CONS_Printf("M_DrawMenuMessage: too long segment in %s\n", msg);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
strcpy(string, msg + start);
|
|
start += i;
|
|
}
|
|
}
|
|
|
|
V_DrawString((BASEVIDWIDTH - V_StringWidth(string, 0))/2, y, V_ALLOWLOWERCASE, string);
|
|
y += 8;
|
|
}
|
|
}
|
|
|
|
//
|
|
// M_Drawer
|
|
// Called after the view has been rendered,
|
|
// but before it has been blitted.
|
|
//
|
|
|
|
void M_Drawer(void)
|
|
{
|
|
if (currentMenu == &MessageDef)
|
|
menuactive = true;
|
|
|
|
if (menuwipe)
|
|
F_WipeStartScreen();
|
|
|
|
if (menuactive)
|
|
{
|
|
if (gamestate == GS_MENU)
|
|
{
|
|
M_DrawMenuBackground();
|
|
}
|
|
else if (!WipeInAction && currentMenu != &PAUSE_PlaybackMenuDef)
|
|
{
|
|
if (rendermode == render_opengl) // OGL can't handle what SW is doing so let's fake it;
|
|
V_DrawFadeScreen(122, 3); // palette index aproximation...
|
|
else // Software can keep its unique fade
|
|
V_DrawCustomFadeScreen("FADEMAP0", 4); // now that's more readable with a faded background (yeah like Quake...)
|
|
}
|
|
|
|
if (currentMenu->drawroutine)
|
|
currentMenu->drawroutine(); // call current menu Draw routine
|
|
|
|
M_DrawMenuForeground();
|
|
|
|
// Draw version down in corner
|
|
// ... but only in the MAIN MENU. I'm a picky bastard.
|
|
if (currentMenu == &MainDef)
|
|
{
|
|
if (customversionstring[0] != '\0')
|
|
{
|
|
V_DrawThinString(vid.dupx, vid.height - 20*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT, "Mod version:");
|
|
V_DrawThinString(vid.dupx, vid.height - 10*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, customversionstring);
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEVELOP // Development -- show revision / branch info
|
|
V_DrawThinString(vid.dupx, vid.height - 20*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, compbranch);
|
|
V_DrawThinString(vid.dupx, vid.height - 10*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, comprevision);
|
|
#else // Regular build
|
|
V_DrawThinString(vid.dupx, vid.height - 10*vid.dupy, V_NOSCALESTART|V_TRANSLUCENT|V_ALLOWLOWERCASE, va("%s", VERSIONSTRING));
|
|
#endif
|
|
}
|
|
|
|
|
|
}
|
|
|
|
// Draw message overlay when needed
|
|
if (menumessage.active)
|
|
M_DrawMenuMessage();
|
|
|
|
// Draw typing overlay when needed, above all other menu elements.
|
|
if (menutyping.active)
|
|
M_DrawMenuTyping();
|
|
}
|
|
|
|
if (menuwipe)
|
|
{
|
|
F_WipeEndScreen();
|
|
F_RunWipe(wipedefs[wipe_menu_final], false, "FADEMAP0", true, false);
|
|
menuwipe = false;
|
|
}
|
|
|
|
// focus lost notification goes on top of everything, even the former everything
|
|
if (window_notinfocus && cv_showfocuslost.value)
|
|
{
|
|
M_DrawTextBox((BASEVIDWIDTH/2) - (60), (BASEVIDHEIGHT/2) - (16), 13, 2);
|
|
if (gamestate == GS_LEVEL && (P_AutoPause() || paused))
|
|
V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2) - (4), highlightflags, "Game Paused");
|
|
else
|
|
V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2) - (4), highlightflags, "Focus Lost");
|
|
}
|
|
}
|
|
|
|
// ==========================================================================
|
|
// GENERIC MENUS
|
|
// ==========================================================================
|
|
|
|
//
|
|
// M_DrawMenuTooltips
|
|
//
|
|
// Draw a banner across the top of the screen, with a description of the current option displayed
|
|
//
|
|
static void M_DrawMenuTooltips(void)
|
|
{
|
|
if (currentMenu->menuitems[itemOn].tooltip != NULL)
|
|
{
|
|
V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUHINT", PU_CACHE), NULL);
|
|
V_DrawCenteredThinString(BASEVIDWIDTH/2, 12, V_ALLOWLOWERCASE|V_6WIDTHSPACE, currentMenu->menuitems[itemOn].tooltip);
|
|
}
|
|
}
|
|
|
|
// Converts a string into question marks.
|
|
// Used for the secrets menu, to hide yet-to-be-unlocked stuff.
|
|
static const char *M_CreateSecretMenuOption(const char *str)
|
|
{
|
|
static char qbuf[32];
|
|
int i;
|
|
|
|
for (i = 0; i < 31; ++i)
|
|
{
|
|
if (!str[i])
|
|
{
|
|
qbuf[i] = '\0';
|
|
return qbuf;
|
|
}
|
|
else if (str[i] != ' ')
|
|
qbuf[i] = '?';
|
|
else
|
|
qbuf[i] = ' ';
|
|
}
|
|
|
|
qbuf[31] = '\0';
|
|
return qbuf;
|
|
}
|
|
|
|
//
|
|
// M_DrawGenericMenu
|
|
//
|
|
// Default, generic text-based list menu, used for Options
|
|
//
|
|
void M_DrawGenericMenu(void)
|
|
{
|
|
INT32 x = currentMenu->x, y = currentMenu->y, w, i, cursory = 0;
|
|
|
|
M_DrawMenuTooltips();
|
|
|
|
for (i = 0; i < currentMenu->numitems; i++)
|
|
{
|
|
if (i == itemOn)
|
|
cursory = y;
|
|
|
|
switch (currentMenu->menuitems[i].status & IT_DISPLAY)
|
|
{
|
|
case IT_PATCH:
|
|
if (currentMenu->menuitems[i].patch && currentMenu->menuitems[i].patch[0])
|
|
{
|
|
if (currentMenu->menuitems[i].status & IT_CENTER)
|
|
{
|
|
patch_t *p;
|
|
p = W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE);
|
|
V_DrawScaledPatch((BASEVIDWIDTH - SHORT(p->width))/2, y, 0, p);
|
|
}
|
|
else
|
|
{
|
|
V_DrawScaledPatch(x, y, 0,
|
|
W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE));
|
|
}
|
|
}
|
|
/* FALLTHRU */
|
|
case IT_NOTHING:
|
|
case IT_DYBIGSPACE:
|
|
y = currentMenu->y + currentMenu->menuitems[i].mvar1;//+= LINEHEIGHT;
|
|
break;
|
|
#if 0
|
|
case IT_BIGSLIDER:
|
|
M_DrawThermo(x, y, currentMenu->menuitems[i].itemaction.cvar);
|
|
y += LINEHEIGHT;
|
|
break;
|
|
#endif
|
|
case IT_STRING:
|
|
case IT_WHITESTRING:
|
|
if (currentMenu->menuitems[i].mvar1)
|
|
y = currentMenu->y+currentMenu->menuitems[i].mvar1;
|
|
if (i == itemOn)
|
|
cursory = y;
|
|
|
|
if ((currentMenu->menuitems[i].status & IT_DISPLAY)==IT_STRING)
|
|
V_DrawString(x, y, 0, currentMenu->menuitems[i].text);
|
|
else
|
|
V_DrawString(x, y, highlightflags, currentMenu->menuitems[i].text);
|
|
|
|
// Cvar specific handling
|
|
switch (currentMenu->menuitems[i].status & IT_TYPE)
|
|
case IT_CVAR:
|
|
{
|
|
consvar_t *cv = currentMenu->menuitems[i].itemaction.cvar;
|
|
switch (currentMenu->menuitems[i].status & IT_CVARTYPE)
|
|
{
|
|
#if 0
|
|
case IT_CV_SLIDER:
|
|
M_DrawSlider(x, y, cv, (i == itemOn));
|
|
case IT_CV_NOPRINT: // color use this
|
|
case IT_CV_INVISSLIDER: // monitor toggles use this
|
|
break;
|
|
#endif
|
|
case IT_CV_STRING:
|
|
M_DrawTextBox(x, y + 4, MAXSTRINGLENGTH, 1);
|
|
V_DrawString(x + 8, y + 12, V_ALLOWLOWERCASE, cv->string);
|
|
if (skullAnimCounter < 4 && i == itemOn)
|
|
V_DrawCharacter(x + 8 + V_StringWidth(cv->string, 0), y + 12,
|
|
'_' | 0x80, false);
|
|
y += 16;
|
|
break;
|
|
default:
|
|
w = V_StringWidth(cv->string, 0);
|
|
V_DrawString(BASEVIDWIDTH - x - w, y,
|
|
((cv->flags & CV_CHEAT) && !CV_IsSetToDefault(cv) ? warningflags : highlightflags), cv->string);
|
|
if (i == itemOn)
|
|
{
|
|
V_DrawCharacter(BASEVIDWIDTH - x - 10 - w - (skullAnimCounter/5), y,
|
|
'\x1C' | highlightflags, false); // left arrow
|
|
V_DrawCharacter(BASEVIDWIDTH - x + 2 + (skullAnimCounter/5), y,
|
|
'\x1D' | highlightflags, false); // right arrow
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
y += STRINGHEIGHT;
|
|
break;
|
|
case IT_STRING2:
|
|
V_DrawString(x, y, 0, currentMenu->menuitems[i].text);
|
|
/* FALLTHRU */
|
|
case IT_DYLITLSPACE:
|
|
y += SMALLLINEHEIGHT;
|
|
break;
|
|
case IT_GRAYPATCH:
|
|
if (currentMenu->menuitems[i].patch && currentMenu->menuitems[i].patch[0])
|
|
V_DrawMappedPatch(x, y, 0,
|
|
W_CachePatchName(currentMenu->menuitems[i].patch,PU_CACHE), graymap);
|
|
y += LINEHEIGHT;
|
|
break;
|
|
case IT_TRANSTEXT:
|
|
if (currentMenu->menuitems[i].mvar1)
|
|
y = currentMenu->y+currentMenu->menuitems[i].mvar1;
|
|
/* FALLTHRU */
|
|
case IT_TRANSTEXT2:
|
|
V_DrawString(x, y, V_TRANSLUCENT, currentMenu->menuitems[i].text);
|
|
y += SMALLLINEHEIGHT;
|
|
break;
|
|
case IT_QUESTIONMARKS:
|
|
if (currentMenu->menuitems[i].mvar1)
|
|
y = currentMenu->y+currentMenu->menuitems[i].mvar1;
|
|
|
|
V_DrawString(x, y, V_TRANSLUCENT|V_OLDSPACING, M_CreateSecretMenuOption(currentMenu->menuitems[i].text));
|
|
y += SMALLLINEHEIGHT;
|
|
break;
|
|
case IT_HEADERTEXT: // draws 16 pixels to the left, in yellow text
|
|
if (currentMenu->menuitems[i].mvar1)
|
|
y = currentMenu->y+currentMenu->menuitems[i].mvar1;
|
|
|
|
V_DrawString(x-16, y, highlightflags, currentMenu->menuitems[i].text);
|
|
y += SMALLLINEHEIGHT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((currentMenu->menuitems[itemOn].status & IT_DISPLAY) == 0)
|
|
cursory = 300; // Put the cursor off screen if we can't even display that option and we're on it, it makes no sense otherwise...
|
|
|
|
// DRAW THE SKULL CURSOR
|
|
if (((currentMenu->menuitems[itemOn].status & IT_DISPLAY) == IT_PATCH)
|
|
|| ((currentMenu->menuitems[itemOn].status & IT_DISPLAY) == IT_NOTHING))
|
|
{
|
|
V_DrawScaledPatch(currentMenu->x + SKULLXOFF, cursory - 5, 0,
|
|
W_CachePatchName("M_CURSOR", PU_CACHE));
|
|
}
|
|
else
|
|
{
|
|
V_DrawScaledPatch(currentMenu->x - 24, cursory, 0,
|
|
W_CachePatchName("M_CURSOR", PU_CACHE));
|
|
V_DrawString(currentMenu->x, cursory, highlightflags, currentMenu->menuitems[itemOn].text);
|
|
}
|
|
}
|
|
|
|
#define GM_STARTX 128
|
|
#define GM_STARTY 80
|
|
#define GM_XOFFSET 17
|
|
#define GM_YOFFSET 34
|
|
|
|
//
|
|
// M_DrawKartGamemodeMenu
|
|
//
|
|
// Huge gamemode-selection list for main menu
|
|
//
|
|
void M_DrawKartGamemodeMenu(void)
|
|
{
|
|
UINT8 n = 0;
|
|
INT32 i, x, y;
|
|
|
|
for (i = 0; i < currentMenu->numitems; i++)
|
|
{
|
|
if (currentMenu->menuitems[i].status == IT_DISABLED)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
n++;
|
|
}
|
|
|
|
n--;
|
|
x = GM_STARTX - ((GM_XOFFSET / 2) * (n-1));
|
|
y = GM_STARTY - ((GM_YOFFSET / 2) * (n-1));
|
|
|
|
M_DrawMenuTooltips();
|
|
|
|
if (menutransition.tics)
|
|
{
|
|
x += 48 * menutransition.tics;
|
|
}
|
|
|
|
for (i = 0; i < currentMenu->numitems; i++)
|
|
{
|
|
if (currentMenu->menuitems[i].status == IT_DISABLED)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (i >= currentMenu->numitems-1)
|
|
{
|
|
x = GM_STARTX + (GM_XOFFSET * 5 / 2);
|
|
y = GM_STARTY + (GM_YOFFSET * 5 / 2);
|
|
|
|
if (menutransition.tics)
|
|
{
|
|
x += 48 * menutransition.tics;
|
|
}
|
|
}
|
|
|
|
switch (currentMenu->menuitems[i].status & IT_DISPLAY)
|
|
{
|
|
case IT_STRING:
|
|
{
|
|
UINT8 *colormap = NULL;
|
|
|
|
if (i == itemOn)
|
|
{
|
|
colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE);
|
|
}
|
|
else
|
|
{
|
|
colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_MOSS, GTC_CACHE);
|
|
}
|
|
|
|
V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, W_CachePatchName("MENUPLTR", PU_CACHE), colormap);
|
|
V_DrawGamemodeString(x + 16, y - 3, V_ALLOWLOWERCASE, colormap, currentMenu->menuitems[i].text);
|
|
}
|
|
break;
|
|
}
|
|
|
|
x += GM_XOFFSET;
|
|
y += GM_YOFFSET;
|
|
}
|
|
}
|
|
|
|
#define MAXMSGLINELEN 256
|
|
|
|
//
|
|
// Draw a textbox, like Quake does, because sometimes it's difficult
|
|
// to read the text with all the stuff in the background...
|
|
//
|
|
void M_DrawTextBox(INT32 x, INT32 y, INT32 width, INT32 boxlines)
|
|
{
|
|
// Solid color textbox.
|
|
V_DrawFill(x+5, y+5, width*8+6, boxlines*8+6, 159);
|
|
//V_DrawFill(x+8, y+8, width*8, boxlines*8, 31);
|
|
}
|
|
|
|
//
|
|
// M_DrawMessageMenu
|
|
//
|
|
// Generic message prompt
|
|
//
|
|
void M_DrawMessageMenu(void)
|
|
{
|
|
INT32 y = currentMenu->y;
|
|
size_t i, start = 0;
|
|
INT16 max;
|
|
char string[MAXMENUMESSAGE];
|
|
INT32 mlines;
|
|
const char *msg = currentMenu->menuitems[0].text;
|
|
|
|
mlines = currentMenu->lastOn>>8;
|
|
max = (INT16)((UINT8)(currentMenu->lastOn & 0xFF)*8);
|
|
|
|
M_DrawTextBox(currentMenu->x, y - 8, (max+7)>>3, mlines);
|
|
|
|
while (*(msg+start))
|
|
{
|
|
size_t len = strlen(msg+start);
|
|
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
if (*(msg+start+i) == '\n')
|
|
{
|
|
memset(string, 0, MAXMENUMESSAGE);
|
|
if (i >= MAXMENUMESSAGE)
|
|
{
|
|
CONS_Printf("M_DrawMessageMenu: too long segment in %s\n", msg);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
strncpy(string,msg+start, i);
|
|
string[i] = '\0';
|
|
start += i;
|
|
i = (size_t)-1; //added : 07-02-98 : damned!
|
|
start++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == strlen(msg+start))
|
|
{
|
|
if (i >= MAXMENUMESSAGE)
|
|
{
|
|
CONS_Printf("M_DrawMessageMenu: too long segment in %s\n", msg);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
strcpy(string, msg + start);
|
|
start += i;
|
|
}
|
|
}
|
|
|
|
V_DrawString((BASEVIDWIDTH - V_StringWidth(string, 0))/2,y,V_ALLOWLOWERCASE,string);
|
|
y += 8; //SHORT(hu_font[0]->height);
|
|
}
|
|
}
|
|
|
|
// Draw an Image Def. Aka, Help images.
|
|
// Defines what image is used in (menuitem_t)->patch.
|
|
// You can even put multiple images in one menu!
|
|
void M_DrawImageDef(void)
|
|
{
|
|
patch_t *patch = W_CachePatchName(currentMenu->menuitems[itemOn].text, PU_CACHE);
|
|
|
|
if (patch->width <= BASEVIDWIDTH)
|
|
{
|
|
V_DrawScaledPatch(0, 0, 0, patch);
|
|
}
|
|
else
|
|
{
|
|
V_DrawSmallScaledPatch(0, 0, 0, patch);
|
|
}
|
|
|
|
if (currentMenu->menuitems[itemOn].mvar1)
|
|
{
|
|
V_DrawString(2,BASEVIDHEIGHT-10, V_YELLOWMAP, va("%d", (itemOn<<1)-1)); // intentionally not highlightflags, unlike below
|
|
V_DrawRightAlignedString(BASEVIDWIDTH-2,BASEVIDHEIGHT-10, V_YELLOWMAP, va("%d", itemOn<<1)); // ditto
|
|
}
|
|
else
|
|
{
|
|
INT32 x = BASEVIDWIDTH>>1, y = (BASEVIDHEIGHT>>1) - 4;
|
|
x += (itemOn ? 1 : -1)*((BASEVIDWIDTH>>2) + 10);
|
|
V_DrawCenteredString(x, y-10, highlightflags, "USE ARROW KEYS");
|
|
V_DrawCharacter(x - 10 - (skullAnimCounter/5), y,
|
|
'\x1C' | highlightflags, false); // left arrow
|
|
V_DrawCharacter(x + 2 + (skullAnimCounter/5), y,
|
|
'\x1D' | highlightflags, false); // right arrow
|
|
V_DrawCenteredString(x, y+10, highlightflags, "TO LEAF THROUGH");
|
|
}
|
|
}
|
|
|
|
//
|
|
// PLAY MENUS
|
|
//
|
|
|
|
static void M_DrawCharSelectCircle(setup_player_t *p, INT16 x, INT16 y)
|
|
{
|
|
angle_t angamt = ANGLE_MAX;
|
|
UINT16 i, numoptions = 0;
|
|
UINT16 l = 0, r = 0;
|
|
|
|
switch (p->mdepth)
|
|
{
|
|
case CSSTEP_ALTS:
|
|
numoptions = setup_chargrid[p->gridx][p->gridy].numskins;
|
|
break;
|
|
case CSSTEP_COLORS:
|
|
numoptions = nummenucolors;
|
|
break;
|
|
case CSSTEP_FOLLOWER:
|
|
numoptions = numfollowers+1;
|
|
break;
|
|
case CSSTEP_FOLLOWERCOLORS:
|
|
numoptions = nummenucolors+2;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
if (numoptions == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
angamt /= numoptions;
|
|
|
|
for (i = 0; i < numoptions; i++)
|
|
{
|
|
fixed_t cx = x << FRACBITS, cy = y << FRACBITS;
|
|
boolean subtract = (i & 1);
|
|
angle_t ang = ((i+1)/2) * angamt;
|
|
patch_t *patch = NULL;
|
|
UINT8 *colormap = NULL;
|
|
fixed_t radius = 28<<FRACBITS;
|
|
UINT16 n = 0;
|
|
|
|
switch (p->mdepth)
|
|
{
|
|
case CSSTEP_ALTS:
|
|
{
|
|
INT16 skin;
|
|
|
|
n = (p->clonenum) + numoptions/2;
|
|
if (subtract)
|
|
n -= ((i+1)/2);
|
|
else
|
|
n += ((i+1)/2);
|
|
n %= numoptions;
|
|
|
|
skin = setup_chargrid[p->gridx][p->gridy].skinlist[n];
|
|
patch = faceprefix[skin][FACE_RANK];
|
|
colormap = R_GetTranslationColormap(skin, skins[skin].prefcolor, GTC_MENUCACHE);
|
|
radius = 24<<FRACBITS;
|
|
|
|
cx -= (SHORT(patch->width) << FRACBITS) >> 1;
|
|
cy -= (SHORT(patch->height) << FRACBITS) >> 1;
|
|
break;
|
|
}
|
|
|
|
case CSSTEP_COLORS:
|
|
{
|
|
INT16 diff;
|
|
|
|
if (i == 0)
|
|
{
|
|
n = l = r = M_GetColorBefore(p->color, numoptions/2, false);
|
|
}
|
|
else if (subtract)
|
|
{
|
|
n = l = M_GetColorBefore(l, 1, false);
|
|
}
|
|
else
|
|
{
|
|
n = r = M_GetColorAfter(r, 1, false);
|
|
}
|
|
|
|
colormap = R_GetTranslationColormap(TC_DEFAULT, n, GTC_MENUCACHE);
|
|
|
|
diff = (numoptions - i)/2; // only 0 when i == numoptions-1
|
|
|
|
if (diff == 0)
|
|
patch = W_CachePatchName("COLORSP2", PU_CACHE);
|
|
else if (abs(diff) < 25)
|
|
patch = W_CachePatchName("COLORSP1", PU_CACHE);
|
|
else
|
|
patch = W_CachePatchName("COLORSP0", PU_CACHE);
|
|
|
|
cx -= (SHORT(patch->width) << FRACBITS) >> 1;
|
|
break;
|
|
}
|
|
|
|
case CSSTEP_FOLLOWER:
|
|
{
|
|
follower_t *fl = NULL;
|
|
|
|
n = (p->followern + 1) + numoptions/2;
|
|
if (subtract)
|
|
n -= ((i+1)/2);
|
|
else
|
|
n += ((i+1)/2);
|
|
n %= numoptions;
|
|
|
|
if (n == 0)
|
|
{
|
|
patch = W_CachePatchName("K_NOBLNS", PU_CACHE);
|
|
}
|
|
else
|
|
{
|
|
fl = &followers[n - 1];
|
|
patch = W_CachePatchName(fl->icon, PU_CACHE);
|
|
|
|
colormap = R_GetTranslationColormap(TC_DEFAULT,
|
|
K_GetEffectiveFollowerColor(p->followercolor, p->color),
|
|
GTC_MENUCACHE
|
|
);
|
|
}
|
|
|
|
radius = 24<<FRACBITS;
|
|
|
|
cx -= (SHORT(patch->width) << FRACBITS) >> 1;
|
|
cy -= (SHORT(patch->height) << FRACBITS) >> 1;
|
|
break;
|
|
}
|
|
|
|
case CSSTEP_FOLLOWERCOLORS:
|
|
{
|
|
INT16 diff;
|
|
UINT16 col;
|
|
|
|
if (i == 0)
|
|
{
|
|
n = l = r = M_GetColorBefore(p->followercolor, numoptions/2, true);
|
|
}
|
|
else if (subtract)
|
|
{
|
|
n = l = M_GetColorBefore(l, 1, true);
|
|
}
|
|
else
|
|
{
|
|
n = r = M_GetColorAfter(r, 1, true);
|
|
}
|
|
|
|
col = K_GetEffectiveFollowerColor(n, p->color);
|
|
|
|
colormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE);
|
|
|
|
diff = (numoptions - i)/2; // only 0 when i == numoptions-1
|
|
|
|
if (diff == 0)
|
|
patch = W_CachePatchName("COLORSP2", PU_CACHE);
|
|
else if (abs(diff) < 25)
|
|
patch = W_CachePatchName("COLORSP1", PU_CACHE);
|
|
else
|
|
patch = W_CachePatchName("COLORSP0", PU_CACHE);
|
|
|
|
cx -= (SHORT(patch->width) << FRACBITS) >> 1;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (subtract)
|
|
ang = (signed)(ANGLE_90 - ang);
|
|
else
|
|
ang = ANGLE_90 + ang;
|
|
|
|
if (numoptions & 1)
|
|
ang = (signed)(ang - (angamt/2));
|
|
|
|
if (p->rotate)
|
|
ang = (signed)(ang + ((angamt / CSROTATETICS) * p->rotate));
|
|
|
|
cx += FixedMul(radius, FINECOSINE(ang >> ANGLETOFINESHIFT));
|
|
cy -= FixedMul(radius, FINESINE(ang >> ANGLETOFINESHIFT)) / 3;
|
|
|
|
V_DrawFixedPatch(cx, cy, FRACUNIT, 0, patch, colormap);
|
|
if (p->mdepth == CSSTEP_ALTS && n != p->clonenum)
|
|
V_DrawFixedPatch(cx, cy, FRACUNIT, V_TRANSLUCENT, W_CachePatchName("ICONDARK", PU_CACHE), NULL);
|
|
}
|
|
}
|
|
|
|
// returns false if the character couldn't be rendered
|
|
static boolean M_DrawCharacterSprite(INT16 x, INT16 y, INT16 skin, INT32 addflags, UINT8 *colormap)
|
|
{
|
|
UINT8 spr;
|
|
spritedef_t *sprdef;
|
|
spriteframe_t *sprframe;
|
|
patch_t *sprpatch;
|
|
|
|
UINT32 flags = 0;
|
|
UINT32 frame;
|
|
|
|
spr = P_GetSkinSprite2(&skins[skin], SPR2_FSTN, NULL);
|
|
sprdef = &skins[skin].sprites[spr];
|
|
|
|
if (!sprdef->numframes) // No frames ??
|
|
return false; // Can't render!
|
|
|
|
frame = states[S_KART_FAST].frame & FF_FRAMEMASK;
|
|
if (frame >= sprdef->numframes) // Walking animation missing
|
|
frame = 0; // Try to use standing frame
|
|
|
|
sprframe = &sprdef->spriteframes[frame];
|
|
sprpatch = W_CachePatchNum(sprframe->lumppat[1], PU_CACHE);
|
|
|
|
if (sprframe->flip & 1) // Only for first sprite
|
|
flags |= V_FLIP; // This sprite is left/right flipped!
|
|
|
|
if (skins[skin].flags & SF_HIRES)
|
|
{
|
|
V_DrawFixedPatch(x<<FRACBITS,
|
|
y<<FRACBITS,
|
|
skins[skin].highresscale,
|
|
flags, sprpatch, colormap);
|
|
}
|
|
else
|
|
V_DrawMappedPatch(x, y, addflags|flags, sprpatch, colormap);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Returns false is the follower shouldn't be rendered.
|
|
// 'num' can be used to directly specify the follower number, but doing this will not animate it.
|
|
// if a setup_player_t is specified instead, its data will be used to animate the follower sprite.
|
|
static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, INT32 addflags, UINT16 color, setup_player_t *p)
|
|
{
|
|
|
|
spritedef_t *sprdef;
|
|
spriteframe_t *sprframe;
|
|
patch_t *patch;
|
|
INT32 followernum;
|
|
state_t *usestate;
|
|
UINT32 useframe;
|
|
follower_t fl;
|
|
UINT8 *colormap = NULL;
|
|
|
|
if (p != NULL)
|
|
followernum = p->followern;
|
|
else
|
|
followernum = num;
|
|
|
|
// Don't draw if we're outta bounds.
|
|
if (followernum < 0 || followernum >= numfollowers)
|
|
return false;
|
|
|
|
fl = followers[followernum];
|
|
|
|
if (p != NULL)
|
|
{
|
|
usestate = p->follower_state;
|
|
useframe = p->follower_frame;
|
|
}
|
|
else
|
|
{
|
|
usestate = &states[followers[followernum].followstate];
|
|
useframe = usestate->frame & FF_FRAMEMASK;
|
|
}
|
|
|
|
sprdef = &sprites[usestate->sprite];
|
|
|
|
// draw the follower
|
|
|
|
if (useframe >= sprdef->numframes)
|
|
useframe = 0; // frame doesn't exist, we went beyond it... what?
|
|
|
|
sprframe = &sprdef->spriteframes[useframe];
|
|
patch = W_CachePatchNum(sprframe->lumppat[1], PU_CACHE);
|
|
|
|
if (sprframe->flip & 2) // Only for first sprite
|
|
{
|
|
if (addflags & V_FLIP)
|
|
addflags &= ~V_FLIP;
|
|
else
|
|
addflags |= V_FLIP; // This sprite is left/right flipped!
|
|
}
|
|
|
|
fixed_t sine = 0;
|
|
|
|
if (p != NULL)
|
|
{
|
|
sine = FixedMul(fl.bobamp, FINESINE(((FixedMul(4 * M_TAU_FIXED, fl.bobspeed) * p->follower_timer)>>ANGLETOFINESHIFT) & FINEMASK));
|
|
color = K_GetEffectiveFollowerColor(p->followercolor, p->color);
|
|
}
|
|
|
|
colormap = R_GetTranslationColormap(TC_DEFAULT, color, GTC_MENUCACHE);
|
|
V_DrawFixedPatch((x)*FRACUNIT, ((y-12)*FRACUNIT) + sine - fl.zoffs, fl.scale, addflags, patch, colormap);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void M_DrawCharSelectSprite(UINT8 num, INT16 x, INT16 y)
|
|
{
|
|
setup_player_t *p = &setup_player[num];
|
|
UINT8 cnum = p->clonenum;
|
|
|
|
// for p1 alone don't try to preview things on pages that don't exist lol.
|
|
if (p->mdepth == CSSTEP_CHARS && setup_numplayers == 1)
|
|
cnum = setup_page;
|
|
|
|
INT16 skin = setup_chargrid[p->gridx][p->gridy].skinlist[cnum];
|
|
UINT8 color = p->color;
|
|
UINT8 *colormap = R_GetTranslationColormap(skin, color, GTC_MENUCACHE);
|
|
INT32 flags = 0;
|
|
|
|
// Flip for left-side players
|
|
if (!(num & 1))
|
|
flags ^= V_FLIP;
|
|
|
|
if (skin >= 0)
|
|
M_DrawCharacterSprite(x, y, skin, flags, colormap);
|
|
}
|
|
|
|
static void M_DrawCharSelectPreview(UINT8 num)
|
|
{
|
|
INT16 x = 11, y = 5;
|
|
char letter = 'A' + num;
|
|
setup_player_t *p = &setup_player[num];
|
|
|
|
if (num & 1)
|
|
x += 233;
|
|
|
|
if (num > 1)
|
|
y += 99;
|
|
|
|
V_DrawScaledPatch(x, y+6, V_TRANSLUCENT, W_CachePatchName("PREVBACK", PU_CACHE));
|
|
|
|
if (p->mdepth >= CSSTEP_CHARS)
|
|
{
|
|
M_DrawCharSelectSprite(num, x+32, y+75);
|
|
|
|
if (p->mdepth >= CSSTEP_FOLLOWER)
|
|
{
|
|
M_DrawFollowerSprite(x+16, y+75, -1, !(num & 1) ? V_FLIP : 0, 0, p);
|
|
}
|
|
|
|
M_DrawCharSelectCircle(p, x+32, y+64);
|
|
}
|
|
|
|
if ((setup_animcounter/10) & 1 && gamestate == GS_MENU) // Not drawn outside of GS_MENU.
|
|
{
|
|
if (p->mdepth == CSSTEP_NONE && num == setup_numplayers)
|
|
{
|
|
V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PSTART", PU_CACHE));
|
|
}
|
|
else if (p->mdepth >= CSSTEP_READY)
|
|
{
|
|
V_DrawScaledPatch(x+1, y+36, 0, W_CachePatchName("4PREADY", PU_CACHE));
|
|
}
|
|
}
|
|
|
|
V_DrawScaledPatch(x+9, y+2, 0, W_CachePatchName("FILEBACK", PU_CACHE));
|
|
V_DrawScaledPatch(x, y+2, 0, W_CachePatchName(va("CHARSEL%c", letter), PU_CACHE));
|
|
if (p->mdepth > CSSTEP_PROFILE)
|
|
{
|
|
profile_t *pr = PR_GetProfile(p->profilen);
|
|
V_DrawCenteredFileString(x+16+18, y+2, 0, pr->profilename);
|
|
}
|
|
else
|
|
{
|
|
V_DrawFileString(x+16, y+2, 0, "PLAYER");
|
|
}
|
|
|
|
// Profile selection
|
|
if (p->mdepth == CSSTEP_PROFILE)
|
|
{
|
|
INT16 px = x+12;
|
|
INT16 py = y+48 - p->profilen*12;
|
|
UINT8 maxp = PR_GetNumProfiles();
|
|
|
|
UINT8 i = 0;
|
|
UINT8 j;
|
|
|
|
for (i = 0; i < maxp; i++)
|
|
{
|
|
profile_t *pr = PR_GetProfile(i);
|
|
INT16 dist = abs(p->profilen - i);
|
|
INT32 notSelectable = 0;
|
|
SINT8 belongsTo = -1;
|
|
|
|
if (i != PROFILE_GUEST)
|
|
{
|
|
for (j = 0; j < setup_numplayers; j++)
|
|
{
|
|
if (setup_player[j].mdepth > CSSTEP_PROFILE
|
|
&& setup_player[j].profilen == i)
|
|
{
|
|
belongsTo = j;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (belongsTo != -1 && belongsTo != num)
|
|
{
|
|
notSelectable |= V_TRANSLUCENT;
|
|
}
|
|
|
|
if (dist > 2)
|
|
{
|
|
py += 12;
|
|
continue;
|
|
}
|
|
|
|
if (dist == 2)
|
|
{
|
|
V_DrawCenteredFileString(px+26, py, notSelectable, pr->version ? pr->profilename : "NEW");
|
|
V_DrawScaledPatch(px, py, V_TRANSLUCENT, W_CachePatchName("FILEBACK", PU_CACHE));
|
|
}
|
|
else
|
|
{
|
|
V_DrawScaledPatch(px, py, 0, W_CachePatchName("FILEBACK", PU_CACHE));
|
|
|
|
if (i != p->profilen || ((setup_animcounter/10) & 1))
|
|
{
|
|
V_DrawCenteredFileString(px+26, py, notSelectable, pr->version ? pr->profilename : "NEW");
|
|
}
|
|
}
|
|
py += 12;
|
|
}
|
|
|
|
}
|
|
// "Changes?"
|
|
else if (p->mdepth == CSSTEP_ASKCHANGES)
|
|
{
|
|
UINT8 i;
|
|
char choices[2][9] = {"All good", "Change"};
|
|
INT32 xpos = x+8;
|
|
INT32 ypos = y+38;
|
|
|
|
V_DrawFileString(xpos, ypos, 0, "READY?");
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
UINT8 cy = ypos+16 + (i*10);
|
|
|
|
if (p->changeselect == i)
|
|
V_DrawScaledPatch(xpos, cy, 0, W_CachePatchName("M_CURSOR", PU_CACHE));
|
|
|
|
V_DrawThinString(xpos+16, cy, (p->changeselect == i ? highlightflags : 0)|V_6WIDTHSPACE, choices[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void M_DrawCharSelectExplosions(void)
|
|
{
|
|
UINT8 i;
|
|
|
|
for (i = 0; i < CSEXPLOSIONS; i++)
|
|
{
|
|
INT16 quadx, quady;
|
|
UINT8 *colormap;
|
|
UINT8 frame;
|
|
|
|
if (setup_explosions[i].tics == 0 || setup_explosions[i].tics > 5)
|
|
continue;
|
|
|
|
frame = 6 - setup_explosions[i].tics;
|
|
|
|
quadx = 4 * (setup_explosions[i].x / 3);
|
|
quady = 4 * (setup_explosions[i].y / 3);
|
|
|
|
colormap = R_GetTranslationColormap(TC_DEFAULT, setup_explosions[i].color, GTC_MENUCACHE);
|
|
|
|
V_DrawMappedPatch(
|
|
82 + (setup_explosions[i].x*16) + quadx - 6,
|
|
22 + (setup_explosions[i].y*16) + quady - 6,
|
|
0, W_CachePatchName(va("CHCNFRM%d", frame), PU_CACHE),
|
|
colormap
|
|
);
|
|
}
|
|
}
|
|
|
|
#define IDLELEN 8
|
|
#define SELECTLEN (8 + IDLELEN + 7 + IDLELEN)
|
|
|
|
static void M_DrawCharSelectCursor(UINT8 num)
|
|
{
|
|
static const char *idleframes[IDLELEN] = {
|
|
"CHHOV1", "CHHOV1", "CHHOV1", "CHHOV2", "CHHOV1", "CHHOV3", "CHHOV1", "CHHOV2"
|
|
};
|
|
static const char *selectframesa[SELECTLEN] = {
|
|
"CHHOV1", "CHPIKA1", "CHHOV2", "CHPIKA2", "CHHOV3", "CHPIKA3", "CHHOV2", "CHPIKA4",
|
|
"CHHOV1", "CHHOV1", "CHHOV1", "CHHOV2", "CHHOV1", "CHHOV3", "CHHOV1", "CHHOV2",
|
|
"CHPIKA5", "CHHOV2", "CHPIKA6", "CHHOV3", "CHPIKA7", "CHHOV2", "CHPIKA8",
|
|
"CHHOV1", "CHHOV1", "CHHOV1", "CHHOV2", "CHHOV1", "CHHOV3", "CHHOV1", "CHHOV2"
|
|
};
|
|
static const char *selectframesb[SELECTLEN] = {
|
|
NULL, "CHPIKB1", NULL, "CHPIKB2", NULL, "CHPIKB3", NULL, "CHPIKB4",
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
"CHPIKB5", NULL, "CHPIKB6", NULL, "CHPIKB7", NULL, "CHPIKB8",
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
|
};
|
|
|
|
setup_player_t *p = &setup_player[num];
|
|
char letter = 'A' + num;
|
|
UINT8 *colormap;
|
|
INT16 x, y;
|
|
INT16 quadx, quady;
|
|
|
|
quadx = 4 * (p->gridx / 3);
|
|
quady = 4 * (p->gridy / 3);
|
|
|
|
x = 82 + (p->gridx*16) + quadx - 13;
|
|
y = 22 + (p->gridy*16) + quady - 12;
|
|
|
|
// profiles skew the graphic to the right slightly
|
|
if (optionsmenu.profile)
|
|
x += 64;
|
|
|
|
colormap = R_GetTranslationColormap(TC_DEFAULT, (p->color != SKINCOLOR_NONE ? p->color : SKINCOLOR_GREY), GTC_MENUCACHE);
|
|
|
|
if (p->mdepth >= CSSTEP_READY)
|
|
{
|
|
V_DrawMappedPatch(x, y, 0, W_CachePatchName("CHCNFRM0", PU_CACHE), colormap);
|
|
}
|
|
else if (p->mdepth > CSSTEP_CHARS)
|
|
{
|
|
V_DrawMappedPatch(x, y, 0, W_CachePatchName(selectframesa[setup_animcounter % SELECTLEN], PU_CACHE), colormap);
|
|
if (selectframesb[(setup_animcounter-1) % SELECTLEN] != NULL)
|
|
V_DrawMappedPatch(x, y, V_TRANSLUCENT, W_CachePatchName(selectframesb[(setup_animcounter-1) % SELECTLEN], PU_CACHE), colormap);
|
|
}
|
|
else
|
|
{
|
|
V_DrawMappedPatch(x, y, 0, W_CachePatchName(idleframes[setup_animcounter % IDLELEN], PU_CACHE), colormap);
|
|
}
|
|
|
|
if (p->mdepth < CSSTEP_READY)
|
|
V_DrawMappedPatch(x, y, 0, W_CachePatchName(va("CSELH%c", letter), PU_CACHE), colormap);
|
|
}
|
|
|
|
#undef IDLE
|
|
#undef IDLELEN
|
|
#undef SELECTLEN
|
|
|
|
// Draw character profile card.
|
|
// Moved here because in the case of profile edition this is drawn in the charsel menu.
|
|
static void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p)
|
|
{
|
|
setup_player_t *sp = &setup_player[0]; // When editing profile character, we'll always be checking for what P1 is doing.
|
|
patch_t *card = W_CachePatchName("PR_CARD", PU_CACHE);
|
|
patch_t *cardbot = W_CachePatchName("PR_CARDB", PU_CACHE);
|
|
patch_t *pwrlv = W_CachePatchName("PR_PWR", PU_CACHE);
|
|
UINT8 *colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_BLACK, GTC_CACHE);
|
|
INT32 skinnum = -1;
|
|
INT32 powerlevel = -1;
|
|
|
|
char pname[PROFILENAMELEN+1] = "NEW";
|
|
|
|
if (p != NULL && p->version)
|
|
{
|
|
colormap = R_GetTranslationColormap(TC_DEFAULT, PR_GetProfileColor(p), GTC_CACHE);
|
|
strcpy(pname, p->profilename);
|
|
skinnum = R_SkinAvailable(p->skinname);
|
|
powerlevel = p->powerlevels[0]; // Only display race power level.
|
|
}
|
|
|
|
// check setup_player for colormap for the card.
|
|
// we'll need to check again for drawing afterwards unfortunately.
|
|
if (sp->mdepth >= CSSTEP_CHARS)
|
|
colormap = R_GetTranslationColormap(skinnum, sp->color, GTC_MENUCACHE);
|
|
|
|
// Card
|
|
V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, greyedout ? V_TRANSLUCENT : 0, card, colormap);
|
|
|
|
if (greyedout)
|
|
return; // only used for profiles we can't select.
|
|
|
|
// Draw pwlv if we can
|
|
if (powerlevel > -1)
|
|
{
|
|
V_DrawFixedPatch((x+30)*FRACUNIT, (y+84)*FRACUNIT, FRACUNIT, 0, pwrlv, colormap);
|
|
V_DrawCenteredKartString(x+30, y+87, 0, va("%d\n", powerlevel));
|
|
}
|
|
|
|
// check what setup_player is doing in priority.
|
|
if (sp->mdepth >= CSSTEP_CHARS)
|
|
{
|
|
|
|
skinnum = setup_chargrid[sp->gridx][sp->gridy].skinlist[sp->clonenum];
|
|
|
|
if (skinnum >= 0)
|
|
{
|
|
if (M_DrawCharacterSprite(x-22, y+119, skinnum, V_FLIP, colormap))
|
|
V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap);
|
|
|
|
if (sp->mdepth >= CSSTEP_FOLLOWER)
|
|
{
|
|
if (M_DrawFollowerSprite(x-44 +12, y+119, 0, V_FLIP, 0, sp))
|
|
{
|
|
UINT16 col = K_GetEffectiveFollowerColor(sp->followercolor, sp->color);;
|
|
patch_t *ico = W_CachePatchName(followers[sp->followern].icon, PU_CACHE);
|
|
UINT8 *fcolormap;
|
|
|
|
fcolormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE);
|
|
V_DrawMappedPatch(x+14+18, y+66, 0, ico, fcolormap);
|
|
}
|
|
}
|
|
}
|
|
|
|
M_DrawCharSelectCircle(sp, x-22, y+104);
|
|
}
|
|
else if (skinnum > -1) // otherwise, read from profile.
|
|
{
|
|
UINT16 col = K_GetEffectiveFollowerColor(p->followercolor, p->color);;
|
|
UINT8 fln = K_FollowerAvailable(p->follower);
|
|
|
|
if (M_DrawCharacterSprite(x-22, y+119, skinnum, V_FLIP, colormap))
|
|
V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], colormap);
|
|
|
|
if (M_DrawFollowerSprite(x-44 +12, y+119, fln, V_FLIP, col, NULL))
|
|
{
|
|
patch_t *ico = W_CachePatchName(followers[fln].icon, PU_CACHE);
|
|
UINT8 *fcolormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE);
|
|
V_DrawMappedPatch(x+14+18, y+66, 0, ico, fcolormap);
|
|
}
|
|
}
|
|
|
|
V_DrawCenteredGamemodeString(x, y+24, 0, 0, pname);
|
|
|
|
// Card bottom to overlay the skin preview
|
|
V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, cardbot, colormap);
|
|
|
|
// Profile number & player name
|
|
|
|
if (p != NULL)
|
|
{
|
|
V_DrawProfileNum(x + 37 + 10, y + 131, 0, PR_GetProfileNum(p));
|
|
V_DrawCenteredThinString(x, y + 151, V_GRAYMAP|V_6WIDTHSPACE, p->playername);
|
|
}
|
|
}
|
|
|
|
void M_DrawCharacterSelect(void)
|
|
{
|
|
UINT8 i, j, k;
|
|
UINT8 priority = 0;
|
|
INT16 quadx, quady;
|
|
INT16 skin;
|
|
INT32 basex = optionsmenu.profile != NULL ? 64 : 0;
|
|
|
|
if (setup_numplayers > 0)
|
|
{
|
|
priority = setup_animcounter % setup_numplayers;
|
|
}
|
|
|
|
// We have to loop twice -- first time to draw the drop shadows, a second time to draw the icons.
|
|
for (i = 0; i < 9; i++)
|
|
{
|
|
for (j = 0; j < 9; j++)
|
|
{
|
|
skin = setup_chargrid[i][j].skinlist[setup_page];
|
|
quadx = 4 * (i / 3);
|
|
quady = 4 * (j / 3);
|
|
|
|
// Here's a quick little cheat to save on drawing time!
|
|
// Don't draw a shadow if it'll get covered by another icon
|
|
if ((i % 3 < 2) && (j % 3 < 2))
|
|
{
|
|
if ((setup_chargrid[i+1][j].skinlist[setup_page] != -1)
|
|
&& (setup_chargrid[i][j+1].skinlist[setup_page] != -1)
|
|
&& (setup_chargrid[i+1][j+1].skinlist[setup_page] != -1))
|
|
continue;
|
|
}
|
|
|
|
if (skin != -1)
|
|
V_DrawScaledPatch(basex+ 82 + (i*16) + quadx + 1, 22 + (j*16) + quady + 1, 0, W_CachePatchName("ICONBACK", PU_CACHE));
|
|
}
|
|
}
|
|
|
|
// Draw this inbetween. These drop shadows should be covered by the stat graph, but the icons shouldn't.
|
|
V_DrawScaledPatch(basex+ 3, 2, 0, W_CachePatchName((optionsmenu.profile ? "PR_STGRPH" : "STATGRPH"), PU_CACHE));
|
|
|
|
// Draw the icons now
|
|
for (i = 0; i < 9; i++)
|
|
{
|
|
for (j = 0; j < 9; j++)
|
|
{
|
|
for (k = 0; k < setup_numplayers; k++)
|
|
{
|
|
if (setup_player[k].gridx == i && setup_player[k].gridy == j)
|
|
break; // k == setup_numplayers means no one has it selected
|
|
}
|
|
|
|
skin = setup_chargrid[i][j].skinlist[setup_page];
|
|
quadx = 4 * (i / 3);
|
|
quady = 4 * (j / 3);
|
|
|
|
if (skin != -1)
|
|
{
|
|
UINT8 *colormap;
|
|
|
|
if (k == setup_numplayers)
|
|
colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GREY, GTC_MENUCACHE);
|
|
else
|
|
colormap = R_GetTranslationColormap(skin, skins[skin].prefcolor, GTC_MENUCACHE);
|
|
|
|
V_DrawMappedPatch(basex + 82 + (i*16) + quadx, 22 + (j*16) + quady, 0, faceprefix[skin][FACE_RANK], colormap);
|
|
|
|
// draw dot if there are more alts behind there!
|
|
if (setup_page+1 < setup_chargrid[i][j].numskins)
|
|
V_DrawScaledPatch(basex + 82 + (i*16) + quadx, 22 + (j*16) + quady + 11, 0, W_CachePatchName("ALTSDOT", PU_CACHE));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Explosions when you've made your final selection
|
|
M_DrawCharSelectExplosions();
|
|
|
|
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
|
|
{
|
|
// Draw a preview for each player
|
|
if (optionsmenu.profile == NULL)
|
|
{
|
|
M_DrawCharSelectPreview(i);
|
|
}
|
|
else if (i == 0)
|
|
{
|
|
M_DrawProfileCard(optionsmenu.optx, optionsmenu.opty, false, optionsmenu.profile);
|
|
}
|
|
|
|
if (i >= setup_numplayers)
|
|
continue;
|
|
|
|
// Draw the cursors
|
|
if (i != priority)
|
|
M_DrawCharSelectCursor(i);
|
|
}
|
|
|
|
if (setup_numplayers > 0)
|
|
{
|
|
// Draw the priority player over the other ones
|
|
M_DrawCharSelectCursor(priority);
|
|
}
|
|
}
|
|
|
|
// DIFFICULTY SELECT
|
|
// This is a mix of K_DrawKartGamemodeMenu and the generic menu drawer depending on what we need.
|
|
// This is only ever used here (I hope because this is starting to pile up on hacks to look like the old m_menu.c lol...)
|
|
|
|
void M_DrawRaceDifficulty(void)
|
|
{
|
|
UINT8 n = currentMenu->numitems-4;
|
|
patch_t *box = W_CachePatchName("M_DBOX", PU_CACHE);
|
|
|
|
INT32 i;
|
|
INT32 x = 120;
|
|
INT32 y = 48;
|
|
|
|
M_DrawMenuTooltips();
|
|
|
|
// Draw the box for difficulty...
|
|
V_DrawFixedPatch((111 + 48*menutransition.tics)*FRACUNIT, 33*FRACUNIT, FRACUNIT, 0, box, NULL);
|
|
|
|
if (menutransition.tics)
|
|
{
|
|
x += 48 * menutransition.tics;
|
|
}
|
|
|
|
for (i = 0; i < currentMenu->numitems; i++)
|
|
{
|
|
if (i >= n)
|
|
{
|
|
|
|
x = GM_STARTX + (GM_XOFFSET * 5 / 2);
|
|
y = GM_STARTY + (GM_YOFFSET * 5 / 2);
|
|
|
|
if (i < currentMenu->numitems-1)
|
|
{
|
|
x -= GM_XOFFSET;
|
|
y -= GM_YOFFSET;
|
|
}
|
|
|
|
|
|
if (menutransition.tics)
|
|
{
|
|
x += 48 * menutransition.tics;
|
|
}
|
|
}
|
|
|
|
switch (currentMenu->menuitems[i].status & IT_DISPLAY)
|
|
{
|
|
// This is HACKY......
|
|
|
|
case IT_STRING2:
|
|
{
|
|
|
|
INT32 f = (i == itemOn) ? highlightflags : 0;
|
|
|
|
V_DrawString(140 + 48*menutransition.tics, y, f, currentMenu->menuitems[i].text);
|
|
|
|
if (currentMenu->menuitems[i].status & IT_CVAR)
|
|
{
|
|
// implicitely we'll only take care of normal cvars
|
|
INT32 cx = 260 + 48*menutransition.tics;
|
|
consvar_t *cv = currentMenu->menuitems[i].itemaction.cvar;
|
|
|
|
V_DrawCenteredString(cx, y, f, cv->string);
|
|
|
|
if (i == itemOn)
|
|
{
|
|
|
|
INT32 w = V_StringWidth(cv->string, 0)/2;
|
|
|
|
V_DrawCharacter(cx - 10 - w - (skullAnimCounter/5), y, '\x1C' | highlightflags, false); // left arrow
|
|
V_DrawCharacter(cx + w + 2 + (skullAnimCounter/5), y, '\x1D' | highlightflags, false); // right arrow
|
|
}
|
|
}
|
|
|
|
y += 10;
|
|
|
|
break;
|
|
}
|
|
|
|
case IT_STRING:
|
|
{
|
|
|
|
UINT8 *colormap = NULL;
|
|
|
|
if (i == itemOn)
|
|
{
|
|
colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE);
|
|
}
|
|
else
|
|
{
|
|
colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_MOSS, GTC_CACHE);
|
|
}
|
|
|
|
|
|
if (currentMenu->menuitems[i].status & IT_CVAR)
|
|
{
|
|
|
|
INT32 fx = (x - 48*menutransition.tics);
|
|
INT32 centx = fx + (320-fx)/2 + (menutransition.tics*48); // undo the menutransition movement to redo it here otherwise the text won't move at the same speed lole.
|
|
|
|
// implicitely we'll only take care of normal consvars
|
|
consvar_t *cv = currentMenu->menuitems[i].itemaction.cvar;
|
|
|
|
V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, W_CachePatchName("MENUSHRT", PU_CACHE), colormap);
|
|
V_DrawCenteredGamemodeString(centx, y - 3, V_ALLOWLOWERCASE, colormap, cv->string);
|
|
|
|
if (i == itemOn)
|
|
{
|
|
patch_t *arr_r = W_CachePatchName("GM_ARRL", PU_CACHE);
|
|
patch_t *arr_l = W_CachePatchName("GM_ARRR", PU_CACHE);
|
|
|
|
V_DrawFixedPatch((centx-54 - arr_r->width - (skullAnimCounter/5))*FRACUNIT, (y-3)*FRACUNIT, FRACUNIT, 0, arr_r, colormap);
|
|
V_DrawFixedPatch((centx+54 + (skullAnimCounter/5))*FRACUNIT, (y-3)*FRACUNIT, FRACUNIT, 0, arr_l, colormap);
|
|
}
|
|
|
|
}
|
|
else // not a cvar
|
|
{
|
|
V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, 0, W_CachePatchName("MENUPLTR", PU_CACHE), colormap);
|
|
V_DrawGamemodeString(x + 16, y - 3, V_ALLOWLOWERCASE, colormap, currentMenu->menuitems[i].text);
|
|
}
|
|
x += GM_XOFFSET;
|
|
y += GM_YOFFSET;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// LEVEL SELECT
|
|
|
|
static void M_DrawCupPreview(INT16 y, cupheader_t *cup)
|
|
{
|
|
UINT8 i;
|
|
const INT16 pad = ((vid.width/vid.dupx) - BASEVIDWIDTH)/2;
|
|
INT16 x = -(cupgrid.previewanim % 82) - pad;
|
|
|
|
V_DrawFill(0, y, BASEVIDWIDTH, 54, 31);
|
|
|
|
if (cup && (cup->unlockrequired == -1 || unlockables[cup->unlockrequired].unlocked))
|
|
{
|
|
i = (cupgrid.previewanim / 82) % cup->numlevels;
|
|
while (x < BASEVIDWIDTH + pad)
|
|
{
|
|
lumpnum_t lumpnum;
|
|
patch_t *PictureOfLevel;
|
|
|
|
lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cup->levellist[i]+1)));
|
|
if (lumpnum != LUMPERROR)
|
|
PictureOfLevel = W_CachePatchNum(lumpnum, PU_CACHE);
|
|
else
|
|
PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE);
|
|
|
|
V_DrawSmallScaledPatch(x + 1, y+2, 0, PictureOfLevel);
|
|
i = (i+1) % cup->numlevels;
|
|
x += 82;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
patch_t *st = W_CachePatchName(va("PREVST0%d", (cupgrid.previewanim % 4) + 1), PU_CACHE);
|
|
while (x < BASEVIDWIDTH)
|
|
{
|
|
V_DrawScaledPatch(x+1, y+2, 0, st);
|
|
x += 82;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void M_DrawCupTitle(INT16 y, cupheader_t *cup)
|
|
{
|
|
V_DrawScaledPatch(0, y, 0, W_CachePatchName("MENUHINT", PU_CACHE));
|
|
|
|
if (cup)
|
|
{
|
|
boolean unlocked = (cup->unlockrequired == -1 || unlockables[cup->unlockrequired].unlocked);
|
|
UINT8 *colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GREY, GTC_MENUCACHE);
|
|
patch_t *icon = W_CachePatchName(cup->icon, PU_CACHE);
|
|
const char *str = (unlocked ? va("%s Cup", cup->name) : "???");
|
|
INT16 offset = V_LSTitleLowStringWidth(str, 0) / 2;
|
|
|
|
V_DrawLSTitleLowString(BASEVIDWIDTH/2 - offset, y+6, 0, str);
|
|
|
|
if (unlocked)
|
|
{
|
|
V_DrawMappedPatch(BASEVIDWIDTH/2 - offset - 24, y+5, 0, icon, colormap);
|
|
V_DrawMappedPatch(BASEVIDWIDTH/2 + offset + 3, y+5, 0, icon, colormap);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (currentMenu == &PLAY_LevelSelectDef)
|
|
V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, y+6, 0, va("%s Mode", Gametype_Names[levellist.newgametype]));
|
|
}
|
|
}
|
|
|
|
void M_DrawCupSelect(void)
|
|
{
|
|
UINT8 i, j;
|
|
cupheader_t *cup = kartcupheaders;
|
|
|
|
while (cup)
|
|
{
|
|
if (cup->id == CUPMENU_CURSORID)
|
|
break;
|
|
cup = cup->next;
|
|
}
|
|
|
|
for (i = 0; i < CUPMENU_COLUMNS; i++)
|
|
{
|
|
for (j = 0; j < CUPMENU_ROWS; j++)
|
|
{
|
|
UINT8 id = (i + (j * CUPMENU_COLUMNS)) + (cupgrid.pageno * (CUPMENU_COLUMNS * CUPMENU_ROWS));
|
|
cupheader_t *iconcup = kartcupheaders;
|
|
patch_t *patch = NULL;
|
|
INT16 x, y;
|
|
INT16 icony = 7;
|
|
|
|
while (iconcup)
|
|
{
|
|
if (iconcup->id == id)
|
|
break;
|
|
iconcup = iconcup->next;
|
|
}
|
|
|
|
if (!iconcup)
|
|
break;
|
|
|
|
/*if (iconcup->emeraldnum == 0)
|
|
patch = W_CachePatchName("CUPMON3A", PU_CACHE);
|
|
else*/ if (iconcup->emeraldnum > 7)
|
|
{
|
|
patch = W_CachePatchName("CUPMON2A", PU_CACHE);
|
|
icony = 5;
|
|
}
|
|
else
|
|
patch = W_CachePatchName("CUPMON1A", PU_CACHE);
|
|
|
|
x = 14 + (i*42);
|
|
y = 20 + (j*44) - (30*menutransition.tics);
|
|
|
|
V_DrawScaledPatch(x, y, 0, patch);
|
|
|
|
if (iconcup->unlockrequired != -1 && !unlockables[iconcup->unlockrequired].unlocked)
|
|
{
|
|
patch_t *st = W_CachePatchName(va("ICONST0%d", (cupgrid.previewanim % 4) + 1), PU_CACHE);
|
|
V_DrawScaledPatch(x + 8, y + icony, 0, st);
|
|
}
|
|
else
|
|
{
|
|
V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName(iconcup->icon, PU_CACHE));
|
|
V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName("CUPBOX", PU_CACHE));
|
|
}
|
|
}
|
|
}
|
|
|
|
V_DrawScaledPatch(14 + (cupgrid.x*42) - 4,
|
|
20 + (cupgrid.y*44) - 1 - (24*menutransition.tics),
|
|
0, W_CachePatchName("CUPCURS", PU_CACHE)
|
|
);
|
|
|
|
M_DrawCupPreview(146 + (24*menutransition.tics), cup);
|
|
M_DrawCupTitle(120 - (24*menutransition.tics), cup);
|
|
}
|
|
|
|
static void M_DrawHighLowLevelTitle(INT16 x, INT16 y, INT16 map)
|
|
{
|
|
char word1[22];
|
|
char word2[22];
|
|
UINT8 word1len = 0;
|
|
UINT8 word2len = 0;
|
|
INT16 x2 = x;
|
|
UINT8 i;
|
|
|
|
if (!mapheaderinfo[map] || !mapheaderinfo[map]->lvlttl[0])
|
|
return;
|
|
|
|
if (mapheaderinfo[map]->zonttl[0])
|
|
{
|
|
boolean one = true;
|
|
boolean two = true;
|
|
|
|
for (i = 0; i < 22; i++)
|
|
{
|
|
if (!one && !two)
|
|
break;
|
|
|
|
if (mapheaderinfo[map]->lvlttl[i] && one)
|
|
{
|
|
word1[word1len] = mapheaderinfo[map]->lvlttl[i];
|
|
word1len++;
|
|
}
|
|
else
|
|
one = false;
|
|
|
|
if (mapheaderinfo[map]->zonttl[i] && two)
|
|
{
|
|
word2[word2len] = mapheaderinfo[map]->zonttl[i];
|
|
word2len++;
|
|
}
|
|
else
|
|
two = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
boolean donewithone = false;
|
|
|
|
for (i = 0; i < 22; i++)
|
|
{
|
|
if (!mapheaderinfo[map]->lvlttl[i])
|
|
break;
|
|
|
|
if (mapheaderinfo[map]->lvlttl[i] == ' ')
|
|
{
|
|
if (!donewithone)
|
|
{
|
|
donewithone = true;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (donewithone)
|
|
{
|
|
word2[word2len] = mapheaderinfo[map]->lvlttl[i];
|
|
word2len++;
|
|
}
|
|
else
|
|
{
|
|
word1[word1len] = mapheaderinfo[map]->lvlttl[i];
|
|
word1len++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mapheaderinfo[map]->actnum)
|
|
{
|
|
word2[word2len] = ' ';
|
|
word2len++;
|
|
|
|
word2[word2len] = '0' + mapheaderinfo[map]->actnum;
|
|
word2len++;
|
|
}
|
|
|
|
word1[word1len] = '\0';
|
|
word2[word2len] = '\0';
|
|
|
|
{
|
|
char addlen[3];
|
|
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
if (!word1[i])
|
|
break;
|
|
|
|
addlen[i] = word1[i];
|
|
}
|
|
|
|
addlen[i] = '\0';
|
|
x2 += V_LSTitleLowStringWidth(addlen, 0);
|
|
}
|
|
|
|
|
|
if (word1len)
|
|
V_DrawLSTitleHighString(x, y, 0, word1);
|
|
if (word2len)
|
|
V_DrawLSTitleLowString(x2, y+28, 0, word2);
|
|
}
|
|
|
|
static void M_DrawLevelSelectBlock(INT16 x, INT16 y, INT16 map, boolean redblink, boolean greyscale)
|
|
{
|
|
lumpnum_t lumpnum;
|
|
patch_t *PictureOfLevel;
|
|
UINT8 *colormap = NULL;
|
|
|
|
if (greyscale)
|
|
colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GREY, GTC_MENUCACHE);
|
|
|
|
lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(map+1)));
|
|
if (lumpnum != LUMPERROR)
|
|
PictureOfLevel = W_CachePatchNum(lumpnum, PU_CACHE);
|
|
else
|
|
PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE);
|
|
|
|
if (redblink)
|
|
V_DrawScaledPatch(3+x, y, 0, W_CachePatchName("LVLSEL2", PU_CACHE));
|
|
else
|
|
V_DrawScaledPatch(3+x, y, 0, W_CachePatchName("LVLSEL", PU_CACHE));
|
|
|
|
V_DrawSmallMappedPatch(9+x, y+6, 0, PictureOfLevel, colormap);
|
|
M_DrawHighLowLevelTitle(98+x, y+8, map);
|
|
}
|
|
|
|
void M_DrawLevelSelect(void)
|
|
{
|
|
INT16 i;
|
|
INT16 start = M_GetFirstLevelInList(levellist.newgametype);
|
|
INT16 map = start;
|
|
INT16 t = (64*menutransition.tics), tay = 0;
|
|
INT16 y = 80 - (12 * levellist.y);
|
|
boolean tatransition = ((menutransition.startmenu == &PLAY_TimeAttackDef || menutransition.endmenu == &PLAY_TimeAttackDef) && menutransition.tics);
|
|
|
|
if (tatransition)
|
|
{
|
|
t = -t;
|
|
tay = t/2;
|
|
}
|
|
|
|
for (i = 0; i < M_CountLevelsToShowInList(levellist.newgametype); i++)
|
|
{
|
|
INT16 lvlx = t, lvly = y;
|
|
|
|
while (!M_CanShowLevelInList(map, levellist.newgametype) && map < NUMMAPS)
|
|
map++;
|
|
|
|
if (map >= NUMMAPS)
|
|
break;
|
|
|
|
if (i == levellist.cursor && tatransition)
|
|
{
|
|
lvlx = 0;
|
|
lvly = max(2, y+tay);
|
|
}
|
|
|
|
M_DrawLevelSelectBlock(lvlx, lvly, map,
|
|
(i == levellist.cursor && (((skullAnimCounter / 4) & 1) || tatransition)),
|
|
(i != levellist.cursor)
|
|
);
|
|
|
|
y += 72;
|
|
map++;
|
|
}
|
|
|
|
M_DrawCupTitle(tay, levellist.selectedcup);
|
|
}
|
|
|
|
void M_DrawTimeAttack(void)
|
|
{
|
|
INT16 map = levellist.choosemap;
|
|
INT16 t = (48*menutransition.tics);
|
|
INT16 leftedge = 149+t+16;
|
|
INT16 rightedge = 149+t+155;
|
|
INT16 opty = 140;
|
|
INT32 w;
|
|
lumpnum_t lumpnum;
|
|
UINT8 i;
|
|
consvar_t *cv;
|
|
|
|
M_DrawLevelSelectBlock(0, 2, map, true, false);
|
|
|
|
//V_DrawFill(24-t, 82, 100, 100, 36); // size test
|
|
|
|
V_DrawScaledPatch(149+t, 70, 0, W_CachePatchName("BESTTIME", PU_CACHE));
|
|
|
|
if (currentMenu == &PLAY_TimeAttackDef)
|
|
{
|
|
lumpnum = W_CheckNumForName(va("%sR", G_BuildMapName(map+1)));
|
|
if (lumpnum != LUMPERROR)
|
|
V_DrawScaledPatch(24-t, 82, 0, W_CachePatchNum(lumpnum, PU_CACHE));
|
|
|
|
V_DrawRightAlignedString(rightedge-12, 82, highlightflags, "BEST LAP:");
|
|
K_drawKartTimestamp(0, 162+t, 88, 0, 2);
|
|
|
|
V_DrawRightAlignedString(rightedge-12, 112, highlightflags, "BEST TIME:");
|
|
K_drawKartTimestamp(0, 162+t, 118, map, 1);
|
|
}
|
|
else
|
|
opty = 80;
|
|
|
|
for (i = 0; i < currentMenu->numitems; i++)
|
|
{
|
|
UINT32 f = (i == itemOn) ? highlightflags : 0;
|
|
|
|
switch (currentMenu->menuitems[i].status & IT_DISPLAY)
|
|
{
|
|
|
|
case IT_HEADERTEXT:
|
|
|
|
V_DrawString(leftedge, opty, highlightflags, currentMenu->menuitems[i].text);
|
|
opty += 10;
|
|
break;
|
|
|
|
case IT_STRING:
|
|
|
|
if (i >= currentMenu->numitems-1)
|
|
V_DrawRightAlignedString(rightedge, opty, f, currentMenu->menuitems[i].text);
|
|
else
|
|
V_DrawString(leftedge, opty, f, currentMenu->menuitems[i].text);
|
|
opty += 10;
|
|
|
|
// Cvar specific handling
|
|
|
|
if (currentMenu->menuitems[i].status & IT_CVAR)
|
|
{
|
|
cv = currentMenu->menuitems[i].itemaction.cvar;
|
|
|
|
w = V_StringWidth(cv->string, 0);
|
|
V_DrawString(leftedge, opty, f, cv->string);
|
|
if (i == itemOn)
|
|
{
|
|
V_DrawCharacter(leftedge - 10 - (skullAnimCounter/5), opty, '\x1C' | f, false); // left arrow
|
|
V_DrawCharacter(leftedge + w + 2+ (skullAnimCounter/5), opty, '\x1D' | f, false); // right arrow
|
|
}
|
|
opty += 10;
|
|
}
|
|
|
|
break;
|
|
case IT_SPACE:
|
|
opty += 4;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// This draws the options of a given menu in a fashion specific to the multiplayer option select screen (host game / server browser etc)
|
|
// First argument is the menu to draw the options from, the 2nd one is a table that contains which option is to be "extendded"
|
|
// Format for each option: {extended? (only used by the ticker), max extension (likewise), # of lines to extend}
|
|
|
|
// NOTE: This is pretty rigid and only intended for use with the multiplayer options menu which has *3* choices.
|
|
|
|
static void M_MPOptDrawer(menu_t *m, INT16 extend[3][3])
|
|
{
|
|
// This is a copypaste of the generic gamemode menu code with a few changes.
|
|
// TODO: Allow specific options to "expand" into smaller ones.
|
|
|
|
patch_t *buttback = W_CachePatchName("M_PLAT2", PU_CACHE);
|
|
INT32 i, x = 132, y = 32; // Dirty magic numbers for now but they work out.
|
|
|
|
for (i = 0; i < m->numitems; i++)
|
|
{
|
|
|
|
switch (m->menuitems[i].status & IT_DISPLAY)
|
|
{
|
|
case IT_STRING:
|
|
{
|
|
UINT8 *colormap = NULL;
|
|
INT16 j = 0;
|
|
|
|
if ((currentMenu == m && i == itemOn) || extend[i][0]) // Selected / unfolded
|
|
{
|
|
colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE);
|
|
}
|
|
else
|
|
{
|
|
colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_MOSS, GTC_CACHE);
|
|
}
|
|
|
|
if (extend[i][2])
|
|
{
|
|
for (j=0; j < extend[i][2]/2; j++)
|
|
{
|
|
// Draw rectangles that look like the current selected item starting from the top of the actual selection graphic and going up to where it's supposed to go.
|
|
// With colour 169 (that's the index of the shade of black the plague colourization gives us. ...No I don't like using a magic number either.
|
|
V_DrawFill(x + (extend[i][2]/2) - j - (buttback->width/2), (y + extend[i][2]) - (2*j), 225, 2, 169);
|
|
}
|
|
}
|
|
V_DrawFixedPatch((x + (extend[i][2]/2)) *FRACUNIT, (y + extend[i][2])*FRACUNIT, FRACUNIT, 0, buttback, colormap);
|
|
V_DrawCenteredGamemodeString(x, y - 3, V_ALLOWLOWERCASE, colormap, m->menuitems[i].text);
|
|
}
|
|
break;
|
|
}
|
|
|
|
x += GM_XOFFSET;
|
|
y += GM_YOFFSET + extend[i][2];
|
|
}
|
|
}
|
|
|
|
// Draws the EGGA CHANNEL background.
|
|
static void M_DrawEggaChannel(void)
|
|
{
|
|
patch_t *background = W_CachePatchName("M_EGGACH", PU_CACHE);
|
|
|
|
V_DrawFill(0, 0, 999, 999, 25);
|
|
V_DrawFixedPatch(160<<FRACBITS, 104<<FRACBITS, FRACUNIT, 0, background, NULL);
|
|
V_DrawVhsEffect(false); // VHS the background! (...sorry OGL my love)
|
|
}
|
|
|
|
// Multiplayer mode option select
|
|
void M_DrawMPOptSelect(void)
|
|
{
|
|
M_DrawEggaChannel();
|
|
M_DrawMenuTooltips();
|
|
M_MPOptDrawer(&PLAY_MP_OptSelectDef, mpmenu.modewinextend);
|
|
}
|
|
|
|
// Multiplayer mode option select: HOST GAME!
|
|
// A rehash of the generic menu drawer adapted to fit into that cramped space. ...A small sacrifice for utility
|
|
void M_DrawMPHost(void)
|
|
{
|
|
|
|
patch_t *gobutt = W_CachePatchName("M_BUTTGO", PU_CACHE); // I'm very mature
|
|
INT32 xp = 40, yp = 64, i = 0, w = 0; // Starting position for the text drawing.
|
|
M_DrawMPOptSelect(); // Draw the Multiplayer option select menu first
|
|
|
|
// Now draw our host options...
|
|
for (i = 0; i < currentMenu->numitems; i++)
|
|
{
|
|
|
|
if (i == currentMenu->numitems-1)
|
|
{
|
|
|
|
UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_MOSS, GTC_CACHE);
|
|
if (i == itemOn)
|
|
colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE);
|
|
|
|
// Ideally we'd calculate this but it's not worth it for a 1-off menu probably.....
|
|
V_DrawFixedPatch(202<<FRACBITS, 100<<FRACBITS, FRACUNIT, 0, gobutt, colormap);
|
|
V_DrawCenteredGamemodeString(202 + (gobutt->width/2), 100 -3, V_ALLOWLOWERCASE, colormap, currentMenu->menuitems[i].text);
|
|
}
|
|
else
|
|
{
|
|
switch (currentMenu->menuitems[i].status & IT_DISPLAY)
|
|
{
|
|
case IT_STRING:
|
|
{
|
|
V_DrawString(xp, yp, V_ALLOWLOWERCASE | (i == itemOn ? highlightflags : 0), currentMenu->menuitems[i].text);
|
|
|
|
// Cvar specific handling
|
|
switch (currentMenu->menuitems[i].status & IT_TYPE)
|
|
{
|
|
case IT_CVAR:
|
|
{
|
|
consvar_t *cv = currentMenu->menuitems[i].itemaction.cvar;
|
|
switch (currentMenu->menuitems[i].status & IT_CVARTYPE)
|
|
{
|
|
case IT_CV_STRING:
|
|
V_DrawThinString(xp + 96, yp, V_ALLOWLOWERCASE, cv->string);
|
|
if (skullAnimCounter < 4 && i == itemOn)
|
|
V_DrawCharacter(xp + 93 + V_ThinStringWidth(cv->string, 0), yp +1, '_' | 0x80, false);
|
|
|
|
break;
|
|
|
|
default:
|
|
w = V_StringWidth(cv->string, 0);
|
|
V_DrawString(xp + 138 - w, yp, ((cv->flags & CV_CHEAT) && !CV_IsSetToDefault(cv) ? warningflags : highlightflags), cv->string);
|
|
if (i == itemOn)
|
|
{
|
|
V_DrawCharacter(xp + 138 - 10 - w - (skullAnimCounter/5), yp, '\x1C' | highlightflags, false); // left arrow
|
|
V_DrawCharacter(xp + 138 + 2 + (skullAnimCounter/5), yp, '\x1D' | highlightflags, false); // right arrow
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
xp += 5;
|
|
yp += 11;
|
|
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// Multiplayer mode option select: JOIN BY IP
|
|
// Once again we'll copypaste 1 bit of the generic menu handler we used for hosting but we only need it for IT_CV_STRING since that's all we got :)
|
|
// (I don't like duplicating code but I rather this than some horrible all-in-one function with too many options...)
|
|
void M_DrawMPJoinIP(void)
|
|
{
|
|
|
|
//patch_t *minibutt = W_CachePatchName("M_SBUTT", PU_CACHE);
|
|
// There is no such things as mini butts, only thick thighs to rest your head on.
|
|
//patch_t *minigo = W_CachePatchName("M_SGO", PU_CACHE);
|
|
patch_t *typebar = W_CachePatchName("M_TYPEB", PU_CACHE);
|
|
|
|
//UINT8 *colormap = NULL;
|
|
UINT8 *colormapc = NULL;
|
|
|
|
INT32 xp = 73, yp = 133, i = 0; // Starting position for the text drawing.
|
|
M_DrawMPOptSelect(); // Draw the Multiplayer option select menu first
|
|
|
|
|
|
// Now draw our host options...
|
|
for (i = 0; i < currentMenu->numitems; i++)
|
|
{
|
|
|
|
switch (currentMenu->menuitems[i].status & IT_DISPLAY)
|
|
{
|
|
case IT_STRING:
|
|
{
|
|
|
|
char str[MAXSTRINGLENGTH];
|
|
strcpy(str, currentMenu->menuitems[i].text);
|
|
|
|
// The last 3 options of this menu are to be the joined IP addresses...
|
|
if (currentMenu->numitems - i <= NUMLOGIP)
|
|
{
|
|
UINT8 index = NUMLOGIP - (currentMenu->numitems - i);
|
|
if (strlen(joinedIPlist[index][1])) // Try drawing server name
|
|
strcpy(str, joinedIPlist[index][1]);
|
|
else if (strlen(joinedIPlist[index][0])) // If that fails, get the address
|
|
strcpy(str, joinedIPlist[index][0]);
|
|
else
|
|
strcpy(str, "---"); // If that fails too then there's nothing!
|
|
}
|
|
|
|
V_DrawString(xp, yp, V_ALLOWLOWERCASE | ((i == itemOn || currentMenu->menuitems[i].status & IT_SPACE) ? highlightflags : 0), str);
|
|
|
|
// Cvar specific handling
|
|
switch (currentMenu->menuitems[i].status & IT_TYPE)
|
|
{
|
|
case IT_CVAR:
|
|
{
|
|
consvar_t *cv = currentMenu->menuitems[i].itemaction.cvar;
|
|
switch (currentMenu->menuitems[i].status & IT_CVARTYPE)
|
|
{
|
|
case IT_CV_STRING:
|
|
|
|
//colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_MOSS, GTC_CACHE);
|
|
colormapc = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE);
|
|
|
|
V_DrawFixedPatch((xp + 20)<<FRACBITS, (yp-3)<<FRACBITS, FRACUNIT, 0, typebar, colormapc); // Always consider that this is selected otherwise it clashes.
|
|
V_DrawThinString(xp + 26, yp-1, V_ALLOWLOWERCASE, cv->string);
|
|
if (skullAnimCounter < 4 && i == itemOn)
|
|
V_DrawCharacter(xp + 24 + V_ThinStringWidth(cv->string, 0), yp, '_' | 0x80, false);
|
|
|
|
/*// On this specific menu the only time we'll ever see this is for the connect by IP typefield.
|
|
// Draw the small GO button here (and the text which is a separate graphic)
|
|
V_DrawFixedPatch((xp + 20 + typebar->width -4)<<FRACBITS, (yp-3)<<FRACBITS, FRACUNIT, 0, minibutt, i == itemOn ? colormapc : colormap);
|
|
V_DrawFixedPatch((xp + 20 + typebar->width -4 + (minibutt->width/2))<<FRACBITS, (yp-3-5)<<FRACBITS, FRACUNIT, 0, minigo, i == itemOn ? colormapc : NULL);*/
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
xp += 5;
|
|
yp += 11;
|
|
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// Multiplayer room select
|
|
void M_DrawMPRoomSelect(void)
|
|
{
|
|
// Greyscale colormaps for the option that's not selected's background
|
|
UINT8 *colormap_l = NULL;
|
|
UINT8 *colormap_r = NULL;
|
|
|
|
patch_t *bg_l = W_CachePatchName("BG_MPS21", PU_CACHE);
|
|
patch_t *bg_r = W_CachePatchName("BG_MPS22", PU_CACHE);
|
|
|
|
patch_t *split = W_CachePatchName("MPSPLIT1", PU_CACHE);
|
|
|
|
patch_t *butt1[] = {W_CachePatchName("MP_B1", PU_CACHE), W_CachePatchName("MP_B12", PU_CACHE)};
|
|
patch_t *butt2[] = {W_CachePatchName("MP_B2", PU_CACHE), W_CachePatchName("MP_B22", PU_CACHE)};
|
|
|
|
patch_t *scrollp[] = {W_CachePatchName("MP_SCR1", PU_CACHE), W_CachePatchName("MP_SCR2", PU_CACHE)};
|
|
patch_t *drawp = scrollp[mpmenu.room];
|
|
|
|
fixed_t scrollposx[] = {(BASEVIDWIDTH/4)<<FRACBITS, (BASEVIDWIDTH/2 + BASEVIDWIDTH/4)<<FRACBITS};
|
|
UINT8 i;
|
|
INT32 soffy = 0;
|
|
|
|
if (mpmenu.room)
|
|
colormap_l = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GREY, GTC_CACHE);
|
|
else
|
|
colormap_r = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GREY, GTC_CACHE);
|
|
|
|
// Draw the 2 sides of the background
|
|
V_DrawFixedPatch(0, 0, FRACUNIT, 0, bg_l, colormap_l);
|
|
V_DrawFixedPatch(0, 0, FRACUNIT, 0, bg_r, colormap_r);
|
|
|
|
|
|
// Draw the black split:
|
|
V_DrawFixedPatch(160<<FRACBITS, 0, FRACUNIT, 0, split, NULL);
|
|
|
|
// Vertical scrolling stuff
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
V_DrawFixedPatch(scrollposx[mpmenu.room], ((-((mpmenu.ticker*2) % drawp->height)) + soffy)*FRACUNIT , FRACUNIT, V_ADD, drawp, NULL);
|
|
soffy += scrollp[mpmenu.room]->height;
|
|
}
|
|
|
|
|
|
// Draw buttons:
|
|
V_DrawFixedPatch(160<<FRACBITS, 100<<FRACBITS, FRACUNIT, mpmenu.room ? (5<<V_ALPHASHIFT) : 0, butt1[(mpmenu.room) ? 1 : 0], NULL);
|
|
V_DrawFixedPatch(160<<FRACBITS, 100<<FRACBITS, FRACUNIT, (!mpmenu.room) ? (5<<V_ALPHASHIFT) : 0, butt2[(!mpmenu.room) ? 1 : 0], NULL);
|
|
}
|
|
|
|
// SERVER BROWSER
|
|
void M_DrawMPServerBrowser(void)
|
|
{
|
|
patch_t *text1 = W_CachePatchName("MENUBGT1", PU_CACHE);
|
|
patch_t *text2 = W_CachePatchName("MENUBGT2", PU_CACHE);
|
|
|
|
patch_t *raceh = W_CachePatchName("M_SERV1", PU_CACHE);
|
|
patch_t *batlh = W_CachePatchName("M_SERV2", PU_CACHE);
|
|
|
|
patch_t *racehs = W_CachePatchName("M_SERV12", PU_CACHE);
|
|
patch_t *batlhs = W_CachePatchName("M_SERV22", PU_CACHE);
|
|
|
|
fixed_t text1loop = SHORT(text1->height)*FRACUNIT;
|
|
fixed_t text2loop = SHORT(text2->width)*FRACUNIT;
|
|
|
|
const UINT8 startx = 18;
|
|
const UINT8 basey = 56;
|
|
const INT32 starty = basey - 18*mpmenu.scrolln + mpmenu.slide;
|
|
INT32 ypos = 0;
|
|
UINT8 i;
|
|
|
|
// background stuff
|
|
V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("BG_MPS3", PU_CACHE), NULL);
|
|
|
|
V_DrawFixedPatch(0, (BASEVIDHEIGHT + 16) * FRACUNIT, FRACUNIT, V_TRANSLUCENT, W_CachePatchName("MENUBG2", PU_CACHE), NULL);
|
|
|
|
V_DrawFixedPatch(-bgText2Scroll, (BASEVIDHEIGHT-8) * FRACUNIT,
|
|
FRACUNIT, V_TRANSLUCENT, text2, NULL);
|
|
V_DrawFixedPatch(-bgText2Scroll + text2loop, (BASEVIDHEIGHT-8) * FRACUNIT,
|
|
FRACUNIT, V_TRANSLUCENT, text2, NULL);
|
|
|
|
V_DrawFixedPatch(8 * FRACUNIT, -bgText1Scroll,
|
|
FRACUNIT, V_TRANSLUCENT, text1, NULL);
|
|
V_DrawFixedPatch(8 * FRACUNIT, -bgText1Scroll + text1loop,
|
|
FRACUNIT, V_TRANSLUCENT, text1, NULL);
|
|
|
|
// bgText<x>Scroll is already handled by the menu background.
|
|
|
|
// the actual server list.
|
|
for (i = 0; i < serverlistcount; i++)
|
|
{
|
|
|
|
boolean racegt = strcmp(serverlist[i].info.gametypename, "Race") == 0;
|
|
INT32 transflag = 0;
|
|
INT32 basetransflag = 0;
|
|
|
|
if (serverlist[i].info.numberofplayer >= serverlist[i].info.maxplayer)
|
|
basetransflag = 5;
|
|
|
|
/*if (i < mpmenu.servernum)
|
|
// if slide > 0, then we went DOWN in the list and have to fade that server out.
|
|
if (mpmenu.slide > 0)
|
|
transflag = basetransflag + (18-mpmenu.slide)/2;
|
|
else if (mpmenu.slide < 0)
|
|
transflag = 10 - basetransflag - (18-mpmenu.slide)/2;*/
|
|
|
|
transflag = basetransflag;
|
|
|
|
if (transflag >= 0 && transflag < 10)
|
|
{
|
|
transflag = transflag << V_ALPHASHIFT; // shift the translucency flag.
|
|
|
|
if (itemOn == 2 && mpmenu.servernum == i)
|
|
V_DrawFixedPatch(startx*FRACUNIT, (starty + ypos)*FRACUNIT, FRACUNIT, transflag, racegt ? racehs : batlhs, NULL);
|
|
else
|
|
V_DrawFixedPatch(startx*FRACUNIT, (starty + ypos)*FRACUNIT, FRACUNIT, transflag, racegt ? raceh : batlh, NULL);
|
|
|
|
// Server name:
|
|
V_DrawString(startx+11, starty + ypos + 6, V_ALLOWLOWERCASE|transflag, serverlist[i].info.servername);
|
|
|
|
// Ping:
|
|
V_DrawThinString(startx + 191, starty + ypos + 7, V_6WIDTHSPACE|transflag, va("%03d", serverlist[i].info.time));
|
|
|
|
// Playercount
|
|
V_DrawThinString(startx + 214, starty + ypos + 7, V_6WIDTHSPACE|transflag, va("%02d/%02d", serverlist[i].info.numberofplayer, serverlist[i].info.maxplayer));
|
|
|
|
// Power Level
|
|
V_DrawThinString(startx + 248, starty + ypos, V_6WIDTHSPACE|transflag, va("%04d PLv", serverlist[i].info.avgpwrlv));
|
|
|
|
// game speed if applicable:
|
|
if (racegt)
|
|
{
|
|
UINT8 speed = serverlist[i].info.kartvars & SV_SPEEDMASK;
|
|
patch_t *pp = W_CachePatchName(va("M_SDIFF%d", speed), PU_CACHE);
|
|
|
|
V_DrawFixedPatch((startx + 251)*FRACUNIT, (starty + ypos + 9)*FRACUNIT, FRACUNIT, transflag, pp, NULL);
|
|
}
|
|
}
|
|
ypos += SERVERSPACE;
|
|
}
|
|
|
|
// Draw genericmenu ontop!
|
|
V_DrawFill(0, 0, 320, 52, 31);
|
|
V_DrawFill(0, 53, 320, 1, 31);
|
|
V_DrawFill(0, 55, 320, 1, 31);
|
|
|
|
V_DrawCenteredGamemodeString(160, 2, V_ALLOWLOWERCASE, 0, "Server Browser");
|
|
|
|
// normal menu options
|
|
M_DrawGenericMenu();
|
|
|
|
}
|
|
|
|
// OPTIONS MENU
|
|
|
|
// Draws the cogs and also the options background!
|
|
static void M_DrawOptionsCogs(void)
|
|
{
|
|
// 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)
|
|
{
|
|
patch_t *butt = W_CachePatchName("OPT_BUTT", PU_CACHE);
|
|
UINT8 *c = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE);
|
|
|
|
V_DrawFixedPatch((optionsmenu.optx)*FRACUNIT, (optionsmenu.opty)*FRACUNIT, FRACUNIT, 0, butt, c);
|
|
V_DrawCenteredGamemodeString((optionsmenu.optx)-3, (optionsmenu.opty) - 16, V_ALLOWLOWERCASE, c, OPTIONS_MainDef.menuitems[OPTIONS_MainDef.lastOn].text);
|
|
}
|
|
|
|
void M_DrawOptions(void)
|
|
{
|
|
UINT8 i;
|
|
INT32 x = 140 - (48*itemOn) + optionsmenu.offset;
|
|
INT32 y = 70 + optionsmenu.offset;
|
|
patch_t *buttback = W_CachePatchName("OPT_BUTT", PU_CACHE);
|
|
|
|
UINT8 *c = NULL;
|
|
|
|
M_DrawOptionsCogs();
|
|
|
|
for (i=0; i < currentMenu->numitems; i++)
|
|
{
|
|
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|tflag, (i == itemOn ? c : NULL), currentMenu->menuitems[i].text);
|
|
}
|
|
|
|
y += 48;
|
|
x += 48;
|
|
}
|
|
|
|
M_DrawMenuTooltips();
|
|
|
|
if (menutransition.tics)
|
|
M_DrawOptionsMovingButton();
|
|
|
|
}
|
|
|
|
void M_DrawGenericOptions(void)
|
|
{
|
|
INT32 x = currentMenu->x - menutransition.tics*48, y = currentMenu->y, w, i, cursory = 0;
|
|
|
|
M_DrawOptionsCogs();
|
|
M_DrawMenuTooltips();
|
|
M_DrawOptionsMovingButton();
|
|
|
|
for (i = 0; i < currentMenu->numitems; i++)
|
|
{
|
|
if (i == itemOn)
|
|
cursory = y;
|
|
switch (currentMenu->menuitems[i].status & IT_DISPLAY)
|
|
{
|
|
case IT_PATCH:
|
|
if (currentMenu->menuitems[i].patch && currentMenu->menuitems[i].patch[0])
|
|
{
|
|
if (currentMenu->menuitems[i].status & IT_CENTER)
|
|
{
|
|
patch_t *p;
|
|
p = W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE);
|
|
V_DrawScaledPatch((BASEVIDWIDTH - SHORT(p->width))/2, y, 0, p);
|
|
}
|
|
else
|
|
{
|
|
V_DrawScaledPatch(x, y, 0,
|
|
W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE));
|
|
}
|
|
}
|
|
/* FALLTHRU */
|
|
case IT_NOTHING:
|
|
case IT_DYBIGSPACE:
|
|
y += SMALLLINEHEIGHT;
|
|
break;
|
|
#if 0
|
|
case IT_BIGSLIDER:
|
|
M_DrawThermo(x, y, currentMenu->menuitems[i].itemaction.cvar);
|
|
y += LINEHEIGHT;
|
|
break;
|
|
#endif
|
|
case IT_STRING:
|
|
case IT_WHITESTRING:
|
|
if (i == itemOn)
|
|
cursory = y;
|
|
|
|
if ((currentMenu->menuitems[i].status & IT_DISPLAY)==IT_STRING)
|
|
V_DrawString(x, y, 0, currentMenu->menuitems[i].text);
|
|
else
|
|
V_DrawString(x, y, highlightflags, currentMenu->menuitems[i].text);
|
|
|
|
// Cvar specific handling
|
|
switch (currentMenu->menuitems[i].status & IT_TYPE)
|
|
case IT_CVAR:
|
|
{
|
|
consvar_t *cv = currentMenu->menuitems[i].itemaction.cvar;
|
|
switch (currentMenu->menuitems[i].status & IT_CVARTYPE)
|
|
{
|
|
case IT_CV_SLIDER:
|
|
M_DrawSlider(x, y, cv, (i == itemOn));
|
|
case IT_CV_NOPRINT: // color use this
|
|
case IT_CV_INVISSLIDER: // monitor toggles use this
|
|
break;
|
|
case IT_CV_STRING:
|
|
M_DrawTextBox(x, y + 4, MAXSTRINGLENGTH, 1);
|
|
V_DrawString(x + 8, y + 12, V_ALLOWLOWERCASE, cv->string);
|
|
if (skullAnimCounter < 4 && i == itemOn)
|
|
V_DrawCharacter(x + 8 + V_StringWidth(cv->string, 0), y + 12,
|
|
'_' | 0x80, false);
|
|
y += 16;
|
|
break;
|
|
default:
|
|
w = V_StringWidth(cv->string, 0);
|
|
V_DrawString(BASEVIDWIDTH - x - w, y,
|
|
((cv->flags & CV_CHEAT) && !CV_IsSetToDefault(cv) ? warningflags : highlightflags), cv->string);
|
|
if (i == itemOn)
|
|
{
|
|
V_DrawCharacter(BASEVIDWIDTH - x - 10 - w - (skullAnimCounter/5), y,
|
|
'\x1C' | highlightflags, false); // left arrow
|
|
V_DrawCharacter(BASEVIDWIDTH - x + 2 + (skullAnimCounter/5), y,
|
|
'\x1D' | highlightflags, false); // right arrow
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
y += STRINGHEIGHT;
|
|
break;
|
|
case IT_STRING2:
|
|
V_DrawString(x, y, 0, currentMenu->menuitems[i].text);
|
|
/* FALLTHRU */
|
|
case IT_DYLITLSPACE:
|
|
case IT_SPACE:
|
|
y += (currentMenu->menuitems[i].mvar1 ? currentMenu->menuitems[i].mvar1 : SMALLLINEHEIGHT);
|
|
break;
|
|
case IT_GRAYPATCH:
|
|
if (currentMenu->menuitems[i].patch && currentMenu->menuitems[i].patch[0])
|
|
V_DrawMappedPatch(x, y, 0,
|
|
W_CachePatchName(currentMenu->menuitems[i].patch,PU_CACHE), graymap);
|
|
y += (currentMenu->menuitems[i].mvar1 ? currentMenu->menuitems[i].mvar1 : SMALLLINEHEIGHT);
|
|
break;
|
|
case IT_TRANSTEXT:
|
|
if (currentMenu->menuitems[i].mvar1)
|
|
y = currentMenu->y+currentMenu->menuitems[i].mvar1;
|
|
/* FALLTHRU */
|
|
case IT_TRANSTEXT2:
|
|
V_DrawString(x, y, V_TRANSLUCENT, currentMenu->menuitems[i].text);
|
|
y += SMALLLINEHEIGHT;
|
|
break;
|
|
case IT_QUESTIONMARKS:
|
|
if (currentMenu->menuitems[i].mvar1)
|
|
y = currentMenu->y+currentMenu->menuitems[i].mvar1;
|
|
|
|
V_DrawString(x, y, V_TRANSLUCENT|V_OLDSPACING, M_CreateSecretMenuOption(currentMenu->menuitems[i].text));
|
|
y += SMALLLINEHEIGHT;
|
|
break;
|
|
case IT_HEADERTEXT: // draws 16 pixels to the left, in yellow text
|
|
if (currentMenu->menuitems[i].mvar1)
|
|
y = currentMenu->y+currentMenu->menuitems[i].mvar1;
|
|
|
|
V_DrawString(x-16, y, highlightflags, currentMenu->menuitems[i].text);
|
|
y += SMALLLINEHEIGHT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// DRAW THE SKULL CURSOR
|
|
if (((currentMenu->menuitems[itemOn].status & IT_DISPLAY) == IT_PATCH)
|
|
|| ((currentMenu->menuitems[itemOn].status & IT_DISPLAY) == IT_NOTHING))
|
|
{
|
|
V_DrawScaledPatch(x + SKULLXOFF, cursory - 5, 0,
|
|
W_CachePatchName("M_CURSOR", PU_CACHE));
|
|
}
|
|
else
|
|
{
|
|
V_DrawScaledPatch(x - 24, cursory, 0,
|
|
W_CachePatchName("M_CURSOR", PU_CACHE));
|
|
V_DrawString(x, cursory, highlightflags, currentMenu->menuitems[itemOn].text);
|
|
}
|
|
}
|
|
|
|
// *Heavily* simplified version of the generic options menu, cattered only towards erasing profiles.
|
|
void M_DrawProfileErase(void)
|
|
{
|
|
INT32 x = currentMenu->x - menutransition.tics*48, y = currentMenu->y-SMALLLINEHEIGHT, i, cursory = 0;
|
|
UINT8 np = PR_GetNumProfiles();
|
|
|
|
M_DrawOptionsCogs();
|
|
M_DrawMenuTooltips();
|
|
M_DrawOptionsMovingButton();
|
|
|
|
for (i = 1; i < np; i++)
|
|
{
|
|
|
|
profile_t *pr = PR_GetProfile(i);
|
|
|
|
if (i == optionsmenu.eraseprofilen)
|
|
{
|
|
cursory = y;
|
|
V_DrawScaledPatch(x - 24, cursory, 0, W_CachePatchName("M_CURSOR", PU_CACHE));
|
|
}
|
|
|
|
V_DrawString(x, y, i == optionsmenu.eraseprofilen ? highlightflags : 0, va("PRF%03d - %s (%s)", i, pr->profilename, pr->playername));
|
|
y += SMALLLINEHEIGHT;
|
|
}
|
|
}
|
|
|
|
// Draws profile selection
|
|
void M_DrawProfileSelect(void)
|
|
{
|
|
INT32 i;
|
|
const INT32 maxp = PR_GetNumProfiles();
|
|
INT32 x = 160 - optionsmenu.profilen*(128 + 128/8) + optionsmenu.offset;
|
|
INT32 y = 35 + menutransition.tics*32;
|
|
|
|
M_DrawOptionsCogs();
|
|
M_DrawMenuTooltips();
|
|
|
|
// This shouldn't be drawn when a profile is selected as optx/opty are used to move the card.
|
|
if (optionsmenu.profile == NULL && menutransition.tics)
|
|
M_DrawOptionsMovingButton();
|
|
|
|
for (i=0; i < MAXPROFILES+1; i++) // +1 because the default profile does not count
|
|
{
|
|
profile_t *p = PR_GetProfile(i);
|
|
|
|
// don't draw the card in this specific scenario
|
|
if (!(menutransition.tics && optionsmenu.profile != NULL && optionsmenu.profilen == i))
|
|
M_DrawProfileCard(x, y, i > maxp, p);
|
|
|
|
x += 128 + 128/8;
|
|
}
|
|
|
|
// needs to be drawn since it happens on the transition
|
|
if (optionsmenu.profile != NULL)
|
|
M_DrawProfileCard(optionsmenu.optx, optionsmenu.opty, false, optionsmenu.profile);
|
|
|
|
|
|
}
|
|
|
|
// Profile edition menu
|
|
void M_DrawEditProfile(void)
|
|
{
|
|
|
|
INT32 y = 34;
|
|
INT32 x = 145;
|
|
INT32 i;
|
|
|
|
M_DrawOptionsCogs();
|
|
|
|
// Tooltip
|
|
// The text is slightly shifted hence why we don't just use M_DrawMenuTooltips()
|
|
V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUHINT", PU_CACHE), NULL);
|
|
if (currentMenu->menuitems[itemOn].tooltip != NULL)
|
|
{
|
|
V_DrawCenteredThinString(224, 12, V_ALLOWLOWERCASE|V_6WIDTHSPACE, currentMenu->menuitems[itemOn].tooltip);
|
|
}
|
|
|
|
// Draw the menu options...
|
|
for (i = 0; i < currentMenu->numitems; i++)
|
|
{
|
|
|
|
UINT8 *colormap = NULL;
|
|
INT32 tflag = (currentMenu->menuitems[i].status & IT_TRANSTEXT) ? V_TRANSLUCENT : 0;
|
|
|
|
if (i == itemOn)
|
|
colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE);
|
|
|
|
// Background
|
|
V_DrawFill(0, y, 400 - (menutransition.tics*64), 24, itemOn == i ? 169 : 30); // 169 is the plague colourization
|
|
// Text
|
|
V_DrawGamemodeString(x + (menutransition.tics*32), y - 6, V_ALLOWLOWERCASE|tflag, colormap, currentMenu->menuitems[i].text);
|
|
|
|
// Cvar specific handling
|
|
/*switch (currentMenu->menuitems[i].status & IT_TYPE)
|
|
{
|
|
case IT_CVAR:
|
|
{
|
|
consvar_t *cv = currentMenu->menuitems[i].itemaction.cvar;
|
|
switch (currentMenu->menuitems[i].status & IT_CVARTYPE)
|
|
{
|
|
case IT_CV_STRING:
|
|
V_DrawFill(0, y+24, 400 - (menutransition.tics*64), 16, itemOn == i ? 169 : 30); // 169 is the plague colourization
|
|
V_DrawString(x + 8, y + 29, V_ALLOWLOWERCASE, cv->string);
|
|
if (skullAnimCounter < 4 && i == itemOn)
|
|
V_DrawCharacter(x + 8 + V_StringWidth(cv->string, 0), y + 29, '_' | 0x80, false);
|
|
y += 16;
|
|
}
|
|
}
|
|
}*/
|
|
|
|
y += 34;
|
|
}
|
|
|
|
// Finally, draw the card ontop
|
|
if (optionsmenu.profile != NULL)
|
|
{
|
|
M_DrawProfileCard(optionsmenu.optx, optionsmenu.opty, false, optionsmenu.profile);
|
|
}
|
|
}
|
|
|
|
// Controller offsets to center on each button.
|
|
INT16 controlleroffsets[][2] = {
|
|
{0, 0}, // gc_none
|
|
{70, 112}, // gc_up
|
|
{70, 112}, // gc_down
|
|
{70, 112}, // gc_left
|
|
{70, 112}, // gc_right
|
|
{208, 200}, // gc_a
|
|
{237, 181}, // gc_b
|
|
{267, 166}, // gc_c
|
|
{191, 164}, // gc_x
|
|
{215, 149}, // gc_y
|
|
{242, 137}, // gc_z
|
|
{55, 102}, // gc_l
|
|
{253, 102}, // gc_r
|
|
{149, 187}, // gc_start
|
|
};
|
|
|
|
// Controller patches for button presses.
|
|
// {patch if not pressed, patch if pressed}
|
|
// if NULL, draws nothing.
|
|
// reminder that lumpnames can only be 8 chars at most. (+1 for \0)
|
|
|
|
char controllerpresspatch[9][2][9] = {
|
|
{"", "BTP_A"}, // MBT_A
|
|
{"", "BTP_B"}, // MBT_B
|
|
{"", "BTP_C"}, // MBT_C
|
|
{"", "BTP_X"}, // MBT_X
|
|
{"", "BTP_Y"}, // MBT_Y
|
|
{"", "BTP_Z"}, // MBT_Z
|
|
{"BTNP_L", "BTP_L"},// MBT_L
|
|
{"BTNP_R", "BTP_R"},// MBT_R
|
|
{"", "BTP_ST"} // MBT_START
|
|
};
|
|
|
|
|
|
// the control stuff.
|
|
// Dear god.
|
|
void M_DrawProfileControls(void)
|
|
{
|
|
const UINT8 spacing = 34;
|
|
INT32 y = 16 - (optionsmenu.controlscroll*spacing);
|
|
INT32 x = 8;
|
|
INT32 i, j, k;
|
|
const UINT8 pid = 0;
|
|
|
|
M_DrawOptionsCogs();
|
|
|
|
V_DrawScaledPatch(BASEVIDWIDTH*2/3 - optionsmenu.contx, BASEVIDHEIGHT/2 -optionsmenu.conty, 0, W_CachePatchName("PR_CONT", PU_CACHE));
|
|
|
|
// Draw button presses...
|
|
// @TODO: Dpad when we get the sprites for it.
|
|
|
|
for (i = 0; i < 9; i++)
|
|
{
|
|
INT32 bt = 1<<i;
|
|
if (M_MenuButtonHeld(pid, bt))
|
|
{
|
|
if (controllerpresspatch[i][1][0] != '\0')
|
|
V_DrawScaledPatch(BASEVIDWIDTH*2/3 - optionsmenu.contx, BASEVIDHEIGHT/2 -optionsmenu.conty, 0, W_CachePatchName(controllerpresspatch[i][1], PU_CACHE));
|
|
}
|
|
else
|
|
{
|
|
if (controllerpresspatch[i][0][0] != '\0')
|
|
V_DrawScaledPatch(BASEVIDWIDTH*2/3 - optionsmenu.contx, BASEVIDHEIGHT/2 -optionsmenu.conty, 0, W_CachePatchName(controllerpresspatch[i][0], PU_CACHE));
|
|
}
|
|
}
|
|
|
|
if (optionsmenu.trycontroller)
|
|
{
|
|
optionsmenu.tcontx = BASEVIDWIDTH*2/3 - 10;
|
|
optionsmenu.tconty = BASEVIDHEIGHT/2 +70;
|
|
|
|
V_DrawCenteredString(160, 180, highlightflags, va("PRESS NOTHING FOR %d SEC TO GO BACK", optionsmenu.trycontroller/TICRATE));
|
|
return; // Don't draw the rest if we're trying the controller.
|
|
}
|
|
|
|
// Tooltip
|
|
// The text is slightly shifted hence why we don't just use M_DrawMenuTooltips()
|
|
V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUHINT", PU_CACHE), NULL);
|
|
if (currentMenu->menuitems[itemOn].tooltip != NULL)
|
|
{
|
|
V_DrawCenteredThinString(229, 12, V_ALLOWLOWERCASE|V_6WIDTHSPACE, currentMenu->menuitems[itemOn].tooltip);
|
|
}
|
|
|
|
V_DrawFill(0, 0, 138, 200, 31); // Black border
|
|
|
|
// Draw the menu options...
|
|
for (i = 0; i < currentMenu->numitems; i++)
|
|
{
|
|
char buf[256];
|
|
INT32 keys[MAXINPUTMAPPING];
|
|
|
|
// cursor
|
|
if (i == itemOn)
|
|
{
|
|
for (j=0; j < 24; j++)
|
|
V_DrawFill(0, (y)+j, 128+j, 1, 73);
|
|
}
|
|
|
|
switch (currentMenu->menuitems[i].status & IT_DISPLAY)
|
|
{
|
|
case IT_HEADERTEXT:
|
|
V_DrawFill(0, y+17, 124, 1, 0); // underline
|
|
V_DrawString(x, y+8, 0, currentMenu->menuitems[i].text);
|
|
y += spacing;
|
|
break;
|
|
|
|
case IT_STRING:
|
|
V_DrawString(x, y+1, (i == itemOn ? highlightflags : 0), currentMenu->menuitems[i].text);
|
|
y += spacing;
|
|
break;
|
|
|
|
case IT_STRING2:
|
|
{
|
|
boolean drawnpatch = false;
|
|
|
|
if (currentMenu->menuitems[i].patch)
|
|
{
|
|
V_DrawScaledPatch(x+12, y+12, 0, W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE));
|
|
drawnpatch = true;
|
|
}
|
|
else
|
|
V_DrawString(x, y+1, (i == itemOn ? highlightflags : 0), currentMenu->menuitems[i].text);
|
|
|
|
if (currentMenu->menuitems[i].status & IT_CVAR) // not the proper way to check but this menu only has normal onoff cvars.
|
|
{
|
|
INT32 w;
|
|
consvar_t *cv = currentMenu->menuitems[i].itemaction.cvar;
|
|
|
|
w = V_StringWidth(cv->string, 0);
|
|
V_DrawString(x + 12, y + 12, ((cv->flags & CV_CHEAT) && !CV_IsSetToDefault(cv) ? warningflags : highlightflags), cv->string);
|
|
if (i == itemOn)
|
|
{
|
|
V_DrawCharacter(x - (skullAnimCounter/5), y+12, '\x1C' | highlightflags, false); // left arrow
|
|
V_DrawCharacter(x + 12 + w + 2 + (skullAnimCounter/5) , y+12, '\x1D' | highlightflags, false); // right arrow
|
|
}
|
|
}
|
|
else if (currentMenu->menuitems[i].status & IT_CONTROL)
|
|
{
|
|
UINT32 vflags = V_6WIDTHSPACE;
|
|
INT32 gc = currentMenu->menuitems[i].mvar1;
|
|
|
|
UINT8 available = 0, set = 0;
|
|
|
|
// Get userbound controls...
|
|
for (k = 0; k < MAXINPUTMAPPING; k++)
|
|
{
|
|
keys[k] = optionsmenu.tempcontrols[gc][k];
|
|
if (keys[k] == KEY_NULL)
|
|
continue;
|
|
set++;
|
|
if (!G_KeyIsAvailable(keys[k], cv_usejoystick[0].value))
|
|
continue;
|
|
available++;
|
|
};
|
|
|
|
buf[0] = '\0';
|
|
|
|
|
|
// Cool as is this, this doesn't actually help show accurate info because of how some players would set inputs with keyboard and controller at once in a volatile way...
|
|
// @TODO: Address that mess, somehow?
|
|
|
|
// Can't reach any of them?
|
|
/*if (available == 0)
|
|
{
|
|
if (((3*optionsmenu.ticker)/(2*TICRATE)) & 1) // 1.5 seconds
|
|
{
|
|
vflags |= V_ORANGEMAP;
|
|
#ifdef SHOWCONTROLDEFAULT
|
|
if (G_KeyBindIsNecessary(gc))
|
|
{
|
|
// Get the defaults for essential keys.
|
|
// Went through all the trouble of making this look cool,
|
|
// then realised defaulting should only apply to menus.
|
|
// Too much opportunity for confusion if kept.
|
|
for (k = 0; k < MAXINPUTMAPPING; k++)
|
|
{
|
|
keys[k] = gamecontroldefault[gc][k];
|
|
if (keys[k] == KEY_NULL)
|
|
continue;
|
|
available++;
|
|
}
|
|
set = available;
|
|
}
|
|
else if (set)
|
|
#else
|
|
if (!set)
|
|
{
|
|
if (!G_KeyBindIsNecessary(gc))
|
|
vflags = V_REDMAP|V_6WIDTHSPACE;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
strcpy(buf, "CURRENTLY UNAVAILABLE");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
vflags |= V_REDMAP;
|
|
}
|
|
}*/
|
|
|
|
if (buf[0])
|
|
;
|
|
else if (!set)
|
|
strcpy(buf, "\x85NOT BOUND");
|
|
else
|
|
{
|
|
for (k = 0; k < MAXINPUTMAPPING; k++)
|
|
{
|
|
if (keys[k] == KEY_NULL)
|
|
continue;
|
|
|
|
if (k > 0)
|
|
strcat(buf," / ");
|
|
|
|
if (k == 2 && drawnpatch) // hacky...
|
|
strcat(buf, "\n");
|
|
|
|
strcat(buf, G_KeynumToString (keys[k]));
|
|
}
|
|
}
|
|
|
|
// don't shift the text if we didn't draw a patch.
|
|
V_DrawThinString(x + (drawnpatch ? 32 : 0), y + (drawnpatch ? 2 : 12), vflags, buf);
|
|
|
|
// controller dest coords:
|
|
if (itemOn == i && gc > 0 && gc <= gc_start)
|
|
{
|
|
optionsmenu.tcontx = controlleroffsets[gc][0];
|
|
optionsmenu.tconty = controlleroffsets[gc][1];
|
|
}
|
|
}
|
|
|
|
|
|
y += spacing;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Overlay for control binding
|
|
if (optionsmenu.bindcontrol)
|
|
{
|
|
INT16 reversetimer = TICRATE*5 - optionsmenu.bindtimer;
|
|
INT32 fade = reversetimer;
|
|
INT32 ypos;
|
|
|
|
if (fade > 9)
|
|
fade = 9;
|
|
|
|
ypos = (BASEVIDHEIGHT/2) - 4 +16*(9 - fade);
|
|
V_DrawFadeScreen(31, fade);
|
|
|
|
M_DrawTextBox((BASEVIDWIDTH/2) - (120), ypos - 12, 30, 4);
|
|
|
|
V_DrawCenteredString(BASEVIDWIDTH/2, ypos, 0, va("Press key #%d for control", optionsmenu.bindcontrol));
|
|
V_DrawCenteredString(BASEVIDWIDTH/2, ypos +10, 0, va("\"%s\"", currentMenu->menuitems[itemOn].text));
|
|
V_DrawCenteredString(BASEVIDWIDTH/2, ypos +20, highlightflags, va("(WAIT %d SECONDS TO SKIP)", optionsmenu.bindtimer/TICRATE));
|
|
}
|
|
}
|
|
|
|
// Draw the video modes list, a-la-Quake
|
|
void M_DrawVideoModes(void)
|
|
{
|
|
INT32 i, j, row, col;
|
|
|
|
M_DrawOptionsCogs();
|
|
M_DrawMenuTooltips();
|
|
M_DrawOptionsMovingButton();
|
|
|
|
V_DrawCenteredString(BASEVIDWIDTH/2 + menutransition.tics*64, currentMenu->y,
|
|
highlightflags, "Choose mode, reselect to change default");
|
|
|
|
row = 41 + menutransition.tics*64;
|
|
col = currentMenu->y + 14;
|
|
for (i = 0; i < optionsmenu.vidm_nummodes; i++)
|
|
{
|
|
if (i == optionsmenu.vidm_selected)
|
|
V_DrawString(row, col, highlightflags, optionsmenu.modedescs[i].desc);
|
|
// Show multiples of 320x200 as green.
|
|
else
|
|
V_DrawString(row, col, (optionsmenu.modedescs[i].goodratio) ? recommendedflags : 0, optionsmenu.modedescs[i].desc);
|
|
|
|
col += 8;
|
|
if ((i % optionsmenu.vidm_column_size) == (optionsmenu.vidm_column_size-1))
|
|
{
|
|
row += 7*13;
|
|
col = currentMenu->y + 14;
|
|
}
|
|
}
|
|
|
|
if (optionsmenu.vidm_testingmode > 0)
|
|
{
|
|
INT32 testtime = (optionsmenu.vidm_testingmode/TICRATE) + 1;
|
|
|
|
M_CentreText(menutransition.tics*64, currentMenu->y + 75,
|
|
va("Previewing mode %c%dx%d",
|
|
(SCR_IsAspectCorrect(vid.width, vid.height)) ? 0x83 : 0x80,
|
|
vid.width, vid.height));
|
|
M_CentreText(menutransition.tics*64, currentMenu->y + 75+8,
|
|
"Press ENTER again to keep this mode");
|
|
M_CentreText(menutransition.tics*64, currentMenu->y + 75+16,
|
|
va("Wait %d second%s", testtime, (testtime > 1) ? "s" : ""));
|
|
M_CentreText(menutransition.tics*64, currentMenu->y + 75+24,
|
|
"or press ESC to return");
|
|
|
|
}
|
|
else
|
|
{
|
|
M_CentreText(menutransition.tics*64, currentMenu->y + 75,
|
|
va("Current mode is %c%dx%d",
|
|
(SCR_IsAspectCorrect(vid.width, vid.height)) ? 0x83 : 0x80,
|
|
vid.width, vid.height));
|
|
M_CentreText(menutransition.tics*64, currentMenu->y + 75+8,
|
|
va("Default mode is %c%dx%d",
|
|
(SCR_IsAspectCorrect(cv_scr_width.value, cv_scr_height.value)) ? 0x83 : 0x80,
|
|
cv_scr_width.value, cv_scr_height.value));
|
|
|
|
V_DrawCenteredString(BASEVIDWIDTH/2 + menutransition.tics*64, currentMenu->y + 75+16,
|
|
recommendedflags, "Marked modes are recommended.");
|
|
V_DrawCenteredString(BASEVIDWIDTH/2 + menutransition.tics*64, currentMenu->y + 75+24,
|
|
highlightflags, "Other modes may have visual errors.");
|
|
V_DrawCenteredString(BASEVIDWIDTH/2 + menutransition.tics*64, currentMenu->y + 75+32,
|
|
highlightflags, "Larger modes may have performance issues.");
|
|
}
|
|
|
|
// Draw the cursor for the VidMode menu
|
|
i = 41 - 10 + ((optionsmenu.vidm_selected / optionsmenu.vidm_column_size)*7*13) + menutransition.tics*64;
|
|
j = currentMenu->y + 14 + ((optionsmenu.vidm_selected % optionsmenu.vidm_column_size)*8);
|
|
|
|
V_DrawScaledPatch(i - 8, j, 0,
|
|
W_CachePatchName("M_CURSOR", PU_CACHE));
|
|
}
|
|
|
|
// Gameplay Item Tggles:
|
|
tic_t shitsfree = 0;
|
|
|
|
void M_DrawItemToggles(void)
|
|
{
|
|
const INT32 edges = 8;
|
|
const INT32 height = 4;
|
|
const INT32 spacing = 35;
|
|
const INT32 column = itemOn/height;
|
|
//const INT32 row = itemOn%height;
|
|
INT32 leftdraw, rightdraw, totaldraw;
|
|
INT32 x = currentMenu->x + menutransition.tics*64, y = currentMenu->y;
|
|
INT32 onx = 0, ony = 0;
|
|
consvar_t *cv;
|
|
INT32 i, translucent, drawnum;
|
|
|
|
M_DrawOptionsCogs();
|
|
M_DrawMenuTooltips();
|
|
M_DrawOptionsMovingButton();
|
|
|
|
// Find the available space around column
|
|
leftdraw = rightdraw = column;
|
|
totaldraw = 0;
|
|
for (i = 0; (totaldraw < edges*2 && i < edges*4); i++)
|
|
{
|
|
if (rightdraw+1 < (currentMenu->numitems/height)+1)
|
|
{
|
|
rightdraw++;
|
|
totaldraw++;
|
|
}
|
|
if (leftdraw-1 >= 0)
|
|
{
|
|
leftdraw--;
|
|
totaldraw++;
|
|
}
|
|
}
|
|
|
|
for (i = leftdraw; i <= rightdraw; i++)
|
|
{
|
|
INT32 j;
|
|
|
|
for (j = 0; j < height; j++)
|
|
{
|
|
const INT32 thisitem = (i*height)+j;
|
|
|
|
if (thisitem >= currentMenu->numitems)
|
|
break;
|
|
|
|
if (thisitem == itemOn)
|
|
{
|
|
onx = x;
|
|
ony = y;
|
|
y += spacing;
|
|
continue;
|
|
}
|
|
|
|
if (currentMenu->menuitems[thisitem].mvar1 == 0)
|
|
{
|
|
V_DrawScaledPatch(x, y, 0, W_CachePatchName("K_ISBG", PU_CACHE));
|
|
V_DrawScaledPatch(x, y, 0, W_CachePatchName("K_ISTOGL", PU_CACHE));
|
|
y += spacing;
|
|
continue;
|
|
}
|
|
|
|
if (currentMenu->menuitems[thisitem].mvar1 == 255)
|
|
{
|
|
V_DrawScaledPatch(x, y, 0, W_CachePatchName("K_ISBGD", PU_CACHE));
|
|
y += spacing;
|
|
continue;
|
|
}
|
|
|
|
cv = KartItemCVars[currentMenu->menuitems[thisitem].mvar1-1];
|
|
translucent = (cv->value ? 0 : V_TRANSLUCENT);
|
|
|
|
switch (currentMenu->menuitems[thisitem].mvar1)
|
|
{
|
|
case KRITEM_DUALSNEAKER:
|
|
case KRITEM_DUALJAWZ:
|
|
drawnum = 2;
|
|
break;
|
|
case KRITEM_TRIPLESNEAKER:
|
|
case KRITEM_TRIPLEBANANA:
|
|
case KRITEM_TRIPLEORBINAUT:
|
|
drawnum = 3;
|
|
break;
|
|
case KRITEM_QUADORBINAUT:
|
|
drawnum = 4;
|
|
break;
|
|
case KRITEM_TENFOLDBANANA:
|
|
drawnum = 10;
|
|
break;
|
|
default:
|
|
drawnum = 0;
|
|
break;
|
|
}
|
|
|
|
if (cv->value)
|
|
V_DrawScaledPatch(x, y, 0, W_CachePatchName("K_ISBG", PU_CACHE));
|
|
else
|
|
V_DrawScaledPatch(x, y, 0, W_CachePatchName("K_ISBGD", PU_CACHE));
|
|
|
|
if (drawnum != 0)
|
|
{
|
|
V_DrawScaledPatch(x, y, 0, W_CachePatchName("K_ISMUL", PU_CACHE));
|
|
V_DrawScaledPatch(x, y, translucent, W_CachePatchName(K_GetItemPatch(currentMenu->menuitems[thisitem].mvar1, true), PU_CACHE));
|
|
V_DrawString(x+24, y+31, V_ALLOWLOWERCASE|translucent, va("x%d", drawnum));
|
|
}
|
|
else
|
|
V_DrawScaledPatch(x, y, translucent, W_CachePatchName(K_GetItemPatch(currentMenu->menuitems[thisitem].mvar1, true), PU_CACHE));
|
|
|
|
y += spacing;
|
|
}
|
|
|
|
x += spacing;
|
|
y = currentMenu->y;
|
|
}
|
|
|
|
{
|
|
if (currentMenu->menuitems[itemOn].mvar1 == 0)
|
|
{
|
|
V_DrawScaledPatch(onx-1, ony-2, 0, W_CachePatchName("K_ITBG", PU_CACHE));
|
|
V_DrawScaledPatch(onx-1, ony-2, 0, W_CachePatchName("K_ITTOGL", PU_CACHE));
|
|
}
|
|
else if (currentMenu->menuitems[itemOn].mvar1 == 255)
|
|
{
|
|
V_DrawScaledPatch(onx-1, ony-2, 0, W_CachePatchName("K_ITBGD", PU_CACHE));
|
|
if (shitsfree)
|
|
{
|
|
INT32 trans = V_TRANSLUCENT;
|
|
if (shitsfree-1 > TICRATE-5)
|
|
trans = ((10-TICRATE)+shitsfree-1)<<V_ALPHASHIFT;
|
|
else if (shitsfree < 5)
|
|
trans = (10-shitsfree)<<V_ALPHASHIFT;
|
|
V_DrawScaledPatch(onx-1, ony-2, trans, W_CachePatchName("K_ITFREE", PU_CACHE));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cv = KartItemCVars[currentMenu->menuitems[itemOn].mvar1-1];
|
|
translucent = (cv->value ? 0 : V_TRANSLUCENT);
|
|
|
|
switch (currentMenu->menuitems[itemOn].mvar1)
|
|
{
|
|
case KRITEM_DUALSNEAKER:
|
|
case KRITEM_DUALJAWZ:
|
|
drawnum = 2;
|
|
break;
|
|
case KRITEM_TRIPLESNEAKER:
|
|
case KRITEM_TRIPLEBANANA:
|
|
drawnum = 3;
|
|
break;
|
|
case KRITEM_TENFOLDBANANA:
|
|
drawnum = 10;
|
|
break;
|
|
default:
|
|
drawnum = 0;
|
|
break;
|
|
}
|
|
|
|
if (cv->value)
|
|
V_DrawScaledPatch(onx-1, ony-2, 0, W_CachePatchName("K_ITBG", PU_CACHE));
|
|
else
|
|
V_DrawScaledPatch(onx-1, ony-2, 0, W_CachePatchName("K_ITBGD", PU_CACHE));
|
|
|
|
if (drawnum != 0)
|
|
{
|
|
V_DrawScaledPatch(onx-1, ony-2, 0, W_CachePatchName("K_ITMUL", PU_CACHE));
|
|
V_DrawScaledPatch(onx-1, ony-2, translucent, W_CachePatchName(K_GetItemPatch(currentMenu->menuitems[itemOn].mvar1, false), PU_CACHE));
|
|
V_DrawScaledPatch(onx+27, ony+39, translucent, W_CachePatchName("K_ITX", PU_CACHE));
|
|
V_DrawKartString(onx+37, ony+34, translucent, va("%d", drawnum));
|
|
}
|
|
else
|
|
V_DrawScaledPatch(onx-1, ony-2, translucent, W_CachePatchName(K_GetItemPatch(currentMenu->menuitems[itemOn].mvar1, false), PU_CACHE));
|
|
}
|
|
}
|
|
|
|
if (shitsfree)
|
|
shitsfree--;
|
|
}
|
|
|
|
|
|
// EXTRAS:
|
|
// Copypasted from options but separate either way in case we want it to look more unique later down the line.
|
|
void M_DrawExtrasMovingButton(void)
|
|
{
|
|
patch_t *butt = W_CachePatchName("OPT_BUTT", PU_CACHE);
|
|
UINT8 *c = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_CACHE);
|
|
|
|
V_DrawFixedPatch((extrasmenu.extx)*FRACUNIT, (extrasmenu.exty)*FRACUNIT, FRACUNIT, 0, butt, c);
|
|
V_DrawCenteredGamemodeString((extrasmenu.extx)-3, (extrasmenu.exty) - 16, V_ALLOWLOWERCASE, c, EXTRAS_MainDef.menuitems[EXTRAS_MainDef.lastOn].text);
|
|
}
|
|
|
|
void M_DrawExtras(void)
|
|
{
|
|
UINT8 i;
|
|
INT32 x = 140 - (48*itemOn) + extrasmenu.offset;
|
|
INT32 y = 70 + extrasmenu.offset;
|
|
patch_t *buttback = W_CachePatchName("OPT_BUTT", PU_CACHE);
|
|
patch_t *bg = W_CachePatchName("M_XTRABG", PU_CACHE);
|
|
|
|
UINT8 *c = NULL;
|
|
|
|
V_DrawFixedPatch(0, 0, FRACUNIT, 0, bg, NULL);
|
|
|
|
for (i=0; i < currentMenu->numitems; i++)
|
|
{
|
|
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|tflag, (i == itemOn ? c : NULL), currentMenu->menuitems[i].text);
|
|
}
|
|
|
|
y += 48;
|
|
x += 48;
|
|
}
|
|
|
|
M_DrawMenuTooltips();
|
|
|
|
if (menutransition.tics)
|
|
M_DrawExtrasMovingButton();
|
|
|
|
}
|
|
|
|
//
|
|
// INGAME / PAUSE MENUS
|
|
//
|
|
|
|
// PAUSE
|
|
|
|
// PAUSE MAIN MENU
|
|
void M_DrawPause(void)
|
|
{
|
|
|
|
SINT8 i;
|
|
SINT8 itemsdrawn = 0;
|
|
SINT8 countdown = 0;
|
|
INT16 ypos = -50; // Draw 3 items from selected item (y=100 - 3 items spaced by 50 px each... you get the idea.)
|
|
INT16 dypos;
|
|
|
|
INT16 offset = menutransition.tics ? floor(pow(2, (double)menutransition.tics)) : pausemenu.openoffset;
|
|
INT16 arrxpos = 150 + 2*offset; // To draw the background arrow.
|
|
|
|
INT16 j = 0;
|
|
char word1[MAXSTRINGLENGTH];
|
|
INT16 word1len = 0;
|
|
char word2[MAXSTRINGLENGTH];
|
|
INT16 word2len = 0;
|
|
boolean sok = false;
|
|
|
|
patch_t *pausebg = W_CachePatchName("M_STRIPU", PU_CACHE);
|
|
patch_t *vertbg = W_CachePatchName("M_STRIPV", PU_CACHE);
|
|
patch_t *pausetext = W_CachePatchName("M_PAUSET", PU_CACHE);
|
|
|
|
patch_t *arrstart = W_CachePatchName("M_PTIP", PU_CACHE);
|
|
patch_t *arrfill = W_CachePatchName("M_PFILL", PU_CACHE);
|
|
|
|
//V_DrawFadeScreen(0xFF00, 16);
|
|
|
|
// "PAUSED"
|
|
V_DrawFixedPatch(-offset*FRACUNIT, 0, FRACUNIT, V_ADD, pausebg, NULL);
|
|
V_DrawFixedPatch(-offset*FRACUNIT, 0, FRACUNIT, 0, pausetext, NULL);
|
|
|
|
// Vertical Strip:
|
|
V_DrawFixedPatch((230 + offset)<<FRACBITS, 0, FRACUNIT, V_ADD, vertbg, NULL);
|
|
|
|
// Okay that's cool but which icon do we draw first? let's roll back from itemOn!
|
|
// At most we'll draw 7 items, 1 in the center, 3 above, 3 below.
|
|
// Which means... let's count down from itemOn
|
|
for (i = itemOn; countdown < 3; countdown++)
|
|
{
|
|
i--;
|
|
if (i < 0)
|
|
i = currentMenu->numitems-1;
|
|
|
|
while (currentMenu->menuitems[i].status == IT_DISABLED)
|
|
{
|
|
i--;
|
|
|
|
if (i < 0)
|
|
i = currentMenu->numitems-1;
|
|
}
|
|
}
|
|
|
|
// Aaaaand now we can start drawing!
|
|
// Reminder that we set the patches of the options to the description since we're not using that. I'm smart, I know...
|
|
|
|
// Draw the background arrow
|
|
V_DrawFixedPatch(arrxpos<<FRACBITS, 100<<FRACBITS, FRACUNIT, 0, arrstart, NULL);
|
|
|
|
while ((arrxpos - arrfill->width) < BASEVIDWIDTH)
|
|
{
|
|
V_DrawFixedPatch(arrxpos<<FRACBITS, 100<<FRACBITS, FRACUNIT, 0, arrfill, NULL);
|
|
arrxpos += arrfill->width;
|
|
}
|
|
|
|
while (itemsdrawn < 7)
|
|
{
|
|
|
|
switch (currentMenu->menuitems[i].status & IT_DISPLAY)
|
|
{
|
|
case IT_STRING:
|
|
{
|
|
|
|
char iconame[9]; // 8 chars + \0
|
|
patch_t *pp;
|
|
|
|
if (i == itemOn)
|
|
{
|
|
strcpy(iconame, currentMenu->menuitems[i].tooltip);
|
|
iconame[7] = '2'; // Yes this is a stupid hack. Replace the last character with a 2 when we're selecting this graphic.
|
|
|
|
pp = W_CachePatchName(iconame, PU_CACHE);
|
|
}
|
|
else
|
|
pp = W_CachePatchName(currentMenu->menuitems[i].tooltip, PU_CACHE);
|
|
|
|
// 294 - 261 = 33
|
|
// We need to move 33 px in 50 tics which means we move 33/50 = 0.66 px every tic = 2/3 of the offset.
|
|
// trust me i graduated highschool!!!!
|
|
|
|
// Multiply by -1 or 1 depending on whether we're below or above 100 px.
|
|
// This double ternary is awful, yes.
|
|
|
|
dypos = ypos + pausemenu.offset;
|
|
V_DrawFixedPatch( ((i == itemOn ? (294 - pausemenu.offset*2/3 * (dypos > 100 ? 1 : -1)) : 261) + offset) << FRACBITS, (dypos)*FRACUNIT, FRACUNIT, 0, pp, NULL);
|
|
|
|
ypos += 50;
|
|
itemsdrawn++; // We drew that!
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
i++; // Regardless of whether we drew or not, go to the next item in the menu.
|
|
if (i > currentMenu->numitems)
|
|
{
|
|
i = 0;
|
|
while (!(currentMenu->menuitems[i].status & IT_DISPLAY))
|
|
i++;
|
|
|
|
}
|
|
}
|
|
|
|
// Draw the string!
|
|
// ...but first get what we need to get.
|
|
while (currentMenu->menuitems[itemOn].text[j] && j < MAXSTRINGLENGTH)
|
|
{
|
|
char c = currentMenu->menuitems[itemOn].text[j];
|
|
|
|
if (c == ' ' && !sok)
|
|
{
|
|
sok = true;
|
|
j++;
|
|
continue; // We don't care about this :moyai:
|
|
}
|
|
|
|
if (sok)
|
|
{
|
|
word2[word2len] = c;
|
|
word2len++;
|
|
}
|
|
else
|
|
{
|
|
word1[word1len] = c;
|
|
word1len++;
|
|
}
|
|
|
|
j++;
|
|
}
|
|
|
|
word1[word1len] = '\0';
|
|
word2[word2len] = '\0';
|
|
|
|
// If there's no 2nd word, take this opportunity to center this line of text.
|
|
if (word1len)
|
|
V_DrawCenteredLSTitleHighString(220 + offset*2, 75 + (!word2len ? 10 : 0), 0, word1);
|
|
|
|
if (word2len)
|
|
V_DrawCenteredLSTitleLowString(220 + offset*2, 103, 0, word2);
|
|
}
|
|
|
|
tic_t playback_last_menu_interaction_leveltime = 0;
|
|
|
|
void M_DrawPlaybackMenu(void)
|
|
{
|
|
INT16 i;
|
|
patch_t *icon = NULL;
|
|
UINT8 *activemap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GOLD, GTC_MENUCACHE);
|
|
UINT32 transmap = max(0, (INT32)(leveltime - playback_last_menu_interaction_leveltime - 4*TICRATE)) / 5;
|
|
transmap = min(8, transmap) << V_ALPHASHIFT;
|
|
|
|
if (leveltime - playback_last_menu_interaction_leveltime >= 6*TICRATE)
|
|
playback_last_menu_interaction_leveltime = leveltime - 6*TICRATE;
|
|
|
|
// Toggle items
|
|
if (paused && !demo.rewinding)
|
|
{
|
|
PAUSE_PlaybackMenu[playback_pause].status = PAUSE_PlaybackMenu[playback_fastforward].status = PAUSE_PlaybackMenu[playback_rewind].status = IT_DISABLED;
|
|
PAUSE_PlaybackMenu[playback_resume].status = PAUSE_PlaybackMenu[playback_advanceframe].status = PAUSE_PlaybackMenu[playback_backframe].status = IT_CALL|IT_STRING;
|
|
|
|
if (itemOn >= playback_rewind && itemOn <= playback_fastforward)
|
|
itemOn += playback_backframe - playback_rewind;
|
|
}
|
|
else
|
|
{
|
|
PAUSE_PlaybackMenu[playback_pause].status = PAUSE_PlaybackMenu[playback_fastforward].status = PAUSE_PlaybackMenu[playback_rewind].status = IT_CALL|IT_STRING;
|
|
PAUSE_PlaybackMenu[playback_resume].status = PAUSE_PlaybackMenu[playback_advanceframe].status = PAUSE_PlaybackMenu[playback_backframe].status = IT_DISABLED;
|
|
|
|
if (itemOn >= playback_backframe && itemOn <= playback_advanceframe)
|
|
itemOn -= playback_backframe - playback_rewind;
|
|
}
|
|
|
|
if (modeattacking)
|
|
{
|
|
for (i = playback_viewcount; i <= playback_view4; i++)
|
|
PAUSE_PlaybackMenu[i].status = IT_DISABLED;
|
|
|
|
//PAUSE_PlaybackMenu[playback_moreoptions].mvar1 = 72;
|
|
//PAUSE_PlaybackMenu[playback_quit].mvar1 = 88;
|
|
PAUSE_PlaybackMenu[playback_quit].mvar1 = 72;
|
|
|
|
//currentMenu->x = BASEVIDWIDTH/2 - 52;
|
|
currentMenu->x = BASEVIDWIDTH/2 - 44;
|
|
}
|
|
else
|
|
{
|
|
PAUSE_PlaybackMenu[playback_viewcount].status = IT_ARROWS|IT_STRING;
|
|
|
|
for (i = 0; i <= splitscreen; i++)
|
|
PAUSE_PlaybackMenu[playback_view1+i].status = IT_ARROWS|IT_STRING;
|
|
for (i = splitscreen+1; i < 4; i++)
|
|
PAUSE_PlaybackMenu[playback_view1+i].status = IT_DISABLED;
|
|
|
|
//PAUSE_PlaybackMenu[playback_moreoptions].mvar1 = 156;
|
|
//PAUSE_PlaybackMenu[playback_quit].mvar1 = 172;
|
|
PAUSE_PlaybackMenu[playback_quit].mvar1 = 156;
|
|
|
|
//currentMenu->x = BASEVIDWIDTH/2 - 94;
|
|
currentMenu->x = BASEVIDWIDTH/2 - 88;
|
|
}
|
|
|
|
// wip
|
|
//M_DrawTextBox(currentMenu->x-68, currentMenu->y-7, 15, 15);
|
|
//M_DrawCenteredMenu();
|
|
|
|
for (i = 0; i < currentMenu->numitems; i++)
|
|
{
|
|
UINT8 *inactivemap = NULL;
|
|
|
|
if (i >= playback_view1 && i <= playback_view4)
|
|
{
|
|
if (modeattacking) continue;
|
|
|
|
if (splitscreen >= i - playback_view1)
|
|
{
|
|
INT32 ply = displayplayers[i - playback_view1];
|
|
|
|
icon = faceprefix[players[ply].skin][FACE_RANK];
|
|
if (i != itemOn)
|
|
inactivemap = R_GetTranslationColormap(players[ply].skin, players[ply].skincolor, GTC_MENUCACHE);
|
|
}
|
|
else if (currentMenu->menuitems[i].patch && W_CheckNumForName(currentMenu->menuitems[i].patch) != LUMPERROR)
|
|
icon = W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE);
|
|
}
|
|
else if (currentMenu->menuitems[i].status == IT_DISABLED)
|
|
continue;
|
|
else if (currentMenu->menuitems[i].patch && W_CheckNumForName(currentMenu->menuitems[i].patch) != LUMPERROR)
|
|
icon = W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE);
|
|
|
|
if ((i == playback_fastforward && cv_playbackspeed.value > 1) || (i == playback_rewind && demo.rewinding))
|
|
V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].mvar1, currentMenu->y, V_SNAPTOTOP, icon, R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_JAWZ, GTC_MENUCACHE));
|
|
else
|
|
V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].mvar1, currentMenu->y, V_SNAPTOTOP, icon, (i == itemOn) ? activemap : inactivemap);
|
|
|
|
if (i == itemOn)
|
|
{
|
|
V_DrawCharacter(currentMenu->x + currentMenu->menuitems[i].mvar1 + 4, currentMenu->y + 14,
|
|
'\x1A' | V_SNAPTOTOP|highlightflags, false);
|
|
|
|
V_DrawCenteredString(BASEVIDWIDTH/2, currentMenu->y + 18, V_SNAPTOTOP|V_ALLOWLOWERCASE, currentMenu->menuitems[i].text);
|
|
|
|
if ((currentMenu->menuitems[i].status & IT_TYPE) == IT_ARROWS)
|
|
{
|
|
char *str;
|
|
|
|
if (!(i == playback_viewcount && splitscreen == 3))
|
|
V_DrawCharacter(BASEVIDWIDTH/2 - 4, currentMenu->y + 28 - (skullAnimCounter/5),
|
|
'\x1A' | V_SNAPTOTOP|highlightflags, false); // up arrow
|
|
|
|
if (!(i == playback_viewcount && splitscreen == 0))
|
|
V_DrawCharacter(BASEVIDWIDTH/2 - 4, currentMenu->y + 48 + (skullAnimCounter/5),
|
|
'\x1B' | V_SNAPTOTOP|highlightflags, false); // down arrow
|
|
|
|
switch (i)
|
|
{
|
|
case playback_viewcount:
|
|
str = va("%d", splitscreen+1);
|
|
break;
|
|
|
|
case playback_view1:
|
|
case playback_view2:
|
|
case playback_view3:
|
|
case playback_view4:
|
|
str = player_names[displayplayers[i - playback_view1]]; // 0 to 3
|
|
break;
|
|
|
|
default: // shouldn't ever be reached but whatever
|
|
continue;
|
|
}
|
|
|
|
V_DrawCenteredString(BASEVIDWIDTH/2, currentMenu->y + 38, V_SNAPTOTOP|V_ALLOWLOWERCASE|highlightflags, str);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// replay hut...
|
|
// ...dear lord this is messy, but Ima be real I ain't fixing this.
|
|
|
|
#define SCALEDVIEWWIDTH (vid.width/vid.dupx)
|
|
#define SCALEDVIEWHEIGHT (vid.height/vid.dupy)
|
|
void M_DrawReplayHutReplayInfo(void)
|
|
{
|
|
lumpnum_t lumpnum;
|
|
patch_t *patch;
|
|
UINT8 *colormap;
|
|
INT32 x, y, w, h;
|
|
|
|
switch (extrasmenu.demolist[dir_on[menudepthleft]].type)
|
|
{
|
|
case MD_NOTLOADED:
|
|
V_DrawCenteredString(160, 40, V_SNAPTOTOP, "Loading replay information...");
|
|
break;
|
|
|
|
case MD_INVALID:
|
|
V_DrawCenteredString(160, 40, V_SNAPTOTOP|warningflags, "This replay cannot be played.");
|
|
break;
|
|
|
|
case MD_SUBDIR:
|
|
break; // Can't think of anything to draw here right now
|
|
|
|
case MD_OUTDATED:
|
|
V_DrawThinString(17, 64, V_SNAPTOTOP|V_ALLOWLOWERCASE|V_TRANSLUCENT|highlightflags, "Recorded on an outdated version.");
|
|
/* FALLTHRU */
|
|
default:
|
|
// Draw level stuff
|
|
x = 15; y = 15;
|
|
|
|
// A 160x100 image of the level as entry MAPxxP
|
|
//CONS_Printf("%d %s\n", extrasmenu.demolist[dir_on[menudepthleft]].map, G_BuildMapName(extrasmenu.demolist[dir_on[menudepthleft]].map));
|
|
lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(extrasmenu.demolist[dir_on[menudepthleft]].map)));
|
|
if (lumpnum != LUMPERROR)
|
|
patch = W_CachePatchNum(lumpnum, PU_CACHE);
|
|
else
|
|
patch = W_CachePatchName("M_NOLVL", PU_CACHE);
|
|
|
|
if (!(extrasmenu.demolist[dir_on[menudepthleft]].kartspeed & DF_ENCORE))
|
|
V_DrawSmallScaledPatch(x, y, V_SNAPTOTOP, patch);
|
|
else
|
|
{
|
|
w = SHORT(patch->width);
|
|
h = SHORT(patch->height);
|
|
V_DrawSmallScaledPatch(x+(w>>1), y, V_SNAPTOTOP|V_FLIP, patch);
|
|
|
|
{
|
|
static angle_t rubyfloattime = 0;
|
|
const fixed_t rubyheight = FINESINE(rubyfloattime>>ANGLETOFINESHIFT);
|
|
V_DrawFixedPatch((x+(w>>2))<<FRACBITS, ((y+(h>>2))<<FRACBITS) - (rubyheight<<1), FRACUNIT, V_SNAPTOTOP, W_CachePatchName("RUBYICON", PU_CACHE), NULL);
|
|
rubyfloattime += (ANGLE_MAX/NEWTICRATE);
|
|
}
|
|
}
|
|
|
|
x += 85;
|
|
|
|
if (mapheaderinfo[extrasmenu.demolist[dir_on[menudepthleft]].map-1])
|
|
V_DrawString(x, y, V_SNAPTOTOP, G_BuildMapTitle(extrasmenu.demolist[dir_on[menudepthleft]].map));
|
|
else
|
|
V_DrawString(x, y, V_SNAPTOTOP|V_ALLOWLOWERCASE|V_TRANSLUCENT, "Level is not loaded.");
|
|
|
|
if (extrasmenu.demolist[dir_on[menudepthleft]].numlaps)
|
|
V_DrawThinString(x, y+9, V_SNAPTOTOP|V_ALLOWLOWERCASE, va("(%d laps)", extrasmenu.demolist[dir_on[menudepthleft]].numlaps));
|
|
|
|
V_DrawString(x, y+20, V_SNAPTOTOP|V_ALLOWLOWERCASE, extrasmenu.demolist[dir_on[menudepthleft]].gametype == GT_RACE ?
|
|
va("Race (%s speed)", kartspeed_cons_t[(extrasmenu.demolist[dir_on[menudepthleft]].kartspeed & ~DF_ENCORE) + 1].strvalue) :
|
|
"Battle Mode");
|
|
|
|
if (!extrasmenu.demolist[dir_on[menudepthleft]].standings[0].ranking)
|
|
{
|
|
// No standings were loaded!
|
|
V_DrawString(x, y+39, V_SNAPTOTOP|V_ALLOWLOWERCASE|V_TRANSLUCENT, "No standings available.");
|
|
|
|
|
|
break;
|
|
}
|
|
|
|
V_DrawThinString(x, y+29, V_SNAPTOTOP|highlightflags, "WINNER");
|
|
V_DrawString(x+38, y+30, V_SNAPTOTOP|V_ALLOWLOWERCASE, extrasmenu.demolist[dir_on[menudepthleft]].standings[0].name);
|
|
|
|
if (extrasmenu.demolist[dir_on[menudepthleft]].gametype == GT_RACE)
|
|
{
|
|
V_DrawThinString(x, y+39, V_SNAPTOTOP|highlightflags, "TIME");
|
|
V_DrawRightAlignedString(x+84, y+40, V_SNAPTOTOP, va("%d'%02d\"%02d",
|
|
G_TicsToMinutes(extrasmenu.demolist[dir_on[menudepthleft]].standings[0].timeorscore, true),
|
|
G_TicsToSeconds(extrasmenu.demolist[dir_on[menudepthleft]].standings[0].timeorscore),
|
|
G_TicsToCentiseconds(extrasmenu.demolist[dir_on[menudepthleft]].standings[0].timeorscore)
|
|
));
|
|
}
|
|
else
|
|
{
|
|
V_DrawThinString(x, y+39, V_SNAPTOTOP|highlightflags, "SCORE");
|
|
V_DrawString(x+32, y+40, V_SNAPTOTOP, va("%d", extrasmenu.demolist[dir_on[menudepthleft]].standings[0].timeorscore));
|
|
}
|
|
|
|
// Character face!
|
|
|
|
// Lat: 08/06/2020: For some reason missing skins have their value set to 255 (don't even ask me why I didn't write this)
|
|
// and for an even STRANGER reason this passes the first check below, so we're going to make sure that the skin here ISN'T 255 before we do anything stupid.
|
|
|
|
if (extrasmenu.demolist[dir_on[menudepthleft]].standings[0].skin != 0xFF)
|
|
{
|
|
patch = faceprefix[extrasmenu.demolist[dir_on[menudepthleft]].standings[0].skin][FACE_WANTED];
|
|
colormap = R_GetTranslationColormap(
|
|
extrasmenu.demolist[dir_on[menudepthleft]].standings[0].skin,
|
|
extrasmenu.demolist[dir_on[menudepthleft]].standings[0].color,
|
|
GTC_MENUCACHE);
|
|
}
|
|
else
|
|
{
|
|
patch = W_CachePatchName("M_NOWANT", PU_CACHE);
|
|
colormap = R_GetTranslationColormap(
|
|
TC_RAINBOW,
|
|
extrasmenu.demolist[dir_on[menudepthleft]].standings[0].color,
|
|
GTC_MENUCACHE);
|
|
}
|
|
|
|
V_DrawMappedPatch(BASEVIDWIDTH-15 - SHORT(patch->width), y+20, V_SNAPTOTOP, patch, colormap);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
void M_DrawReplayHut(void)
|
|
{
|
|
INT32 x, y, cursory = 0;
|
|
INT16 i;
|
|
INT16 replaylistitem = currentMenu->numitems-2;
|
|
boolean processed_one_this_frame = false;
|
|
|
|
static UINT16 replayhutmenuy = 0;
|
|
|
|
M_DrawEggaChannel();
|
|
|
|
// Draw menu choices
|
|
x = currentMenu->x;
|
|
y = currentMenu->y;
|
|
|
|
if (itemOn > replaylistitem)
|
|
{
|
|
itemOn = replaylistitem;
|
|
dir_on[menudepthleft] = sizedirmenu-1;
|
|
extrasmenu.replayScrollTitle = 0; extrasmenu.replayScrollDelay = TICRATE; extrasmenu.replayScrollDir = 1;
|
|
}
|
|
else if (itemOn < replaylistitem)
|
|
{
|
|
dir_on[menudepthleft] = 0;
|
|
extrasmenu.replayScrollTitle = 0; extrasmenu.replayScrollDelay = TICRATE; extrasmenu.replayScrollDir = 1;
|
|
}
|
|
|
|
if (itemOn == replaylistitem)
|
|
{
|
|
INT32 maxy;
|
|
// Scroll menu items if needed
|
|
cursory = y + currentMenu->menuitems[replaylistitem].mvar1 + dir_on[menudepthleft]*10;
|
|
maxy = y + currentMenu->menuitems[replaylistitem].mvar1 + sizedirmenu*10;
|
|
|
|
if (cursory > maxy - 20)
|
|
cursory = maxy - 20;
|
|
|
|
if (cursory - replayhutmenuy > SCALEDVIEWHEIGHT-50)
|
|
replayhutmenuy += (cursory-SCALEDVIEWHEIGHT-replayhutmenuy + 51)/2;
|
|
else if (cursory - replayhutmenuy < 110)
|
|
replayhutmenuy += (max(0, cursory-110)-replayhutmenuy - 1)/2;
|
|
}
|
|
else
|
|
replayhutmenuy /= 2;
|
|
|
|
y -= replayhutmenuy;
|
|
|
|
// Draw static menu items
|
|
for (i = 0; i < replaylistitem; i++)
|
|
{
|
|
INT32 localy = y + currentMenu->menuitems[i].mvar1;
|
|
|
|
if (localy < 65)
|
|
continue;
|
|
|
|
if (i == itemOn)
|
|
cursory = localy;
|
|
|
|
if ((currentMenu->menuitems[i].status & IT_DISPLAY)==IT_STRING)
|
|
V_DrawString(x, localy, V_SNAPTOTOP|V_SNAPTOLEFT, currentMenu->menuitems[i].text);
|
|
else
|
|
V_DrawString(x, localy, V_SNAPTOTOP|V_SNAPTOLEFT|highlightflags, currentMenu->menuitems[i].text);
|
|
}
|
|
|
|
y += currentMenu->menuitems[replaylistitem].mvar1;
|
|
|
|
for (i = 0; i < (INT16)sizedirmenu; i++)
|
|
{
|
|
INT32 localy = y+i*10;
|
|
INT32 localx = x;
|
|
|
|
if (localy < 65)
|
|
continue;
|
|
if (localy >= SCALEDVIEWHEIGHT)
|
|
break;
|
|
|
|
if (extrasmenu.demolist[i].type == MD_NOTLOADED && !processed_one_this_frame)
|
|
{
|
|
processed_one_this_frame = true;
|
|
G_LoadDemoInfo(&extrasmenu.demolist[i]);
|
|
}
|
|
|
|
if (extrasmenu.demolist[i].type == MD_SUBDIR)
|
|
{
|
|
localx += 8;
|
|
V_DrawScaledPatch(x - 4, localy, V_SNAPTOTOP|V_SNAPTOLEFT, W_CachePatchName(dirmenu[i][DIR_TYPE] == EXT_UP ? "M_RBACK" : "M_RFLDR", PU_CACHE));
|
|
}
|
|
|
|
if (itemOn == replaylistitem && i == (INT16)dir_on[menudepthleft])
|
|
{
|
|
cursory = localy;
|
|
|
|
if (extrasmenu.replayScrollDelay)
|
|
extrasmenu.replayScrollDelay--;
|
|
else if (extrasmenu.replayScrollDir > 0)
|
|
{
|
|
if (extrasmenu.replayScrollTitle < (V_StringWidth(extrasmenu.demolist[i].title, 0) - (SCALEDVIEWWIDTH - (x<<1)))<<1)
|
|
extrasmenu.replayScrollTitle++;
|
|
else
|
|
{
|
|
extrasmenu.replayScrollDelay = TICRATE;
|
|
extrasmenu.replayScrollDir = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (extrasmenu.replayScrollTitle > 0)
|
|
extrasmenu.replayScrollTitle--;
|
|
else
|
|
{
|
|
extrasmenu.replayScrollDelay = TICRATE;
|
|
extrasmenu.replayScrollDir = 1;
|
|
}
|
|
}
|
|
|
|
V_DrawString(localx - (extrasmenu.replayScrollTitle>>1), localy, V_SNAPTOTOP|V_SNAPTOLEFT|highlightflags|V_ALLOWLOWERCASE, extrasmenu.demolist[i].title);
|
|
}
|
|
else
|
|
V_DrawString(localx, localy, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, extrasmenu.demolist[i].title);
|
|
}
|
|
|
|
// Draw scrollbar
|
|
y = sizedirmenu*10 + currentMenu->menuitems[replaylistitem].mvar1 + 30;
|
|
if (y > SCALEDVIEWHEIGHT-80)
|
|
{
|
|
V_DrawFill(BASEVIDWIDTH-4, 75, 4, SCALEDVIEWHEIGHT-80, V_SNAPTOTOP|V_SNAPTORIGHT|159);
|
|
V_DrawFill(BASEVIDWIDTH-3, 76 + (SCALEDVIEWHEIGHT-80) * replayhutmenuy / y, 2, (((SCALEDVIEWHEIGHT-80) * (SCALEDVIEWHEIGHT-80))-1) / y - 1, V_SNAPTOTOP|V_SNAPTORIGHT|149);
|
|
}
|
|
|
|
// Draw the cursor
|
|
V_DrawScaledPatch(currentMenu->x - 24, cursory, V_SNAPTOTOP|V_SNAPTOLEFT,
|
|
W_CachePatchName("M_CURSOR", PU_CACHE));
|
|
V_DrawString(currentMenu->x, cursory, V_SNAPTOTOP|V_SNAPTOLEFT|highlightflags, currentMenu->menuitems[itemOn].text);
|
|
|
|
// Now draw some replay info!
|
|
V_DrawFill(10, 10, 300, 60, V_SNAPTOTOP|159);
|
|
|
|
if (itemOn == replaylistitem)
|
|
{
|
|
M_DrawReplayHutReplayInfo();
|
|
}
|
|
}
|
|
|
|
void M_DrawReplayStartMenu(void)
|
|
{
|
|
const char *warning;
|
|
UINT8 i;
|
|
|
|
M_DrawEggaChannel();
|
|
M_DrawGenericMenu();
|
|
|
|
#define STARTY 62-(extrasmenu.replayScrollTitle>>1)
|
|
// Draw rankings beyond first
|
|
for (i = 1; i < MAXPLAYERS && extrasmenu.demolist[dir_on[menudepthleft]].standings[i].ranking; i++)
|
|
{
|
|
patch_t *patch;
|
|
UINT8 *colormap;
|
|
|
|
V_DrawRightAlignedString(BASEVIDWIDTH-100, STARTY + i*20, V_SNAPTOTOP|highlightflags, va("%2d", extrasmenu.demolist[dir_on[menudepthleft]].standings[i].ranking));
|
|
V_DrawThinString(BASEVIDWIDTH-96, STARTY + i*20, V_SNAPTOTOP|V_ALLOWLOWERCASE, extrasmenu.demolist[dir_on[menudepthleft]].standings[i].name);
|
|
|
|
if (extrasmenu.demolist[dir_on[menudepthleft]].standings[i].timeorscore == UINT32_MAX-1)
|
|
V_DrawThinString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, "NO CONTEST");
|
|
else if (extrasmenu.demolist[dir_on[menudepthleft]].gametype == GT_RACE)
|
|
V_DrawRightAlignedString(BASEVIDWIDTH-40, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d'%02d\"%02d",
|
|
G_TicsToMinutes(extrasmenu.demolist[dir_on[menudepthleft]].standings[i].timeorscore, true),
|
|
G_TicsToSeconds(extrasmenu.demolist[dir_on[menudepthleft]].standings[i].timeorscore),
|
|
G_TicsToCentiseconds(extrasmenu.demolist[dir_on[menudepthleft]].standings[i].timeorscore)
|
|
));
|
|
else
|
|
V_DrawString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d", extrasmenu.demolist[dir_on[menudepthleft]].standings[i].timeorscore));
|
|
|
|
// Character face!
|
|
|
|
// Lat: 08/06/2020: For some reason missing skins have their value set to 255 (don't even ask me why I didn't write this)
|
|
// and for an even STRANGER reason this passes the first check below, so we're going to make sure that the skin here ISN'T 255 before we do anything stupid.
|
|
|
|
if (extrasmenu.demolist[dir_on[menudepthleft]].standings[i].skin != 0xFF)
|
|
{
|
|
patch = faceprefix[extrasmenu.demolist[dir_on[menudepthleft]].standings[i].skin][FACE_RANK];
|
|
colormap = R_GetTranslationColormap(
|
|
extrasmenu.demolist[dir_on[menudepthleft]].standings[i].skin,
|
|
extrasmenu.demolist[dir_on[menudepthleft]].standings[i].color,
|
|
GTC_MENUCACHE);
|
|
}
|
|
else
|
|
{
|
|
patch = W_CachePatchName("M_NORANK", PU_CACHE);
|
|
colormap = R_GetTranslationColormap(
|
|
TC_RAINBOW,
|
|
extrasmenu.demolist[dir_on[menudepthleft]].standings[i].color,
|
|
GTC_MENUCACHE);
|
|
}
|
|
|
|
V_DrawMappedPatch(BASEVIDWIDTH-5 - SHORT(patch->width), STARTY + i*20, V_SNAPTOTOP, patch, colormap);
|
|
}
|
|
#undef STARTY
|
|
|
|
// Handle scrolling rankings
|
|
if (extrasmenu.replayScrollDelay)
|
|
extrasmenu.replayScrollDelay--;
|
|
else if (extrasmenu.replayScrollDir > 0)
|
|
{
|
|
if (extrasmenu.replayScrollTitle < (i*20 - SCALEDVIEWHEIGHT + 100)<<1)
|
|
extrasmenu.replayScrollTitle++;
|
|
else
|
|
{
|
|
extrasmenu.replayScrollDelay = TICRATE;
|
|
extrasmenu.replayScrollDir = -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (extrasmenu.replayScrollTitle > 0)
|
|
extrasmenu.replayScrollTitle--;
|
|
else
|
|
{
|
|
extrasmenu.replayScrollDelay = TICRATE;
|
|
extrasmenu.replayScrollDir = 1;
|
|
}
|
|
}
|
|
|
|
V_DrawFill(10, 10, 300, 60, V_SNAPTOTOP|159);
|
|
M_DrawReplayHutReplayInfo();
|
|
|
|
V_DrawString(10, 72, V_SNAPTOTOP|highlightflags|V_ALLOWLOWERCASE, extrasmenu.demolist[dir_on[menudepthleft]].title);
|
|
|
|
// Draw a warning prompt if needed
|
|
switch (extrasmenu.demolist[dir_on[menudepthleft]].addonstatus)
|
|
{
|
|
case DFILE_ERROR_CANNOTLOAD:
|
|
warning = "Some addons in this replay cannot be loaded.\nYou can watch anyway, but desyncs may occur.";
|
|
break;
|
|
|
|
case DFILE_ERROR_NOTLOADED:
|
|
case DFILE_ERROR_INCOMPLETEOUTOFORDER:
|
|
warning = "Loading addons will mark your game as modified, and Record Attack may be unavailable.\nYou can watch without loading addons, but desyncs may occur.";
|
|
break;
|
|
|
|
case DFILE_ERROR_EXTRAFILES:
|
|
warning = "You have addons loaded that were not present in this replay.\nYou can watch anyway, but desyncs may occur.";
|
|
break;
|
|
|
|
case DFILE_ERROR_OUTOFORDER:
|
|
warning = "You have this replay's addons loaded, but they are out of order.\nYou can watch anyway, but desyncs may occur.";
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
V_DrawSmallString(4, BASEVIDHEIGHT-14, V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, warning);
|
|
}
|
|
|
|
// Draw misc menus:
|
|
|
|
// Addons (this is merely copypasted, original code by toaster)
|
|
|
|
#define lsheadingheight 16
|
|
|
|
#define width 4
|
|
#define vpadding 27
|
|
#define h (BASEVIDHEIGHT-(2*vpadding))
|
|
#define NUMCOLOURS 8 // when toast's coding it's british english hacker fucker
|
|
static void M_DrawTemperature(INT32 x, fixed_t t)
|
|
{
|
|
INT32 y;
|
|
|
|
// bounds check
|
|
if (t > FRACUNIT)
|
|
t = FRACUNIT;
|
|
/*else if (t < 0) -- not needed
|
|
t = 0;*/
|
|
|
|
// scale
|
|
if (t > 1)
|
|
t = (FixedMul(h<<FRACBITS, t)>>FRACBITS);
|
|
|
|
// border
|
|
V_DrawFill(x - 1, vpadding, 1, h, 0);
|
|
V_DrawFill(x + width, vpadding, 1, h, 0);
|
|
V_DrawFill(x - 1, vpadding-1, width+2, 1, 0);
|
|
V_DrawFill(x - 1, vpadding+h, width+2, 1, 0);
|
|
|
|
// bar itself
|
|
y = h;
|
|
if (t)
|
|
for (t = h - t; y > 0; y--)
|
|
{
|
|
UINT8 colours[NUMCOLOURS] = {135, 133, 92, 77, 114, 178, 161, 162};
|
|
UINT8 c;
|
|
if (y <= t) break;
|
|
if (y+vpadding >= BASEVIDHEIGHT/2)
|
|
c = 185;
|
|
else
|
|
c = colours[(NUMCOLOURS*(y-1))/(h/2)];
|
|
V_DrawFill(x, y-1 + vpadding, width, 1, c);
|
|
}
|
|
|
|
// fill the rest of the backing
|
|
if (y)
|
|
V_DrawFill(x, vpadding, width, y, 30);
|
|
}
|
|
#undef width
|
|
#undef vpadding
|
|
#undef h
|
|
#undef NUMCOLOURS
|
|
|
|
|
|
// Just do this here instead.
|
|
static void M_CacheAddonPatches(void)
|
|
{
|
|
addonsp[EXT_FOLDER] = W_CachePatchName("M_FFLDR", PU_STATIC);
|
|
addonsp[EXT_UP] = W_CachePatchName("M_FBACK", PU_STATIC);
|
|
addonsp[EXT_NORESULTS] = W_CachePatchName("M_FNOPE", PU_STATIC);
|
|
addonsp[EXT_TXT] = W_CachePatchName("M_FTXT", PU_STATIC);
|
|
addonsp[EXT_CFG] = W_CachePatchName("M_FCFG", PU_STATIC);
|
|
addonsp[EXT_WAD] = W_CachePatchName("M_FWAD", PU_STATIC);
|
|
#ifdef USE_KART
|
|
addonsp[EXT_KART] = W_CachePatchName("M_FKART", PU_STATIC);
|
|
#endif
|
|
addonsp[EXT_PK3] = W_CachePatchName("M_FPK3", PU_STATIC);
|
|
addonsp[EXT_SOC] = W_CachePatchName("M_FSOC", PU_STATIC);
|
|
addonsp[EXT_LUA] = W_CachePatchName("M_FLUA", PU_STATIC);
|
|
addonsp[NUM_EXT] = W_CachePatchName("M_FUNKN", PU_STATIC);
|
|
addonsp[NUM_EXT+1] = W_CachePatchName("M_FSEL", PU_STATIC);
|
|
addonsp[NUM_EXT+2] = W_CachePatchName("M_FLOAD", PU_STATIC);
|
|
addonsp[NUM_EXT+3] = W_CachePatchName("M_FSRCH", PU_STATIC);
|
|
addonsp[NUM_EXT+4] = W_CachePatchName("M_FSAVE", PU_STATIC);
|
|
}
|
|
|
|
#define addonsseperation 16
|
|
|
|
void M_DrawAddons(void)
|
|
{
|
|
INT32 x, y;
|
|
ssize_t i, m;
|
|
const UINT8 *flashcol = NULL;
|
|
UINT8 hilicol;
|
|
|
|
M_CacheAddonPatches();
|
|
|
|
// hack: If we're calling this from GS_MENU, that means we're in the extras menu!
|
|
// so draw the apropriate background
|
|
if (gamestate == GS_MENU)
|
|
{
|
|
patch_t *bg = W_CachePatchName("M_XTRABG", PU_CACHE);
|
|
V_DrawFixedPatch(0, 0, FRACUNIT, 0, bg, NULL);
|
|
}
|
|
|
|
if (Playing())
|
|
V_DrawCenteredString(BASEVIDWIDTH/2, 5, warningflags, "Adding files mid-game may cause problems.");
|
|
else
|
|
V_DrawCenteredString(BASEVIDWIDTH/2, 5, 0, (recommendedflags == V_SKYMAP ? LOCATIONSTRING2 : LOCATIONSTRING1));
|
|
|
|
if (numwadfiles <= mainwads+1)
|
|
y = 0;
|
|
else if (numwadfiles >= MAX_WADFILES)
|
|
y = FRACUNIT;
|
|
else
|
|
{
|
|
y = FixedDiv(((ssize_t)(numwadfiles) - (ssize_t)(mainwads+1))<<FRACBITS, ((ssize_t)MAX_WADFILES - (ssize_t)(mainwads+1))<<FRACBITS);
|
|
if (y > FRACUNIT) // happens because of how we're shrinkin' it a little
|
|
y = FRACUNIT;
|
|
}
|
|
|
|
M_DrawTemperature(BASEVIDWIDTH - 19 - 5, y);
|
|
|
|
// DRAW MENU
|
|
x = currentMenu->x;
|
|
y = currentMenu->y + 1;
|
|
|
|
hilicol = V_GetStringColormap(highlightflags)[0];
|
|
|
|
y -= 16;
|
|
|
|
V_DrawString(x-21, y + (lsheadingheight - 12), highlightflags|V_ALLOWLOWERCASE, M_AddonsHeaderPath());
|
|
V_DrawFill(x-21, y + (lsheadingheight - 3), MAXSTRINGLENGTH*8+6, 1, hilicol);
|
|
//V_DrawFill(x-21, y + (lsheadingheight - 2), MAXSTRINGLENGTH*8+6, 1, 30);
|
|
|
|
y += 10;
|
|
|
|
M_DrawTextBox(x - (21 + 5), y, MAXSTRINGLENGTH, 1);
|
|
if (menusearch[0])
|
|
V_DrawString(x - 18, y+8, V_ALLOWLOWERCASE, menusearch+1);
|
|
else
|
|
V_DrawString(x - 18, y+8, V_ALLOWLOWERCASE|V_TRANSLUCENT, "Type to search...");
|
|
|
|
V_DrawSmallScaledPatch(x - (21 + 5 + 16), y+4, (menusearch[0] ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+3]);
|
|
|
|
y += 21;
|
|
|
|
m = (addonsseperation*(2*numaddonsshown + 1)) + 2*(16-addonsseperation);
|
|
V_DrawFill(x - 21, y - 1, MAXSTRINGLENGTH*8+6, m, 159);
|
|
|
|
// scrollbar!
|
|
if (sizedirmenu <= (2*numaddonsshown + 1))
|
|
i = 0;
|
|
else
|
|
{
|
|
ssize_t q = m;
|
|
m = ((2*numaddonsshown + 1) * m)/sizedirmenu;
|
|
if (dir_on[menudepthleft] <= numaddonsshown) // all the way up
|
|
i = 0;
|
|
else if (sizedirmenu <= (dir_on[menudepthleft] + numaddonsshown + 1)) // all the way down
|
|
i = q-m;
|
|
else
|
|
i = ((dir_on[menudepthleft] - numaddonsshown) * (q-m))/(sizedirmenu - (2*numaddonsshown + 1));
|
|
}
|
|
|
|
V_DrawFill(x + MAXSTRINGLENGTH*8+5 - 21, (y - 1) + i, 1, m, hilicol);
|
|
|
|
// get bottom...
|
|
m = dir_on[menudepthleft] + numaddonsshown + 1;
|
|
if (m > (ssize_t)sizedirmenu)
|
|
m = sizedirmenu;
|
|
|
|
// then compute top and adjust bottom if needed!
|
|
if (m < (2*numaddonsshown + 1))
|
|
{
|
|
m = min(sizedirmenu, 2*numaddonsshown + 1);
|
|
i = 0;
|
|
}
|
|
else
|
|
i = m - (2*numaddonsshown + 1);
|
|
|
|
if (i != 0)
|
|
V_DrawString(19, y+4 - (skullAnimCounter/5), highlightflags, "\x1A");
|
|
|
|
if (skullAnimCounter < 4)
|
|
flashcol = V_GetStringColormap(highlightflags);
|
|
|
|
y -= (16-addonsseperation);
|
|
|
|
for (; i < m; i++)
|
|
{
|
|
UINT32 flags = V_ALLOWLOWERCASE;
|
|
if (y > BASEVIDHEIGHT) break;
|
|
if (dirmenu[i])
|
|
#define type (UINT8)(dirmenu[i][DIR_TYPE])
|
|
{
|
|
if (type & EXT_LOADED)
|
|
{
|
|
flags |= V_TRANSLUCENT;
|
|
V_DrawSmallScaledPatch(x-(16+4), y, V_TRANSLUCENT, addonsp[(type & ~EXT_LOADED)]);
|
|
V_DrawSmallScaledPatch(x-(16+4), y, 0, addonsp[NUM_EXT+2]);
|
|
}
|
|
else
|
|
V_DrawSmallScaledPatch(x-(16+4), y, 0, addonsp[(type & ~EXT_LOADED)]);
|
|
|
|
if ((size_t)i == dir_on[menudepthleft])
|
|
{
|
|
V_DrawFixedPatch((x-(16+4))<<FRACBITS, (y)<<FRACBITS, FRACUNIT/2, 0, addonsp[NUM_EXT+1], flashcol);
|
|
flags = V_ALLOWLOWERCASE|highlightflags;
|
|
}
|
|
|
|
#define charsonside 14
|
|
if (dirmenu[i][DIR_LEN] > (charsonside*2 + 3))
|
|
V_DrawString(x, y+4, flags, va("%.*s...%s", charsonside, dirmenu[i]+DIR_STRING, dirmenu[i]+DIR_STRING+dirmenu[i][DIR_LEN]-(charsonside+1)));
|
|
#undef charsonside
|
|
else
|
|
V_DrawString(x, y+4, flags, dirmenu[i]+DIR_STRING);
|
|
}
|
|
#undef type
|
|
y += addonsseperation;
|
|
}
|
|
|
|
if (m != (ssize_t)sizedirmenu)
|
|
V_DrawString(19, y-12 + (skullAnimCounter/5), highlightflags, "\x1B");
|
|
|
|
y = BASEVIDHEIGHT - currentMenu->y + 1;
|
|
|
|
x = BASEVIDWIDTH - (x - (21 + 5 + 16)) - 16;
|
|
V_DrawSmallScaledPatch(x, y + 4, ((!majormods) ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+4]);
|
|
|
|
if (modifiedgame)
|
|
V_DrawSmallScaledPatch(x, y + 4, 0, addonsp[NUM_EXT+2]);
|
|
}
|
|
|
|
#undef addonsseperation
|