mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-11-29 23:22:01 +00:00
e8aa715763
GL: r_dynamic -1 is now r_temporalscenecache 1, which makes menu options etc a little friendlier. fixed a serious memory leak. GL: Lightmaps are now uploaded using pbos to reduce cpu stalls (especially with temporalscenecache) and the resulting periodic framerate drops. Requires gl4.4. PM: moved manifest-downloads to the package manager. still needs some proper testing. PM: Fixed bug with downloading updates from every known mirror for that update. PM: Fixed bug with duplicate mirrors... PM: menuqc is now able to query available updates. engine's Draw_TextBox centers the text box more appropriately and easily. SV: added sv_autooffload cvar, when set the map command will automatically create a server in a separate process to reduce the effects of stutter in inefficient ssqc mods. Menu: menu_mods now shares data with getgamedirinfo builtin. MenuQC: Added some extra properties to the getgamedirinfo builtin. MenuQC: Added Menu_RendererRestarted entrypoint. MenuQC: _vid_renderer_opts cvar now has a value that actually reflects the windowing systems in the build, rather than just renderers. CQSC: Added getlocationname builtin. ALSA: device names are now more consistent with other audio drivers. SV: added unsavegame console command, to delete unwanted saved games. SV: hashtable entries are now saved into saved games. SV: reworked player remapping strategy when loading games. Player slots are now directly swapped serverside, not reconnected. SV: resend all csqc entity state when a client signals that it started recording a demo. SV: Added SOLID_BSPTRIGGER as a shapely alternative to the aabb SOLID_TRIGGER. modelindex must still be set for this to work. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5668 fc73d0e0-1445-4013-8a0c-d673dee63da5
2556 lines
66 KiB
C
2556 lines
66 KiB
C
//read menu.h
|
|
|
|
#include "quakedef.h"
|
|
#include "shader.h"
|
|
|
|
//draws the size specified, plus a little extra border (about 8 pixels in each direction, could be more though).
|
|
//size is in vpixels.
|
|
void Draw_ApproxTextBox (float x, float y, float width, float height)
|
|
{
|
|
mpic_t *p;
|
|
float cx, cy;
|
|
int n, lines, columns;
|
|
|
|
x -= 8;
|
|
y -= 8;
|
|
|
|
p = R2D_SafeCachePic ("gfx/box_tl.lmp");
|
|
if (R_GetShaderSizes(p, NULL, NULL, false) != true) //assume none exist
|
|
{ //simple fill.
|
|
R2D_ImageColours(0.1, 0.1, 0.1, 0.9);
|
|
R2D_FillBlock(x, y, width + 16, height + 16);
|
|
R2D_ImageColours(1.0, 1.0, 1.0, 1.0);
|
|
return;
|
|
}
|
|
|
|
//okay, we're drawing it with pics.
|
|
//expand the border to keep things centred.
|
|
lines = ceil(height/8);
|
|
y -= (lines*8-height)/2;
|
|
|
|
columns = ceil(width/16)*2; //columns must be a multiple of 2.
|
|
x -= (columns*8-width)/2;
|
|
|
|
// draw left side
|
|
cx = x;
|
|
cy = y;
|
|
if (p)
|
|
R2D_ScalePic (cx, cy, 8, 8, p);
|
|
p = R2D_SafeCachePic ("gfx/box_ml.lmp");
|
|
for (n = 0; n < lines; n++)
|
|
{
|
|
cy += 8;
|
|
if (p)
|
|
R2D_ScalePic (cx, cy, 8, 8, p);
|
|
}
|
|
p = R2D_SafeCachePic ("gfx/box_bl.lmp");
|
|
if (p)
|
|
R2D_ScalePic (cx, cy+8, 8, 8, p);
|
|
|
|
// draw middle
|
|
cx += 8;
|
|
while (columns > 0)
|
|
{
|
|
cy = y;
|
|
p = R2D_SafeCachePic ("gfx/box_tm.lmp");
|
|
if (p)
|
|
R2D_ScalePic (cx, cy, 16, 8, p);
|
|
p = R2D_SafeCachePic ("gfx/box_mm.lmp");
|
|
for (n = 0; n < lines; n++)
|
|
{
|
|
cy += 8;
|
|
if (n == 1)
|
|
p = R2D_SafeCachePic ("gfx/box_mm2.lmp");
|
|
if (p)
|
|
R2D_ScalePic (cx, cy, 16, 8, p);
|
|
}
|
|
p = R2D_SafeCachePic ("gfx/box_bm.lmp");
|
|
if (p)
|
|
R2D_ScalePic (cx, cy+8, 16, 8, p);
|
|
columns -= 2;
|
|
cx += 16;
|
|
}
|
|
|
|
// draw right side
|
|
cy = y;
|
|
p = R2D_SafeCachePic ("gfx/box_tr.lmp");
|
|
if (p)
|
|
R2D_ScalePic (cx, cy, 8, 8, p);
|
|
p = R2D_SafeCachePic ("gfx/box_mr.lmp");
|
|
for (n = 0; n < lines; n++)
|
|
{
|
|
cy += 8;
|
|
if (p)
|
|
R2D_ScalePic (cx, cy, 8, 8, p);
|
|
}
|
|
p = R2D_SafeCachePic ("gfx/box_br.lmp");
|
|
if (p)
|
|
R2D_ScalePic (cx, cy+8, 8, 8, p);
|
|
}
|
|
|
|
#ifndef NOBUILTINMENUS
|
|
|
|
static int omousex;
|
|
static int omousey;
|
|
static qboolean mousemoved;
|
|
static qboolean bindingactive;
|
|
extern cvar_t cl_cursor;
|
|
extern cvar_t cl_cursorsize;
|
|
extern cvar_t cl_cursorbias;
|
|
extern cvar_t m_preset_chosen;
|
|
extern menu_t *topmenu;
|
|
menuoption_t *M_NextSelectableItem(emenu_t *m, menuoption_t *old);
|
|
|
|
#ifdef HEXEN2
|
|
//this function is so fucked up.
|
|
//firstly, the source image uses 0 for transparent instead of 255. this means we need special handling. *sigh*.
|
|
//secondly we have to avoid sampling too much of the image, because i chars seem to have stray white pixels in them
|
|
//thirdly, we hard-code (by eye) the space between chars, which should be different for any character pair.
|
|
//but we're lazy so we don't consider the next char. italic fonts are annoying like that. feel free to refudge it.
|
|
void Draw_Hexen2BigFontString(int x, int y, const char *text)
|
|
{
|
|
int c;
|
|
int sx, sy;
|
|
mpic_t *p;
|
|
p = R_RegisterShader ("gfx/menu/bigfont.lmp", SUF_2D,
|
|
"{\n"
|
|
"if $nofixed\n"
|
|
"program default2d\n"
|
|
"endif\n"
|
|
"affine\n"
|
|
"nomipmaps\n"
|
|
"{\n"
|
|
"clampmap $diffuse\n"
|
|
"rgbgen vertex\n"
|
|
"alphagen vertex\n"
|
|
"blendfunc gl_one gl_one_minus_src_alpha\n"
|
|
"}\n"
|
|
"sort additive\n"
|
|
"}\n");
|
|
if (!p->defaulttextures->base)
|
|
{
|
|
void *file;
|
|
qofs_t fsize = FS_LoadFile("gfx/menu/bigfont.lmp", &file);
|
|
if (file)
|
|
{
|
|
unsigned int w = ((unsigned int*)file)[0];
|
|
unsigned int h = ((unsigned int*)file)[1];
|
|
if (8+w*h==fsize)
|
|
p->defaulttextures->base = R_LoadReplacementTexture("gfx/menu/bigfont.lmp", NULL, IF_NOPCX|IF_PREMULTIPLYALPHA|IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_CLAMP, (qbyte*)file+8, w, h, TF_H2_TRANS8_0);
|
|
FS_FreeFile(file); //got image data
|
|
}
|
|
if (!p->defaulttextures->base)
|
|
p->defaulttextures->base = R_LoadHiResTexture("gfx/menu/bigfont.lmp", NULL, IF_PREMULTIPLYALPHA|IF_UIPIC|IF_NOPICMIP|IF_NOMIPMAP|IF_CLAMP);
|
|
}
|
|
|
|
while(*text)
|
|
{
|
|
c = *text++;
|
|
if (c >= 'a' && c <= 'z')
|
|
{
|
|
sx = ((c-'a')%8)*20;
|
|
sy = ((c-'a')/8)*20;
|
|
}
|
|
else if (c >= 'A' && c <= 'Z')
|
|
{
|
|
c = c - 'A' + 'a';
|
|
sx = ((c-'a')%8)*20;
|
|
sy = ((c-'a')/8)*20;
|
|
}
|
|
else// if (*text <= ' ')
|
|
{
|
|
sx=-1;
|
|
sy=-1;
|
|
}
|
|
if(sx>=0)
|
|
R2D_SubPic(x, y, 18, 20, p, sx, sy, 20*8, 20*4);
|
|
|
|
switch(c)
|
|
{
|
|
case 'a': x+=15; break;
|
|
case 'b': x+=15; break;
|
|
case 'c': x+=15; break;
|
|
case 'd': x+=15; break;
|
|
case 'e': x+=15; break;
|
|
case 'f': x+=15; break;
|
|
case 'g': x+=15; break;
|
|
case 'h': x+=15; break;
|
|
case 'i': x+=10; break;
|
|
case 'j': x+=15; break;
|
|
case 'k': x+=18; break;
|
|
case 'l': x+=15; break;
|
|
case 'm': x+=18; break;
|
|
case 'n': x+=15; break;
|
|
case 'o': x+=15; break;
|
|
case 'p': x+=15; break;
|
|
case 'q': x+=18; break;
|
|
case 'r': x+=18; break;
|
|
case 's': x+=13; break;
|
|
case 't': x+=15; break;
|
|
case 'u': x+=15; break;
|
|
case 'v': x+=12; break;
|
|
case 'w': x+=15; break;
|
|
case 'x': x+=18; break;
|
|
case 'y': x+=15; break;
|
|
case 'z': x+=18; break;
|
|
default: x+=20; break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
mpic_t *QBigFontWorks(void)
|
|
{
|
|
mpic_t *p;
|
|
int i;
|
|
char *names[] = {
|
|
"gfx/mcharset.lmp",
|
|
"mcharset.lmp",
|
|
"textures/gfx/mcharset.lmp",
|
|
"textures/mcharset.lmp",
|
|
NULL
|
|
};
|
|
for (i = 0; names[i]; i++)
|
|
{
|
|
p = R2D_SafeCachePic (names[i]);
|
|
if (p && R_GetShaderSizes(p, NULL, NULL, true))
|
|
return p;
|
|
}
|
|
return NULL;
|
|
}
|
|
void Draw_BigFontString(int x, int y, const char *text)
|
|
{
|
|
int sx, sy;
|
|
mpic_t *p;
|
|
p = QBigFontWorks();
|
|
if (!p)
|
|
{
|
|
Draw_AltFunString(x, y + (20-8)/2, text);
|
|
return;
|
|
}
|
|
|
|
{ //a hack for scaling
|
|
p->width = 20*8;
|
|
p->height = 20*8;
|
|
}
|
|
|
|
while(*text)
|
|
{
|
|
if (*text >= 'A' && *text <= 'Z')
|
|
{
|
|
sx = ((*text-'A')%8)*(p->width>>3);
|
|
sy = ((*text-'A')/8)*(p->height>>3);
|
|
}
|
|
else if (*text >= 'a' && *text <= 'z')
|
|
{
|
|
sx = ((*text-'a'+26)%8)*(p->width>>3);
|
|
sy = ((*text-'a'+26)/8)*(p->height>>3);
|
|
}
|
|
else if (*text >= '0' && *text <= '1')
|
|
{
|
|
sx = ((*text-'0'+26*2)%8)*(p->width>>3);
|
|
sy = ((*text-'0'+26*2)/8)*(p->height>>3);
|
|
}
|
|
else if (*text == ':')
|
|
{
|
|
sx = ((*text-'0'+26*2+10)%8)*(p->width>>3);
|
|
sy = ((*text-'0'+26*2+10)/8)*(p->height>>3);
|
|
}
|
|
else if (*text == '/')
|
|
{
|
|
sx = ((*text-'0'+26*2+11)%8)*(p->width>>3);
|
|
sy = ((*text-'0'+26*2+11)/8)*(p->height>>3);
|
|
}
|
|
else// if (*text <= ' ')
|
|
{
|
|
sx=-1;
|
|
sy=-1;
|
|
}
|
|
if(sx>=0)
|
|
R2D_SubPic(x, y, 20, 20, p, sx, sy, 20*8, 20*8);
|
|
x+=(p->width>>3);
|
|
text++;
|
|
}
|
|
}
|
|
|
|
char *menudotstyle;
|
|
int maxdots;
|
|
int mindot;
|
|
int dotofs;
|
|
|
|
static void MenuTooltipChange(emenu_t *menu, const char *text)
|
|
{
|
|
unsigned int MAX_CHARS=1024;
|
|
menutooltip_t *mtt;
|
|
if (menu->tooltip)
|
|
{
|
|
Z_Free(menu->tooltip);
|
|
menu->tooltip = NULL;
|
|
}
|
|
|
|
if (!text || !text[0] || vid.width < 320 || vid.height < 200)
|
|
return;
|
|
|
|
// allocate new tooltip structure, copy text to structure
|
|
mtt = (menutooltip_t *)Z_Malloc(sizeof(menutooltip_t) + sizeof(conchar_t)*MAX_CHARS);
|
|
mtt->end = COM_ParseFunString(CON_WHITEMASK, text, mtt->text, sizeof(conchar_t)*MAX_CHARS, false);
|
|
|
|
menu->tooltip = mtt;
|
|
}
|
|
|
|
static qboolean MI_Selectable(menuoption_t *op)
|
|
{
|
|
switch(op->common.type)
|
|
{
|
|
case mt_text:
|
|
return false;
|
|
case mt_button:
|
|
return true;
|
|
#ifdef HEXEN2
|
|
case mt_hexen2buttonbigfont:
|
|
return true;
|
|
#endif
|
|
case mt_qbuttonbigfont:
|
|
return true;
|
|
case mt_menudot:
|
|
return false;
|
|
case mt_picturesel:
|
|
return true;
|
|
case mt_picture:
|
|
return false;
|
|
case mt_framestart:
|
|
return false;
|
|
case mt_frameend:
|
|
return true;
|
|
case mt_box:
|
|
return false;
|
|
case mt_slider:
|
|
return true;
|
|
case mt_checkbox:
|
|
return true;
|
|
case mt_edit:
|
|
return true;
|
|
case mt_bind:
|
|
return true;
|
|
case mt_combo:
|
|
return true;
|
|
case mt_custom:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static qboolean M_MouseMoved(emenu_t *menu)
|
|
{
|
|
int ypos = menu->ypos, framescroll = 0;
|
|
menuoption_t *option;
|
|
qboolean havemouseitem = false;
|
|
// if (menu->prev && !menu->exclusive)
|
|
// if (M_MouseMoved(menu->prev))
|
|
// return true;
|
|
if (bindingactive)
|
|
return true;
|
|
for(option = menu->options; option; option = option->common.next)
|
|
{
|
|
if (option->common.ishidden)
|
|
continue;
|
|
|
|
if (mousecursor_x > menu->xpos+option->common.posx-option->common.extracollide && mousecursor_x < menu->xpos+option->common.posx+option->common.width)
|
|
{
|
|
if (mousecursor_y > ypos+option->common.posy && mousecursor_y < ypos+option->common.posy+option->common.height)
|
|
{
|
|
if (MI_Selectable(option))
|
|
{
|
|
if (menu->mouseitem != option)
|
|
{
|
|
menu->mouseitem = option;
|
|
menu->tooltiptime = realtime + 1;
|
|
MenuTooltipChange(menu, menu->mouseitem->common.tooltip);
|
|
}
|
|
havemouseitem = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
switch(option->common.type)
|
|
{
|
|
default:
|
|
break;
|
|
case mt_framestart:
|
|
ypos += framescroll;
|
|
framescroll = 0;
|
|
break;
|
|
case mt_frameend:
|
|
{
|
|
menuoption_t *opt2;
|
|
int maxy = option->frame.common.posy;
|
|
for (opt2 = option->common.next; opt2; opt2 = opt2->common.next)
|
|
{
|
|
if (opt2->common.posy + opt2->common.height > maxy)
|
|
maxy = opt2->common.posy + opt2->common.height;
|
|
}
|
|
maxy -= vid.height-8;
|
|
framescroll += option->frame.frac * maxy;
|
|
ypos -= option->frame.frac * maxy;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!havemouseitem && menu->mouseitem)
|
|
{
|
|
menu->mouseitem = NULL;
|
|
MenuTooltipChange(menu, NULL);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void M_CheckMouseMove(emenu_t *m)
|
|
{
|
|
if (omousex != (int)mousecursor_x || omousey != (int)mousecursor_y)
|
|
mousemoved = true;
|
|
else
|
|
mousemoved = false;
|
|
omousex = mousecursor_x;
|
|
omousey = mousecursor_y;
|
|
|
|
if (mousemoved)
|
|
M_MouseMoved(m);
|
|
}
|
|
|
|
static float M_DrawScrollbar(int x, int y, int width, int height, float frac, qboolean mgrabbed)
|
|
{
|
|
float unused = 0;
|
|
mpic_t *pic;
|
|
int knob=y;
|
|
|
|
R2D_ImageColours(1,1,1,1);
|
|
|
|
pic = R2D_SafeCachePic("scrollbars/slidebg.tga");
|
|
if (pic && R_GetShaderSizes(pic, NULL, NULL, false)>0)
|
|
{
|
|
unused = 8*2+64; //top+bottom are 8 pixels, knob is 64
|
|
R2D_ScalePic(x + width - 8, y+8, 8, height-16, pic);
|
|
|
|
pic = R2D_SafeCachePic("scrollbars/arrow_up.tga");
|
|
R2D_ScalePic(x + width - 8, y, 8, 8, pic);
|
|
|
|
pic = R2D_SafeCachePic("scrollbars/arrow_down.tga");
|
|
R2D_ScalePic(x + width - 8, y + height - 8, 8, 8, pic);
|
|
|
|
knob += 8;
|
|
knob += frac * (float)(height-(unused));
|
|
|
|
pic = R2D_SafeCachePic("scrollbars/slider.tga");
|
|
R2D_ScalePic(x + width - 8, knob, 8, 64, pic);
|
|
}
|
|
else
|
|
{
|
|
unused = width; //top+bottom are invisible, knob is square
|
|
R2D_ImageColours(0.1, 0.1, 0.2, 1.0);
|
|
R2D_FillBlock(x, y, width, height);
|
|
|
|
knob += frac * (height-unused);
|
|
|
|
R2D_ImageColours(0.35, 0.35, 0.55, 1.0);
|
|
R2D_FillBlock(x, knob, width, width);
|
|
R2D_ImageColours(1,1,1,1);
|
|
}
|
|
|
|
if (mgrabbed)
|
|
{
|
|
float my;
|
|
|
|
my = mousecursor_y - y;
|
|
my -= unused/2;
|
|
my /= height-unused;
|
|
if (my > 1)
|
|
my = 1;
|
|
if (my < 0)
|
|
my = 0;
|
|
frac = my;
|
|
}
|
|
return frac;
|
|
}
|
|
|
|
static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, emenu_t *menu)
|
|
{
|
|
int i;
|
|
mpic_t *p;
|
|
int pw,ph;
|
|
int framescroll = 0;
|
|
|
|
if (option && option->common.type == mt_box && !option->common.ishidden)
|
|
{ //FIXME: why is this here? why is this special?
|
|
Draw_ApproxTextBox(xpos+option->common.posx, ypos+option->common.posy, option->box.width, option->box.height);
|
|
option = option->common.next;
|
|
}
|
|
|
|
for (; option; option = option->common.next)
|
|
{
|
|
if (option->common.ishidden)
|
|
continue;
|
|
|
|
if (&menu->menu == topmenu && menu->mouseitem == option && option->common.type != mt_frameend)
|
|
{
|
|
float alphamax = 0.5, alphamin = 0.2;
|
|
R2D_ImageColours(.5,.4,0,(sin(realtime*2)+1)*0.5*(alphamax-alphamin)+alphamin);
|
|
R2D_FillBlock(xpos+menu->mouseitem->common.posx, ypos+menu->mouseitem->common.posy, menu->mouseitem->common.width, menu->mouseitem->common.height);
|
|
R2D_ImageColours(1,1,1,1);
|
|
}
|
|
switch(option->common.type)
|
|
{
|
|
case mt_menucursor:
|
|
if ((int)(realtime*4)&1)
|
|
Draw_FunString(xpos+option->common.posx, ypos+option->common.posy, "^Ue00d");
|
|
break;
|
|
case mt_text:
|
|
if (!option->text.text)
|
|
{ //blinking cursor image hack (FIXME)
|
|
if ((int)(realtime*4)&1)
|
|
Draw_FunString(xpos+option->common.posx, ypos+option->common.posy, "^Ue00d");
|
|
}
|
|
else if (option->common.width)
|
|
Draw_FunStringWidth(xpos + option->common.posx, ypos+option->common.posy, option->text.text, option->common.width, option->text.rightalign, option->text.isred);
|
|
else if (option->text.isred)
|
|
Draw_AltFunString(xpos+option->common.posx, ypos+option->common.posy, option->text.text);
|
|
else
|
|
Draw_FunString(xpos+option->common.posx, ypos+option->common.posy, option->text.text);
|
|
break;
|
|
case mt_button:
|
|
Draw_FunStringWidth(xpos + option->common.posx, ypos+option->common.posy, option->button.text, option->common.width, option->button.rightalign, !menu->cursoritem && menu->selecteditem == option);
|
|
break;
|
|
#ifdef HEXEN2
|
|
case mt_hexen2buttonbigfont:
|
|
Draw_Hexen2BigFontString(xpos+option->common.posx, ypos+option->common.posy, option->button.text);
|
|
break;
|
|
#endif
|
|
case mt_qbuttonbigfont:
|
|
Draw_BigFontString(xpos+option->common.posx, ypos+option->common.posy, option->button.text);
|
|
break;
|
|
case mt_menudot:
|
|
i = (int)(realtime * 10)%maxdots;
|
|
p = R2D_SafeCachePic(va(menudotstyle, i+mindot ));
|
|
if (R_GetShaderSizes(p, NULL, NULL, false)>0)
|
|
R2D_ScalePic(xpos+option->common.posx, ypos+option->common.posy+dotofs, option->common.width, option->common.height, p);
|
|
else if ((int)(realtime*4)&1)
|
|
Draw_FunString(xpos+option->common.posx, ypos+option->common.posy + (option->common.height-8)/2, "^a^Ue00d");
|
|
break;
|
|
case mt_picturesel:
|
|
p = NULL;
|
|
if (menu->selecteditem && menu->selecteditem->common.posx == option->common.posx && menu->selecteditem->common.posy == option->common.posy)
|
|
{
|
|
char selname[MAX_QPATH];
|
|
Q_strncpyz(selname, option->picture.picturename, sizeof(selname));
|
|
COM_StripExtension(selname, selname, sizeof(selname));
|
|
Q_strncatz(selname, "_sel", sizeof(selname));
|
|
p = R2D_SafeCachePic(selname);
|
|
}
|
|
if (!R_GetShaderSizes(p, &pw, &ph, false))
|
|
p = R2D_SafeCachePic(option->picture.picturename);
|
|
|
|
if (R_GetShaderSizes(p, &pw, &ph, false)>0)
|
|
{
|
|
float scale = (option->common.height?option->common.height:20.0)/ph;
|
|
R2D_ScalePic(xpos+option->common.posx, ypos+option->common.posy, option->common.width?option->common.width:(pw*scale), ph*scale, p);
|
|
}
|
|
break;
|
|
case mt_picture:
|
|
p = R2D_SafeCachePic(option->picture.picturename);
|
|
if (R_GetShaderSizes(p, NULL, NULL, false)>0) R2D_ScalePic(xpos+option->common.posx, ypos+option->common.posy, option->common.width, option->common.height, p);
|
|
break;
|
|
case mt_framestart:
|
|
ypos += framescroll;
|
|
framescroll = 0;
|
|
if (R2D_Flush)
|
|
R2D_Flush();
|
|
BE_Scissor(NULL);
|
|
break;
|
|
case mt_frameend:
|
|
{
|
|
srect_t srect;
|
|
menuoption_t *opt2;
|
|
extern qboolean keydown[];
|
|
int maxy = option->frame.common.posy;
|
|
option->frame.common.width = 16;
|
|
option->frame.common.posx = vid.width - option->frame.common.width - xpos;
|
|
option->frame.common.height = vid.height-8-maxy - ypos;
|
|
for (opt2 = option->common.next; opt2; opt2 = opt2->common.next)
|
|
{
|
|
if (opt2->common.posy + opt2->common.height > maxy)
|
|
maxy = opt2->common.posy + opt2->common.height;
|
|
}
|
|
maxy -= vid.height-8;
|
|
|
|
if (maxy < 0)
|
|
{
|
|
option->frame.mousedown = false;
|
|
option->frame.frac = 0;
|
|
option->frame.common.width = 0;
|
|
option->frame.common.height = 0;
|
|
}
|
|
else
|
|
{
|
|
if (!keydown[K_MOUSE1])
|
|
option->frame.mousedown = false;
|
|
option->frame.frac = M_DrawScrollbar(xpos+option->frame.common.posx, ypos+option->common.posy, option->frame.common.width, option->frame.common.height, option->frame.frac, option->frame.mousedown);
|
|
|
|
if (R2D_Flush)
|
|
R2D_Flush();
|
|
srect.x = 0;
|
|
srect.y = (float)(ypos+option->common.posy) / vid.height;
|
|
srect.width = 1;
|
|
srect.height = 1 - srect.y;
|
|
srect.dmin = -99999;
|
|
srect.dmax = 99999;
|
|
srect.y = (1-srect.y) - srect.height;
|
|
BE_Scissor(&srect);
|
|
|
|
framescroll += option->frame.frac * maxy;
|
|
ypos -= option->frame.frac * maxy;
|
|
}
|
|
}
|
|
break;
|
|
case mt_box:
|
|
Draw_ApproxTextBox(xpos+option->common.posx, ypos+option->common.posy, option->box.width, option->box.height);
|
|
break;
|
|
case mt_slider:
|
|
if (option->slider.var)
|
|
{
|
|
#define SLIDER_RANGE 10
|
|
float range;
|
|
int i;
|
|
int x = xpos+option->common.posx;
|
|
int y = ypos+option->common.posy;
|
|
int s;
|
|
|
|
range = (option->slider.current - option->slider.min)/(option->slider.max-option->slider.min);
|
|
|
|
if (option->slider.text)
|
|
{
|
|
Draw_FunStringWidth(x, y, option->slider.text, option->slider.textwidth, true, !menu->cursoritem && menu->selecteditem == option);
|
|
x += option->slider.textwidth + 3*8;
|
|
}
|
|
|
|
if (range < 0)
|
|
range = 0;
|
|
if (range > 1)
|
|
range = 1;
|
|
option->slider.vx = x;
|
|
x -= 8;
|
|
Font_BeginString(font_default, x, y, &x, &y);
|
|
x = Font_DrawChar(x, y, CON_WHITEMASK, 0xe080);
|
|
s = x;
|
|
for (i=0 ; i<SLIDER_RANGE ; i++)
|
|
x = Font_DrawChar(x, y, CON_WHITEMASK, 0xe081);
|
|
Font_DrawChar(x, y, CON_WHITEMASK, 0xe082);
|
|
Font_DrawChar(s + (x-s) * range - Font_CharWidth(CON_WHITEMASK, 0xe083)/2, y, CON_WHITEMASK, 0xe083);
|
|
Font_EndString(font_default);
|
|
}
|
|
break;
|
|
case mt_checkbox:
|
|
{
|
|
int x = xpos+option->common.posx;
|
|
int y = ypos+option->common.posy;
|
|
qboolean on;
|
|
if (option->check.func)
|
|
on = option->check.func(&option->check, menu, CHK_CHECKED);
|
|
else if (!option->check.var)
|
|
on = option->check.value;
|
|
else if (option->check.bits) //bits is a bitmask for use with cvars (users can be clumsy, so bittage of 0 uses non-zero as true, but sets only bit 1)
|
|
{
|
|
if (option->check.var->latched_string)
|
|
on = atoi(option->check.var->latched_string)&option->check.bits;
|
|
else
|
|
on = (int)(option->check.var->value)&option->check.bits;
|
|
}
|
|
else
|
|
{
|
|
if (option->check.var->latched_string)
|
|
on = !!atof(option->check.var->latched_string);
|
|
else
|
|
on = !!option->check.var->value;
|
|
}
|
|
|
|
if (option->check.text)
|
|
{
|
|
Draw_FunStringWidth(x, y, option->check.text, option->check.textwidth, true, !menu->cursoritem && menu->selecteditem == option);
|
|
x += option->check.textwidth + 3*8;
|
|
}
|
|
#if 0
|
|
if (on)
|
|
Draw_Character (x, y, 0xe083);
|
|
else
|
|
Draw_Character (x, y, 0xe081);
|
|
#endif
|
|
if (!menu->cursoritem && menu->selecteditem == option)
|
|
Draw_AltFunString (x, y, on ? "on" : "off");
|
|
else
|
|
Draw_FunString (x, y, on ? "on" : "off");
|
|
}
|
|
break;
|
|
case mt_edit:
|
|
{
|
|
int x = xpos+option->common.posx;
|
|
int y = ypos+option->common.posy;
|
|
|
|
Draw_FunStringWidth(x, y, option->edit.caption, option->edit.captionwidth, true, !menu->cursoritem && menu->selecteditem == option);
|
|
x += option->edit.captionwidth + 3*8;
|
|
if (option->edit.slim)
|
|
x += 8; // more space for cursor
|
|
else
|
|
Draw_ApproxTextBox(x, y, 16*8, 8);
|
|
Draw_FunString(x, y, option->edit.text);
|
|
|
|
if (menu->selecteditem == option && (int)(realtime*4) & 1)
|
|
{
|
|
vid.ime_allow = true;
|
|
vid.ime_position[0] = x;
|
|
vid.ime_position[1] = y+8;
|
|
|
|
x += strlen(option->edit.text)*8;
|
|
Draw_FunString(x, y, "^Ue00b");
|
|
}
|
|
}
|
|
break;
|
|
case mt_bind:
|
|
{
|
|
int x = xpos+option->common.posx;
|
|
int y = ypos+option->common.posy;
|
|
int keys[8], keymods[countof(keys)];
|
|
int keycount;
|
|
const char *keyname;
|
|
int j;
|
|
|
|
Draw_FunStringWidth(x, y, option->bind.caption, option->bind.captionwidth, true, !menu->cursoritem && menu->selecteditem == option);
|
|
x += option->bind.captionwidth + 3*8;
|
|
|
|
keycount = M_FindKeysForCommand (0, cl_forceseat.ival, option->bind.command, keys, keymods, countof(keys));
|
|
|
|
if (bindingactive && menu->selecteditem == option)
|
|
Draw_FunString (x, y, "Press key");
|
|
else if (!keycount)
|
|
Draw_FunString (x, y, "???");
|
|
else
|
|
{
|
|
for (j = 0; j < keycount; j++)
|
|
{ /*these offsets are wrong*/
|
|
if (j)
|
|
{
|
|
Draw_FunString (x + 8, y, "or");
|
|
x += 32;
|
|
}
|
|
keyname = Key_KeynumToString (keys[j], keymods[j]);
|
|
Draw_FunString (x, y, keyname);
|
|
x += strlen(keyname) * 8;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case mt_combo:
|
|
{
|
|
int x = xpos+option->common.posx;
|
|
int y = ypos+option->common.posy;
|
|
|
|
Draw_FunStringWidth(x, y, option->combo.caption, option->combo.captionwidth, true, !menu->cursoritem && menu->selecteditem == option);
|
|
x += option->combo.captionwidth + 3*8;
|
|
|
|
if (option->combo.numoptions)
|
|
{
|
|
if (!menu->cursoritem && menu->selecteditem == option)
|
|
Draw_AltFunString(x, y, option->combo.options[option->combo.selectedoption]);
|
|
else
|
|
Draw_FunString(x, y, option->combo.options[option->combo.selectedoption]);
|
|
}
|
|
}
|
|
break;
|
|
case mt_custom:
|
|
option->custom.draw(xpos+option->common.posx, ypos+option->common.posy, &option->custom, menu);
|
|
break;
|
|
default:
|
|
Sys_Error("Bad item type\n");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void MenuDraw(emenu_t *menu)
|
|
{
|
|
if (!menu->dontexpand)
|
|
menu->xpos = ((vid.width - 320)>>1);
|
|
if (menu->predraw)
|
|
menu->predraw(menu);
|
|
MenuDrawItems(menu->xpos, menu->ypos, menu->options, menu);
|
|
// draw tooltip
|
|
if (menu->mouseitem && menu->tooltip && realtime > menu->tooltiptime)
|
|
{
|
|
// menuoption_t *option = menu->mouseitem;
|
|
|
|
// if (omousex > menu->xpos+option->common.posx && omousex < menu->xpos+option->common.posx+option->common.width)
|
|
// if (omousey > menu->ypos+option->common.posy && omousey < menu->ypos+option->common.posy+option->common.height)
|
|
{
|
|
int x = omousex+8;
|
|
int y = omousey+8;
|
|
int w;
|
|
int h;
|
|
int l, lines;
|
|
conchar_t *line_start[16];
|
|
conchar_t *line_end[countof(line_start)];
|
|
|
|
//figure out the line breaks
|
|
Font_BeginString(font_default, 0, 0, &l, &l);
|
|
lines = Font_LineBreaks(menu->tooltip->text, menu->tooltip->end, min(vid.pixelwidth/2, 30*8*vid.pixelwidth/vid.width), countof(line_start), line_start, line_end);
|
|
Font_EndString(font_default);
|
|
|
|
//figure out how wide that makes the tip
|
|
w = 0;
|
|
h = lines*8;
|
|
for (l = 0; l < lines; l++)
|
|
{
|
|
int lw = Font_LineWidth(line_start[l], line_end[l])*vid.width/vid.pixelwidth;
|
|
if (w < lw)
|
|
w = lw;
|
|
}
|
|
|
|
// keep the tooltip within view
|
|
if (x + w >= vid.width)
|
|
x = vid.width - w - 1;
|
|
if (y + h >= vid.height)
|
|
y -= h;
|
|
|
|
// draw the background
|
|
Draw_ApproxTextBox(x, y, w, lines*8);
|
|
|
|
//draw the text
|
|
Font_BeginString(font_default, x, y, &x, &y);
|
|
for (l = 0; l < lines; l++)
|
|
{
|
|
Font_LineDraw(x, y, line_start[l], line_end[l]);
|
|
y += Font_CharHeight();
|
|
}
|
|
Font_EndString(font_default);
|
|
}
|
|
}
|
|
|
|
if (menu->postdraw)
|
|
menu->postdraw(menu);
|
|
}
|
|
|
|
|
|
menutext_t *MC_AddWhiteText(emenu_t *menu, int lhs, int rhs, int y, const char *text, int rightalign)
|
|
{
|
|
menutext_t *n = Z_Malloc(sizeof(menutext_t) + (text?strlen(text):0)+1);
|
|
n->common.type = mt_text;
|
|
n->common.iszone = true;
|
|
n->common.posx = lhs;
|
|
n->common.posy = y;
|
|
n->common.width = (rhs)?rhs-lhs:0;
|
|
n->rightalign = rightalign;
|
|
if (text)
|
|
{
|
|
n->text = (char*)(n+1);
|
|
strcpy((char*)(n+1), (text));
|
|
}
|
|
|
|
n->common.next = menu->options;
|
|
menu->options = (menuoption_t *)n;
|
|
return n;
|
|
}
|
|
|
|
menutext_t *MC_AddBufferedText(emenu_t *menu, int lhs, int rhs, int y, const char *text, int rightalign, qboolean red)
|
|
{
|
|
menutext_t *n = Z_Malloc(sizeof(menutext_t) + strlen(text)+1);
|
|
n->common.type = mt_text;
|
|
n->common.iszone = true;
|
|
n->common.posx = lhs;
|
|
n->common.posy = y;
|
|
n->common.width = rhs?rhs-lhs:0;
|
|
n->text = (char *)(n+1);
|
|
strcpy((char *)(n+1), text);
|
|
n->isred = red;
|
|
|
|
if (rightalign && text)
|
|
n->common.posx -= strlen(text)*8;
|
|
|
|
n->common.next = menu->options;
|
|
menu->options = (menuoption_t *)n;
|
|
return n;
|
|
}
|
|
|
|
menutext_t *MC_AddRedText(emenu_t *menu, int lhs, int rhs, int y, const char *text, int rightalign)
|
|
{
|
|
menutext_t *n;
|
|
n = MC_AddWhiteText(menu, lhs, rhs, y, text, rightalign);
|
|
n->isred = true;
|
|
return n;
|
|
}
|
|
|
|
menubind_t *MC_AddBind(emenu_t *menu, int cx, int bx, int y, const char *caption, char *command, char *tooltip)
|
|
{
|
|
menubind_t *n = Z_Malloc(sizeof(*n) + strlen(caption)+1 + strlen(command)+1 + (tooltip?strlen(tooltip)+1:0));
|
|
n->common.type = mt_bind;
|
|
n->common.iszone = true;
|
|
n->common.posx = cx;
|
|
n->common.posy = y;
|
|
n->captionwidth = bx-cx;
|
|
n->caption = (char *)(n+1);
|
|
strcpy(n->caption, caption);
|
|
n->command = n->caption+strlen(n->caption)+1;
|
|
strcpy(n->command, command);
|
|
if (tooltip)
|
|
{
|
|
char *tip = n->command+strlen(n->command)+1;
|
|
n->common.tooltip = tip;
|
|
strcpy(tip, tooltip);
|
|
}
|
|
n->common.width = n->captionwidth + 64;
|
|
n->common.height = 8;
|
|
|
|
n->common.next = menu->options;
|
|
menu->options = (menuoption_t *)n;
|
|
return n;
|
|
}
|
|
|
|
menupicture_t *MC_AddSelectablePicture(emenu_t *menu, int x, int y, int height, char *picname)
|
|
{
|
|
char selname[MAX_QPATH];
|
|
menupicture_t *n;
|
|
|
|
if (qrenderer == QR_NONE)
|
|
return NULL;
|
|
|
|
Q_strncpyz(selname, picname, sizeof(selname));
|
|
COM_StripExtension(selname, selname, sizeof(selname));
|
|
Q_strncatz(selname, "_sel", sizeof(selname));
|
|
|
|
R2D_SafeCachePic(picname);
|
|
R2D_SafeCachePic(selname);
|
|
|
|
n = Z_Malloc(sizeof(menupicture_t) + strlen(picname)+1);
|
|
n->common.type = mt_picturesel;
|
|
n->common.iszone = true;
|
|
n->common.posx = x;
|
|
n->common.posy = y;
|
|
n->common.height = height;
|
|
n->picturename = (char *)(n+1);
|
|
strcpy(n->picturename, picname);
|
|
|
|
n->common.next = menu->options;
|
|
menu->options = (menuoption_t *)n;
|
|
return n;
|
|
}
|
|
|
|
menupicture_t *MC_AddPicture(emenu_t *menu, int x, int y, int width, int height, char *picname)
|
|
{
|
|
menupicture_t *n;
|
|
if (qrenderer == QR_NONE)
|
|
return NULL;
|
|
|
|
R2D_SafeCachePic(picname);
|
|
|
|
n = Z_Malloc(sizeof(menupicture_t) + strlen(picname)+1);
|
|
n->common.type = mt_picture;
|
|
n->common.iszone = true;
|
|
n->common.posx = x;
|
|
n->common.posy = y;
|
|
n->common.width = width;
|
|
n->common.height = height;
|
|
n->picturename = (char *)(n+1);
|
|
strcpy(n->picturename, picname);
|
|
|
|
n->common.next = menu->options;
|
|
menu->options = (menuoption_t *)n;
|
|
return n;
|
|
}
|
|
|
|
menupicture_t *MC_AddCenterPicture(emenu_t *menu, int y, int height, char *picname)
|
|
{
|
|
int x;
|
|
int width;
|
|
mpic_t *p;
|
|
|
|
if (qrenderer == QR_NONE)
|
|
return NULL;
|
|
p = R2D_SafeCachePic(picname);
|
|
if (!p)
|
|
{
|
|
x = 320/2;
|
|
width = 64;
|
|
}
|
|
else
|
|
{
|
|
int pwidth, pheight;
|
|
if (R_GetShaderSizes(p, &pwidth, &pheight, true))
|
|
width = (pwidth * (float)height) / pheight;
|
|
else
|
|
width = 64;
|
|
x = (320-(int)width)/2;
|
|
}
|
|
|
|
return MC_AddPicture(menu, x, y, width, height, picname);
|
|
}
|
|
|
|
menuoption_t *MC_AddCursorSmall(emenu_t *menu, menuresel_t *reselection, int x, int y)
|
|
{
|
|
menuoption_t *n = Z_Malloc(sizeof(menucommon_t));
|
|
if (reselection)
|
|
menu->reselection = reselection;
|
|
n->common.type = mt_menucursor;
|
|
n->common.iszone = true;
|
|
n->common.posx = x;
|
|
n->common.posy = y;
|
|
|
|
n->common.next = menu->options;
|
|
menu->options = (menuoption_t *)n;
|
|
|
|
|
|
if (menu->reselection)
|
|
{
|
|
menuoption_t *sel, *firstsel = M_NextSelectableItem(menu, NULL);
|
|
for (sel = firstsel; sel; )
|
|
{
|
|
if (sel->common.posx == menu->reselection->x && sel->common.posy == menu->reselection->y)
|
|
{
|
|
menu->selecteditem = sel;
|
|
n->common.posy = sel->common.posy;
|
|
break;
|
|
}
|
|
sel = M_NextSelectableItem(menu, sel);
|
|
if (sel == firstsel)
|
|
break;
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
|
|
menupicture_t *MC_AddCursor(emenu_t *menu, menuresel_t *reselection, int x, int y)
|
|
{
|
|
int i;
|
|
menupicture_t *n = Z_Malloc(sizeof(menupicture_t));
|
|
if (reselection)
|
|
menu->reselection = reselection;
|
|
n->common.type = mt_menudot;
|
|
n->common.iszone = true;
|
|
n->common.posx = x;
|
|
n->common.posy = y;
|
|
n->common.width = 20;
|
|
n->common.height = 20;
|
|
|
|
n->common.next = menu->options;
|
|
menu->options = (menuoption_t *)n;
|
|
|
|
switch(M_GameType())
|
|
{
|
|
#ifdef Q2CLIENT
|
|
case MGT_QUAKE2:
|
|
//AND QUAKE 2 WINS!!!
|
|
menudotstyle = "pics/m_cursor%i.pcx";
|
|
mindot = 0;
|
|
maxdots = 15;
|
|
dotofs=0;
|
|
|
|
//this is *obviously* the correct size... not.
|
|
n->common.width = 22;
|
|
n->common.height = 29;
|
|
break;
|
|
#endif
|
|
#ifdef HEXEN2
|
|
case MGT_HEXEN2:
|
|
//AND THE WINNER IS HEXEN 2!!!
|
|
menudotstyle = "gfx/menu/menudot%i.lmp";
|
|
mindot = 1;
|
|
maxdots = 8;
|
|
dotofs=-2;
|
|
break;
|
|
#endif
|
|
default:
|
|
//QUAKE 1 WINS BY DEFAULT!
|
|
menudotstyle = "gfx/menudot%i.lmp";
|
|
mindot = 1;
|
|
maxdots = 6;
|
|
dotofs=0;
|
|
break;
|
|
}
|
|
|
|
//cache them all. this avoids weird flickering as things get dynamically loaded
|
|
for (i = 0; i < maxdots; i++)
|
|
R2D_SafeCachePic(va(menudotstyle, i+mindot));
|
|
|
|
if (menu->reselection)
|
|
{
|
|
menuoption_t *sel, *firstsel = M_NextSelectableItem(menu, NULL);
|
|
for (sel = firstsel; sel; )
|
|
{
|
|
if (sel->common.posx == menu->reselection->x && sel->common.posy == menu->reselection->y)
|
|
{
|
|
menu->selecteditem = sel;
|
|
n->common.posy = sel->common.posy;
|
|
break;
|
|
}
|
|
sel = M_NextSelectableItem(menu, sel);
|
|
if (sel == firstsel)
|
|
break;
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
|
|
menuedit_t *MC_AddEdit(emenu_t *menu, int cx, int ex, int y, char *text, char *def)
|
|
{
|
|
menuedit_t *n = Z_Malloc(sizeof(menuedit_t)+strlen(text)+1);
|
|
n->slim = false;
|
|
n->common.type = mt_edit;
|
|
n->common.iszone = true;
|
|
n->common.posx = cx;
|
|
n->common.posy = y;
|
|
n->common.width = ex-cx+(17)*8;
|
|
n->common.height = n->slim?8:16;
|
|
n->modified = true;
|
|
n->captionwidth = ex-cx;
|
|
n->caption = (char *)(n+1);
|
|
strcpy((char *)(n+1), text);
|
|
Q_strncpyz(n->text, def, sizeof(n->text));
|
|
|
|
n->common.next = menu->options;
|
|
menu->options = (menuoption_t *)n;
|
|
return n;
|
|
}
|
|
|
|
menuedit_t *MC_AddEditCvar(emenu_t *menu, int cx, int ex, int y, char *text, char *name, qboolean isslim)
|
|
{
|
|
menuedit_t *n = Z_Malloc(sizeof(menuedit_t)+strlen(text)+1);
|
|
cvar_t *cvar;
|
|
cvar = Cvar_Get(name, "", CVAR_USERCREATED|CVAR_ARCHIVE, NULL); //well, this is a menu/
|
|
n->slim = isslim;
|
|
n->common.type = mt_edit;
|
|
n->common.iszone = true;
|
|
n->common.posx = cx;
|
|
n->common.posy = y;
|
|
n->common.width = ex-cx+(17)*8;
|
|
n->common.height = n->slim?8:16;
|
|
n->common.tooltip = cvar->description;
|
|
n->modified = true;
|
|
n->captionwidth = ex-cx;
|
|
n->caption = (char *)(n+1);
|
|
strcpy((char *)(n+1), text);
|
|
n->cvar = cvar;
|
|
#ifdef _DEBUG
|
|
if (!(cvar->flags & CVAR_ARCHIVE))
|
|
Con_Printf("Warning: %s is not set for archiving\n", cvar->name);
|
|
#endif
|
|
Q_strncpyz(n->text, cvar->string, sizeof(n->text));
|
|
|
|
n->common.next = menu->options;
|
|
menu->options = (menuoption_t *)n;
|
|
return n;
|
|
}
|
|
|
|
menubox_t *MC_AddBox(emenu_t *menu, int x, int y, int width, int height)
|
|
{
|
|
menubox_t *n = Z_Malloc(sizeof(menubox_t));
|
|
n->common.type = mt_box;
|
|
n->common.iszone = true;
|
|
n->common.posx = x;
|
|
n->common.posy = y;
|
|
n->width = width;
|
|
n->height = height;
|
|
|
|
n->common.next = menu->options;
|
|
menu->options = (menuoption_t *)n;
|
|
return n;
|
|
}
|
|
|
|
menucustom_t *MC_AddCustom(emenu_t *menu, int x, int y, void *dptr, int dint, const char *tooltip)
|
|
{
|
|
menucustom_t *n = Z_Malloc(sizeof(menucustom_t) + (tooltip?strlen(tooltip)+1:0));
|
|
n->common.type = mt_custom;
|
|
n->common.iszone = true;
|
|
n->common.posx = x;
|
|
n->common.posy = y;
|
|
n->dptr = dptr;
|
|
n->dint = dint;
|
|
n->common.tooltip = tooltip?strcpy((char*)(n+1), tooltip):NULL;
|
|
|
|
n->common.next = menu->options;
|
|
menu->options = (menuoption_t *)n;
|
|
return n;
|
|
}
|
|
|
|
menucheck_t *MC_AddCheckBox(emenu_t *menu, int tx, int cx, int y, const char *text, cvar_t *var, int bits)
|
|
{
|
|
menucheck_t *n = Z_Malloc(sizeof(menucheck_t)+strlen(text)+1);
|
|
n->common.type = mt_checkbox;
|
|
n->common.iszone = true;
|
|
n->common.posx = tx;
|
|
n->common.posy = y;
|
|
n->common.height = 8;
|
|
n->textwidth = cx - tx;
|
|
n->common.width = cx-tx + 7*8;
|
|
n->common.tooltip = var?var->description:NULL;
|
|
n->text = (char *)(n+1);
|
|
strcpy((char *)(n+1), text);
|
|
n->var = var;
|
|
n->bits = bits;
|
|
|
|
#ifdef _DEBUG
|
|
if (var)
|
|
{
|
|
if (!(var->flags & CVAR_ARCHIVE))
|
|
Con_Printf("Warning: %s is not set for archiving\n", var->name);
|
|
else if (var->flags & (CVAR_RENDERERLATCH|CVAR_VIDEOLATCH))
|
|
Con_Printf("Warning: %s requires a vid_restart\n", var->name);
|
|
}
|
|
#endif
|
|
|
|
n->common.next = menu->options;
|
|
menu->options = (menuoption_t *)n;
|
|
return n;
|
|
}
|
|
menuframe_t *MC_AddFrameStart(emenu_t *menu, int y)
|
|
{
|
|
menuframe_t *n = Z_Malloc(sizeof(menuframe_t));
|
|
n->common.type = mt_framestart;
|
|
n->common.iszone = true;
|
|
n->common.posx = 0;
|
|
n->common.posy = y;
|
|
n->common.height = 0;
|
|
n->common.width = 0;
|
|
|
|
n->common.next = menu->options;
|
|
menu->options = (menuoption_t *)n;
|
|
return n;
|
|
}
|
|
menuframe_t *MC_AddFrameEnd(emenu_t *menu, int y)
|
|
{
|
|
menuframe_t *n = Z_Malloc(sizeof(menuframe_t));
|
|
n->common.type = mt_frameend;
|
|
n->common.iszone = true;
|
|
n->common.posx = 0;
|
|
n->common.posy = y;
|
|
n->common.height = 0;
|
|
n->common.width = 0;
|
|
|
|
n->common.next = menu->options;
|
|
menu->options = (menuoption_t *)n;
|
|
return n;
|
|
}
|
|
menucheck_t *MC_AddCheckBoxFunc(emenu_t *menu, int tx, int cx, int y, const char *text, qboolean (*func) (menucheck_t *option, emenu_t *menu, chk_set_t set), int bits)
|
|
{
|
|
menucheck_t *n = Z_Malloc(sizeof(menucheck_t)+strlen(text)+1);
|
|
n->common.type = mt_checkbox;
|
|
n->common.iszone = true;
|
|
n->common.posx = tx;
|
|
n->common.posy = y;
|
|
n->common.height = 8;
|
|
n->textwidth = cx - tx;
|
|
n->common.width = cx-tx + 7*8;
|
|
n->text = (char *)(n+1);
|
|
strcpy((char *)(n+1), text);
|
|
n->func = func;
|
|
n->bits = bits;
|
|
|
|
n->common.next = menu->options;
|
|
menu->options = (menuoption_t *)n;
|
|
return n;
|
|
}
|
|
|
|
//delta may be 0
|
|
menuslider_t *MC_AddSlider(emenu_t *menu, int tx, int sx, int y, const char *text, cvar_t *var, float min, float max, float delta)
|
|
{
|
|
menuslider_t *n = Z_Malloc(sizeof(menuslider_t)+strlen(text)+1);
|
|
n->common.type = mt_slider;
|
|
n->common.iszone = true;
|
|
n->common.posx = tx;
|
|
n->common.posy = y;
|
|
n->common.height = 8;
|
|
n->common.width = sx-tx + (SLIDER_RANGE+5)*8;
|
|
n->common.tooltip = var->description;
|
|
n->var = var;
|
|
n->textwidth = sx-tx;
|
|
n->text = (char *)(n+1);
|
|
strcpy((char *)(n+1), text);
|
|
|
|
if (var)
|
|
{
|
|
n->current = var->value;
|
|
|
|
#ifdef _DEBUG
|
|
if (!(var->flags & CVAR_ARCHIVE))
|
|
Con_Printf("Warning: %s is not set for archiving\n", var->name);
|
|
#endif
|
|
}
|
|
|
|
n->min = min;
|
|
n->max = max;
|
|
n->smallchange = delta;
|
|
n->largechange = delta*5;
|
|
|
|
n->common.next = menu->options;
|
|
menu->options = (menuoption_t *)n;
|
|
|
|
return n;
|
|
}
|
|
|
|
menucombo_t *MC_AddCombo(emenu_t *menu, int tx, int cx, int y, const char *caption, const char **ops, int initialvalue)
|
|
{
|
|
int numopts;
|
|
int optlen;
|
|
int maxoptlen;
|
|
int optbufsize;
|
|
menucombo_t *n;
|
|
char **newops;
|
|
char *optbuf;
|
|
int i;
|
|
|
|
maxoptlen = 0;
|
|
optbufsize = sizeof(char*);
|
|
numopts = 0;
|
|
optlen = 0;
|
|
while(ops[numopts])
|
|
{
|
|
optlen = strlen(ops[numopts]);
|
|
if (maxoptlen < optlen)
|
|
maxoptlen = optlen;
|
|
optbufsize += optlen+1+sizeof(char*);
|
|
numopts++;
|
|
}
|
|
|
|
|
|
n = Z_Malloc(sizeof(*n) + optbufsize);
|
|
newops = (char **)(n+1);
|
|
optbuf = (char*)(newops + numopts+1);
|
|
n->common.type = mt_combo;
|
|
n->common.iszone = true;
|
|
n->common.posx = tx;
|
|
n->common.posy = y;
|
|
n->common.height = 8;
|
|
n->common.width = cx-tx + maxoptlen*8;
|
|
n->captionwidth = cx-tx;
|
|
n->caption = caption;
|
|
n->options = (const char **)newops;
|
|
|
|
n->common.next = menu->options;
|
|
menu->options = (menuoption_t *)n;
|
|
|
|
n->numoptions = numopts;
|
|
for (i = 0; i < numopts; i++)
|
|
{
|
|
strcpy(optbuf, ops[i]);
|
|
newops[i] = optbuf;
|
|
optbuf += strlen(optbuf)+1;
|
|
}
|
|
newops[i] = NULL;
|
|
|
|
if (initialvalue && initialvalue >= n->numoptions)
|
|
{
|
|
Con_Printf("WARNING: Fixed initialvalue for %s\n", caption);
|
|
initialvalue = n->numoptions-1;
|
|
}
|
|
n->selectedoption = initialvalue;
|
|
|
|
return n;
|
|
}
|
|
menucombo_t *MC_AddCvarCombo(emenu_t *menu, int tx, int cx, int y, const char *caption, cvar_t *cvar, const char **ops, const char **values)
|
|
{
|
|
int numopts;
|
|
int optlen;
|
|
int maxoptlen;
|
|
int optbufsize;
|
|
menucombo_t *n;
|
|
char **newops;
|
|
char **newvalues;
|
|
char *optbuf;
|
|
int i;
|
|
|
|
maxoptlen = 0;
|
|
optbufsize = sizeof(char*)*2 + strlen(caption)+1;
|
|
numopts = 0;
|
|
optlen = 0;
|
|
while(ops[numopts])
|
|
{
|
|
optlen = strlen(ops[numopts]);
|
|
if (maxoptlen < optlen)
|
|
maxoptlen = optlen;
|
|
optbufsize += optlen+1+sizeof(char*);
|
|
optbufsize += strlen(values[numopts])+1+sizeof(char*);
|
|
numopts++;
|
|
}
|
|
|
|
|
|
|
|
n = Z_Malloc(sizeof(*n) + optbufsize);
|
|
newops = (char **)(n+1);
|
|
newvalues = (char**)(newops + numopts+1);
|
|
optbuf = (char*)(newvalues + numopts+1);
|
|
n->common.type = mt_combo;
|
|
n->common.iszone = true;
|
|
n->common.posx = tx;
|
|
n->common.posy = y;
|
|
n->common.height = 8;
|
|
n->common.width = cx-tx + maxoptlen*8;
|
|
n->common.tooltip = cvar->description;
|
|
n->captionwidth = cx-tx;
|
|
|
|
strcpy(optbuf, caption);
|
|
n->caption = optbuf;
|
|
optbuf += strlen(optbuf)+1;
|
|
|
|
n->options = (char const*const*)newops;
|
|
n->values = (char const*const*)newvalues;
|
|
n->cvar = cvar;
|
|
|
|
// if (!(cvar->flags & CVAR_ARCHIVE))
|
|
// Con_Printf("Warning: %s is not set for archiving\n", cvar->name);
|
|
|
|
n->selectedoption = 0;
|
|
|
|
n->common.next = menu->options;
|
|
menu->options = (menuoption_t *)n;
|
|
|
|
n->numoptions = numopts;
|
|
for (i = 0; i < numopts; i++)
|
|
{
|
|
if (!strcmp(values[i], cvar->string))
|
|
n->selectedoption = i;
|
|
|
|
strcpy(optbuf, ops[i]);
|
|
newops[i] = optbuf;
|
|
optbuf += strlen(optbuf)+1;
|
|
|
|
strcpy(optbuf, values[i]);
|
|
newvalues[i] = optbuf;
|
|
optbuf += strlen(optbuf)+1;
|
|
}
|
|
newops[i] = NULL;
|
|
newvalues[i] = NULL;
|
|
|
|
return n;
|
|
}
|
|
|
|
menubutton_t *MC_AddConsoleCommand(emenu_t *menu, int lhs, int rhs, int y, const char *text, const char *command)
|
|
{
|
|
menubutton_t *n = Z_Malloc(sizeof(menubutton_t)+strlen(text)+1+strlen(command)+1);
|
|
n->common.type = mt_button;
|
|
n->common.iszone = true;
|
|
n->common.posx = lhs;
|
|
n->common.posy = y;
|
|
n->common.height = 8;
|
|
n->common.width = rhs?rhs - lhs:strlen(text)*8;
|
|
n->rightalign = true;
|
|
n->text = (char *)(n+1);
|
|
strcpy((char *)(n+1), text);
|
|
n->command = n->text + strlen(n->text)+1;
|
|
strcpy((char *)n->command, command);
|
|
|
|
n->common.next = menu->options;
|
|
menu->options = (menuoption_t *)n;
|
|
return n;
|
|
}
|
|
|
|
menubutton_t *MC_AddConsoleCommandQBigFont(emenu_t *menu, int x, int y, const char *text, const char *command)
|
|
{
|
|
menubutton_t *n = Z_Malloc(sizeof(menubutton_t)+strlen(text)+1+strlen(command)+1);
|
|
n->common.type = mt_qbuttonbigfont;
|
|
n->common.iszone = true;
|
|
n->common.posx = x;
|
|
n->common.posy = y;
|
|
n->common.height = 20;
|
|
n->common.width = strlen(text)*20;
|
|
n->text = (char *)(n+1);
|
|
strcpy((char *)(n+1), text);
|
|
n->command = n->text + strlen(n->text)+1;
|
|
strcpy((char *)n->command, command);
|
|
|
|
n->common.next = menu->options;
|
|
menu->options = (menuoption_t *)n;
|
|
return n;
|
|
}
|
|
#ifdef HEXEN2
|
|
menubutton_t *MC_AddConsoleCommandHexen2BigFont(emenu_t *menu, int x, int y, const char *text, const char *command)
|
|
{
|
|
menubutton_t *n = Z_Malloc(sizeof(menubutton_t)+strlen(text)+1+strlen(command)+1);
|
|
n->common.type = mt_hexen2buttonbigfont;
|
|
n->common.iszone = true;
|
|
n->common.posx = x;
|
|
n->common.posy = y;
|
|
n->common.height = 20;
|
|
n->common.width = strlen(text)*20;
|
|
n->text = (char *)(n+1);
|
|
strcpy((char *)(n+1), text);
|
|
n->command = n->text + strlen(n->text)+1;
|
|
strcpy((char *)n->command, command);
|
|
|
|
n->common.next = menu->options;
|
|
menu->options = (menuoption_t *)n;
|
|
return n;
|
|
}
|
|
#endif
|
|
menubutton_t *MC_AddCommand(emenu_t *menu, int lhs, int rhs, int y, char *text, qboolean (*command) (union menuoption_s *,struct emenu_s *,int))
|
|
{
|
|
menubutton_t *n = Z_Malloc(sizeof(menubutton_t));
|
|
n->common.type = mt_button;
|
|
n->common.iszone = true;
|
|
n->common.posx = lhs;
|
|
n->common.posy = y;
|
|
n->rightalign = true;
|
|
n->text = text;
|
|
n->command = NULL;
|
|
n->key = command;
|
|
n->common.height = 8;
|
|
n->common.width = rhs?rhs-lhs:strlen(text)*8;
|
|
|
|
n->common.next = menu->options;
|
|
menu->options = (menuoption_t *)n;
|
|
return n;
|
|
}
|
|
|
|
menubutton_t *VARGS MC_AddConsoleCommandf(emenu_t *menu, int lhs, int rhs, int y, int rightalign, const char *text, char *command, ...)
|
|
{
|
|
va_list argptr;
|
|
static char string[1024];
|
|
menubutton_t *n;
|
|
|
|
va_start (argptr, command);
|
|
vsnprintf (string,sizeof(string)-1, command,argptr);
|
|
va_end (argptr);
|
|
|
|
n = Z_Malloc(sizeof(menubutton_t) + strlen(string)+1);
|
|
n->common.type = mt_button;
|
|
n->common.iszone = true;
|
|
n->common.posx = lhs;
|
|
n->common.posy = y;
|
|
n->common.width = rhs-lhs;
|
|
n->rightalign = rightalign;
|
|
n->text = text;
|
|
n->command = (char *)(n+1);
|
|
strcpy((char *)(n+1), string);
|
|
|
|
n->common.next = menu->options;
|
|
menu->options = (menuoption_t *)n;
|
|
return n;
|
|
}
|
|
|
|
void MC_Slider_Key(menuslider_t *option, int key)
|
|
{
|
|
float range = option->current;
|
|
float delta;
|
|
|
|
float ix = option->vx;
|
|
float ex = ix + 10*8;
|
|
|
|
if (option->smallchange)
|
|
delta = option->smallchange;
|
|
else
|
|
delta = 0.1;
|
|
|
|
if (key == K_LEFTARROW || key == K_KP_LEFTARROW || key == K_GP_DPAD_LEFT || key == K_GP_A || key == K_MWHEELDOWN)
|
|
{
|
|
range -= delta;
|
|
if (option->min > option->max)
|
|
range = bound(option->max, range, option->min);
|
|
else
|
|
range = bound(option->min, range, option->max);
|
|
option->current = range;
|
|
}
|
|
else if (key == K_RIGHTARROW || key == K_KP_RIGHTARROW || key == K_GP_DPAD_RIGHT || key == K_GP_B || key == K_MWHEELUP)
|
|
{
|
|
range += delta;
|
|
if (option->min > option->max)
|
|
range = bound(option->max, range, option->min);
|
|
else
|
|
range = bound(option->min, range, option->max);
|
|
option->current = range;
|
|
}
|
|
else if (key == K_MOUSE1 && mousecursor_x >= ix-8 && mousecursor_x < ex+8)
|
|
{
|
|
range = (mousecursor_x - ix) / (ex - ix);
|
|
range = option->min + range*(option->max-option->min);
|
|
if (option->min > option->max)
|
|
range = bound(option->max, range, option->min);
|
|
else
|
|
range = bound(option->min, range, option->max);
|
|
option->current = range;
|
|
}
|
|
else if (key == K_ENTER || key == K_KP_ENTER || key == K_GP_START || key == K_MOUSE1)
|
|
{
|
|
if (range == option->max)
|
|
range = option->min;
|
|
else
|
|
{
|
|
range += delta;
|
|
if (option->min > option->max)
|
|
{
|
|
if (range < option->max-delta/2)
|
|
range = option->max;
|
|
}
|
|
else
|
|
if (range > option->max-delta/2)
|
|
range = option->max;
|
|
}
|
|
option->current = range;
|
|
}
|
|
else if (key == K_DEL || key == K_BACKSPACE)
|
|
{
|
|
if (option->var && option->var->defaultstr)
|
|
option->current = atof(option->var->defaultstr);
|
|
else
|
|
option->current = (option->max-option->min)/2;
|
|
}
|
|
else
|
|
return;
|
|
|
|
S_LocalSound ("misc/menu2.wav");
|
|
if (option->var)
|
|
Cvar_SetValue(option->var, option->current);
|
|
}
|
|
|
|
void MC_CheckBox_Key(menucheck_t *option, emenu_t *menu, int key)
|
|
{
|
|
if (key != K_ENTER && key != K_KP_ENTER && key != K_GP_START && key != K_GP_A && key != K_GP_B && key != K_LEFTARROW && key != K_KP_LEFTARROW && key != K_GP_DPAD_LEFT && key != K_RIGHTARROW && key != K_KP_LEFTARROW && key != K_GP_DPAD_RIGHT && key != K_MWHEELUP && key != K_MWHEELDOWN && key != K_MOUSE1)
|
|
return;
|
|
if (option->func)
|
|
option->func(option, menu, CHK_TOGGLE);
|
|
else if (!option->var)
|
|
option->value = !option->value;
|
|
else
|
|
{
|
|
if (option->bits)
|
|
{
|
|
int old;
|
|
if (option->var->latched_string)
|
|
old = atoi(option->var->latched_string);
|
|
else
|
|
old = option->var->value;
|
|
|
|
if (old & option->bits)
|
|
Cvar_SetValue(option->var, old&~option->bits);
|
|
else
|
|
Cvar_SetValue(option->var, old|option->bits);
|
|
}
|
|
else
|
|
{
|
|
if (option->var->latched_string)
|
|
Cvar_SetValue(option->var, !atof(option->var->latched_string));
|
|
else
|
|
Cvar_SetValue(option->var, !option->var->value);
|
|
}
|
|
S_LocalSound ("misc/menu2.wav");
|
|
}
|
|
}
|
|
|
|
void MC_EditBox_Key(menuedit_t *edit, int key, unsigned int unicode)
|
|
{
|
|
int len = strlen(edit->text);
|
|
if (key == K_DEL || key == K_BACKSPACE)
|
|
{
|
|
if (!len)
|
|
return;
|
|
edit->text[len-1] = '\0';
|
|
}
|
|
else if (!unicode)
|
|
return;
|
|
else
|
|
{
|
|
if (unicode < 128)
|
|
{
|
|
if (len < sizeof(edit->text))
|
|
{
|
|
edit->text[len] = unicode;
|
|
edit->text[len+1] = '\0';
|
|
}
|
|
}
|
|
}
|
|
|
|
edit->modified = true;
|
|
|
|
if (edit->cvar)
|
|
{
|
|
Cvar_Set(edit->cvar, edit->text);
|
|
S_LocalSound ("misc/menu2.wav");
|
|
}
|
|
}
|
|
|
|
void MC_Combo_Key(menucombo_t *combo, int key)
|
|
{
|
|
if (key == K_ENTER || key == K_KP_ENTER || key == K_GP_START || key == K_RIGHTARROW || key == K_KP_RIGHTARROW || key == K_GP_DPAD_RIGHT || key == K_GP_B || key == K_MWHEELDOWN || key == K_MOUSE1)
|
|
{
|
|
combo->selectedoption++;
|
|
if (combo->selectedoption >= combo->numoptions)
|
|
combo->selectedoption = 0;
|
|
|
|
changed:
|
|
if (combo->cvar && combo->numoptions)
|
|
Cvar_Set(combo->cvar, (char *)combo->values[combo->selectedoption]);
|
|
S_LocalSound ("misc/menu2.wav");
|
|
}
|
|
else if (key == K_LEFTARROW || key == K_KP_LEFTARROW || key == K_GP_DPAD_LEFT || key == K_GP_A || key == K_MWHEELUP)
|
|
{
|
|
combo->selectedoption--;
|
|
if (combo->selectedoption < 0)
|
|
combo->selectedoption = combo->numoptions?combo->numoptions-1:0;
|
|
goto changed;
|
|
}
|
|
}
|
|
|
|
static qboolean menu_mousedown;
|
|
extern menu_t *menu_script;
|
|
static void M_Draw (menu_t *menu)
|
|
{
|
|
emenu_t *m = (emenu_t*)menu;
|
|
qboolean stillactive = false;
|
|
|
|
if (!Key_Dest_Has(kdm_menu))
|
|
{
|
|
M_RemoveAllMenus(false);
|
|
menu_mousedown = false;
|
|
return;
|
|
}
|
|
if ((!menu_script || scr_con_current))
|
|
{
|
|
if (m->selecteditem && m->selecteditem->common.type == mt_slider && (m->selecteditem->slider.var == &v_gamma || m->selecteditem->slider.var == &v_contrast))
|
|
/*no menu tint if we're trying to adjust gamma*/;
|
|
else
|
|
R2D_FadeScreen ();
|
|
}
|
|
|
|
R2D_ImageColours(1, 1, 1, 1);
|
|
|
|
if (m)
|
|
{
|
|
M_CheckMouseMove(m);
|
|
|
|
MenuDraw(m);
|
|
stillactive = true;
|
|
}
|
|
|
|
if (!stillactive)
|
|
Key_Dest_Remove(kdm_menu);
|
|
}
|
|
|
|
static qboolean M_KeyEvent(menu_t *m, qboolean isdown, unsigned int devid, int key, int unicode)
|
|
{
|
|
emenu_t *menu = (emenu_t*)m;
|
|
if (isdown)
|
|
{
|
|
if (key == K_MOUSE1) //mouse clicks are deferred until the release event. this is for touch screens and aiming.
|
|
{
|
|
if (menu->mouseitem && menu->mouseitem->common.type == mt_frameend)
|
|
menu->mouseitem->frame.mousedown = true;
|
|
else
|
|
menu_mousedown = true;
|
|
}
|
|
else if (key == K_LSHIFT || key == K_RSHIFT || key == K_LALT || key == K_RALT || key == K_LCTRL || key == K_RCTRL)
|
|
; //modifiers are sent on up events instead.
|
|
else
|
|
M_Complex_Key (menu, key, unicode);
|
|
return true; //eat all keys...
|
|
}
|
|
else
|
|
{
|
|
if (key == K_MOUSE1 && menu_mousedown)
|
|
M_Complex_Key (menu, key, unicode);
|
|
else if (key == K_LSHIFT || key == K_RSHIFT || key == K_LALT || key == K_RALT || key == K_LCTRL || key == K_RCTRL)
|
|
M_Complex_Key (menu, key, unicode);
|
|
menu_mousedown = false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void M_Release (menu_t *m)
|
|
{
|
|
emenu_t *menu = (emenu_t*)m;
|
|
menuoption_t *op, *oop;
|
|
if (menu->reselection)
|
|
{
|
|
menu->reselection->x = menu->selecteditem->common.posx;
|
|
menu->reselection->y = menu->selecteditem->common.posy;
|
|
}
|
|
|
|
if (menu->remove)
|
|
menu->remove(menu);
|
|
|
|
op = menu->options;
|
|
while(op)
|
|
{
|
|
oop = op;
|
|
op = op->common.next;
|
|
if (oop->common.iszone)
|
|
Z_Free(oop);
|
|
}
|
|
menu->options=NULL;
|
|
|
|
if (menu->tooltip)
|
|
{
|
|
Z_Free(menu->tooltip);
|
|
menu->tooltip = NULL;
|
|
}
|
|
|
|
if (menu->iszone)
|
|
{
|
|
menu->iszone=false;
|
|
Z_Free(menu);
|
|
}
|
|
}
|
|
|
|
emenu_t *M_CreateMenu (int extrasize)
|
|
{
|
|
emenu_t *menu;
|
|
menu = Z_Malloc(sizeof(emenu_t)+extrasize);
|
|
menu->iszone=true;
|
|
menu->data = menu+1;
|
|
|
|
menu->menu.cursor = &key_customcursor[kc_console];
|
|
/*void (*videoreset) (struct menu_s *); //called after a video mode switch / shader reload.
|
|
void (*release) (struct menu_s *); //
|
|
qboolean (*keyevent)(struct menu_s *, qboolean isdown, unsigned int devid, int key, int unicode); //true if key was handled
|
|
qboolean (*mousemove)(struct menu_s *, qboolean abs, unsigned int devid, float x, float y);
|
|
qboolean (*joyaxis) (struct menu_s *, unsigned int devid, int axis, float val);
|
|
void (*drawmenu) (struct menu_s *);
|
|
*/
|
|
menu->menu.drawmenu = M_Draw;
|
|
menu->menu.keyevent = M_KeyEvent;
|
|
menu->menu.release = M_Release;
|
|
Menu_Push(&menu->menu, false);
|
|
|
|
return menu;
|
|
}
|
|
void M_RemoveMenu (emenu_t *menu)
|
|
{
|
|
Menu_Unlink((menu_t*)menu);
|
|
}
|
|
|
|
void M_ReloadMenus(void)
|
|
{
|
|
menu_t *m;
|
|
|
|
for (m = topmenu; m; m = m->prev)
|
|
{
|
|
if (m->videoreset)
|
|
m->videoreset(m);
|
|
}
|
|
}
|
|
|
|
void M_RemoveAllMenus (qboolean leaveprompts)
|
|
{
|
|
menu_t **link, *m;
|
|
|
|
for (link = &topmenu; *link; )
|
|
{
|
|
m = *link;
|
|
if (m->persist && leaveprompts)
|
|
link = &m->prev;
|
|
else
|
|
Menu_Unlink(m);
|
|
}
|
|
|
|
}
|
|
void M_MenuPop_f (void)
|
|
{
|
|
if (!topmenu)
|
|
return;
|
|
Menu_Unlink(topmenu);
|
|
}
|
|
|
|
menuoption_t *M_NextItem(emenu_t *m, menuoption_t *old)
|
|
{
|
|
menuoption_t *op = m->options;
|
|
while(op->common.next)
|
|
{
|
|
if (op->common.next == old)
|
|
return op;
|
|
|
|
op = op->common.next;
|
|
}
|
|
return op;
|
|
}
|
|
menuoption_t *M_NextSelectableItem(emenu_t *m, menuoption_t *old)
|
|
{
|
|
menuoption_t *op;
|
|
|
|
if (!old)
|
|
old = M_NextItem(m, old);
|
|
|
|
op = old;
|
|
|
|
while (1)
|
|
{
|
|
if (!op)
|
|
op = m->options;
|
|
|
|
op = M_NextItem(m, op);
|
|
if (!op)
|
|
op = m->options;
|
|
|
|
if (op == old)
|
|
{
|
|
if (op->common.type == mt_slider || op->common.type == mt_checkbox || op->common.type == mt_button || op->common.type == mt_hexen2buttonbigfont || op->common.type == mt_qbuttonbigfont || op->common.type == mt_edit || op->common.type == mt_combo || op->common.type == mt_bind || (op->common.type == mt_custom && op->custom.key))
|
|
return op;
|
|
return NULL; //whoops.
|
|
}
|
|
|
|
if (op->common.type == mt_slider || op->common.type == mt_checkbox || op->common.type == mt_button || op->common.type == mt_hexen2buttonbigfont || op->common.type == mt_qbuttonbigfont || op->common.type == mt_edit || op->common.type == mt_combo || op->common.type == mt_bind || (op->common.type == mt_custom && op->custom.key))
|
|
if (!op->common.ishidden)
|
|
return op;
|
|
}
|
|
}
|
|
|
|
menuoption_t *M_PrevSelectableItem(emenu_t *m, menuoption_t *old)
|
|
{
|
|
menuoption_t *op;
|
|
|
|
if (!old)
|
|
old = m->options;
|
|
|
|
op = old;
|
|
|
|
while (1)
|
|
{
|
|
if (!op)
|
|
op = m->options;
|
|
|
|
op = op->common.next;
|
|
if (!op)
|
|
op = m->options;
|
|
|
|
if (op == old)
|
|
return old; //whoops.
|
|
|
|
if (op->common.type == mt_slider || op->common.type == mt_checkbox || op->common.type == mt_button || op->common.type == mt_hexen2buttonbigfont || op->common.type == mt_qbuttonbigfont || op->common.type == mt_edit || op->common.type == mt_combo || op->common.type == mt_bind || (op->common.type == mt_custom && op->custom.key))
|
|
if (!op->common.ishidden)
|
|
return op;
|
|
}
|
|
}
|
|
|
|
void M_Complex_Key(emenu_t *currentmenu, int key, int unicode)
|
|
{
|
|
if (!currentmenu)
|
|
return; //erm...
|
|
|
|
M_CheckMouseMove(currentmenu);
|
|
|
|
if (currentmenu->key)
|
|
if (currentmenu->key(key, currentmenu))
|
|
return;
|
|
|
|
if (currentmenu->selecteditem && currentmenu->selecteditem->common.type == mt_custom && (key == K_DOWNARROW || key == K_KP_DOWNARROW || key == K_GP_DPAD_DOWN || key == K_GP_A || key == K_GP_B || key == K_UPARROW || key == K_KP_UPARROW || key == K_GP_DPAD_UP || key == K_TAB || key == K_MWHEELUP || key == K_MWHEELDOWN))
|
|
if (currentmenu->selecteditem->custom.key)
|
|
if (currentmenu->selecteditem->custom.key(¤tmenu->selecteditem->custom, currentmenu, key, unicode))
|
|
return;
|
|
|
|
if (currentmenu->selecteditem && currentmenu->selecteditem->common.type == mt_bind)
|
|
{
|
|
if (bindingactive)
|
|
{
|
|
//don't let key 0 be bound here. unicode-only keys are also not bindable.
|
|
if (key == 0)
|
|
return;
|
|
|
|
#ifdef HEXEN2
|
|
if (M_GameType() == MGT_HEXEN2)
|
|
S_LocalSound ("raven/menu1.wav");
|
|
else
|
|
#endif
|
|
S_LocalSound ("misc/menu1.wav");
|
|
|
|
if (key != K_ESCAPE && key != '`')
|
|
{
|
|
int modifiers = 0;
|
|
extern qboolean keydown[];
|
|
if (keydown[K_LSHIFT] && key != K_LSHIFT)
|
|
modifiers |= 1;
|
|
if (keydown[K_RSHIFT] && key != K_RSHIFT)
|
|
modifiers |= 1;
|
|
if (keydown[K_LALT] && key != K_LALT)
|
|
modifiers |= 2;
|
|
if (keydown[K_RALT] && key != K_RALT)
|
|
modifiers |= 2;
|
|
if (keydown[K_LCTRL] && key != K_LCTRL)
|
|
modifiers |= 4;
|
|
if (keydown[K_RCTRL] && key != K_RCTRL)
|
|
modifiers |= 4;
|
|
|
|
Cbuf_InsertText (va("bind \"%s\" \"%s\"\n", Key_KeynumToString (key, modifiers), currentmenu->selecteditem->bind.command), RESTRICT_LOCAL, false);
|
|
}
|
|
bindingactive = false;
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch(key)
|
|
{
|
|
case K_MOUSE2:
|
|
case K_ESCAPE:
|
|
case K_GP_BACK:
|
|
//remove
|
|
M_RemoveMenu(currentmenu);
|
|
#ifdef HEXEN2
|
|
if (M_GameType() == MGT_HEXEN2)
|
|
S_LocalSound ("raven/menu3.wav");
|
|
else
|
|
#endif
|
|
S_LocalSound ("misc/menu3.wav");
|
|
break;
|
|
case K_TAB:
|
|
case K_DOWNARROW:
|
|
case K_KP_DOWNARROW:
|
|
case K_GP_DPAD_DOWN:
|
|
currentmenu->selecteditem = M_NextSelectableItem(currentmenu, currentmenu->selecteditem);
|
|
|
|
if (currentmenu->selecteditem)
|
|
{
|
|
#ifdef HEXEN2
|
|
if (M_GameType() == MGT_HEXEN2)
|
|
S_LocalSound ("raven/menu1.wav");
|
|
else
|
|
#endif
|
|
S_LocalSound ("misc/menu1.wav");
|
|
|
|
if (currentmenu->cursoritem)
|
|
currentmenu->cursoritem->common.posy = currentmenu->selecteditem->common.posy;
|
|
}
|
|
break;
|
|
case K_UPARROW:
|
|
case K_KP_UPARROW:
|
|
case K_GP_DPAD_UP:
|
|
currentmenu->selecteditem = M_PrevSelectableItem(currentmenu, currentmenu->selecteditem);
|
|
|
|
if (currentmenu->selecteditem)
|
|
{
|
|
#ifdef HEXEN2
|
|
if (M_GameType() == MGT_HEXEN2)
|
|
S_LocalSound ("raven/menu1.wav");
|
|
else
|
|
#endif
|
|
S_LocalSound ("misc/menu1.wav");
|
|
|
|
if (currentmenu->cursoritem)
|
|
currentmenu->cursoritem->common.posy = currentmenu->selecteditem->common.posy;
|
|
}
|
|
break;
|
|
|
|
case K_MOUSE1:
|
|
case K_MOUSE3:
|
|
case K_MOUSE4:
|
|
case K_MOUSE5:
|
|
case K_MOUSE6:
|
|
case K_MOUSE7:
|
|
case K_MOUSE8:
|
|
case K_MOUSE9:
|
|
case K_MOUSE10:
|
|
case K_MWHEELUP:
|
|
case K_MWHEELDOWN:
|
|
if (!currentmenu->mouseitem)
|
|
break;
|
|
if (currentmenu->mouseitem && currentmenu->selecteditem != currentmenu->mouseitem)
|
|
{
|
|
currentmenu->selecteditem = currentmenu->mouseitem;
|
|
#ifdef HEXEN2
|
|
if (M_GameType() == MGT_HEXEN2)
|
|
S_LocalSound ("raven/menu1.wav");
|
|
else
|
|
#endif
|
|
S_LocalSound ("misc/menu1.wav");
|
|
|
|
if (currentmenu->cursoritem)
|
|
currentmenu->cursoritem->common.posy = currentmenu->selecteditem->common.posy;
|
|
}
|
|
//fall through
|
|
default:
|
|
if (!currentmenu->selecteditem)
|
|
{
|
|
if (!currentmenu->options)
|
|
return;
|
|
currentmenu->selecteditem = currentmenu->options;
|
|
}
|
|
switch(currentmenu->selecteditem->common.type)
|
|
{
|
|
case mt_slider:
|
|
MC_Slider_Key(¤tmenu->selecteditem->slider, key);
|
|
break;
|
|
case mt_checkbox:
|
|
MC_CheckBox_Key(¤tmenu->selecteditem->check, currentmenu, key);
|
|
break;
|
|
case mt_button:
|
|
case mt_hexen2buttonbigfont:
|
|
case mt_qbuttonbigfont:
|
|
if (!currentmenu->selecteditem->button.command)
|
|
currentmenu->selecteditem->button.key(currentmenu->selecteditem, currentmenu, key);
|
|
else if (key == K_ENTER || key == K_KP_ENTER || key == K_GP_START || key == K_GP_A || key == K_GP_B || key == K_MOUSE1)
|
|
{
|
|
Cbuf_AddText(currentmenu->selecteditem->button.command, RESTRICT_LOCAL);
|
|
#ifdef HEXEN2
|
|
if (M_GameType() == MGT_HEXEN2)
|
|
S_LocalSound ("raven/menu2.wav");
|
|
else
|
|
#endif
|
|
S_LocalSound ("misc/menu2.wav");
|
|
}
|
|
break;
|
|
case mt_custom:
|
|
if (currentmenu->selecteditem->custom.key)
|
|
currentmenu->selecteditem->custom.key(¤tmenu->selecteditem->custom, currentmenu, key, unicode);
|
|
break;
|
|
case mt_edit:
|
|
MC_EditBox_Key(¤tmenu->selecteditem->edit, key, unicode);
|
|
break;
|
|
case mt_combo:
|
|
MC_Combo_Key(¤tmenu->selecteditem->combo, key);
|
|
break;
|
|
case mt_bind:
|
|
if (key == K_ENTER || key == K_KP_ENTER || key == K_GP_START || key == K_GP_A || key == K_GP_B || key == K_MOUSE1)
|
|
bindingactive = true;
|
|
else if (key == K_BACKSPACE || key == K_DEL)
|
|
M_UnbindCommand (currentmenu->selecteditem->bind.command);
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
qboolean MC_Main_Key (int key, emenu_t *menu) //here purly to restart demos.
|
|
{
|
|
if (key == K_ESCAPE || key == K_GP_BACK || key == K_MOUSE2)
|
|
{
|
|
extern cvar_t con_stayhidden;
|
|
|
|
//don't spam menu open+close events if we're not going to be allowing the console to appear
|
|
if (con_stayhidden.ival && cls.state == ca_disconnected)
|
|
if (!CL_TryingToConnect())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static int M_Main_AddExtraOptions(emenu_t *mainm, int y)
|
|
{
|
|
if (Cmd_AliasExist("mod_menu", RESTRICT_LOCAL))
|
|
{MC_AddConsoleCommandQBigFont(mainm, 72, y, va("%-14s", Cvar_Get("mod_menu", "Mod Menu", 0, NULL)->string), "mod_menu\n"); y += 20;}
|
|
if (Cmd_Exists("xmpp"))
|
|
{MC_AddConsoleCommandQBigFont(mainm, 72, y, "Social ", "xmpp\n"); y += 20;}
|
|
if (Cmd_Exists("irc"))
|
|
{MC_AddConsoleCommandQBigFont(mainm, 72, y, "IRC ", "irc\n"); y += 20;}
|
|
if (Cmd_Exists("qi"))
|
|
{MC_AddConsoleCommandQBigFont(mainm, 72, y, "Quake Injector", "qi\n"); y += 20;}
|
|
else if (PM_CanInstall("qi"))
|
|
{MC_AddConsoleCommandQBigFont(mainm, 72, y, "Get Quake Injector", "pkg reset; pkg add qi; pkg apply\n"); y += 20;}
|
|
if (Cmd_Exists("menu_download"))
|
|
{
|
|
#ifdef WEBCLIENT
|
|
MC_AddConsoleCommandQBigFont(mainm, 72, y, "Updates ", "menu_download\n"); y += 20;
|
|
#else
|
|
MC_AddConsoleCommandQBigFont(mainm, 72, y, "Packages ", "menu_download\n"); y += 20;
|
|
#endif
|
|
}
|
|
if (Cmd_Exists("menu_mods"))
|
|
{
|
|
MC_AddConsoleCommandQBigFont(mainm, 72, y, "Mods ", "menu_mods\n"); y += 20;
|
|
y += 20;
|
|
}
|
|
|
|
return y;
|
|
}
|
|
|
|
void M_Menu_Main_f (void)
|
|
{
|
|
extern cvar_t m_helpismedia;
|
|
menubutton_t *b;
|
|
emenu_t *mainm = NULL;
|
|
mpic_t *p;
|
|
static menuresel_t resel;
|
|
int y;
|
|
|
|
#ifndef SERVERONLY
|
|
if (isDedicated || !Renderer_Started())
|
|
return;
|
|
#endif
|
|
|
|
#ifdef CSQC_DAT
|
|
if (CSQC_ConsoleCommand(-1, va("%s %s", Cmd_Argv(0), Cmd_Args())))
|
|
return;
|
|
#endif
|
|
|
|
/* if (cls.demoplayback)
|
|
{
|
|
m_save_demonum = cls.demonum;
|
|
cls.demonum = -1;
|
|
}
|
|
*/
|
|
SCR_EndLoadingPlaque(); //just in case...
|
|
|
|
/*
|
|
if (0)
|
|
{
|
|
int x, i;
|
|
guiinfo_t *gui;
|
|
m_state = m_complex;
|
|
key_dest = key_menu;
|
|
m_entersound = true;
|
|
|
|
mainm = M_CreateMenu(sizeof(guiinfo_t));
|
|
mainm->key = MC_GuiKey;
|
|
mainm->xpos=0;
|
|
gui = (guiinfo_t *)mainm->data;
|
|
gui->text[0] = "Single";
|
|
gui->text[1] = "Multiplayer";
|
|
gui->text[2] = "Quit";
|
|
for (x = 0, i = 0; gui->text[i]; i++)
|
|
{
|
|
gui->op[i] = MC_AddRedText(mainm, x, 0, gui->text[i], false);
|
|
x+=(strlen(gui->text[i])+1)*8;
|
|
}
|
|
return;
|
|
}
|
|
*/
|
|
|
|
S_LocalSound ("misc/menu2.wav");
|
|
|
|
#ifdef Q2CLIENT
|
|
if (M_GameType() == MGT_QUAKE2) //quake2 main menu.
|
|
{
|
|
if (R_GetShaderSizes(R2D_SafeCachePic("pics/m_main_quit"), NULL, NULL, true) > 0)
|
|
{
|
|
int itemheight = 32;
|
|
|
|
mainm = M_CreateMenu(0);
|
|
mainm->key = MC_Main_Key;
|
|
|
|
MC_AddPicture(mainm, 0, 4, 38, 166, "pics/m_main_plaque");
|
|
MC_AddPicture(mainm, 0, 173, 36, 42, "pics/m_main_logo");
|
|
#ifndef CLIENTONLY
|
|
MC_AddSelectablePicture(mainm, 68, 13, itemheight, "pics/m_main_game");
|
|
#endif
|
|
MC_AddSelectablePicture(mainm, 68, 53, itemheight, "pics/m_main_multiplayer");
|
|
MC_AddSelectablePicture(mainm, 68, 93, itemheight, "pics/m_main_options");
|
|
MC_AddSelectablePicture(mainm, 68, 133, itemheight, "pics/m_main_video");
|
|
MC_AddSelectablePicture(mainm, 68, 173, itemheight, "pics/m_main_quit");
|
|
|
|
#ifndef CLIENTONLY
|
|
b = MC_AddConsoleCommand (mainm, 68, 320, 13, "", "menu_single\n");
|
|
b->common.tooltip = "Singleplayer.";
|
|
mainm->selecteditem = (menuoption_t *)b;
|
|
b->common.width = 12*20;
|
|
b->common.height = itemheight;
|
|
#endif
|
|
b = MC_AddConsoleCommand (mainm, 68, 320, 53, "", "menu_multi\n");
|
|
b->common.tooltip = "Multiplayer.";
|
|
#ifdef CLIENTONLY
|
|
mainm->selecteditem = (menuoption_t *)b;
|
|
#endif
|
|
b->common.width = 12*20;
|
|
b->common.height = itemheight;
|
|
b = MC_AddConsoleCommand (mainm, 68, 320, 93, "", "menu_options\n");
|
|
b->common.tooltip = "Options.";
|
|
b->common.width = 12*20;
|
|
b->common.height = itemheight;
|
|
b = MC_AddConsoleCommand (mainm, 68, 320, 133, "", "menu_video\n");
|
|
b->common.tooltip = "Video Options.";
|
|
b->common.width = 12*20;
|
|
b->common.height = itemheight;
|
|
b = MC_AddConsoleCommand (mainm, 68, 320, 173, "", "menu_quit\n");
|
|
b->common.tooltip = "Quit to DOS.";
|
|
b->common.width = 12*20;
|
|
b->common.height = itemheight;
|
|
|
|
mainm->cursoritem = (menuoption_t *)MC_AddCursor(mainm, &resel, 42, mainm->selecteditem->common.posy);
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
#ifdef HEXEN2
|
|
if (M_GameType() == MGT_HEXEN2)
|
|
{
|
|
p = R2D_SafeCachePic("gfx/menu/title0.lmp");
|
|
if (R_GetShaderSizes(p, NULL, NULL, true) > 0)
|
|
{
|
|
int y = 64;
|
|
mainm = M_CreateMenu(0);
|
|
mainm->key = MC_Main_Key;
|
|
|
|
MC_AddPicture(mainm, 16, 0, 35, 176, "gfx/menu/hplaque.lmp");
|
|
MC_AddCenterPicture(mainm, 0, 60, "gfx/menu/title0.lmp");
|
|
|
|
#ifndef CLIENTONLY
|
|
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, y, "Single Player", "menu_single\n");
|
|
mainm->selecteditem = (menuoption_t *)b;
|
|
b->common.width = 12*20;
|
|
b->common.height = 20;
|
|
y += 20;
|
|
#endif
|
|
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, y, "MultiPlayer", "menu_multi\n");
|
|
#ifdef CLIENTONLY
|
|
mainm->selecteditem = (menuoption_t *)b;
|
|
#endif
|
|
b->common.width = 12*20;
|
|
b->common.height = 20;
|
|
y += 20;
|
|
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, y, "Options", "menu_options\n");
|
|
b->common.width = 12*20;
|
|
b->common.height = 20;
|
|
y += 20;
|
|
if (m_helpismedia.value)
|
|
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, y, "Media", "menu_media\n");
|
|
else
|
|
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, y, "Help", "help\n");
|
|
b->common.width = 12*20;
|
|
b->common.height = 20;
|
|
y += 20;
|
|
|
|
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, y, "Mods", "menu_modshelp\n");
|
|
b->common.width = 12*20;
|
|
b->common.height = 20;
|
|
y += 20;
|
|
|
|
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, y, "Quit", "menu_quit\n");
|
|
b->common.width = 12*20;
|
|
b->common.height = 20;
|
|
y += 20;
|
|
|
|
mainm->cursoritem = (menuoption_t *)MC_AddCursor(mainm, &resel, 56, mainm->selecteditem->common.posy);
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
if (QBigFontWorks())
|
|
{
|
|
p = R2D_SafeCachePic("gfx/ttl_main.lmp");
|
|
if (R_GetShaderSizes(p, NULL, NULL, true) > 0)
|
|
{
|
|
mainm = M_CreateMenu(0);
|
|
mainm->key = MC_Main_Key;
|
|
MC_AddPicture(mainm, 16, 4, 32, 144, "gfx/qplaque.lmp");
|
|
|
|
MC_AddCenterPicture(mainm, 4, 24, "gfx/ttl_main.lmp");
|
|
|
|
y = 32;
|
|
mainm->selecteditem = (menuoption_t *)
|
|
#ifndef CLIENTONLY
|
|
MC_AddConsoleCommandQBigFont (mainm, 72, y, "Single ", "menu_single\n"); y += 20;
|
|
#endif
|
|
MC_AddConsoleCommandQBigFont (mainm, 72, y, "Multiplayer ", "menu_multi\n"); y += 20;
|
|
MC_AddConsoleCommandQBigFont (mainm, 72, y, "Options ", "menu_options\n"); y += 20;
|
|
if (m_helpismedia.value)
|
|
{MC_AddConsoleCommandQBigFont(mainm, 72, y, "Media ", "menu_media\n"); y += 20;}
|
|
else
|
|
{MC_AddConsoleCommandQBigFont(mainm, 72, y, "Help ", "help\n"); y += 20;}
|
|
y = M_Main_AddExtraOptions(mainm, y);
|
|
#ifdef FTE_TARGET_WEB
|
|
MC_AddConsoleCommandQBigFont (mainm, 72, y, "Save Settings ", "menu_quit\n"); y += 20;
|
|
#else
|
|
MC_AddConsoleCommandQBigFont (mainm, 72, y, "Quit ", "menu_quit\n"); y += 20;
|
|
#endif
|
|
|
|
mainm->cursoritem = (menuoption_t *)MC_AddCursor(mainm, &resel, 54, 32);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int width;
|
|
p = R2D_SafeCachePic("gfx/mainmenu.lmp");
|
|
R2D_SafeCachePic("gfx/ttl_main.lmp");
|
|
if (R_GetShaderSizes(p, &width, NULL, true) > 0)
|
|
{
|
|
mainm = M_CreateMenu(0);
|
|
|
|
mainm->key = MC_Main_Key;
|
|
MC_AddPicture(mainm, 16, 4, 32, 144, "gfx/qplaque.lmp");
|
|
|
|
MC_AddCenterPicture(mainm, 4, 24, "gfx/ttl_main.lmp");
|
|
MC_AddPicture(mainm, 72, 32, 240, 112, "gfx/mainmenu.lmp");
|
|
|
|
b=MC_AddConsoleCommand (mainm, 72, 312, 32, "", "menu_single\n");
|
|
b->common.tooltip = "Start singleplayer Quake game.";
|
|
mainm->selecteditem = (menuoption_t *)b;
|
|
b->common.width = width;
|
|
b->common.height = 20;
|
|
b=MC_AddConsoleCommand (mainm, 72, 312, 52, "", "menu_multi\n");
|
|
b->common.tooltip = "Multiplayer menu.";
|
|
b->common.width = width;
|
|
b->common.height = 20;
|
|
b=MC_AddConsoleCommand (mainm, 72, 312, 72, "", "menu_options\n");
|
|
b->common.tooltip = "Options menu.";
|
|
b->common.width = width;
|
|
b->common.height = 20;
|
|
if (m_helpismedia.value)
|
|
{
|
|
b=MC_AddConsoleCommand(mainm, 72, 312, 92, "", "menu_media\n");
|
|
b->common.tooltip = "Media menu.";
|
|
}
|
|
else
|
|
{
|
|
b=MC_AddConsoleCommand(mainm, 72, 312, 92, "", "help\n");
|
|
b->common.tooltip = "Help menu.";
|
|
}
|
|
b->common.width = width;
|
|
b->common.height = 20;
|
|
b=MC_AddConsoleCommand (mainm, 72, 312, 112, "", "menu_quit\n");
|
|
#ifdef FTE_TARGET_WEB
|
|
b->common.tooltip = "Save settings to local storage.";
|
|
#else
|
|
b->common.tooltip = "Exit to DOS.";
|
|
#endif
|
|
b->common.width = width;
|
|
b->common.height = 20;
|
|
|
|
M_Main_AddExtraOptions(mainm, 112+20);
|
|
|
|
mainm->cursoritem = (menuoption_t *)MC_AddCursor(mainm, &resel, 54, 32);
|
|
}
|
|
}
|
|
|
|
if (!mainm)
|
|
{
|
|
mainm = M_CreateMenu(0);
|
|
MC_AddRedText(mainm, 16, 170, 0, "MAIN MENU", false);
|
|
|
|
y = 36;
|
|
mainm->selecteditem = (menuoption_t *)
|
|
//skip menu_single if we don't seem to have any content.
|
|
MC_AddConsoleCommandQBigFont (mainm, 72, y, "Join server", "menu_servers\n"); y += 20;
|
|
MC_AddConsoleCommandQBigFont (mainm, 72, y, "Options", "menu_options\n"); y += 20;
|
|
y = M_Main_AddExtraOptions(mainm, y);
|
|
MC_AddConsoleCommandQBigFont (mainm, 72, y, "Quit", "menu_quit\n"); y += 20;
|
|
|
|
mainm->cursoritem = (menuoption_t *)MC_AddCursor(mainm, &resel, 54, 36);
|
|
}
|
|
|
|
if (!m_preset_chosen.ival)
|
|
M_Menu_Preset_f();
|
|
}
|
|
|
|
int MC_AddBulk(struct emenu_s *menu, menuresel_t *resel, menubulk_t *bulk, int xstart, int xtextend, int y)
|
|
{
|
|
int selectedy = y;
|
|
menuoption_t *selected = NULL;
|
|
|
|
while (bulk)
|
|
{
|
|
menuoption_t *control;
|
|
int x = xtextend;
|
|
int xleft;
|
|
int spacing = 8;
|
|
|
|
if (bulk->text)
|
|
{ //lots of fancy code just to figure out the correct width of the string. yay. :(
|
|
int px, py;
|
|
conchar_t buffer[2048], *end;
|
|
if (font_default)
|
|
{
|
|
end = COM_ParseFunString(CON_WHITEMASK, bulk->text, buffer, sizeof(buffer), false);
|
|
Font_BeginString(font_default, 0, 0, &px, &py);
|
|
px = Font_LineWidth(buffer, end);
|
|
Font_EndString(NULL);
|
|
}
|
|
else
|
|
{
|
|
Con_DPrintf("MC_AddBulk: default font not initialised yet\n");
|
|
px = strlen(bulk->text)*8;
|
|
}
|
|
|
|
x -= ((float)px * vid.width) / vid.rotpixelwidth;
|
|
}
|
|
xleft = x - xstart;
|
|
|
|
switch (bulk->type)
|
|
{
|
|
case mt_text:
|
|
switch (bulk->variant)
|
|
{
|
|
case -1: // end of menu
|
|
default:
|
|
bulk = NULL;
|
|
control = NULL;
|
|
continue;
|
|
case 0: // white text
|
|
control = (union menuoption_s *)MC_AddWhiteText(menu, xleft, xtextend, y, bulk->text, bulk->rightalign);
|
|
break;
|
|
case 1: // red text
|
|
control = (union menuoption_s *)MC_AddRedText(menu, xleft, xtextend, y, bulk->text, bulk->rightalign);
|
|
break;
|
|
case 2: // spacing
|
|
spacing = bulk->spacing;
|
|
control = NULL;
|
|
break;
|
|
}
|
|
break;
|
|
case mt_button:
|
|
switch (bulk->variant)
|
|
{
|
|
default:
|
|
case 0: // console command
|
|
control = (union menuoption_s *)MC_AddConsoleCommand(menu, xleft, xtextend, y, bulk->text, bulk->consolecmd);
|
|
break;
|
|
case 1: // function command
|
|
control = (union menuoption_s *)MC_AddCommand(menu, xleft, xtextend, y, bulk->text, bulk->command);
|
|
break;
|
|
}
|
|
break;
|
|
case mt_checkbox:
|
|
control = (union menuoption_s *)MC_AddCheckBox(menu, xleft, xtextend, y, bulk->text, bulk->cvar, bulk->flags);
|
|
control->check.func = bulk->func;
|
|
break;
|
|
case mt_slider:
|
|
control = (union menuoption_s *)MC_AddSlider(menu, xleft, xtextend, y, bulk->text, bulk->cvar, bulk->min, bulk->max, bulk->delta);
|
|
break;
|
|
case mt_combo:
|
|
switch (bulk->variant)
|
|
{
|
|
default:
|
|
case 0: // cvar combo
|
|
control = (union menuoption_s *)MC_AddCvarCombo(menu, xleft, xtextend, y, bulk->text, bulk->cvar, bulk->options, bulk->values);
|
|
break;
|
|
case 1: // combo with return value
|
|
if (bulk->selectedoption < 0)
|
|
{ //invalid...
|
|
control = NULL;
|
|
spacing = 0;
|
|
break;
|
|
}
|
|
control = (union menuoption_s *)MC_AddCombo(menu, xleft, xtextend, y, bulk->text, bulk->options, bulk->selectedoption);
|
|
break;
|
|
}
|
|
break;
|
|
case mt_edit:
|
|
switch (bulk->variant)
|
|
{
|
|
default:
|
|
case 0:
|
|
y += 4;
|
|
control = (union menuoption_s *)MC_AddEditCvar(menu, xleft, xtextend, y, bulk->text, bulk->cvarname, false);
|
|
spacing += 4;
|
|
break;
|
|
case 1:
|
|
control = (union menuoption_s *)MC_AddEditCvar(menu, xleft, xtextend, y, bulk->text, bulk->cvarname, true);
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
Con_Printf(CON_ERROR "Invalid type in bulk menu!\n");
|
|
bulk = NULL;
|
|
continue;
|
|
}
|
|
|
|
if (bulk->ret)
|
|
*bulk->ret = control;
|
|
if (control && MI_Selectable(control) && !selected)
|
|
selected = control;
|
|
if (control && bulk->tooltip)
|
|
control->common.tooltip = bulk->tooltip;
|
|
if (control && xleft > 0)
|
|
control->common.extracollide = xleft;
|
|
y += spacing;
|
|
|
|
bulk++;
|
|
}
|
|
|
|
menu->selecteditem = selected;
|
|
if (selected)
|
|
selectedy = selected->common.posy;
|
|
menu->cursoritem = (menuoption_t*)MC_AddCursorSmall(menu, resel, xtextend + 8, selectedy);
|
|
return y;
|
|
}
|
|
#endif
|