fteqw/engine/client/m_items.c
Spoike 4149c85ab6 tweeks and changes for android.
audio mixer revamped to cope with threads. 'cache' memory functions no longer used for audio.
added windows acm code to decode mp3 files.
audio playback rates scale with game speed. snd_playbackrate added to control the rate of new samples.
sv_gamespeed no longer needs a map change.
fixed '=' on german keymaps and in_builtinkeymap 0 (and similar issues). bug: keybind names still use US keymap.
added support for rmqe's 24bit network precision.
fixed byterate reporting to no longer be protocol-dependant (nq rates are no longer wildly inaccurate).
removed waterjumping when already dead.
fixed model matrix for viewmodels (modelview unchanged), thus fixing rtlighting on viewmodels.
Added bspx support for rgblighting, lightingdir, and (preliminary)brushlists.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@4001 fc73d0e0-1445-4013-8a0c-d673dee63da5
2012-02-27 12:23:15 +00:00

2195 lines
52 KiB
C

//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;
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, 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)
{
extern int mousecursor_x, mousecursor_y;
qboolean foundexclusive = false;
int mgt;
menu_t *menu;
menuoption_t *option;
if (omousex != mousecursor_x || omousey != 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 = 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.data)->options, (menu_t *)option->custom.data);
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 *data)
{
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->data = data;
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;
}
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);
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 DrawCursor(int prydoncursornum)
{
extern int mousecursor_x, mousecursor_y;
mpic_t *p;
if (!*cl_cursor.string)
p = NULL;
else
p = R2D_SafeCachePic(cl_cursor.string);
if (p)
{
R2D_ImageColours(1, 1, 1, 1);
R2D_Image(mousecursor_x-cl_cursorbias.value, mousecursor_y-cl_cursorbias.value, cl_cursorsize.value, cl_cursorsize.value, 0, 0, 1, 1, p);
}
else
{
int x, y;
Font_BeginString(font_conchar, mousecursor_x, mousecursor_y, &x, &y);
x -= Font_CharWidth('+' | 0xe000 | CON_WHITEMASK)/2;
y -= Font_CharHeight()/2;
Font_DrawChar(x, y, '+' | 0xe000 | CON_WHITEMASK);
Font_EndString(font_conchar);
}
}
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);
}
SCR_DrawCursor(0);
}
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)
{
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;
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 = 20;
#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 = 20;
b = MC_AddConsoleCommand (mainm, 68, 93, "", "menu_options\n");
b->common.tooltip = "Options.";
b->common.width = 12*20;
b->common.height = 20;
b = MC_AddConsoleCommand (mainm, 68, 133, "", "menu_video\n");
b->common.tooltip = "Video Options.";
b->common.width = 12*20;
b->common.height = 20;
b = MC_AddConsoleCommand (mainm, 68, 173, "", "menu_quit\n");
b->common.tooltip = "Quit to DOS.";
b->common.width = 12*20;
b->common.height = 20;
mainm->cursoritem = (menuoption_t *)MC_AddCursor(mainm, 42, mainm->selecteditem->common.posy);
}
return;
}
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);
}
}
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;
}