//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; 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) { Draw_FunStringWidth(x, y, option->slider.text, option->slider.textwidth, true, !menu->cursoritem && menu->selecteditem == option); x += option->slider.textwidth + 3*8; } if (range < 0) range = 0; if (range > 1) range = 1; option->slider.vx = x; x -= 8; Font_BeginString(font_default, x, y, &x, &y); x = Font_DrawChar(x, y, 0xe080 | CON_WHITEMASK); s = x; for (i=0 ; icommon.posx; int y = ypos+option->common.posy; qboolean on; if (option->check.func) on = option->check.func(&option->check, menu, CHK_CHECKED); else if (!option->check.var) on = option->check.value; else if (option->check.bits) //bits is a bitmask for use with cvars (users can be clumsy, so bittage of 0 uses non-zero as true, but sets only bit 1) { if (option->check.var->latched_string) on = atoi(option->check.var->latched_string)&option->check.bits; else on = (int)(option->check.var->value)&option->check.bits; } else { if (option->check.var->latched_string) on = !!atof(option->check.var->latched_string); else on = !!option->check.var->value; } if (option->check.text) { Draw_FunStringWidth(x, y, option->check.text, option->check.textwidth, true, !menu->cursoritem && menu->selecteditem == option); x += option->check.textwidth + 3*8; } #if 0 if (on) Draw_Character (x, y, 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; Draw_FunStringWidth(x, y, option->edit.caption, option->edit.captionwidth, true, !menu->cursoritem && menu->selecteditem == option); x += option->edit.captionwidth + 3*8; if (option->edit.slim) x += 8; // more space for cursor else Draw_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 keys[2]; char *keyname; Draw_FunStringWidth(x, y, option->bind.caption, option->bind.captionwidth, true, !menu->cursoritem && menu->selecteditem == option); x += option->bind.captionwidth + 3*8; { extern cvar_t cl_forcesplitclient; 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; Draw_FunStringWidth(x, y, option->combo.caption, option->combo.captionwidth, true, !menu->cursoritem && menu->selecteditem == option); x += option->combo.captionwidth + 3*8; if (option->combo.numoptions) { if (!menu->cursoritem && menu->selecteditem == option) Draw_AltFunString(x, y, option->combo.options[option->combo.selectedoption]); else Draw_FunString(x, y, option->combo.options[option->combo.selectedoption]); } } break; case mt_custom: option->custom.draw(xpos+option->common.posx, ypos+option->common.posy, &option->custom, menu); break; default: Sys_Error("Bad item type\n"); break; } 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 cx, int bx, int y, const char *caption, char *command) { menubind_t *n = Z_Malloc(sizeof(*n) + strlen(caption)+1 + strlen(command)+1); n->common.type = mt_bind; n->common.iszone = true; n->common.posx = cx; n->common.posy = y; n->captionwidth = bx-cx; n->caption = (char *)(n+1); strcpy(n->caption, caption); n->command = n->caption+strlen(n->caption)+1; strcpy(n->command, command); n->common.width = n->captionwidth + 64; n->common.height = 8; n->common.next = menu->options; menu->options = (menuoption_t *)n; return n; } menupicture_t *MC_AddSelectablePicture(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 cx, int ex, int y, char *text, char *def) { menuedit_t *n = Z_Malloc(sizeof(menuedit_t)+strlen(text)+1); n->slim = false; n->common.type = mt_edit; n->common.iszone = true; n->common.posx = cx; n->common.posy = y; n->common.width = ex-cx+(17)*8; n->common.height = n->slim?8:16; n->modified = true; n->captionwidth = ex-cx; n->caption = (char *)(n+1); strcpy((char *)(n+1), text); Q_strncpyz(n->text, def, sizeof(n->text)); n->common.next = menu->options; menu->options = (menuoption_t *)n; return n; } menuedit_t *MC_AddEditCvar(menu_t *menu, int cx, int ex, int y, char *text, char *name, qboolean isslim) { menuedit_t *n = Z_Malloc(sizeof(menuedit_t)+strlen(text)+1); cvar_t *cvar; cvar = Cvar_Get(name, "", CVAR_USERCREATED|CVAR_ARCHIVE, NULL); //well, this is a menu/ n->slim = isslim; n->common.type = mt_edit; n->common.iszone = true; n->common.posx = cx; n->common.posy = y; n->common.width = ex-cx+(17)*8; n->common.height = n->slim?8:16; n->common.tooltip = cvar->description; n->modified = true; n->captionwidth = ex-cx; n->caption = (char *)(n+1); strcpy((char *)(n+1), text); n->cvar = cvar; #ifdef _DEBUG if (!(cvar->flags & CVAR_ARCHIVE)) Con_Printf("Warning: %s is not set for archiving\n", cvar->name); #endif Q_strncpyz(n->text, cvar->string, sizeof(n->text)); n->common.next = menu->options; menu->options = (menuoption_t *)n; return n; } menubox_t *MC_AddBox(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 tx, int cx, int y, const char *text, cvar_t *var, int bits) { menucheck_t *n = Z_Malloc(sizeof(menucheck_t)+strlen(text)+1); n->common.type = mt_checkbox; n->common.iszone = true; n->common.posx = tx; n->common.posy = y; n->common.height = 8; n->textwidth = cx - tx; n->common.width = cx-tx + 7*8; n->common.tooltip = var?var->description:NULL; n->text = (char *)(n+1); strcpy((char *)(n+1), text); n->var = var; n->bits = bits; #ifdef _DEBUG if (var) if (!(var->flags & CVAR_ARCHIVE)) Con_Printf("Warning: %s is not set for archiving\n", var->name); else if (var->flags & CVAR_RENDERERLATCH) 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 tx, int cx, 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 = tx; n->common.posy = y; n->common.height = 8; n->textwidth = cx - tx; n->common.width = cx-tx + 7*8; n->text = (char *)(n+1); strcpy((char *)(n+1), text); n->func = func; n->bits = bits; n->common.next = menu->options; menu->options = (menuoption_t *)n; return n; } //delta may be 0 menuslider_t *MC_AddSlider(menu_t *menu, int tx, int sx, int y, const char *text, cvar_t *var, float min, float max, float delta) { menuslider_t *n = Z_Malloc(sizeof(menuslider_t)+strlen(text)+1); n->common.type = mt_slider; n->common.iszone = true; n->common.posx = tx; n->common.posy = y; n->common.height = 8; n->common.width = sx-tx + (SLIDER_RANGE+5)*8; n->common.tooltip = var->description; n->var = var; n->textwidth = sx-tx; n->text = (char *)(n+1); strcpy((char *)(n+1), text); if (var) { n->current = var->value; #ifdef _DEBUG if (!(var->flags & CVAR_ARCHIVE)) Con_Printf("Warning: %s is not set for archiving\n", var->name); #endif } n->min = min; n->max = max; n->smallchange = delta; n->largechange = delta*5; n->common.next = menu->options; menu->options = (menuoption_t *)n; return n; } menucombo_t *MC_AddCombo(menu_t *menu, int tx, int cx, int y, const char *caption, const char **ops, int initialvalue) { int numopts; int optlen; int maxoptlen; int optbufsize; menucombo_t *n; char **newops; char *optbuf; int i; maxoptlen = 0; optbufsize = sizeof(char*); numopts = 0; optlen = 0; while(ops[numopts]) { optlen = strlen(ops[numopts]); if (maxoptlen < optlen) maxoptlen = optlen; optbufsize += optlen+1+sizeof(char*); numopts++; } n = Z_Malloc(sizeof(*n) + optbufsize); newops = (char **)(n+1); optbuf = (char*)(newops + numopts+1); n->common.type = mt_combo; n->common.iszone = true; n->common.posx = tx; n->common.posy = y; n->common.height = 8; n->common.width = cx-tx + maxoptlen*8; n->captionwidth = cx-tx; n->caption = caption; n->options = (const char **)newops; n->common.next = menu->options; menu->options = (menuoption_t *)n; n->numoptions = numopts; for (i = 0; i < numopts; i++) { strcpy(optbuf, ops[i]); newops[i] = optbuf; optbuf += strlen(optbuf)+1; } newops[i] = NULL; if (initialvalue >= 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 tx, int cx, int y, const char *caption, cvar_t *cvar, const char **ops, const char **values) { int numopts; int optlen; int maxoptlen; int optbufsize; menucombo_t *n; char **newops; char **newvalues; char *optbuf; int i; maxoptlen = 0; optbufsize = sizeof(char*)*2 + strlen(caption)+1; numopts = 0; optlen = 0; while(ops[numopts]) { optlen = strlen(ops[numopts]); if (maxoptlen < optlen) maxoptlen = optlen; optbufsize += optlen+1+sizeof(char*); optbufsize += strlen(values[numopts])+1+sizeof(char*); numopts++; } n = Z_Malloc(sizeof(*n) + optbufsize); newops = (char **)(n+1); newvalues = (char**)(newops + numopts+1); optbuf = (char*)(newvalues + numopts+1); n->common.type = mt_combo; n->common.iszone = true; n->common.posx = tx; n->common.posy = y; n->common.height = 8; n->common.width = cx-tx + maxoptlen*8; n->common.tooltip = cvar->description; n->captionwidth = cx-tx; strcpy(optbuf, caption); n->caption = optbuf; optbuf += strlen(optbuf)+1; n->options = (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; float ix = option->vx; float ex = ix + 10*8; if (option->smallchange) delta = option->smallchange; else delta = 0.1; if (key == K_LEFTARROW || key == K_MWHEELDOWN) { range -= delta; if (range < option->min) range = option->min; option->current = range; } else if (key == K_RIGHTARROW || key == K_MWHEELUP) { range += delta; if (range > option->max) range = option->max; option->current = range; } else if (key == K_MOUSE1 && mousecursor_x >= ix-8 && mousecursor_x < ex+8) { range = (mousecursor_x - ix) / (ex - ix); range = option->min + range*(option->max-option->min); option->current = range; } else if (key == K_ENTER || key == K_KP_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; } else return; S_LocalSound ("misc/menu2.wav"); if (option->var) Cvar_SetValue(option->var, option->current); } void MC_CheckBox_Key(menucheck_t *option, menu_t *menu, int key) { if (key != K_ENTER && key != K_KP_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_KP_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]); S_LocalSound ("misc/menu2.wav"); } 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_Remove(kdm_menu); 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(¤tmenu->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(¤tmenu->selecteditem->slider, key); break; case mt_checkbox: MC_CheckBox_Key(¤tmenu->selecteditem->check, currentmenu, key); break; case mt_button: case mt_hexen2buttonbigfont: case mt_qbuttonbigfont: if (!currentmenu->selecteditem->button.command) currentmenu->selecteditem->button.key(currentmenu->selecteditem, currentmenu, key); else if (key == K_ENTER || key == K_KP_ENTER || key == K_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(¤tmenu->selecteditem->custom, currentmenu, key); break; case mt_edit: MC_EditBox_Key(¤tmenu->selecteditem->edit, key, unicode); break; case mt_combo: MC_Combo_Key(¤tmenu->selecteditem->combo, key); break; case mt_bind: if (key == K_ENTER || key == K_KP_ENTER || key == K_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_KP_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_Remove(kdm_menu); 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_Add(kdm_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_Add(kdm_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_Add(kdm_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_Add(kdm_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, xleft, xtextend, y, bulk->text, bulk->cvar, bulk->flags); control->check.func = bulk->func; break; case mt_slider: control = (union menuoption_s *)MC_AddSlider(menu, xleft, xtextend, y, bulk->text, bulk->cvar, bulk->min, bulk->max, bulk->delta); break; case mt_combo: switch (bulk->variant) { default: case 0: // cvar combo control = (union menuoption_s *)MC_AddCvarCombo(menu, xleft, xtextend, y, bulk->text, bulk->cvar, bulk->options, bulk->values); break; case 1: // combo with return value control = (union menuoption_s *)MC_AddCombo(menu, xleft, xtextend, y, bulk->text, bulk->options, bulk->selectedoption); break; } break; case mt_edit: switch (bulk->variant) { default: case 0: y += 4; control = (union menuoption_s *)MC_AddEditCvar(menu, xleft, xtextend, y, bulk->text, bulk->cvarname, false); spacing += 4; break; case 1: control = (union menuoption_s *)MC_AddEditCvar(menu, xleft, xtextend, y, bulk->text, bulk->cvarname, true); break; } break; default: Con_Printf(CON_ERROR "Invalid type in bulk menu!\n"); bulk = NULL; continue; } if (bulk->ret) *bulk->ret = control; if (control && MI_Selectable(control) && !selected) selected = control; if (control && bulk->tooltip) control->common.tooltip = bulk->tooltip; if (control && xleft > 0) control->common.extracollide = xleft; y += spacing; bulk++; } menu->selecteditem = selected; if (selected) selectedy = selected->common.posy; menu->cursoritem = (menuoption_t*)MC_AddWhiteText(menu, xtextend + 8, selectedy, NULL, false); return y; }