From fda407c76ce54e72dfccb825e7c98ff1cc8510dc Mon Sep 17 00:00:00 2001 From: ColdSpirit Date: Fri, 21 Jan 2022 03:58:48 +0400 Subject: [PATCH] WIP profile apply fix: subscribe to cvar changes --- qcsrc/dpdefs/upstream/menudefs.qc | 1 + qcsrc/menu/_mod.inc | 1 + qcsrc/menu/_mod.qh | 1 + qcsrc/menu/cvarcallbackitem.qc | 11 ++ qcsrc/menu/cvarcallbackitem.qh | 11 ++ qcsrc/menu/menu.qc | 49 +++++-- qcsrc/menu/menu.qh | 7 + .../xonotic/dialog_multiplayer_profile.qc | 130 ++++++++++++------ .../xonotic/dialog_multiplayer_profile.qh | 3 + 9 files changed, 162 insertions(+), 52 deletions(-) create mode 100644 qcsrc/menu/cvarcallbackitem.qc create mode 100644 qcsrc/menu/cvarcallbackitem.qh diff --git a/qcsrc/dpdefs/upstream/menudefs.qc b/qcsrc/dpdefs/upstream/menudefs.qc index 63d2c6388..47549c82c 100644 --- a/qcsrc/dpdefs/upstream/menudefs.qc +++ b/qcsrc/dpdefs/upstream/menudefs.qc @@ -18,6 +18,7 @@ void(float keynr, float ascii) m_keydown; void(float width, float height) m_draw; void(float mode) m_toggle; void() m_shutdown; +void(string name, string oldValue, string newValue) m_cvar_changed; // optional: float(float) m_gethostcachecategory; ///////////////////////////////////////////////////////// diff --git a/qcsrc/menu/_mod.inc b/qcsrc/menu/_mod.inc index d2a8d702f..b029bc1cf 100644 --- a/qcsrc/menu/_mod.inc +++ b/qcsrc/menu/_mod.inc @@ -1,4 +1,5 @@ // generated file; do not modify +#include #include #include #include diff --git a/qcsrc/menu/_mod.qh b/qcsrc/menu/_mod.qh index da8675831..f3020f519 100644 --- a/qcsrc/menu/_mod.qh +++ b/qcsrc/menu/_mod.qh @@ -1,4 +1,5 @@ // generated file; do not modify +#include #include #include #include diff --git a/qcsrc/menu/cvarcallbackitem.qc b/qcsrc/menu/cvarcallbackitem.qc new file mode 100644 index 000000000..da9aabc62 --- /dev/null +++ b/qcsrc/menu/cvarcallbackitem.qc @@ -0,0 +1,11 @@ +#include "cvarcallbackitem.qh" + +entity createCvarCallbackItem(string theCvarName, cvar_callback theCallback, entity theContext) +{ + entity item; + item = NEW(CvarCallbackItem); + item.cvarName = theCvarName; + item.callback = theCallback; + item.context = theContext; + return item; +} \ No newline at end of file diff --git a/qcsrc/menu/cvarcallbackitem.qh b/qcsrc/menu/cvarcallbackitem.qh new file mode 100644 index 000000000..0b8621dc6 --- /dev/null +++ b/qcsrc/menu/cvarcallbackitem.qh @@ -0,0 +1,11 @@ +#pragma once + +#define cvar_callback void(string, string, string, entity) + +CLASS(CvarCallbackItem, Object) + ATTRIB(CvarCallbackItem, cvarName, string); + ATTRIB(CvarCallbackItem, callback, cvar_callback); // old value, new value + ATTRIB(CvarCallbackItem, context, entity); +ENDCLASS(CvarCallbackItem) + +entity createCvarCallbackItem(string theCvarName, cvar_callback theCallback, entity theContext); \ No newline at end of file diff --git a/qcsrc/menu/menu.qc b/qcsrc/menu/menu.qc index 710dca1e6..0eb3def83 100644 --- a/qcsrc/menu/menu.qc +++ b/qcsrc/menu/menu.qc @@ -416,9 +416,9 @@ int menuTooltipState; // 0: static, 1: fading in, 2: fading out, 3: forced fadi bool m_testmousetooltipbox(vector pos) { return !( - (pos.x >= menuTooltipOrigin.x && pos.x < menuTooltipOrigin.x + menuTooltipSize.x) - && (pos.y >= menuTooltipOrigin.y && pos.y < menuTooltipOrigin.y + menuTooltipSize.y) - ); + (pos.x >= menuTooltipOrigin.x && pos.x < menuTooltipOrigin.x + menuTooltipSize.x) + && (pos.y >= menuTooltipOrigin.y && pos.y < menuTooltipOrigin.y + menuTooltipSize.y) + ); } bool m_testtooltipbox(vector tooltippos) { @@ -710,12 +710,12 @@ void m_draw(float width, float height) { menuNotTheFirstFrame = true; if (Menu_Active && !cvar("menu_video_played")) - { - localcmd("cd loop $menu_cdtrack\n"); - // TODO: use this when we have a welcome sound - //localcmd("cd loop $menu_cdtrack; play sound/announcer/default/welcome.wav\n"); - menuLogoAlpha = -0.8; // no idea why, but when I start this at zero, it jumps instead of fading FIXME - } + { + localcmd("cd loop $menu_cdtrack\n"); + // TODO: use this when we have a welcome sound + //localcmd("cd loop $menu_cdtrack; play sound/announcer/default/welcome.wav\n"); + menuLogoAlpha = -0.8; // no idea why, but when I start this at zero, it jumps instead of fading FIXME + } // ALWAYS set this cvar; if we start but menu is not active, this means we want no background music! localcmd("set menu_video_played 1\n"); } @@ -739,8 +739,8 @@ void m_draw(float width, float height) if (Menu_Active) { if (getmousetarget() == (menuMouseMode ? MT_CLIENT : MT_MENU) - && (getkeydest() == KEY_MENU || getkeydest() == KEY_MENU_GRABBED)) - setkeydest(keyGrabber ? KEY_MENU_GRABBED : KEY_MENU); + && (getkeydest() == KEY_MENU || getkeydest() == KEY_MENU_GRABBED)) + setkeydest(keyGrabber ? KEY_MENU_GRABBED : KEY_MENU); else m_hide(); } @@ -999,3 +999,30 @@ void m_play_click_sound(string soundfile) if (!cvar("menu_sounds")) return; localsound(soundfile); } + +void m_cvar_changed(string theCvarName, string oldValue, string newValue) +{ + if (!menuInitialized) return; + + // some variables have name "\n" + // LOG_INFOF("========> QC: Cvar \"%s\" changed from \"%s\" to \"%s\"", + // strreplace("\n", "\\n", theCvarName), + // strreplace("\n", "\\n", oldValue), + // strreplace("\n", "\\n", newValue) + // ); + + // search for subscribed callbacks and call them + IL_EACH(cvarCallbackItems, it.cvarName == theCvarName, + { + // LOG_INFOF("=========> Callback triggered for %s", it.name); + entity theContext = it.context; + it.callback(theCvarName, oldValue, newValue, theContext); + }); +} + +void cvar_onChangeSubscribe(string theCvarName, cvar_callback theCallback, entity theContext) +{ + entity callbackItem; + callbackItem = createCvarCallbackItem(theCvarName, theCallback, theContext); + IL_PUSH(cvarCallbackItems, callbackItem); +} \ No newline at end of file diff --git a/qcsrc/menu/menu.qh b/qcsrc/menu/menu.qh index 2df331950..5243b6059 100644 --- a/qcsrc/menu/menu.qh +++ b/qcsrc/menu/menu.qh @@ -8,6 +8,8 @@ #include #include +#include "cvarcallbackitem.qh" + const int GAME_ISSERVER = BIT(0); const int GAME_CONNECTED = BIT(1); const int GAME_DEVELOPER = BIT(2); @@ -57,3 +59,8 @@ const string MENU_SOUND_WINNER = "sound/menu/winner.wav"; void m_play_focus_sound(); void m_play_click_sound(string soundfile); + +// callback list and method to subscribe +IntrusiveList cvarCallbackItems; +STATIC_INIT(cvarCallbackItems) { cvarCallbackItems = IL_NEW(); } +void cvar_onChangeSubscribe(string, cvar_callback, entity); \ No newline at end of file diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_profile.qc b/qcsrc/menu/xonotic/dialog_multiplayer_profile.qc index e885ee0b0..028c740e0 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_profile.qc +++ b/qcsrc/menu/xonotic/dialog_multiplayer_profile.qc @@ -23,17 +23,94 @@ entity makeXonoticProfileTab() me = NEW(XonoticProfileTab); me.configureDialog(me); + string name = cvar_string("_cl_name"); + string playermodel = cvar_string("_cl_playermodel"); + string playerskin = cvar_string("_cl_playerskin"); + string color = cvar_string("_cl_color"); + // if color unset, set random color (init it) - if (cvar_string("_cl_color") == cvar_defstring("_cl_color")) + if (color == cvar_defstring("_cl_color")) { // idk what meaning of 16, maybe just COLOR_BUTTONS_COUNT+1 float randomColor = 16 * floor(random() * COLOR_BUTTONS_COUNT) + floor(random() * COLOR_BUTTONS_COUNT); - cvar_set("_cl_color", ftos(randomColor)); + color = ftos(randomColor); + cvar_set("_cl_color", color); } + // copy field values from game cvars + cvar_set(MENU_CVAR_COLOR, color); + cvar_set(MENU_CVAR_NAME, name); + cvar_set(MENU_CVAR_SKIN, playerskin); + cvar_set(MENU_CVAR_MODEL, playermodel); + + // subscribe to cvar changes + + // stable branch cvars + cvar_onChangeSubscribe("_cl_name", onCvarChanged, me); + cvar_onChangeSubscribe("_cl_color", onCvarChanged, me); + cvar_onChangeSubscribe("_cl_playermodel", onCvarChanged, me); + + // master branch cvars + cvar_onChangeSubscribe("name", onCvarChanged, me); + cvar_onChangeSubscribe("topcolor", onCvarChanged, me); + cvar_onChangeSubscribe("bottomcolor", onCvarChanged, me); + cvar_onChangeSubscribe("playermodel", onCvarChanged, me); + return me; } +// if values changed from console, update it in menu +void onCvarChanged(string cvarName, string oldValue, string newValue, entity me) +{ + // IMPORTANT: dont change same cvars that was observed to avoid infinite loop + + // nothing to change + if (oldValue == newValue) + { + return; + } + + // not update field values when tab in 'edit mode' + if (!me.applyButton.disabled) + { + return; + } + + // LOG_INFOF("[onCvarChanged]: %s, %s, %s", cvarName, oldValue, newValue); + + // name + if (cvarName == "_cl_name" || cvarName == "name") + { + cvar_set(MENU_CVAR_NAME, newValue); + me.nameInput.loadCvars(me.nameInput); + } + + // model + else if (cvarName == "_cl_playermodel" || cvarName == "playermodel") + { + cvar_set(MENU_CVAR_MODEL, newValue); + me.playerModelSelector.loadCvars(me.playerModelSelector); + me.playerModelSelector.go(me.playerModelSelector, 0); + } + + // skin (skin not tested, dont know what cvars it use) + // else if (cvarName == "_cl_playerskin" || cvarName == "playerskin") + // { + // } + + // color + else if (cvarName == "_cl_color") + { + cvar_set(MENU_CVAR_COLOR, newValue); + me.updateColor(me); + } + else if (cvarName == "topcolor" || cvarName == "bottomcolor") + { + cvar_set(MENU_CVAR_COLOR, cvar_string("_cl_color")); + me.updateColor(me); + } +} + void XonoticProfileTab_draw(entity me) { string name = cvar_string("_cl_name"); @@ -42,44 +119,6 @@ void XonoticProfileTab_draw(entity me) me.playerNameLabel.alpha = ((mod(time * 2, 2) < 1) ? 1 : 0); else me.playerNameLabel.alpha = me.playerNameLabelAlpha; - - // if values changed from console, update it in menu - if (me.applyButton.disabled) - { - // name field - string inputName = cvar_string(MENU_CVAR_NAME); - if (name != inputName) - { - cvar_set(MENU_CVAR_NAME, name); - me.nameInput.loadCvars(me.nameInput); - } - - // color buttons - string color = cvar_string("_cl_color"); - string inputColor = cvar_string(MENU_CVAR_COLOR); - if (color != inputColor) - { - cvar_set(MENU_CVAR_COLOR, color); - for (int i = 0; i < COLOR_BUTTONS_COUNT; i++) - { - me.colorButtonGroup1[i].loadCvars(me.colorButtonGroup1[i]); - me.colorButtonGroup2[i].loadCvars(me.colorButtonGroup2[i]); - } - } - - // player model - string skin = cvar_string("_cl_playerskin"); - string skinInput = cvar_string(MENU_CVAR_SKIN); - string model = cvar_string("_cl_playermodel"); - string modelInput = cvar_string(MENU_CVAR_MODEL); - if (skin != skinInput || model != modelInput) - { - cvar_set(MENU_CVAR_SKIN, skin); - cvar_set(MENU_CVAR_MODEL, model); - me.playerModelSelector.loadCvars(me.playerModelSelector); - me.playerModelSelector.go(me.playerModelSelector, 0); - } - } SUPER(XonoticProfileTab).draw(me); } @@ -92,7 +131,7 @@ void XonoticProfileTab_fill(entity me) entity e, label; float i; me.applyButton = makeXonoticCommandButton(_("Apply immediately"), '0 0 0', - "_cl_color \"$"MENU_CVAR_COLOR"\"" + "_cl_color \"$"MENU_CVAR_COLOR"\";" "color -1 -1;" // apply colors contained in _cl_color "name \"$"MENU_CVAR_NAME"\";" "playermodel $"MENU_CVAR_MODEL";" @@ -221,3 +260,12 @@ void XonoticProfileTab_fill(entity me) me.gotoRC(me, me.rows - 1, 0); me.TD(me, 1, me.columns, me.applyButton); } + +void XonoticProfileTab_updateColor(entity me) +{ + for (int i = 0; i < COLOR_BUTTONS_COUNT; i++) + { + me.colorButtonGroup1[i].loadCvars(me.colorButtonGroup1[i]); + me.colorButtonGroup2[i].loadCvars(me.colorButtonGroup2[i]); + } +} \ No newline at end of file diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_profile.qh b/qcsrc/menu/xonotic/dialog_multiplayer_profile.qh index ccf772518..db7393613 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_profile.qh +++ b/qcsrc/menu/xonotic/dialog_multiplayer_profile.qh @@ -5,6 +5,7 @@ const int COLOR_BUTTONS_COUNT = 15; CLASS(XonoticProfileTab, XonoticTab) METHOD(XonoticProfileTab, fill, void(entity)); METHOD(XonoticProfileTab, draw, void(entity)); + METHOD(XonoticProfileTab, updateColor, void(entity)); ATTRIB(XonoticProfileTab, intendedWidth, float, 0.9); ATTRIB(XonoticProfileTab, rows, float, 23); ATTRIB(XonoticProfileTab, columns, float, 6.1); // added extra .2 for center space @@ -17,3 +18,5 @@ CLASS(XonoticProfileTab, XonoticTab) ATTRIB(XonoticProfileTab, playerModelSelector, entity); ENDCLASS(XonoticProfileTab) entity makeXonoticProfileTab(); + +cvar_callback onCvarChanged; \ No newline at end of file -- 2.39.2