1
0
Fork 0
forked from fte/fteqw
fteqw/engine/client/m_items.c

2202 lines
52 KiB
C
Raw Normal View History

//read menu.h
#include "quakedef.h"
#include "shader.h"
int omousex;
int omousey;
qboolean mousemoved;
qboolean bindingactive;
extern cvar_t cl_cursor;
extern cvar_t cl_cursorsize;
extern cvar_t cl_cursorbias;
extern cvar_t m_preset_chosen;
menu_t *currentmenu;
menu_t *firstmenu;
void Draw_TextBox (int x, int y, int width, int lines)
{
mpic_t *p;
int cx, cy;
int n;
// draw left side
cx = x;
cy = y;
p = R2D_SafeCachePic ("gfx/box_tl.lmp");
if (!p) //assume none exist
{
R2D_ImageColours(0.0, 0.0, 0.0, 1.0);
R2D_FillBlock(x, y, width*16 + 16, 8 * (2 + lines));
R2D_ImageColours(1.0, 1.0, 1.0, 1.0);
return;
}
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 (width > 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);
width -= 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);
}
void Draw_Hexen2BigFontString(int x, int y, const char *text)
{
int sx, sy;
mpic_t *p;
unsigned int hack;
hack = d_8to24rgbtable[0];
d_8to24rgbtable[0] = 0;
p = R2D_SafeCachePic ("gfx/menu/bigfont.lmp");
d_8to24rgbtable[0] = hack;
while(*text)
{
if (*text >= 'a' && *text <= 'z')
{
sx = ((*text-'a')%8)*20;
sy = ((*text-'a')/8)*20;
}
else if (*text >= 'A' && *text <= 'Z')
{
sx = ((*text-'A')%8)*20;
sy = ((*text-'A')/8)*20;
}
else// if (*text <= ' ')
{
sx=-1;
sy=-1;
}
if(sx>=0)
R2D_SubPic(x, y, 20, 20, p, sx, sy, 20*8, 20*4);
x+=20;
text++;
}
}
mpic_t *QBigFontWorks(void)
{
mpic_t *p;
p = R2D_SafeCachePic ("gfx/mcharset.lmp");
if (p)
return p;
p = R2D_SafeCachePic ("mcharset.lmp");
if (p)
return p;
p = R2D_SafeCachePic ("textures/gfx/mcharset.lmp");
if (p)
return p;
p = R2D_SafeCachePic ("textures/mcharset.lmp");
if (p)
return p;
return NULL;
}
void Draw_BigFontString(int x, int y, const char *text)
{
int sx, sy;
mpic_t *p;
p = QBigFontWorks();
if (!p)
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 MenuTooltipSplit(menu_t *menu, const char *text)
{
char buf[1024];
char *c, *space;
int lines, lnsize, txsize;
int lnmax;
menutooltip_t *mtt;
if (menu->tooltip)
{
Z_Free(menu->tooltip);
menu->tooltip = NULL;
}
if (!text || !text[0] || vid.width < 320 || vid.height < 200)
return;
// calc a line maximum, use a third of the screen or 30 characters, whichever is bigger
lnmax = (vid.width / 24) - 2;
if (lnmax < 30)
lnmax = 30;
// word wrap
lines = 1;
lnsize = txsize = 0;
space = NULL;
for (c = buf; *text && txsize < sizeof(buf) - 1; text++)
{
if (lnsize >= lnmax)
{
if (space)
{
lnsize = (c - space) - 1;
*space = '\n';
space = NULL;
}
else
{
lnsize = 0;
*c = '\n';
c++;
txsize++;
if (txsize >= sizeof(buf) - 1)
break;
}
lines++;
}
*c = *text;
switch (*c)
{
case '\n':
lines++;
lnsize = 0;
space = NULL;
break;
case ' ':
space = c;
break;
}
c++;
txsize++;
lnsize++;
}
// remove stray newlines at end and terminate string
while (txsize > 0 && buf[txsize - 1] == '\n')
{
lines--;
txsize--;
}
buf[txsize] = '\0';
// allocate new tooltip structure, copy text to structure
mtt = (menutooltip_t *)Z_Malloc(sizeof(menutooltip_t) + sizeof(char *)*lines + txsize + 1);
mtt->lines = (char **)(mtt + 1);
mtt->rows = lines;
Q_memcpy(mtt->lines + lines, buf, txsize + 1);
mtt->lines[0] = (char *)(mtt->lines + lines);
// rescan text, get max column length, convert \n to \0
lines = lnmax = txsize = 0;
for (c = mtt->lines[0]; *c; c++)
{
if (*c == '\n')
{
if (lnmax < txsize)
lnmax = txsize;
txsize = 0;
*c = '\0';
mtt->lines[++lines] = c + 1;
}
else
txsize++;
}
if (lnmax < txsize)
lnmax = txsize;
mtt->columns = lnmax;
menu->tooltip = mtt;
}
static qboolean MI_Selectable(menuoption_t *op)
{
switch(op->common.type)
{
case mt_text:
return false;
case mt_button:
return true;
case mt_hexen2buttonbigfont:
return true;
case mt_qbuttonbigfont:
return true;
case mt_menudot:
return false;
case mt_picturesel:
return true;
case mt_picture:
return false;
case mt_childwindow:
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 void M_CheckMouseMove(void)
{
qboolean foundexclusive = false;
int mgt;
menu_t *menu;
menuoption_t *option;
------------------------------------------------------------------------ r4169 | acceptthis | 2013-01-17 08:55:12 +0000 (Thu, 17 Jan 2013) | 31 lines removed MAX_VISEDICTS limit. PEXT2_REPLACEMENTDELTAS tweaked, now has 4 million entity limit. still not enabled by default. TE_BEAM now maps to a separate TEQW_BEAM to avoid conflicts with QW. added android multitouch emulation for windows/rawinput (in_simulatemultitouch). split topcolor/bottomcolor from scoreboard, for dp's colormap|1024 feature. now using utf-8 for windows consoles. qcc warnings/errors now give clickable console links for quick+easy editing. disabled menutint when the currently active item changes contrast or gamma (for OneManClan). Added support for drawfont/drawfontscale. tweaked the qcvm a little to reduce the number of pointers. .doll file loading. still experimental and will likely crash. requires csqc active, even if its a dummy progs. this will be fixed in time. Still other things that need cleaning up. windows: gl_font "?" shows the standard windows font-selection dialog, and can be used to select windows fonts. not all work. and you probably don't want to use windings. fixed splitscreen support when playing mvds. added mini-scoreboards to splitscreen. editor/debugger now shows asm if there's no linenumber info. also, pressing f1 for help shows the shortcuts. Added support for .framegroups files for psk(psa) and iqm formats. True support for ezquake's colour codes. Mutually exclusive with background colours. path command output slightly more readable. added support for digest_hex (MD4, SHA1, CRC16). skingroups now colourmap correctly. Fix terrain colour hints, and litdata from the wrong bsp. fix ftp dual-homed issue. support epsv command, and enable ipv6 (eprt still not supported). remove d3d11 compilation from the makefile. the required headers are not provided by mingw, and are not available to the build bot, so don't bother. fix v *= v.x and similar opcodes. fteqcc: fixed support for áéíóú type chars in names. utf-8 files now properly supported (even with the utf-8 bom/identifier). utf-16 also supported. fteqcc: fixed '#if 1 == 3 && 4' parsing. fteqcc: -Werror acts on the warning, rather than as a separate error. Line numbers are thus more readable. fteqcc: copyright message now includes compile date instead. fteqccgui: the treeview control is now coloured depending on whether there were warnings/errors in the last compile. fteqccgui: the output window is now focused and scrolls down as compilation progresses. pr_dumpplatform command dumps out some pragmas to convert more serious warnings to errors. This is to avoid the infamous 'fteqcc sucks cos my code sucks' issue. rewrote prespawn/modelist/soundlist code. server tracks progress now. ------------------------------------------------------------------------ git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4167 fc73d0e0-1445-4013-8a0c-d673dee63da5
2013-03-12 22:29:40 +00:00
if (omousex != (int)mousecursor_x || omousey != (int)mousecursor_y)
mousemoved = true;
else
mousemoved = false;
omousex = mousecursor_x;
omousey = mousecursor_y;
if (mousemoved)
{
mgt = M_GameType();
for (menu = firstmenu; menu; menu = menu->parent)
{
if (menu->exclusive)
{
if (foundexclusive)
continue;
foundexclusive=true;
}
for(option = menu->options; option; option = option->common.next)
{
if (mousemoved && !bindingactive && !option->common.ishidden)
{
if (mousecursor_x > menu->xpos+option->common.posx-option->common.extracollide && mousecursor_x < menu->xpos+option->common.posx+option->common.width)
{
if (mousecursor_y > menu->ypos+option->common.posy && mousecursor_y < menu->ypos+option->common.posy+option->common.height)
{
if (MI_Selectable(option))
{
if (menu->selecteditem != option)
{
if (!option->common.noselectionsound)
{
if (mgt == MGT_HEXEN2)
S_LocalSound ("raven/menu1.wav");
else
S_LocalSound ("misc/menu1.wav");
}
menu->selecteditem = option;
menu->tooltiptime = realtime + 1;
MenuTooltipSplit(menu, menu->selecteditem->common.tooltip);
}
if (menu->cursoritem)
menu->cursoritem->common.posy = menu->selecteditem->common.posy;
}
}
}
}
}
}
}
}
static void MenuDrawItems(int xpos, int ypos, menuoption_t *option, menu_t *menu)
{
int i;
mpic_t *p;
while (option)
{
if (!option->common.ishidden)
switch(option->common.type)
{
case mt_text:
if (!option->text.text)
{
if ((int)(realtime*4)&1)
Draw_FunString(xpos+option->common.posx, ypos+option->common.posy, "^Ue00d");
}
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:
if (!menu->cursoritem && menu->selecteditem == option)
Draw_AltFunString(xpos+option->common.posx, ypos+option->common.posy, option->button.text);
else
Draw_FunString(xpos+option->common.posx, ypos+option->common.posy, option->button.text);
break;
case mt_hexen2buttonbigfont:
Draw_Hexen2BigFontString(xpos+option->common.posx, ypos+option->common.posy, option->button.text);
break;
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 ));
R2D_ScalePic(xpos+option->common.posx, ypos+option->common.posy+dotofs, 20, 20, p);
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 (!p)
p = R2D_SafeCachePic(option->picture.picturename);
R2D_ScalePic(xpos+option->common.posx, ypos+option->common.posy, option->common.width?option->common.width:p->width, option->common.height?option->common.height:p->height, p);
break;
case mt_picture:
p = R2D_SafeCachePic(option->picture.picturename);
if (p) R2D_ScalePic(xpos+option->common.posx, ypos+option->common.posy, option->common.width, option->common.height, p);
break;
case mt_childwindow:
MenuDrawItems(xpos+option->common.posx, ypos+option->common.posy, ((menu_t *)option->custom.dptr)->options, (menu_t *)option->custom.dptr);
break;
case mt_box:
Draw_TextBox(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)
{
if (!menu->cursoritem && menu->selecteditem == option)
Draw_AltFunString(x, y, option->slider.text);
else
Draw_FunString(x, y, option->slider.text);
x += strlen(option->slider.text)*8+28;
}
if (range < 0)
range = 0;
if (range > 1)
range = 1;
x -= 8;
Font_BeginString(font_conchar, x, y, &x, &y);
x = Font_DrawChar(x, y, 0xe080 | CON_WHITEMASK);
s = x;
for (i=0 ; i<SLIDER_RANGE ; i++)
x = Font_DrawChar(x, y, 0xe081 | CON_WHITEMASK);
Font_DrawChar(x, y, 0xe082 | CON_WHITEMASK);
Font_DrawChar(s + (x-s) * range - Font_CharWidth(0xe083 | CON_WHITEMASK)/2, y, 0xe083 | CON_WHITEMASK);
Font_EndString(font_conchar);
}
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)
{
if (!menu->cursoritem && menu->selecteditem == option)
Draw_AltFunString(x, y, option->check.text);
else
Draw_FunString(x, y, option->check.text);
x += strlen(option->check.text)*8+28;
}
#if 0
if (on)
Draw_Character (x, y, 131);
else
Draw_Character (x, y, 129);
#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;
//Fixme: variable width fonts
if (!menu->cursoritem && menu->selecteditem == option)
Draw_AltFunString(x, y, option->edit.caption);
else
Draw_FunString(x, y, option->edit.caption);
x+=strlen(option->edit.caption)*8+8;
if (option->edit.slim)
x += 8; // more space for cursor
else
Draw_TextBox(x-8, y-8, 16, 1);
Draw_FunString(x, y, option->edit.text);
if (menu->selecteditem == option && (int)(realtime*4) & 1)
{
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 l;
int keys[2];
char *keyname;
if (!menu->cursoritem && menu->selecteditem == option)
Draw_AltFunString(x, y, option->bind.caption);
else
Draw_FunString(x, y, option->bind.caption);
x += strlen(option->bind.caption)*8+28;
{
extern cvar_t cl_forcesplitclient;
l = strlen (option->bind.command);
M_FindKeysForCommand (cl_forcesplitclient.ival, option->bind.command, keys);
if (bindingactive && menu->selecteditem == option)
{
Draw_FunString (x, y, "Press key");
}
else if (keys[0] == -1)
{
Draw_FunString (x, y, "???");
}
else
{
keyname = Key_KeynumToString (keys[0]);
Draw_FunString (x, y, keyname);
x += strlen(keyname) * 8;
if (keys[1] != -1)
{ /*these offsets are wrong*/
Draw_FunString (x + 8, y, "or");
Draw_FunString (x + 32, y, Key_KeynumToString (keys[1]));
}
}
}
}
break;
case mt_combo:
{
int x = xpos+option->common.posx;
int y = ypos+option->common.posy;
if (!menu->cursoritem && menu->selecteditem == option)
Draw_AltFunString(x, y, option->combo.caption);
else
Draw_FunString(x, y, option->combo.caption);
x += strlen(option->combo.caption)*8+24;
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;
}
option = option->common.next;
}
}
static void MenuDraw(menu_t *menu)
{
if (menu->event)
menu->event(menu);
if (!menu->dontexpand)
menu->xpos = ((vid.width - 320)>>1);
MenuDrawItems(menu->xpos, menu->ypos, menu->options, menu);
// draw tooltip
if (menu->selecteditem && menu->tooltip && realtime > menu->tooltiptime)
{
menuoption_t *option = menu->selecteditem;
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;
int y = omousey;
int w = (menu->tooltip->columns + 3) * 8;
int h = (menu->tooltip->rows + 2) * 8;
int l, lines;
// keep the tooltip within view
if (x + w >= vid.width)
x = vid.width - w - 1;
if (y + h >= vid.height)
y -= h;
// draw tooltip
Draw_TextBox(x, y, menu->tooltip->columns, menu->tooltip->rows);
lines = menu->tooltip->rows;
x += 8;
y += 8;
for (l = 0; l < lines; l++)
{
Draw_FunString(x, y, menu->tooltip->lines[l]);
y += 8;
}
}
}
}
menutext_t *MC_AddWhiteText(menu_t *menu, int x, int y, const char *text, qboolean rightalign)
{
menutext_t *n = Z_Malloc(sizeof(menutext_t));
n->common.type = mt_text;
n->common.iszone = true;
n->common.posx = x;
n->common.posy = y;
n->text = text;
if (rightalign && text)
n->common.posx -= strlen(text)*8;
n->common.next = menu->options;
menu->options = (menuoption_t *)n;
return n;
}
menutext_t *MC_AddBufferedText(menu_t *menu, int x, int y, const char *text, qboolean 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 = x;
n->common.posy = y;
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(menu_t *menu, int x, int y, const char *text, qboolean rightalign)
{
menutext_t *n;
n = MC_AddWhiteText(menu, x, y, text, false);
n->isred = true;
return n;
}
menubind_t *MC_AddBind(menu_t *menu, int x, int y, const char *caption, char *command)
{
menubind_t *n = Z_Malloc(sizeof(menutext_t) + strlen(caption)+1 + strlen(command)+1);
n->common.type = mt_bind;
n->common.iszone = true;
n->common.posx = x;
n->common.posy = y;
n->caption = (char *)(n+1);
strcpy(n->caption, caption);
n->command = n->caption+strlen(n->caption)+1;
strcpy(n->command, command);
n->common.width = strlen(caption)*8 + 64;
n->common.height = 8;
n->common.next = menu->options;
menu->options = (menuoption_t *)n;
return n;
}
menupicture_t *MC_AddSelectablePicture(menu_t *menu, int x, int y, 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->picturename = (char *)(n+1);
strcpy(n->picturename, picname);
n->common.next = menu->options;
menu->options = (menuoption_t *)n;
return n;
}
menupicture_t *MC_AddPicture(menu_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(menu_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
{
width = (p->width * (float)height) / p->height;
x = (320-(int)width)/2;
}
return MC_AddPicture(menu, x, y, width, height, picname);
}
menupicture_t *MC_AddCursor(menu_t *menu, int x, int y)
{
int mgt;
menupicture_t *n = Z_Malloc(sizeof(menupicture_t));
n->common.type = mt_menudot;
n->common.iszone = true;
n->common.posx = x;
n->common.posy = y;
n->common.next = menu->options;
menu->options = (menuoption_t *)n;
mgt = M_GameType();
if (mgt == MGT_QUAKE2)
{ //AND QUAKE 2 WINS!!!
menudotstyle = "m_cursor%i";
mindot = 0;
maxdots = 15;
dotofs=0;
}
else if (mgt == MGT_HEXEN2)
{ //AND THE WINNER IS HEXEN 2!!!
menudotstyle = "gfx/menu/menudot%i.lmp";
mindot = 1;
maxdots = 8;
dotofs=-2;
}
else
{ //QUAKE 1 WINS BY DEFAULT!
menudotstyle = "gfx/menudot%i.lmp";
mindot = 1;
maxdots = 6;
dotofs=0;
}
return n;
}
menuedit_t *MC_AddEdit(menu_t *menu, int x, int y, char *text, char *def)
{
menuedit_t *n = Z_Malloc(sizeof(menuedit_t));
n->common.type = mt_edit;
n->common.iszone = true;
n->common.posx = x;
n->common.posy = y;
n->modified = true;
n->caption = 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_Full(menu_t *menu, int x, 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->common.type = mt_edit;
n->common.iszone = true;
n->common.posx = x;
n->common.posy = y;
n->common.width = (strlen(text)+17)*8;
n->common.height = 8;
n->common.tooltip = cvar->description;
n->modified = true;
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;
n->slim = isslim;
menu->options = (menuoption_t *)n;
return n;
}
menuedit_t *MC_AddEditCvarSlim(menu_t *menu, int x, int y, char *text, char *name)
{
return MC_AddEditCvar_Full(menu, x, y, text, name, true);
}
menuedit_t *MC_AddEditCvar(menu_t *menu, int x, int y, char *text, char *name)
{
return MC_AddEditCvar_Full(menu, x, y, text, name, false);
}
menubox_t *MC_AddBox(menu_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(menu_t *menu, int x, int y, void *dptr, int dint)
{
menucustom_t *n = Z_Malloc(sizeof(menucustom_t));
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.next = menu->options;
menu->options = (menuoption_t *)n;
return n;
}
menucheck_t *MC_AddCheckBox(menu_t *menu, int x, 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 = x;
n->common.posy = y;
n->common.height = 8;
n->common.width = (strlen(text)+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)
Con_Printf("Warning: %s requires a vid_restart\n", var->name);
#endif
n->common.next = menu->options;
menu->options = (menuoption_t *)n;
return n;
}
menucheck_t *MC_AddCheckBoxFunc(menu_t *menu, int x, int y, const char *text, qboolean (*func) (menucheck_t *option, menu_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 = x;
n->common.posy = y;
n->common.height = 8;
n->common.width = (strlen(text)+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(menu_t *menu, int x, 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 = x;
n->common.posy = y;
n->common.height = 8;
n->common.width = (strlen(text)+SLIDER_RANGE+5)*8;
n->common.tooltip = var->description;
n->var = var;
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(menu_t *menu, int x, 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 = x;
n->common.posy = y;
n->common.height = 8;
n->common.width = strlen(caption)*8 + maxoptlen*8;
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 >= n->numoptions)
{
Con_Printf("WARNING: Fixed initialvalue for %s\n", caption);
initialvalue = n->numoptions-1;
}
n->selectedoption = initialvalue;
return n;
}
menucombo_t *MC_AddCvarCombo(menu_t *menu, int x, 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 = x;
n->common.posy = y;
n->common.height = 8;
n->common.width = strlen(caption)*8 + maxoptlen*8;
n->common.tooltip = cvar->description;
strcpy(optbuf, caption);
n->caption = optbuf;
optbuf += strlen(optbuf)+1;
n->options = (const char **)newops;
n->values = (const char **)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(menu_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_button;
n->common.iszone = true;
n->common.posx = x;
n->common.posy = y;
n->common.height = 8;
n->common.width = strlen(text)*8;
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(menu_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;
}
menubutton_t *MC_AddConsoleCommandHexen2BigFont(menu_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;
}
menubutton_t *MC_AddCommand(menu_t *menu, int x, int y, char *text, qboolean (*command) (union menuoption_s *,struct menu_s *,int))
{
menubutton_t *n = Z_Malloc(sizeof(menubutton_t));
n->common.type = mt_button;
n->common.iszone = true;
n->common.posx = x;
n->common.posy = y;
n->text = text;
n->command = NULL;
n->key = command;
n->common.height = 8;
n->common.width = strlen(text)*8;
n->common.next = menu->options;
menu->options = (menuoption_t *)n;
return n;
}
menubutton_t *VARGS MC_AddConsoleCommandf(menu_t *menu, int x, int y, 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 = x;
n->common.posy = y;
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;
if (option->smallchange)
delta = option->smallchange;
else
delta = 0.1;
if (key == K_LEFTARROW || key == K_MWHEELDOWN)
{
range -= delta;
if (range < option->min)
range = option->min;
option->current = range;
if (option->var)
Cvar_SetValue(option->var, range);
}
else if (key == K_RIGHTARROW || key == K_MWHEELUP)
{
range += delta;
if (range > option->max)
range = option->max;
option->current = range;
if (option->var)
Cvar_SetValue(option->var, range);
}
else if (key == K_ENTER || key == K_MOUSE1)
{
range += delta;
if (range >= option->max + delta/2)
range = option->min;
if (range > option->max)
range = option->max;
option->current = range;
if (option->var)
Cvar_SetValue(option->var, range);
}
else
return;
S_LocalSound ("misc/menu2.wav");
}
void MC_CheckBox_Key(menucheck_t *option, menu_t *menu, int key)
{
if (key != K_ENTER && key != K_LEFTARROW && key != K_RIGHTARROW && 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_RIGHTARROW || 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]);
}
else if (key == K_LEFTARROW)
{
combo->selectedoption--;
if (combo->selectedoption < 0)
combo->selectedoption = combo->numoptions-1;
goto changed;
}
}
void M_AddMenuFront (menu_t *menu)
{
menu_t *pmenu;
m_state = m_complex;
if (!firstmenu)
{
M_AddMenu(menu);
return;
}
pmenu = firstmenu;
while(pmenu->parent)
pmenu = pmenu->parent;
pmenu->parent = menu;
menu->child = pmenu;
menu->parent = NULL;
menu->exclusive = true;
menu->xpos = ((vid.width - 320)>>1);
currentmenu = menu;
}
void M_AddMenu (menu_t *menu)
{
m_state = m_complex;
menu->parent = firstmenu;
if (firstmenu)
firstmenu->child = menu;
menu->child = NULL;
firstmenu = menu;
menu->exclusive = true;
currentmenu = menu;
}
menu_t *M_CreateMenu (int extrasize)
{
menu_t *menu;
menu = Z_Malloc(sizeof(menu_t)+extrasize);
menu->iszone=true;
menu->data = menu+1;
M_AddMenu(menu);
return menu;
}
menu_t *M_CreateMenuInfront (int extrasize)
{
menu_t *menu;
menu = Z_Malloc(sizeof(menu_t)+extrasize);
menu->iszone=true;
menu->data = menu+1;
M_AddMenuFront(menu);
menu->exclusive = false;
return menu;
}
void M_HideMenu (menu_t *menu)
{
if (menu == firstmenu)
firstmenu = menu->parent;
else
{
menu_t *prev;
prev = menu->child;
if (prev)
prev->parent = menu->parent;
if (menu->parent)
menu->parent->child = menu;
}
}
void M_RemoveMenu (menu_t *menu)
{
menuoption_t *op, *oop;
if (menu->remove)
menu->remove(menu);
if (menu == firstmenu)
firstmenu = menu->parent;
else
{
menu_t *prev;
prev = menu->child;
if (prev)
prev->parent = menu->parent;
if (menu->parent)
menu->parent->child = 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);
}
if (menu == currentmenu)
currentmenu = firstmenu;
}
void M_RemoveAllMenus (void)
{
if (!firstmenu)
return;
while(firstmenu)
M_RemoveMenu(firstmenu);
}
void M_MenuPop_f (void)
{
if (!currentmenu)
return;
M_RemoveMenu(currentmenu);
}
void M_Complex_Draw(void)
{
menu_t *menu, *cmenu;
qboolean foundexclusive = false;
if (!firstmenu)
{
key_dest = key_game;
m_state = m_none;
return;
}
M_CheckMouseMove();
for (menu = firstmenu; menu; )
{
cmenu = menu;
menu = menu->parent; //this way we can remove the currently drawn menu easily (if needed)
if (cmenu->exclusive)
{
if (foundexclusive)
continue;
foundexclusive=true;
}
MenuDraw(cmenu);
}
}
menuoption_t *M_NextItem(menu_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(menu_t *m, menuoption_t *old)
{
menuoption_t *op;
if (!old)
old = M_NextItem(m, old);
op = old;
while (1)
{
if (!op)
op = currentmenu->options;
op = M_NextItem(m, op);
if (!op)
op = currentmenu->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(menu_t *m, menuoption_t *old)
{
menuoption_t *op;
if (!old)
old = currentmenu->options;
op = old;
while (1)
{
if (!op)
op = currentmenu->options;
op = op->common.next;
if (!op)
op = currentmenu->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(int key, int unicode)
{
int mgt;
mgt = M_GameType();
if (!currentmenu)
return; //erm...
M_CheckMouseMove();
if (currentmenu->key)
if (currentmenu->key(key, currentmenu))
return;
if (currentmenu->selecteditem && currentmenu->selecteditem->common.type == mt_custom && (key == K_DOWNARROW || key == K_UPARROW || key == K_TAB))
if (currentmenu->selecteditem->custom.key)
if (currentmenu->selecteditem->custom.key(&currentmenu->selecteditem->custom, currentmenu, key))
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;
if (mgt == MGT_HEXEN2)
S_LocalSound ("raven/menu1.wav");
else
S_LocalSound ("misc/menu1.wav");
if (key != K_ESCAPE && key != '`')
{
Cbuf_InsertText (va("bind \"%s\" \"%s\"\n", Key_KeynumToString (key), currentmenu->selecteditem->bind.command), RESTRICT_LOCAL, false);
}
bindingactive = false;
return;
}
}
switch(key)
{
case K_MOUSE2:
case K_ESCAPE:
//remove
M_RemoveMenu(currentmenu);
if (mgt == MGT_HEXEN2)
S_LocalSound ("raven/menu3.wav");
else
S_LocalSound ("misc/menu3.wav");
break;
case K_TAB:
case K_DOWNARROW:
currentmenu->selecteditem = M_NextSelectableItem(currentmenu, currentmenu->selecteditem);
if (currentmenu->selecteditem)
{
if (mgt == MGT_HEXEN2)
S_LocalSound ("raven/menu1.wav");
else
S_LocalSound ("misc/menu1.wav");
if (currentmenu->cursoritem)
currentmenu->cursoritem->common.posy = currentmenu->selecteditem->common.posy;
}
break;
case K_UPARROW:
currentmenu->selecteditem = M_PrevSelectableItem(currentmenu, currentmenu->selecteditem);
if (currentmenu->selecteditem)
{
if (mgt == MGT_HEXEN2)
S_LocalSound ("raven/menu1.wav");
else
S_LocalSound ("misc/menu1.wav");
if (currentmenu->cursoritem)
currentmenu->cursoritem->common.posy = currentmenu->selecteditem->common.posy;
}
break;
default:
if (!currentmenu->selecteditem)
{
if (!currentmenu->options)
return;
currentmenu->selecteditem = currentmenu->options;
}
switch(currentmenu->selecteditem->common.type)
{
case mt_slider:
MC_Slider_Key(&currentmenu->selecteditem->slider, key);
break;
case mt_checkbox:
MC_CheckBox_Key(&currentmenu->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_MOUSE1)
{
Cbuf_AddText(currentmenu->selecteditem->button.command, RESTRICT_LOCAL);
if (mgt == MGT_HEXEN2)
S_LocalSound ("raven/menu2.wav");
else
S_LocalSound ("misc/menu2.wav");
}
break;
case mt_custom:
if (currentmenu->selecteditem->custom.key)
currentmenu->selecteditem->custom.key(&currentmenu->selecteditem->custom, currentmenu, key);
break;
case mt_edit:
MC_EditBox_Key(&currentmenu->selecteditem->edit, key, unicode);
break;
case mt_combo:
MC_Combo_Key(&currentmenu->selecteditem->combo, key);
break;
case mt_bind:
if (key == K_ENTER || key == K_MOUSE1)
bindingactive = true;
else if (key == K_BACKSPACE || key == K_DEL)
M_UnbindCommand (currentmenu->selecteditem->bind.command);
default:
break;
}
break;
}
}
typedef struct {
int itemselected;
menu_t *dropout;
menutext_t *op[64];
char *text[64];
menu_t *parent;
} guiinfo_t;
qboolean MC_GuiKey(int key, menu_t *menu)
{
guiinfo_t *info = (guiinfo_t *)menu->data;
switch(key)
{
case K_ESCAPE:
if (info->dropout)
MC_GuiKey(key, info->dropout);
else
{
guiinfo_t *gui;
M_RemoveMenu(menu);
if (menu->parent)
{
gui = (guiinfo_t *)menu->parent->data;
gui->dropout = NULL;
}
}
break;
case K_ENTER:
case K_RIGHTARROW:
if (info->dropout)
MC_GuiKey(key, info->dropout);
else
{
int y, i;
guiinfo_t *gui;
info->dropout = M_CreateMenu(sizeof(guiinfo_t));
currentmenu = info->dropout;
info->dropout->key = MC_GuiKey;
info->dropout->exclusive = false;
info->dropout->parent = menu;
info->dropout->xpos = 0;
info->dropout->ypos = menu->ypos+info->itemselected*8;
for (i = 0; info->text[i]; i++)
if (info->dropout->xpos < strlen(info->text[i]))
info->dropout->xpos = strlen(info->text[i]);
info->dropout->xpos*=8;
info->dropout->xpos+=menu->xpos;
gui = (guiinfo_t *)info->dropout->data;
gui->text[0] = "Hello";
gui->text[1] = "Hello again";
gui->text[2] = "Hello yet again";
for (y = 0, i = 0; gui->text[i]; i++, y+=1*8)
{
info->op[i] = MC_AddRedText(info->dropout, 0, y, gui->text[i], false);
}
}
break;
case K_LEFTARROW:
if (info->dropout)
MC_GuiKey(key, info->dropout);
else
{
guiinfo_t *gui;
M_RemoveMenu(menu);
if (menu->parent)
{
gui = (guiinfo_t *)menu->parent->data;
gui->dropout = NULL;
}
}
break;
case K_UPARROW:
info->op[info->itemselected]->isred = true;
if (info->itemselected)
info->itemselected--;
info->op[info->itemselected]->isred = false;
break;
case K_DOWNARROW:
if (!info->op[info->itemselected])
break;
info->op[info->itemselected]->isred = true;
if (info->text[info->itemselected+1])
info->itemselected++;
info->op[info->itemselected]->isred = false;
break;
}
return true;
}
qboolean MC_Main_Key (int key, menu_t *menu) //here purly to restart demos.
{
if (key == K_ESCAPE)
{
extern int m_save_demonum;
key_dest = key_game;
m_state = m_none;
cls.demonum = m_save_demonum;
if (cls.demonum != -1 && !cls.demoplayback && cls.state == ca_disconnected && COM_CheckParm("-demos"))
CL_NextDemo ();
return true;
}
return false;
}
void M_Menu_Main_f (void)
{
extern cvar_t m_helpismedia;
menubutton_t *b;
menu_t *mainm;
mpic_t *p;
int mgt;
#ifdef CSQC_DAT
if (CSQC_ConsoleCommand(va("%s %s", Cmd_Argv(0), Cmd_Args())))
return;
#endif
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");
mgt = M_GameType();
if (mgt == MGT_QUAKE2) //quake2 main menu.
{
if (R2D_SafeCachePic("pics/m_main_game"))
{
m_state = m_complex;
key_dest = key_menu;
mainm = M_CreateMenu(0);
mainm->key = MC_Main_Key;
MC_AddPicture(mainm, 0, 4, 38, 166, "pics/m_main_plaque");
p = R2D_SafeCachePic("pics/m_main_logo");
if (!p)
return;
MC_AddPicture(mainm, 0, 173, 36, 42, "pics/m_main_logo");
#ifndef CLIENTONLY
MC_AddSelectablePicture(mainm, 68, 13, "pics/m_main_game");
#endif
MC_AddSelectablePicture(mainm, 68, 53, "pics/m_main_multiplayer");
MC_AddSelectablePicture(mainm, 68, 93, "pics/m_main_options");
MC_AddSelectablePicture(mainm, 68, 133, "pics/m_main_video");
MC_AddSelectablePicture(mainm, 68, 173, "pics/m_main_quit");
#ifndef CLIENTONLY
b = MC_AddConsoleCommand (mainm, 68, 13, "", "menu_single\n");
b->common.tooltip = "Singleplayer.";
mainm->selecteditem = (menuoption_t *)b;
b->common.width = 12*20;
b->common.height = 32;
#endif
b = MC_AddConsoleCommand (mainm, 68, 53, "", "menu_multi\n");
b->common.tooltip = "Multiplayer.";
#ifdef CLIENTONLY
mainm->selecteditem = (menuoption_t *)b;
#endif
b->common.width = 12*20;
b->common.height = 32;
b = MC_AddConsoleCommand (mainm, 68, 93, "", "menu_options\n");
b->common.tooltip = "Options.";
b->common.width = 12*20;
b->common.height = 32;
b = MC_AddConsoleCommand (mainm, 68, 133, "", "menu_video\n");
b->common.tooltip = "Video Options.";
b->common.width = 12*20;
b->common.height = 32;
b = MC_AddConsoleCommand (mainm, 68, 173, "", "menu_quit\n");
b->common.tooltip = "Quit to DOS.";
b->common.width = 12*20;
b->common.height = 32;
mainm->cursoritem = (menuoption_t *)MC_AddCursor(mainm, 42, mainm->selecteditem->common.posy);
}
}
else if (mgt == MGT_HEXEN2)
{
m_state = m_complex;
key_dest = key_menu;
mainm = M_CreateMenu(0);
mainm->key = MC_Main_Key;
MC_AddPicture(mainm, 16, 0, 35, 176, "gfx/menu/hplaque.lmp");
p = R2D_SafeCachePic("gfx/menu/title0.lmp");
if (!p)
return;
MC_AddCenterPicture(mainm, 0, 60, "gfx/menu/title0.lmp");
#ifndef CLIENTONLY
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, 64, "Single Player", "menu_single\n");
mainm->selecteditem = (menuoption_t *)b;
b->common.width = 12*20;
b->common.height = 20;
#endif
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, 64+20, "MultiPlayer", "menu_multi\n");
#ifdef CLIENTONLY
mainm->selecteditem = (menuoption_t *)b;
#endif
b->common.width = 12*20;
b->common.height = 20;
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, 64+40, "Options", "menu_options\n");
b->common.width = 12*20;
b->common.height = 20;
if (m_helpismedia.value)
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, 64+60, "Media", "menu_media\n");
else
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, 64+60, "Help", "help\n");
b->common.width = 12*20;
b->common.height = 20;
b=MC_AddConsoleCommandHexen2BigFont (mainm, 80, 64+80, "Quit", "menu_quit\n");
b->common.width = 12*20;
b->common.height = 20;
mainm->cursoritem = (menuoption_t *)MC_AddCursor(mainm, 56, mainm->selecteditem->common.posy);
}
else if (QBigFontWorks())
{
m_state = m_complex;
key_dest = key_menu;
mainm = M_CreateMenu(0);
p = R2D_SafeCachePic("gfx/ttl_main.lmp");
if (!p)
{
MC_AddRedText(mainm, 16, 0, "MAIN MENU", false);
mainm->selecteditem = (menuoption_t *)
MC_AddConsoleCommand (mainm, 64, 32, "Join server", "menu_servers\n");
MC_AddConsoleCommand (mainm, 64, 40, "Options", "menu_options\n");
MC_AddConsoleCommand (mainm, 64, 48, "Quit", "menu_quit\n");
return;
}
mainm->key = MC_Main_Key;
MC_AddPicture(mainm, 16, 4, 32, 144, "gfx/qplaque.lmp");
MC_AddCenterPicture(mainm, 4, 24, "gfx/ttl_main.lmp");
mainm->selecteditem = (menuoption_t *)
MC_AddConsoleCommandQBigFont (mainm, 72, 32, "Single ", "menu_single\n");
MC_AddConsoleCommandQBigFont (mainm, 72, 52, "Multiplayer", "menu_multi\n");
MC_AddConsoleCommandQBigFont (mainm, 72, 72, "Options ", "menu_options\n");
if (m_helpismedia.value)
MC_AddConsoleCommandQBigFont(mainm, 72, 92, "Media ", "menu_media\n");
else
MC_AddConsoleCommandQBigFont(mainm, 72, 92, "Help ", "help\n");
MC_AddConsoleCommandQBigFont (mainm, 72, 112,"Quit ", "menu_quit\n");
mainm->cursoritem = (menuoption_t *)MC_AddCursor(mainm, 54, 32);
}
else
{
m_state = m_complex;
key_dest = key_menu;
mainm = M_CreateMenu(0);
p = R2D_SafeCachePic("gfx/ttl_main.lmp");
if (!p)
{
MC_AddRedText(mainm, 16, 0, "MAIN MENU", false);
mainm->selecteditem = (menuoption_t *)
MC_AddConsoleCommand (mainm, 64, 32, "Join server", "menu_servers\n");
MC_AddConsoleCommand (mainm, 64, 40, "Options", "menu_options\n");
MC_AddConsoleCommand (mainm, 64, 48, "Quit", "menu_quit\n");
return;
}
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");
p = R2D_SafeCachePic("gfx/mainmenu.lmp");
b=MC_AddConsoleCommand (mainm, 72, 32, "", "menu_single\n");
b->common.tooltip = "Start singleplayer Quake game.";
mainm->selecteditem = (menuoption_t *)b;
b->common.width = p->width;
b->common.height = 20;
b=MC_AddConsoleCommand (mainm, 72, 52, "", "menu_multi\n");
b->common.tooltip = "Multiplayer menu.";
b->common.width = p->width;
b->common.height = 20;
b=MC_AddConsoleCommand (mainm, 72, 72, "", "menu_options\n");
b->common.tooltip = "Options menu.";
b->common.width = p->width;
b->common.height = 20;
if (m_helpismedia.value)
{
b=MC_AddConsoleCommand(mainm, 72, 92, "", "menu_media\n");
b->common.tooltip = "Media menu.";
}
else
{
b=MC_AddConsoleCommand(mainm, 72, 92, "", "help\n");
b->common.tooltip = "Help menu.";
}
b->common.width = p->width;
b->common.height = 20;
b=MC_AddConsoleCommand (mainm, 72, 112, "", "menu_quit\n");
b->common.tooltip = "Exit to DOS.";
b->common.width = p->width;
b->common.height = 20;
mainm->cursoritem = (menuoption_t *)MC_AddCursor(mainm, 54, 32);
}
if (!m_preset_chosen.ival)
M_Menu_Preset_f();
}
int MC_AddBulk(struct menu_s *menu, 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)
x -= strlen(bulk->text) * 8;
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, x, y, bulk->text, bulk->rightalign);
break;
case 1: // red text
control = (union menuoption_s *)MC_AddRedText(menu, x, 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, x, y, bulk->text, bulk->consolecmd);
break;
case 1: // function command
control = (union menuoption_s *)MC_AddCommand(menu, x, y, bulk->text, bulk->command);
break;
}
break;
case mt_checkbox:
control = (union menuoption_s *)MC_AddCheckBox(menu, x, y, bulk->text, bulk->cvar, bulk->flags);
control->check.func = bulk->func;
break;
case mt_slider:
control = (union menuoption_s *)MC_AddSlider(menu, x, 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, x, y, bulk->text, bulk->cvar, bulk->options, bulk->values);
break;
case 1: // combo with return value
control = (union menuoption_s *)MC_AddCombo(menu, x, 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, x, y, bulk->text, bulk->cvarname);
spacing += 4;
break;
case 1:
control = (union menuoption_s *)MC_AddEditCvarSlim(menu, x, y, bulk->text, bulk->cvarname);
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_AddWhiteText(menu, xtextend + 8, selectedy, NULL, false);
return y;
}