R_SkinAvailableEX

- Most R_SkinAvailable calls should be returning index into demo.skinlist (same numerical value as when demo was recorded), for demo sync
- A handful of general things permit exception for this
- Expose `replaynumskins` (calculated as `(demo.playback ? demo.numskins : numskins)`) to Lua
    - There's *always* more that can be done for this, but this is the minimum spec that can at least be somewhat stable
This commit is contained in:
toaster 2024-03-04 17:05:23 +00:00 committed by James R
parent 24b850a492
commit 7f0df71558
22 changed files with 78 additions and 36 deletions

View file

@ -1946,7 +1946,7 @@ void D_SRB2Main(void)
profile_t *pr = PR_GetProfile(cv_ttlprofilen.value);
if (pr != NULL)
{
INT32 importskin = R_SkinAvailable(pr->skinname);
INT32 importskin = R_SkinAvailableEx(pr->skinname, false);
if (importskin != -1)
{
skins[importskin].records.wins = pr->wins;

View file

@ -960,7 +960,7 @@ static void SendNameAndColor(const UINT8 n)
// check if player has the skin loaded (cv_skin may have
// the name of a skin that was available in the previous game)
cv_skin[n].value = R_SkinAvailable(cv_skin[n].string);
cv_skin[n].value = R_SkinAvailableEx(cv_skin[n].string, false);
if ((cv_skin[n].value < 0) || !R_SkinUsable(playernum, cv_skin[n].value, false))
{
CV_StealthSet(&cv_skin[n], DEFAULTSKIN);

View file

@ -1345,12 +1345,12 @@ void F_TitleScreenDrawer(void)
if (cache_gametrulystarted == true)
{
const char *eggName = "eggman";
INT32 eggSkin = R_SkinAvailable(eggName);
INT32 eggSkin = R_SkinAvailableEx(eggName, false);
skincolornum_t eggColor = SKINCOLOR_RED;
UINT8 *eggColormap = NULL;
const char *tailsName = "tails";
INT32 tailsSkin = R_SkinAvailable(tailsName);
INT32 tailsSkin = R_SkinAvailableEx(tailsName, false);
skincolornum_t tailsColor = SKINCOLOR_ORANGE;
UINT8 *tailsColormap = NULL;

View file

@ -2064,12 +2064,14 @@ static democharlist_t *G_LoadDemoSkins(savebuffer_t *info, UINT8 *worknumskins,
return NULL;
}
READMEM(info->p, skin, 16);
READMEM(info->p, skinlist[i].name, 16);
skinlist[i].name[16] = '\0';
skinlist[i].namehash = quickncasehash(skinlist[i].name, SKINNAMESIZE);
skinlist[i].kartspeed = READUINT8(info->p);
skinlist[i].kartweight = READUINT8(info->p);
skinlist[i].flags = READUINT32(info->p);
result = R_SkinAvailable(skin);
result = R_SkinAvailableEx(skin, false);
if (result == -1)
{
if (!getclosest)

View file

@ -69,6 +69,8 @@ extern consvar_t cv_recordmultiplayerdemos, cv_netdemosyncquality;
extern tic_t demostarttime;
struct democharlist_t {
char name[17];
UINT32 namehash;
UINT8 mapping; // No, this isn't about levels. It maps to loaded character ID.
UINT8 kartspeed;
UINT8 kartweight;

View file

@ -508,7 +508,7 @@ void srb2::load_ng_gamedata()
for (auto& skinpair : js.skins)
{
INT32 skin = R_SkinAvailable(skinpair.first.c_str());
INT32 skin = R_SkinAvailableEx(skinpair.first.c_str(), false);
skinrecord_t dummyrecord {};
dummyrecord.wins = skinpair.second.records.wins;
@ -683,7 +683,7 @@ void srb2::load_ng_gamedata()
dummywindata[j].best_skin.id = MAXSKINS;
dummywindata[j].best_skin.unloaded = nullptr;
int skinloaded = R_SkinAvailable(cuppair.second.records[j].bestskin.c_str());
int skinloaded = R_SkinAvailableEx(cuppair.second.records[j].bestskin.c_str(), false);
if (skinloaded >= 0)
{
dummywindata[j].best_skin.id = skinloaded;

View file

@ -570,7 +570,7 @@ void HWR_InitModels(void)
addskinmodel:
// add player model
s = R_SkinAvailable(skinname);
s = R_SkinAvailableEx(skinname, false);
if (s != -1)
{
md2_playermodels[s].skin = s;

View file

@ -345,7 +345,7 @@ UINT16 M_GetCvPlayerColor(UINT8 pnum)
if (color != SKINCOLOR_NONE)
return color;
INT32 skin = R_SkinAvailable(cv_skin[pnum].string);
INT32 skin = R_SkinAvailableEx(cv_skin[pnum].string, false);
if (skin == -1)
return SKINCOLOR_NONE;
@ -377,7 +377,7 @@ static void M_DrawMenuParty(void)
// Despite the work put into it, can't use M_GetCvPlayerColor directly - we need to reference skin always.
#define grab_skin_and_colormap(pnum) \
{ \
skin = R_SkinAvailable(cv_skin[pnum].string); \
skin = R_SkinAvailableEx(cv_skin[pnum].string, false); \
color = cv_playercolor[pnum].value; \
if (skin == -1) \
skin = 0; \
@ -2277,7 +2277,7 @@ void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p)
if (p != NULL && p->version)
{
truecol = p->color;
skinnum = R_SkinAvailable(p->skinname);
skinnum = R_SkinAvailableEx(p->skinname, false);
strcpy(pname, p->profilename);
}
@ -6516,7 +6516,7 @@ static void M_DrawChallengeTile(INT16 i, INT16 j, INT32 x, INT32 y, boolean hili
INT32 skin = M_UnlockableFollowerNum(ref);
if (skin != -1)
{
INT32 psk = R_SkinAvailable(cv_skin[0].string);
INT32 psk = R_SkinAvailableEx(cv_skin[0].string, false);
UINT16 col = K_GetEffectiveFollowerColor(followers[skin].defaultcolor, &followers[skin], cv_playercolor[0].value, (psk != -1) ? &skins[psk] : &skins[0]);
colormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE);
pat = W_CachePatchName(followers[skin].icon, PU_CACHE);
@ -6775,7 +6775,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y)
}
case SECRET_FOLLOWER:
{
INT32 skin = R_SkinAvailable(cv_skin[0].string);
INT32 skin = R_SkinAvailableEx(cv_skin[0].string, false);
INT32 fskin = M_UnlockableFollowerNum(ref);
// Draw proximity reference for character
@ -6806,7 +6806,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y)
INT32 colorid = M_UnlockableColorNum(ref);
if (colorid == SKINCOLOR_NONE)
break;
INT32 skin = R_SkinAvailable(cv_skin[0].string);
INT32 skin = R_SkinAvailableEx(cv_skin[0].string, false);
if (skin == -1)
skin = 0;
colormap = R_GetTranslationColormap(skin, colorid, GTC_MENUCACHE);

View file

@ -2266,9 +2266,10 @@ static int lib_rSetPlayerSkin(lua_State *L)
return luaL_error(L, "argument #2 not given (expected number or string)");
else if (lua_type(L, 2) == LUA_TNUMBER) // skin number
{
INT32 skincount = (demo.playback ? demo.numskins : numskins);
i = luaL_checkinteger(L, 2);
if (i < 0 || i >= numskins)
return luaL_error(L, "skin %d (argument #2) out of range (0 - %d)", i, numskins-1);
if (i < 0 || i >= skincount)
return luaL_error(L, "skin %d (argument #2) out of range (0 - %d)", i, skincount-1);
}
else // skin name
{

View file

@ -378,7 +378,7 @@ static int libd_getSprite2Patch(lua_State *L)
i = lua_tonumber(L, 1);
if (i < 0 || i >= MAXSKINS)
return luaL_error(L, "skin number %d out of range (0 - %d)", i, MAXSKINS-1);
if (i >= numskins)
if (i >= (demo.playback ? demo.numskins : numskins))
return 0;
}
else // find skin by name
@ -389,6 +389,9 @@ static int libd_getSprite2Patch(lua_State *L)
return 0;
}
if (demo.playback)
i = demo.skinlist[i].mapping;
lua_remove(L, 1); // remove skin now
if (lua_isnumber(L, 1)) // sprite number given, e.g. SPR2_STIL
@ -1041,6 +1044,9 @@ static int libd_getColormap(lua_State *L)
skinnum = i;
}
if (demo.playback)
skinnum = demo.skinlist[skinnum].mapping;
// all was successful above, now we generate the colormap at last!
colormap = R_GetTranslationColormap(skinnum, color, GTC_CACHE);

View file

@ -721,7 +721,11 @@ static int mobj_set(lua_State *L)
if (skin != -1)
{
if (!mo->player || R_SkinUsable(mo->player-players, skin, false))
{
if (demo.playback)
skin = demo.skinlist[skin].mapping;
mo->skin = &skins[skin];
}
return 0;
}

View file

@ -16,6 +16,7 @@
#include "deh_lua.h"
#include "z_zone.h"
#include "w_wad.h"
#include "r_things.h" // numskins
#include "p_setup.h"
#include "r_state.h"
#include "r_sky.h"
@ -371,6 +372,9 @@ int LUA_PushGlobals(lua_State *L, const char *word)
} else if (fastcmp(word,"replayplayback")) {
lua_pushboolean(L, demo.playback);
return 1;
} else if (fastcmp(word,"replaynumskins")) {
lua_pushinteger(L, (demo.playback ? demo.numskins : numskins));
return 1;
} else if (fastcmp(word, "gamestate")) {
lua_pushinteger(L, gamestate);
return 1;

View file

@ -196,7 +196,7 @@ static int lib_getSkin(lua_State *L)
}
// find skin by name
i = R_SkinAvailable(field);
i = R_SkinAvailableEx(field, false);
if (i != -1)
{
LUA_PushUserdata(L, &skins[i], META_SKIN);

View file

@ -1206,7 +1206,7 @@ void M_UpdateConditionSetsPending(void)
case UCRP_ISCHARACTER:
case UCRP_MAKERETIRE:
{
cn->requirement = R_SkinAvailable(cn->stringvar);
cn->requirement = R_SkinAvailableEx(cn->stringvar, false);
if (cn->requirement < 0)
{
@ -2021,7 +2021,7 @@ static const char *M_GetConditionCharacter(INT32 skin, boolean directlyrequires)
for (j = 0; j < SKINRIVALS; j++)
{
const char *rivalname = skins[i].rivals[j];
INT32 rivalnum = R_SkinAvailable(rivalname);
INT32 rivalnum = R_SkinAvailableEx(rivalname, false);
if (rivalnum != skin)
continue;
@ -3504,7 +3504,7 @@ INT32 M_UnlockableSkinNum(unlockable_t *unlock)
}
// Get the skin from the string.
skinnum = R_SkinAvailable(unlock->stringVar);
skinnum = R_SkinAvailableEx(unlock->stringVar, false);
if (skinnum != -1)
{
unlock->stringVarCache = skinnum;

View file

@ -714,7 +714,7 @@ void M_ChallengesTick(void)
INT32 fskin = M_UnlockableFollowerNum(ref);
if (fskin != -1)
{
INT32 psk = R_SkinAvailable(cv_skin[0].string);
INT32 psk = R_SkinAvailableEx(cv_skin[0].string, false);
if (psk == -1)
psk = 0;
bombcolor = K_GetEffectiveFollowerColor(followers[fskin].defaultcolor, &followers[fskin], cv_playercolor[0].value, &skins[psk]);

View file

@ -84,7 +84,7 @@ public:
{
if (!skinName.empty())
{
this->skinID = R_SkinAvailable(skinName.c_str());
this->skinID = R_SkinAvailableEx(skinName.c_str(), false);
}
this->offset = offset;

View file

@ -203,7 +203,7 @@ static INT16 M_GetMenuCategoryFromFollower(setup_player_t *p)
static void M_SetupProfileGridPos(setup_player_t *p)
{
profile_t *pr = PR_GetProfile(p->profilen);
INT32 i = R_SkinAvailable(pr->skinname);
INT32 i = R_SkinAvailableEx(pr->skinname, false);
INT32 alt = 0; // Hey it's my character's name!
if (i == -1)
@ -247,7 +247,7 @@ static void M_SetupProfileGridPos(setup_player_t *p)
static void M_SetupMidGameGridPos(setup_player_t *p, UINT8 num)
{
INT32 i = R_SkinAvailable(cv_skin[num].zstring);
INT32 i = R_SkinAvailableEx(cv_skin[num].zstring, false);
INT32 alt = 0; // Hey it's my character's name!
if (i == -1)

View file

@ -150,7 +150,7 @@ static boolean P_UnArchivePlayer(savebuffer_t *save)
INT32 skin;
READSTRINGN(save->p, skinname, SKINNAMESIZE);
skin = R_SkinAvailable(skinname);
skin = R_SkinAvailableEx(skinname, false);
if (skin == -1)
{
@ -176,7 +176,7 @@ static boolean P_UnArchivePlayer(savebuffer_t *save)
savedata.bots[pid].valid = true;
READSTRINGN(save->p, skinname, SKINNAMESIZE);
skin = R_SkinAvailable(skinname);
skin = R_SkinAvailableEx(skinname, false);
if (skin == -1)
{

View file

@ -7850,7 +7850,7 @@ static void P_LoadRecordGhosts(void)
if (sameGhosts)
{
INT32 skin = R_SkinAvailable(cv_skin[0].string);
INT32 skin = R_SkinAvailableEx(cv_skin[0].string, false);
if (skin < 0 || !R_SkinUsable(-1, skin, false))
skin = 0; // use default skin
add_ghosts(fmt::format("{}-{}{}", gpath, skins[skin].name, modeprefix), sameGhosts);

View file

@ -1435,7 +1435,7 @@ static void R_ParseSpriteInfoSkin(struct ParseSpriteInfoState *parser)
skinName[sprinfoTokenLength] = '\0';
strlwr(skinName);
skinnum = R_SkinAvailable(skinName);
skinnum = R_SkinAvailableEx(skinName, false);
if (skinnum == -1)
I_Error("Error parsing SPRTINFO lump: Unknown skin \"%s\"", skinName);

View file

@ -136,15 +136,17 @@ static void Sk_SetDefaultValue(skin_t *skin)
}
// Grab the default skin
#define DEFAULTBOTSKINNAME "eggrobo"
UINT8 R_BotDefaultSkin(void)
{
static INT32 defaultbotskin = -1;
if (demo.playback)
return R_SkinAvailableEx(DEFAULTBOTSKINNAME, true);
if (defaultbotskin == -1)
{
const char *defaultbotskinname = "eggrobo";
defaultbotskin = R_SkinAvailable(defaultbotskinname);
defaultbotskin = R_SkinAvailableEx(DEFAULTBOTSKINNAME, false);
if (defaultbotskin == -1)
{
@ -155,6 +157,7 @@ UINT8 R_BotDefaultSkin(void)
return (UINT8)defaultbotskin;
}
#undef DEFAULTBOTSKINNAME
//
// Initialize the basic skins
@ -318,10 +321,29 @@ UINT32 R_GetLocalRandomSkin(void)
// returns true if the skin name is found (loaded from pwad)
// warning return -1 if not found
INT32 R_SkinAvailable(const char *name)
{
return R_SkinAvailableEx(name, true);
}
INT32 R_SkinAvailableEx(const char *name, boolean demoskins)
{
INT32 i;
UINT32 hash = quickncasehash(name, SKINNAMESIZE);
if (demo.playback && demoskins)
{
for (i = 0; i < demo.numskins; i++)
{
if (demo.skinlist[i].namehash != hash)
continue;
if (stricmp(demo.skinlist[i].name,name)!=0)
continue;
return i;
}
}
for (i = 0; i < numskins; i++)
{
if (skins[i].namehash != hash)
@ -946,7 +968,7 @@ void R_AddSkins(UINT16 wadnum, boolean mainfile)
// Others can't go in there because we don't want them to be patchable.
if (!stricmp(stoken, "name"))
{
INT32 skinnum = R_SkinAvailable(value);
INT32 skinnum = R_SkinAvailableEx(value, false);
strlwr(value);
if (skinnum == -1)
STRBUFCPY(skin->name, value);
@ -961,7 +983,7 @@ void R_AddSkins(UINT16 wadnum, boolean mainfile)
snprintf(value2, stringspace,
"%s%d", value, numskins);
value2[stringspace - 1] = '\0';
if (R_SkinAvailable(value2) == -1)
if (R_SkinAvailableEx(value2, false) == -1)
// I'm lazy so if NEW name is already used I leave the 'skin x'
// default skin name set in Sk_SetDefaultValue
STRBUFCPY(skin->name, value2);
@ -1141,7 +1163,7 @@ void R_PatchSkins(UINT16 wadnum, boolean mainfile)
if (!stricmp(stoken, "name"))
{
strlwr(value);
skinnum = R_SkinAvailable(value);
skinnum = R_SkinAvailableEx(value, false);
if (skinnum != -1)
skin = &skins[skinnum];
else

View file

@ -112,6 +112,7 @@ void R_PatchSkins(UINT16 wadnum, boolean mainfile);
// Access
INT32 R_SkinAvailable(const char *name);
INT32 R_SkinAvailableEx(const char *name, boolean demoskins);
boolean R_SkinUsable(INT32 playernum, INT32 skinnum, boolean demoskins);
UINT8 *R_GetSkinAvailabilities(boolean demolock, INT32 botforcecharacter);