Menu-FN: Parsing user.scr and settings.scr in Advanced options is now mostly done. Still need to support LIST type layout elements.

This commit is contained in:
Marco Cawthorne 2023-02-11 17:10:43 -08:00
parent 7518e743b9
commit 370396a415
Signed by: eukara
GPG Key ID: CE2032F0A2882A22
11 changed files with 691 additions and 7 deletions

View File

@ -27,6 +27,7 @@ On idTech 2 BSPs, it will change texture variants when triggered.
# TRIVIA
This entity was introduced in Quake (1996).
*/
class
func_wall:NSRenderableEntity
{
@ -39,6 +40,7 @@ public:
virtual void Trigger(entity, triggermode_t);
#else
virtual void Init(void);
virtual bool CanSpawn(bool);
#endif
};
@ -86,6 +88,13 @@ func_wall::Respawn(void)
ClearAngles();
}
#else
bool
func_wall::CanSpawn(bool clientSide)
{
return true;
}
void
func_wall::Init(void)
{

View File

@ -33,6 +33,11 @@ w_scrollbar.qc
w_updatelist.qc
w_combobox.qc
w_cvartextbox.qc
w_cvarcheckbox.qc
layout_script.qc
m_addserver.qc
m_advancedcontrols.qc
m_advancedcustomize.qc

View File

@ -0,0 +1,214 @@
/* Copyright (c) 2023, Vera Visions, L.L.C. All rights reserved. */
/* Layout Script parser for Menu-FN
Layout scripts are used for the advanced options for server and user settings.
Those are found on the filesystem as 'settings.src' and 'user.scr'.
The layout is usually this:
{
"some_cvar"
{
"Description"
{ TYPE [PARAMETERS ...] }
{ Default Value }
}
[...]
}
of which TYPE can be STRING, NUMBER, BOOL and LIST.
The types for NUMBER specify a range like this:
[...]
{
"Floating point cvar"
{ NUMBER 0.0 1.0 }
{ 0.5 }
}
[...]
And you can set -1 to either min/max to disable a limit on either end.
In the above example we set the default value to something in between
the min/max.
The types for LIST expect a whitespace separated key/value pair:
[...]
{
"Damage setting"
{ LIST "No damage" "0" "All damage" "1" }
{ "1" }
}
[...]
The default value for a LIST type needs to reference a value, not the key.
The LIST parameter may also span multiple lines.
*/
enum
{
LAYBR_OUT,
LAYBR_CVAR,
LAYBR_DESCR,
LAYBR_VALUE, /* could be either type + params or default value */
};
static vector g_layoutPos;
static void
Layout_FromFile_Create(CPageTab target, string cvar, string descr, string type, string parms, string defval)
{
int c = 0;
int x_ofs = 309;
CWidget new = __NULL__;
//print(sprintf("New widget:\n\t%s\n\t%s\n\t%s\n\t%s\n\t%s\n\n", cvar, descr, type, parms, defval));
switch (type) {
case "STRING":
case "NUMBER":
CCVarTextBox txtbox;
new = spawn(CCVarTextBox);
txtbox = (CCVarTextBox)new;
txtbox.m_length = 156;
txtbox.SetCvar(cvar, defval);
break;
case "BOOL":
CCVarCheckBox chkbox;
new = spawn(CCVarCheckBox);
chkbox = (CCVarCheckBox)new;
chkbox.SetCvar(cvar, defval);
x_ofs = 138 + 28;
break;
case "LIST":
//new = spawn(CTextBox);
//break;
default:
print(sprintf("Unknown layout element type %S\n", type));
}
if (!new)
return;
new.SetPos(g_layoutPos[0], g_layoutPos[1]);
target.AddWidget(new);
/* add the label */
CLabel lbl = spawn(CLabel);
lbl.SetPos(x_ofs, g_layoutPos[1] + 5);
lbl.SetText(descr, font_label);
lbl.SetTextHeight(14);
target.AddWidget(lbl);
g_layoutPos[1] += 32;
}
static void
Layout_FromFile_Parse(string line, CPageTab target)
{
static string cvar;
static string descr;
static string type;
static string parms;
static string defval;
static int braced; /* what brace level we are in */
static int depth; /* individual cvar brace depth */
int c = 0;
string key = __NULL__;
c = tokenize_console(line);
for (int i = 0; i < c; i++) {
key = argv(i);
if not (key)
continue;
switch (key) {
case "{":
braced++;
if (braced > LAYBR_CVAR)
depth++;
break;
case "}":
braced--;
/* we just got done with one widget */
if (braced == LAYBR_CVAR) {
Layout_FromFile_Create(target, cvar, descr, type, parms, defval);
c = tokenize_console(line); /* the above function may tokenize */
cvar = __NULL__;
descr = __NULL__;
type = __NULL__;
parms = __NULL__;
defval = __NULL__;
depth = 0;
}
break;
default:
//if (braced >= LAYBR_CVAR)
// print(sprintf("%i (depth %i): %S\n", braced, depth, key));
switch (braced) {
case LAYBR_CVAR:
cvar = key;
break;
case LAYBR_DESCR:
descr = key;
break;
case LAYBR_VALUE:
if (depth == 2) {
if (!type)
type = key;
else {
if (parms)
parms = sprintf("%s %S", parms, key);
else
parms = key;
}
} else if (depth == 3) {
if (defval)
defval = sprintf("%s %S", defval, key);
else
defval = key;
}
break;
default:
}
break;
}
}
}
void
Layout_FromFile(string filename, CWidget target)
{
filestream fh;
string line;
CPageTab tab = spawn(CPageTab);
//print(sprintf("Now loading: %S\n\n\n", filename));
fh = fopen(filename, FILE_READ);
if (fh < 0) {
error(sprintf("Cannot read layout file %S\n", filename));
return;
}
/* reset the starting position */
g_layoutPos = [138, 140];
while ((line = fgets(fh))) {
Layout_FromFile_Parse(line, tab);
}
fclose(fh);
Widget_Add(target, tab);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016-2022 Vera Visions LLC.
* Copyright (c) 2016-2023 Vera Visions LLC.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -63,6 +63,8 @@ menu_advancedcustomize_init(void)
ac_btnCancel.SetExecute(ac_btncancel_start);
ac_btnCancel.SetPos(50,172);
Widget_Add(fn_advcustomize, ac_btnCancel);
Layout_FromFile("user.scr", fn_advcustomize);
}
void

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016-2022 Vera Visions LLC.
* Copyright (c) 2016-2023 Vera Visions LLC.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -14,17 +14,68 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
CWidget fn_advmpoptions;
CMainButton advmpopt_btnDone;
CMainButton advmpopt_btnCancel;
void
advmpopt_btndone_start(void)
{
static void advmpopt_btndone_end(void) {
g_menupage = PAGE_CREATEGAME;
}
localsound("../media/launch_dnmenu1.wav");
header.SetStartEndPos(45,45,50,172);
header.SetStartEndSize(460,80,156,26);
header.m_lerp = 0.0f;
header.m_visible = TRUE;
header.SetHeader(HEAD_ADVANCED);
header.SetExecute(advmpopt_btndone_end);
}
void
advmpopt_btncancel_start(void)
{
static void advmpopt_btncancel_end(void) {
g_menupage = PAGE_CREATEGAME;
}
localsound("../media/launch_dnmenu1.wav");
header.SetStartEndPos(45,45,50,172);
header.SetStartEndSize(460,80,156,26);
header.m_lerp = 0.0f;
header.m_visible = TRUE;
header.SetHeader(HEAD_ADVANCED);
header.SetExecute(advmpopt_btncancel_end);
}
void
menu_advancedmpoptions_init(void)
{
fn_advmpoptions = spawn(CWidget);
advmpopt_btnDone = spawn(CMainButton);
advmpopt_btnDone.SetImage(BTN_DONE);
advmpopt_btnDone.SetExecute(advmpopt_btndone_start);
advmpopt_btnDone.SetPos(50,140);
Widget_Add(fn_advmpoptions, advmpopt_btnDone);
advmpopt_btnCancel = spawn(CMainButton);
advmpopt_btnCancel.SetImage(BTN_CANCEL);
advmpopt_btnCancel.SetExecute(advmpopt_btncancel_start);
advmpopt_btnCancel.SetPos(50,172);
Widget_Add(fn_advmpoptions, advmpopt_btnCancel);
Layout_FromFile("settings.scr", fn_advmpoptions);
}
void
menu_advancedmpoptions_draw(void)
{
Widget_Draw(fn_advmpoptions);
Header_Draw(HEAD_ADVOPTIONS);
}
void
menu_advancedmpoptions_input(float evtype, float scanx, float chary, float devid)
{
Widget_Input(fn_advmpoptions, evtype, scanx, chary, devid);
}

View File

@ -65,6 +65,21 @@ create_btnok_start(void)
header.SetExecute(create_btnok_end);
}
void
create_btnadvanced_start(void)
{
static void create_btnadvanced_end(void) {
g_menupage = PAGE_ADVANCEDMPOPTIONS;
}
localsound("../media/launch_upmenu1.wav");
header.SetStartEndPos(50,172,45,45);
header.SetStartEndSize(156,26,460,80);
header.m_lerp = 0.0f;
header.m_visible = TRUE;
header.SetHeader(HEAD_ADVANCED);
header.SetExecute(create_btnadvanced_end);
}
void
create_btncancel_start(void)
{
@ -95,7 +110,7 @@ menu_creategame_init(void)
fn_create = spawn(CWidget);
create_btnAdv = spawn(CMainButton);
create_btnAdv.SetImage(BTN_ADVOPTIONS);
//create_btnAdv.SetExecute(btn_console);
create_btnAdv.SetExecute(create_btnadvanced_start);
create_btnAdv.SetPos(50,140);
Widget_Add(fn_create, create_btnAdv);

View File

@ -0,0 +1,94 @@
/*
* Copyright (c) 2016-2022 Vera Visions LLC.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
class CCVarCheckBox:CWidget
{
string m_cvar;
float m_value;
int m_hover;
int m_click;
virtual void(float value) m_changed = 0;
virtual void(void) Draw;
virtual void(float, float, float, float) Input;
virtual void(string, string) SetCvar;
virtual void _CVarChanged(void);
};
void
CCVarCheckBox::_CVarChanged(void)
{
cvar_set(m_cvar, ftos(m_value));
//print(sprintf("Cvar %S changed to %d\n", m_cvar, m_value));
}
void
CCVarCheckBox::Draw(void)
{
if (m_click) {
drawpic([g_menuofs[0]+m_x,g_menuofs[1]+m_y], g_bmp[CB_DOWN],
[19,19], [1,1,1], 1.0f, 0);
} else {
if (m_value) {
drawpic([g_menuofs[0]+m_x,g_menuofs[1]+m_y], g_bmp[CB_CHECKED],
[19,19], [1,1,1], 1.0f);
} else {
drawpic([g_menuofs[0]+m_x,g_menuofs[1]+m_y], g_bmp[CB_EMPTY],
[19,19], [1,1,1], 1.0f);
}
}
if (m_hover) {
drawpic([g_menuofs[0]+m_x,g_menuofs[1]+m_y], g_bmp[CB_OVER],
[19,19], [1,1,1], 1.0f, 1);
}
}
void
CCVarCheckBox::Input(float type, float x, float y, float devid)
{
if (Util_CheckMouse(m_x, m_y, 19, 19) == TRUE) {
m_hover = TRUE;
} else {
m_hover = FALSE;
}
if (m_hover && type == IE_KEYDOWN && x == K_MOUSE1) {
m_click = TRUE;
}
if (type == IE_KEYUP && x == K_MOUSE1) {
if (m_click) {
m_click = FALSE;
m_value = TRUE - m_value;
_CVarChanged();
}
}
}
void
CCVarCheckBox::SetCvar(string txt, string defaultvalue)
{
string stringval = cvar_string(txt);
if (!stringval)
m_value = stof(defaultvalue);
else
m_value = cvar(txt);
m_cvar = txt;
_CVarChanged();
}

View File

@ -0,0 +1,134 @@
/*
* Copyright (c) 2016-2022 Vera Visions LLC.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
enumflags
{
TEXTBOX_VISIBLE,
TEXTBOX_HOVER,
TEXTBOX_DOWN,
TEXTBOX_FOCUS,
};
class CCVarTextBox:CWidget
{
string m_text;
int m_length;
int m_flags;
string m_cvar;
void(void) CCVarTextBox;
virtual void(void) Draw;
virtual void(float, float, float, float) Input;
virtual void(string, string) SetCvar;
virtual void(int) SetLength;
virtual void _CVarChanged(void);
};
void
CCVarTextBox::_CVarChanged(void)
{
cvar_set(m_cvar, m_text);
//print(sprintf("Cvar %S changed to %S\n", m_cvar, m_text));
}
void
CCVarTextBox::CCVarTextBox(void)
{
m_length = 184;
}
void
CCVarTextBox::Draw(void)
{
if (g_focuswidget == this) {
drawfill([g_menuofs[0]+m_x,g_menuofs[1]+m_y], [m_length,24], [0.5,0.5,0.5], 1.0f);
} else {
drawfill([g_menuofs[0]+m_x,g_menuofs[1]+m_y], [m_length,24], [0.25,0.25,0.25], 1.0f);
}
drawfill([g_menuofs[0]+m_x+3,g_menuofs[1]+m_y+3], [m_length-6,18], [0,0,0], 1.0f);
drawfont = Font_GetID(font_label);
if (m_flags & TEXTBOX_FOCUS) {
if (rint(time*4) & 1) {
drawstring([g_menuofs[0]+m_x+6,g_menuofs[1]+m_y+6],
sprintf("%s|", m_text), [12,12], col_input_text, 1.0f, 0);
return;
}
}
if (m_text) {
drawstring([g_menuofs[0] + m_x + 6, g_menuofs[1] + m_y + 6], m_text,
[12,12], col_input_text, 1.0f, 0);
}
}
void
CCVarTextBox::Input(float type, float x, float y, float devid)
{
if (type == IE_KEYDOWN) {
switch (x) {
case K_MOUSE1:
if (Util_CheckMouse(m_x,m_y,m_length,24)) {
g_focuswidget = this;
m_flags |= TEXTBOX_DOWN;
}
break;
case K_BACKSPACE:
if (m_flags & TEXTBOX_FOCUS) {
m_text = substring(m_text, 0, strlen(m_text) - 1);
}
break;
case K_ENTER:
case K_KP_ENTER:
_CVarChanged();
break;
case K_ESCAPE:
break;
default:
if (m_flags & TEXTBOX_FOCUS) {
m_text = sprintf("%s%s", m_text, chr2str(y));
}
}
} else if (type == IE_KEYUP) {
if (x == K_MOUSE1) {
if (m_flags & TEXTBOX_DOWN && Util_CheckMouse(m_x,m_y,m_length,24)) {
m_flags |= TEXTBOX_FOCUS;
} else {
m_flags -= (m_flags & TEXTBOX_FOCUS);
}
m_flags -= (m_flags & TEXTBOX_DOWN);
}
}
}
void
CCVarTextBox::SetCvar(string txt, string defaultvalue)
{
m_text = cvar_string(txt);
if (!m_text)
m_text = defaultvalue;
m_cvar = txt;
_CVarChanged();
}
void
CCVarTextBox::SetLength(int len)
{
m_length = len;
}

View File

@ -23,8 +23,10 @@ class CLabel:CWidget
float m_alpha;
int m_font;
void(void) CLabel;
virtual void(void) Draw;
void CLabel(void);
virtual void Draw(void);
virtual void SetText(string, font_s);
virtual void SetTextHeight(int);
};
void
@ -36,11 +38,25 @@ CLabel::CLabel(void)
m_sx = m_sy = 8;
}
void
CLabel::SetText(string txt, font_s font)
{
m_label = txt;
m_font = Font_GetID(font);
m_sx = m_sy = Font_GetHeight(font);
}
void
CLabel::SetTextHeight(int val)
{
m_sx = m_sy = val;
}
void
CLabel::Draw(void)
{
drawfont = m_font;
drawstring([m_x,m_y], m_label, [m_sx,m_sy], m_color, m_alpha, 0);
drawstring([g_menuofs[0] + m_x, g_menuofs[1] + m_y], m_label, [m_sx,m_sy], m_color, m_alpha, 0);
}
/* Sometimes all you need is a static label */

View File

@ -1 +1,143 @@
// TODO
class CPageTab:CWidget
{
int m_page;
int m_maxpage;
CWidget m_children;
int m_items;
int m_lbtnstate;
int m_rbtnstate;
void CPageTab(void);
virtual void Draw(void);
virtual void Input(float, float, float, float);
virtual void AddWidget(CWidget);
};
void
CPageTab::CPageTab(void)
{
m_page = 1;
m_maxpage = 1;
}
.string classname;
void
CPageTab::Draw(void)
{
string lbtn = g_bmp[LARROWDEFAULT];
string rbtn = g_bmp[RARROWDEFAULT];
if (m_lbtnstate & 2)
lbtn = g_bmp[LARROWPRESSED];
else if (m_lbtnstate & 1)
lbtn = g_bmp[LARROWFLYOVER];
if (m_rbtnstate & 2)
rbtn = g_bmp[RARROWPRESSED];
else if (m_rbtnstate & 1)
rbtn = g_bmp[RARROWFLYOVER];
drawpic([g_menuofs[0]+440,g_menuofs[1]+108], lbtn,
[16,16], [1,1,1], 1.0f, 0);
drawpic([g_menuofs[0]+573,g_menuofs[1]+108], rbtn,
[16,16], [1,1,1], 1.0f, 0);
WLabel_Static(460, 111, sprintf("Page %i of %i", m_page, m_maxpage), 14, 14, [1,1,1],
1.0, 0, font_label);
CWidget wNext = m_children;
do {
if (wNext) {
//print(sprintf("%i %s\n", wNext.m_onpage, wNext.classname));
if (wNext.m_onpage == m_page)
wNext.Draw();
}
wNext = wNext.m_next;
} while (wNext);
}
void
CPageTab::Input(float type, float x, float y, float devid)
{
/* left button hover check */
if (Util_CheckMouse(440, 108, 26, 26))
m_lbtnstate |= 1;
else
m_lbtnstate &= ~ 1;
/* right button hover check */
if (Util_CheckMouse(573, 108, 26, 26))
m_rbtnstate |= 1;
else
m_rbtnstate &= ~ 1;
/* left button click check */
if ((m_lbtnstate & 1) && type == IE_KEYDOWN && x == K_MOUSE1) {
m_lbtnstate |= 2;
}
/* right button click check */
m_rbtnstate = Util_CheckMouse(573, 108, 26, 26) ? 1 : 0;
if ((m_rbtnstate & 1) && type == IE_KEYDOWN && x == K_MOUSE1) {
m_rbtnstate |= 2;
}
/* unclick + trigger (if need be) */
if (type == IE_KEYUP && x == K_MOUSE1) {
m_lbtnstate &= ~2;
m_rbtnstate &= ~2;
if (m_lbtnstate & 1) {
m_page--;
if (m_page < 1)
m_page = 1;
}
if (m_rbtnstate & 1) {
m_page++;
if (m_page > m_maxpage)
m_page = m_maxpage;
}
}
/* pass inputs to our children */
CWidget wNext = m_children;
do {
if (wNext) {
if (wNext.m_onpage == m_page)
wNext.Input(type, x, y, devid);
}
wNext = wNext.m_next;
} while (wNext);
}
void
CPageTab::AddWidget(CWidget new)
{
/* 8 widgets plus their label = 16 items per page */
m_maxpage = 1 + floor((float)m_items / 16.0);
new.m_y -= (m_maxpage - 1) * (32*8);
if (m_children) {
CWidget wNext = m_children;
do {
if (!wNext.m_next) {
wNext.m_next = new;
new.m_onpage = m_maxpage;
//print(sprintf("%d adding %S to page %i\n", num_for_edict(this), new.classname, m_maxpage));
m_items++;
return;
}
wNext = wNext.m_next;
} while (wNext);
} else {
m_children = new;
new.m_onpage = m_maxpage;
m_items++;
//print(sprintf("%d adding %S to page %i\n", num_for_edict(this), new.classname, m_maxpage));
}
}

View File

@ -20,6 +20,7 @@ class CWidget
int m_y;
CWidget m_next;
CWidget m_parent;
int m_onpage;
void(void) CWidget;
virtual void(void) Draw;
@ -33,6 +34,7 @@ CWidget::CWidget(void)
{
m_x = g_menuofs[0];
m_y = g_menuofs[1];
m_onpage = 0;
}
void