diff --git a/src/command.c b/src/command.c index 6534fed3d..00749ed8d 100644 --- a/src/command.c +++ b/src/command.c @@ -1165,7 +1165,7 @@ found: if (var == &cv_forceskin) { var->value = R_SkinAvailable(var->string); - if (!R_SkinUnlock(var->value)) + if (!R_SkinUnlock(-1, var->value)) var->value = -1; } else @@ -1478,7 +1478,7 @@ void CV_AddValue(consvar_t *var, INT32 increment) else if (newvalue >= numskins) newvalue = -1; } while ((oldvalue != newvalue) - && !(R_SkinUnlock(newvalue))); + && !(R_SkinUnlock(-1, newvalue))); } else newvalue = var->value + increment; diff --git a/src/d_clisrv.c b/src/d_clisrv.c index f80011efb..e2bee7e0f 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -528,6 +528,7 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i) rsp->skincolor = players[i].skincolor; rsp->skin = LONG(players[i].skin); + rsp->availabilities = players[i].availabilities; // Just in case Lua does something like // modify these at runtime rsp->camerascale = (fixed_t)LONG(players[i].camerascale); @@ -657,6 +658,7 @@ static void resynch_read_player(resynch_pak *rsp) players[i].skincolor = rsp->skincolor; players[i].skin = LONG(rsp->skin); + players[i].availabilities = rsp->availabilities; // Just in case Lua does something like // modify these at runtime players[i].camerascale = (fixed_t)LONG(rsp->camerascale); diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 382329539..8f0aedbf3 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -172,6 +172,7 @@ typedef struct UINT8 skincolor; INT32 skin; + UINT32 availabilities; // Just in case Lua does something like // modify these at runtime fixed_t camerascale; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 55a3b30f9..7fcbac092 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1109,6 +1109,8 @@ static void SendNameAndColor(void) if (!Playing()) return; + players[consoleplayer].availabilities = R_GetSkinAvailabilities(); + // If you're not in a netgame, merely update the skin, color, and name. if (!netgame) { @@ -1127,7 +1129,7 @@ static void SendNameAndColor(void) SetPlayerSkinByNum(consoleplayer, 0); CV_StealthSet(&cv_skin, skins[0].name); } - else if ((foundskin = R_SkinAvailable(cv_skin.string)) != -1 && R_SkinUnlock(foundskin)) + else if ((foundskin = R_SkinAvailable(cv_skin.string)) != -1 && R_SkinUnlock(consoleplayer, foundskin)) { boolean notsame; @@ -1174,7 +1176,7 @@ static void SendNameAndColor(void) // 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.value = R_SkinAvailable(cv_skin.string); - if ((cv_skin.value < 0) || !R_SkinUnlock(cv_skin.value)) + if ((cv_skin.value < 0) || !R_SkinUnlock(consoleplayer, cv_skin.value)) { CV_StealthSet(&cv_skin, DEFAULTSKIN); cv_skin.value = 0; @@ -1182,6 +1184,7 @@ static void SendNameAndColor(void) // Finally write out the complete packet and send it off. WRITESTRINGN(p, cv_playername.zstring, MAXPLAYERNAME); + WRITEUINT32(p, (UINT32)players[consoleplayer].availabilities); WRITEUINT8(p, (UINT8)cv_playercolor.value); WRITEUINT8(p, (UINT8)cv_skin.value); SendNetXCmd(XD_NAMEANDCOLOR, buf, p - buf); @@ -1224,6 +1227,8 @@ static void SendNameAndColor2(void) if (!Playing()) return; + players[secondplaya].availabilities = R_GetSkinAvailabilities(); + // If you're not in a netgame, merely update the skin, color, and name. if (botingame) { @@ -1252,7 +1257,7 @@ static void SendNameAndColor2(void) SetPlayerSkinByNum(secondplaya, forcedskin); CV_StealthSet(&cv_skin2, skins[forcedskin].name); } - else if ((foundskin = R_SkinAvailable(cv_skin2.string)) != -1 && R_SkinUnlock(foundskin)) + else if ((foundskin = R_SkinAvailable(cv_skin2.string)) != -1 && R_SkinUnlock(secondplaya, foundskin)) { boolean notsame; @@ -1307,6 +1312,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) #endif READSTRINGN(*cp, name, MAXPLAYERNAME); + p->availabilities = READUINT32(*cp); color = READUINT8(*cp); skin = READUINT8(*cp); @@ -4042,7 +4048,7 @@ static void Command_Archivetest_f(void) */ static void ForceSkin_OnChange(void) { - if ((server || adminplayer == consoleplayer) && ((cv_forceskin.value == -1 && stricmp(cv_forceskin.string, "None")) || !(R_SkinUnlock(cv_forceskin.value)))) + if ((server || adminplayer == consoleplayer) && ((cv_forceskin.value == -1 && stricmp(cv_forceskin.string, "None")) || !(R_SkinUnlock(-1, cv_forceskin.value)))) { CONS_Printf("Please provide a valid skin name (\"None\" disables).\n"); CV_SetValue(&cv_forceskin, -1); diff --git a/src/d_player.h b/src/d_player.h index f316ec251..8ff591a96 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -342,6 +342,7 @@ typedef struct player_s UINT8 skincolor; INT32 skin; + UINT32 availabilities; UINT32 score; // player score fixed_t dashspeed; // dashing speed diff --git a/src/doomdef.h b/src/doomdef.h index a35c17ba6..ed197e20c 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -222,7 +222,7 @@ extern FILE *logstream; // NOTE: it needs more than this to increase the number of players... #define MAXPLAYERS 32 -#define MAXSKINS MAXPLAYERS +#define MAXSKINS 32 #define PLAYERSMASK (MAXPLAYERS-1) #define MAXPLAYERNAME 21 diff --git a/src/g_game.c b/src/g_game.c index b20d91f9f..3839e0448 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2080,6 +2080,7 @@ void G_PlayerReborn(INT32 player) UINT8 mare; UINT8 skincolor; INT32 skin; + UINT32 availabilities; tic_t jointime; boolean spectator; INT16 bot; @@ -2104,6 +2105,7 @@ void G_PlayerReborn(INT32 player) skincolor = players[player].skincolor; skin = players[player].skin; + availabilities = players[player].availabilities; camerascale = players[player].camerascale; shieldscale = players[player].shieldscale; charability = players[player].charability; @@ -2149,6 +2151,7 @@ void G_PlayerReborn(INT32 player) // save player config truth reborn p->skincolor = skincolor; p->skin = skin; + p->availabilities = availabilities; p->camerascale = camerascale; p->shieldscale = shieldscale; p->charability = charability; diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 93febf209..0935f56de 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -511,6 +511,8 @@ static int mobj_set(lua_State *L) for (i = 0; i < numskins; i++) if (fastcmp(skins[i].name, skin)) { + if (mo->player && !R_SkinUnlock(mo->player-players, i)) + return luaL_error(L, "mobj.skin '%s' not found!", skin); mo->skin = &skins[i]; return 0; } diff --git a/src/m_menu.c b/src/m_menu.c index f682cd1b5..f7025693f 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -3417,7 +3417,7 @@ static void M_PatchSkinNameTable(void) for (j = 0; j < MAXSKINS; j++) { - if (skins[j].name[0] != '\0' && R_SkinUnlock(j)) + if (skins[j].name[0] != '\0' && R_SkinUnlock(-1, j)) { skins_cons_t[j].strvalue = skins[j].realname; skins_cons_t[j].value = j+1; @@ -4780,7 +4780,7 @@ static void M_SetupChoosePlayer(INT32 choice) { name = strtok(Z_StrDup(description[i].skinname), "&"); skinnum = R_SkinAvailable(name); - if ((skinnum != -1) && (R_SkinUnlock(skinnum))) + if ((skinnum != -1) && (R_SkinUnlock(-1, skinnum))) { // Handling order. if (firstvalid == 255) @@ -6525,7 +6525,7 @@ static void M_HandleSetupMultiPlayer(INT32 choice) if (setupm_fakeskin < 0) setupm_fakeskin = numskins-1; } - while ((prev_setupm_fakeskin != setupm_fakeskin) && !(R_SkinUnlock(setupm_fakeskin))); + while ((prev_setupm_fakeskin != setupm_fakeskin) && !(R_SkinUnlock(-1, setupm_fakeskin))); } else if (itemOn == 1) // player color { @@ -6545,7 +6545,7 @@ static void M_HandleSetupMultiPlayer(INT32 choice) if (setupm_fakeskin > numskins-1) setupm_fakeskin = 0; } - while ((prev_setupm_fakeskin != setupm_fakeskin) && !(R_SkinUnlock(setupm_fakeskin))); + while ((prev_setupm_fakeskin != setupm_fakeskin) && !(R_SkinUnlock(-1, setupm_fakeskin))); } else if (itemOn == 1) // player color { diff --git a/src/r_things.c b/src/r_things.c index 089eddba8..03125f921 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2554,13 +2554,26 @@ void R_InitSkins(void) numskins = 0; } +UINT32 R_GetSkinAvailabilities(void) +{ + INT32 s; + UINT32 response = 0; + + for (s = 0; s < MAXSKINS; s++) + { + if (!skins[s].availability || unlockables[skins[s].availability - 1].unlocked) + response |= (1 << s); + } + return response; +} + // returns true if available in circumstances, otherwise nope // warning don't use with an invalid skinnum other than -1 which always returns true -boolean R_SkinUnlock(INT32 skinnum) +boolean R_SkinUnlock(INT32 playernum, INT32 skinnum) { return ((skinnum == -1) // Simplifies things elsewhere, since there's already plenty of checks for less-than-0... || (!skins[skinnum].availability) - || (unlockables[skins[skinnum].availability - 1].unlocked) + || ((playernum != -1) ? (players[playernum].availabilities & (1 << skinnum)) : (unlockables[skins[skinnum].availability - 1].unlocked)) || (modeattacking) // If you have someone else's run you might as well take a look || (Playing() && (R_SkinAvailable(mapheaderinfo[gamemap-1]->forcecharacter) == skinnum)) // Force 1. || (netgame && !(server || adminplayer == consoleplayer) && (cv_forceskin.value == skinnum)) // Force 2. @@ -2588,7 +2601,7 @@ void SetPlayerSkin(INT32 playernum, const char *skinname) INT32 i = R_SkinAvailable(skinname); player_t *player = &players[playernum]; - if ((i != -1) && (!P_IsLocalPlayer(player) || R_SkinUnlock(i))) + if ((i != -1) && R_SkinUnlock(playernum, i)) { SetPlayerSkinByNum(playernum, i); return; @@ -2610,8 +2623,7 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) skin_t *skin = &skins[skinnum]; UINT8 newcolor = 0; - if ((skinnum >= 0 && skinnum < numskins) // Make sure it exists! - && (!P_IsLocalPlayer(player) || R_SkinUnlock(skinnum))) // ...but is it allowed? We must always allow external players to change skin. The server should vet that... + if (skinnum >= 0 && skinnum < numskins && R_SkinUnlock(playernum, skinnum)) // Make sure it exists! { player->skin = skinnum; diff --git a/src/r_things.h b/src/r_things.h index 5684f8a89..123ab2280 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -29,8 +29,6 @@ #define VISSPRITESPERCHUNK (1 << VISSPRITECHUNKBITS) #define VISSPRITEINDEXMASK (VISSPRITESPERCHUNK - 1) -#define DEFAULTNIGHTSSKIN 0 - // Constant arrays used for psprite clipping // and initializing clipping. extern INT16 negonearray[MAXVIDWIDTH]; @@ -71,6 +69,7 @@ void R_DrawMasked(void); // should be all lowercase!! S_SKIN processing does a strlwr #define DEFAULTSKIN "sonic" #define DEFAULTSKIN2 "tails" // secondary player +#define DEFAULTNIGHTSSKIN 0 typedef struct { @@ -204,7 +203,8 @@ extern skin_t skins[MAXSKINS + 1]; void SetPlayerSkin(INT32 playernum,const char *skinname); void SetPlayerSkinByNum(INT32 playernum,INT32 skinnum); // Tails 03-16-2002 -boolean R_SkinUnlock(INT32 skinnum); +boolean R_SkinUnlock(INT32 playernum, INT32 skinnum); +UINT32 R_GetSkinAvailabilities(void); INT32 R_SkinAvailable(const char *name); void R_AddSkins(UINT16 wadnum);