Merge branch 'cv-can-change' into 'next'

Add can_change for console variables

See merge request STJr/SRB2!2196
This commit is contained in:
Logan Aerl Arias 2024-01-17 03:39:39 +00:00
commit 3176abe2e4
4 changed files with 122 additions and 55 deletions

View file

@ -1429,8 +1429,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)
@ -1496,12 +1496,35 @@ 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))
{
// The callback refused the default value on register. How naughty...
// So we just use some fallback value.
if (var->string == NULL)
{
if (var->PossibleValue)
{
// Use PossibleValue
valstr = var->PossibleValue[0].strvalue;
}
else
{
// Else, use an empty string
valstr = "";
}
}
else
{
// Callback returned false, and the game is not registering this variable,
// so we can return safely.
return;
}
}
}
if (var->PossibleValue)
{
@ -1663,16 +1686,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)
{
@ -1685,8 +1698,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

@ -300,6 +300,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;
@ -458,6 +482,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);
}
@ -479,9 +517,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;
@ -672,6 +710,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
@ -228,7 +231,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
@ -236,8 +238,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);
@ -4792,6 +4794,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>
@ -4799,10 +4836,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.
{
@ -4818,17 +4852,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
@ -4839,22 +4863,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.