Add can_change for console variables

This commit is contained in:
Lactozilla 2023-11-01 15:50:59 -03:00
parent cec5338f7c
commit 01922d94f1
4 changed files with 100 additions and 55 deletions

View file

@ -1379,8 +1379,8 @@ void CV_RegisterVar(consvar_t *variable)
#ifdef PARANOIA
if ((variable->flags & CV_NOINIT) && !(variable->flags & CV_CALL))
I_Error("variable %s has CV_NOINIT without CV_CALL\n", variable->name);
if ((variable->flags & CV_CALL) && !variable->func)
I_Error("variable %s has CV_CALL without a function\n", variable->name);
if ((variable->flags & CV_CALL) && !(variable->func || variable->can_change))
I_Error("variable %s has CV_CALL without any callbacks\n", variable->name);
#endif
if (variable->flags & CV_NOINIT)
@ -1446,12 +1446,13 @@ static void Setvalue(consvar_t *var, const char *valstr, boolean stealth)
boolean override = false;
INT32 overrideval = 0;
// If we want messages informing us if cheats have been enabled or disabled,
// we need to rework the consvars a little bit. This call crashes the game
// on load because not all variables will be registered at that time.
/* boolean prevcheats = false;
if (var->flags & CV_CHEAT)
prevcheats = CV_CheatsEnabled(); */
// raise 'can change' code
LUA_CVarChanged(var); // let consolelib know what cvar this is.
if (var->flags & CV_CALL && var->can_change && !stealth)
{
if (!var->can_change(valstr))
return;
}
if (var->PossibleValue)
{
@ -1613,16 +1614,6 @@ found:
}
finish:
// See the note above.
/* if (var->flags & CV_CHEAT)
{
boolean newcheats = CV_CheatsEnabled();
if (!prevcheats && newcheats)
CONS_Printf(M_GetText("Cheats have been enabled.\n"));
else if (prevcheats && !newcheats)
CONS_Printf(M_GetText("Cheats have been disabled.\n"));
} */
if (var->flags & CV_SHOWMODIFONETIME || var->flags & CV_SHOWMODIF)
{
@ -1635,8 +1626,7 @@ finish:
}
var->flags |= CV_MODIFIED;
// raise 'on change' code
LUA_CVarChanged(var); // let consolelib know what cvar this is.
if (var->flags & CV_CALL && !stealth)
if (var->flags & CV_CALL && var->func && !stealth)
var->func();
return;

View file

@ -136,6 +136,7 @@ typedef struct consvar_s //NULL, NULL, 0, NULL, NULL |, 0, NULL, NULL, 0, 0, NUL
INT32 flags; // flags see cvflags_t above
CV_PossibleValue_t *PossibleValue; // table of possible values
void (*func)(void); // called on change, if CV_CALL set
boolean (*can_change)(const char*); // called before change, if CV_CALL set
INT32 value; // for INT32 and fixed_t
const char *string; // value in string
char *zstring; // Either NULL or same as string.
@ -158,6 +159,9 @@ typedef struct consvar_s //NULL, NULL, 0, NULL, NULL |, 0, NULL, NULL, 0, 0, NUL
/* name, defaultvalue, flags, PossibleValue, func */
#define CVAR_INIT( ... ) \
{ __VA_ARGS__, NULL, 0, NULL, NULL, {0, {NULL}}, 0U, (char)0, NULL }
#define CVAR_INIT_WITH_CALLBACKS( ... ) \
{ __VA_ARGS__, 0, NULL, NULL, {0, {NULL}}, 0U, (char)0, NULL }
#ifdef OLD22DEMOCOMPAT

View file

@ -299,6 +299,30 @@ static void Lua_OnChange(void)
lua_remove(gL, 1); // remove LUA_GetErrorMessage
}
static boolean Lua_CanChange(const char *valstr)
{
lua_pushcfunction(gL, LUA_GetErrorMessage);
lua_insert(gL, 1); // Because LUA_Call wants it at index 1.
// From CV_CanChange registry field, get the function for this cvar by name.
lua_getfield(gL, LUA_REGISTRYINDEX, "CV_CanChange");
I_Assert(lua_istable(gL, -1));
lua_pushlightuserdata(gL, this_cvar);
lua_rawget(gL, -2); // get function
LUA_RawPushUserdata(gL, this_cvar);
lua_pushstring(gL, valstr);
boolean result;
LUA_Call(gL, 2, 1, 1); // call function(cvar, valstr)
result = lua_toboolean(gL, -1);
lua_pop(gL, 1); // pop CV_CanChange table
lua_remove(gL, 1); // remove LUA_GetErrorMessage
return result;
}
static int lib_cvRegisterVar(lua_State *L)
{
const char *k;
@ -457,6 +481,20 @@ static int lib_cvRegisterVar(lua_State *L)
lua_pop(L, 1);
cvar->func = Lua_OnChange;
}
else if (cvar->flags & CV_CALL && (k && fasticmp(k, "can_change")))
{
if (!lua_isfunction(L, 4))
{
TYPEERROR("func", LUA_TFUNCTION)
}
lua_getfield(L, LUA_REGISTRYINDEX, "CV_CanChange");
I_Assert(lua_istable(L, 5));
lua_pushlightuserdata(L, cvar);
lua_pushvalue(L, 4);
lua_rawset(L, 5);
lua_pop(L, 1);
cvar->can_change = Lua_CanChange;
}
lua_pop(L, 1);
}
@ -478,9 +516,9 @@ static int lib_cvRegisterVar(lua_State *L)
return luaL_error(L, M_GetText("Variable %s has CV_NOINIT without CV_CALL"), cvar->name);
}
if ((cvar->flags & CV_CALL) && !cvar->func)
if ((cvar->flags & CV_CALL) && !(cvar->func || cvar->can_change))
{
return luaL_error(L, M_GetText("Variable %s has CV_CALL without a function"), cvar->name);
return luaL_error(L, M_GetText("Variable %s has CV_CALL without any callbacks"), cvar->name);
}
cvar->flags |= CV_ALLOWLUA;
@ -674,6 +712,8 @@ int LUA_ConsoleLib(lua_State *L)
lua_setfield(L, LUA_REGISTRYINDEX, "CV_PossibleValue");
lua_newtable(L);
lua_setfield(L, LUA_REGISTRYINDEX, "CV_OnChange");
lua_newtable(L);
lua_setfield(L, LUA_REGISTRYINDEX, "CV_CanChange");
// Push opaque CV_PossibleValue pointers
// Because I don't care enough to bother.

View file

@ -109,6 +109,9 @@ static void Color2_OnChange(void);
static void DummyConsvar_OnChange(void);
static void SoundTest_OnChange(void);
static boolean Skin_CanChange(const char *valstr);
static boolean Skin2_CanChange(const char *valstr);
#ifdef NETGAME_DEVMODE
static void Fishcake_OnChange(void);
#endif
@ -227,7 +230,6 @@ consvar_t cv_seenames = CVAR_INIT ("seenames", "Ally/Foe", CV_SAVE|CV_ALLOWLUA,
consvar_t cv_allowseenames = CVAR_INIT ("allowseenames", "Yes", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, CV_YesNo, NULL);
// names
static char *lastskinnames[2];
consvar_t cv_playername = CVAR_INIT ("name", "Sonic", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name_OnChange);
consvar_t cv_playername2 = CVAR_INIT ("name2", "Tails", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name2_OnChange);
// player colors
@ -235,8 +237,8 @@ UINT16 lastgoodcolor = SKINCOLOR_BLUE, lastgoodcolor2 = SKINCOLOR_BLUE;
consvar_t cv_playercolor = CVAR_INIT ("color", "Blue", CV_CALL|CV_NOINIT|CV_ALLOWLUA, Color_cons_t, Color_OnChange);
consvar_t cv_playercolor2 = CVAR_INIT ("color2", "Orange", CV_CALL|CV_NOINIT|CV_ALLOWLUA, Color_cons_t, Color2_OnChange);
// player's skin, saved for commodity, when using a favorite skins wad..
consvar_t cv_skin = CVAR_INIT ("skin", DEFAULTSKIN, CV_CALL|CV_NOINIT|CV_ALLOWLUA, NULL, Skin_OnChange);
consvar_t cv_skin2 = CVAR_INIT ("skin2", DEFAULTSKIN2, CV_CALL|CV_NOINIT|CV_ALLOWLUA, NULL, Skin2_OnChange);
consvar_t cv_skin = CVAR_INIT_WITH_CALLBACKS ("skin", DEFAULTSKIN, CV_CALL|CV_NOINIT|CV_ALLOWLUA, NULL, Skin_OnChange, Skin_CanChange);
consvar_t cv_skin2 = CVAR_INIT_WITH_CALLBACKS ("skin2", DEFAULTSKIN2, CV_CALL|CV_NOINIT|CV_ALLOWLUA, NULL, Skin2_OnChange, Skin2_CanChange);
// saved versions of the above six
consvar_t cv_defaultplayercolor = CVAR_INIT ("defaultcolor", "Blue", CV_SAVE, Color_cons_t, NULL);
@ -4741,6 +4743,41 @@ static void Name2_OnChange(void)
SendNameAndColor2();
}
static boolean Skin_CanChange(const char *valstr)
{
(void)valstr;
if (!Playing())
return true; // do whatever you want
if (!(multiplayer || netgame)) // In single player.
return true;
if (CanChangeSkin(consoleplayer) && !P_PlayerMoving(consoleplayer))
return true;
else
{
CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n"));
return false;
}
}
static boolean Skin2_CanChange(const char *valstr)
{
(void)valstr;
if (!Playing() || !splitscreen)
return true; // do whatever you want
if (CanChangeSkin(secondarydisplayplayer) && !P_PlayerMoving(secondarydisplayplayer))
return true;
else
{
CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n"));
return false;
}
}
/** Sends a skin change for the console player, unless that player is moving.
* \sa cv_skin, Skin2_OnChange, Color_OnChange
* \author Graue <graue@oceanbase.org>
@ -4748,10 +4785,7 @@ static void Name2_OnChange(void)
static void Skin_OnChange(void)
{
if (!Playing())
return; // do whatever you want
if (lastskinnames[0] == NULL)
lastskinnames[0] = Z_StrDup(cv_skin.string);
return;
if (!(multiplayer || netgame)) // In single player.
{
@ -4767,17 +4801,7 @@ static void Skin_OnChange(void)
return;
}
if (CanChangeSkin(consoleplayer) && !P_PlayerMoving(consoleplayer))
{
SendNameAndColor();
Z_Free(lastskinnames[0]);
lastskinnames[0] = Z_StrDup(cv_skin.string);
}
else
{
CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n"));
CV_StealthSet(&cv_skin, lastskinnames[0]);
}
SendNameAndColor();
}
/** Sends a skin change for the secondary splitscreen player, unless that
@ -4788,22 +4812,9 @@ static void Skin_OnChange(void)
static void Skin2_OnChange(void)
{
if (!Playing() || !splitscreen)
return; // do whatever you want
return;
if (lastskinnames[1] == NULL)
lastskinnames[1] = Z_StrDup(cv_skin2.string);
if (CanChangeSkin(secondarydisplayplayer) && !P_PlayerMoving(secondarydisplayplayer))
{
SendNameAndColor2();
Z_Free(lastskinnames[1]);
lastskinnames[1] = Z_StrDup(cv_skin.string);
}
else
{
CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n"));
CV_StealthSet(&cv_skin2, lastskinnames[1]);
}
SendNameAndColor2();
}
/** Sends a color change for the console player, unless that player is moving.