mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-02-24 14:31:02 +00:00
Merge branch 'followme-internal' into 'master'
Followers See merge request KartKrew/Kart!252
This commit is contained in:
commit
8399079f35
21 changed files with 1602 additions and 38 deletions
|
|
@ -134,6 +134,9 @@ endif
|
|||
ifndef GCC295
|
||||
WFLAGS+=-Wendif-labels
|
||||
endif
|
||||
ifdef GCC40
|
||||
WFLAGS+=-std=gnu89
|
||||
endif
|
||||
ifdef GCC41
|
||||
WFLAGS+=-Wshadow
|
||||
endif
|
||||
|
|
|
|||
316
src/d_netcmd.c
316
src/d_netcmd.c
|
|
@ -116,6 +116,16 @@ static void Skin_OnChange(void);
|
|||
static void Skin2_OnChange(void);
|
||||
static void Skin3_OnChange(void);
|
||||
static void Skin4_OnChange(void);
|
||||
|
||||
static void Follower_OnChange(void);
|
||||
static void Follower2_OnChange(void);
|
||||
static void Follower3_OnChange(void);
|
||||
static void Follower4_OnChange(void);
|
||||
static void Followercolor_OnChange(void);
|
||||
static void Followercolor2_OnChange(void);
|
||||
static void Followercolor3_OnChange(void);
|
||||
static void Followercolor4_OnChange(void);
|
||||
|
||||
static void Color_OnChange(void);
|
||||
static void Color2_OnChange(void);
|
||||
static void Color3_OnChange(void);
|
||||
|
|
@ -292,6 +302,23 @@ consvar_t cv_skin2 = {"skin2", DEFAULTSKIN2, CV_SAVE|CV_CALL|CV_NOINIT, NULL, Sk
|
|||
consvar_t cv_skin3 = {"skin3", DEFAULTSKIN3, CV_SAVE|CV_CALL|CV_NOINIT, NULL, Skin3_OnChange, 0, NULL, NULL, 0, 0, NULL};
|
||||
consvar_t cv_skin4 = {"skin4", DEFAULTSKIN4, CV_SAVE|CV_CALL|CV_NOINIT, NULL, Skin4_OnChange, 0, NULL, NULL, 0, 0, NULL};
|
||||
|
||||
// player's followers. Also saved.
|
||||
consvar_t cv_follower = {"follower", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower_OnChange, 0, NULL, NULL, 0, 0, NULL};
|
||||
consvar_t cv_follower2 = {"follower2", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower2_OnChange, 0, NULL, NULL, 0, 0, NULL};
|
||||
consvar_t cv_follower3 = {"follower3", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower3_OnChange, 0, NULL, NULL, 0, 0, NULL};
|
||||
consvar_t cv_follower4 = {"follower4", "-1", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Follower4_OnChange, 0, NULL, NULL, 0, 0, NULL};
|
||||
|
||||
// player's follower colors... Also saved...
|
||||
consvar_t cv_followercolor = {"followercolor", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor_OnChange, 0, NULL, NULL, 0, 0, NULL};
|
||||
consvar_t cv_followercolor2 = {"followercolor2", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor2_OnChange, 0, NULL, NULL, 0, 0, NULL};
|
||||
consvar_t cv_followercolor3 = {"followercolor3", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor3_OnChange, 0, NULL, NULL, 0, 0, NULL};
|
||||
consvar_t cv_followercolor4 = {"followercolor4", "Match", CV_SAVE|CV_CALL|CV_NOINIT, Followercolor_cons_t, Followercolor4_OnChange, 0, NULL, NULL, 0, 0, NULL};
|
||||
|
||||
|
||||
// Follower toggle
|
||||
static CV_PossibleValue_t followers_cons_t[] = {{0, "Yours only"}, {1, "Everyone's"}, {0, NULL}};
|
||||
consvar_t cv_showfollowers = {"showfollowers", "Everyone's", CV_SAVE, followers_cons_t, 0, 0, NULL, NULL, 0, 0, NULL};
|
||||
|
||||
consvar_t cv_skipmapcheck = {"skipmapcheck", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
|
||||
|
||||
INT32 cv_debug;
|
||||
|
|
@ -804,11 +831,17 @@ void D_RegisterClientCommands(void)
|
|||
|
||||
for (i = 0; i < MAXSKINCOLORS; i++)
|
||||
{
|
||||
Color_cons_t[i].value = i;
|
||||
Color_cons_t[i].strvalue = KartColor_Names[i]; // SRB2kart
|
||||
Color_cons_t[i].value = Followercolor_cons_t[i].value = i;
|
||||
Color_cons_t[i].strvalue = Followercolor_cons_t[i].strvalue = KartColor_Names[i]; // SRB2kart
|
||||
}
|
||||
Color_cons_t[MAXSKINCOLORS].value = 0;
|
||||
Color_cons_t[MAXSKINCOLORS].strvalue = NULL;
|
||||
Color_cons_t[MAXSKINCOLORS].value = Followercolor_cons_t[MAXSKINCOLORS+2].value = 0;
|
||||
Color_cons_t[MAXSKINCOLORS].strvalue = Followercolor_cons_t[MAXSKINCOLORS+2].strvalue = NULL;
|
||||
|
||||
Followercolor_cons_t[MAXSKINCOLORS].value = MAXSKINCOLORS;
|
||||
Followercolor_cons_t[MAXSKINCOLORS].strvalue = "Match"; // Add "Match" option, which will make the follower color match the player's
|
||||
|
||||
Followercolor_cons_t[MAXSKINCOLORS+1].value = MAXSKINCOLORS+1;
|
||||
Followercolor_cons_t[MAXSKINCOLORS+1].strvalue = "Opposite"; // Add "Opposite" option, ...which is like "Match", but for coloropposite.
|
||||
|
||||
if (dedicated)
|
||||
return;
|
||||
|
|
@ -880,18 +913,27 @@ void D_RegisterClientCommands(void)
|
|||
CV_RegisterVar(&cv_playername);
|
||||
CV_RegisterVar(&cv_playercolor);
|
||||
CV_RegisterVar(&cv_skin); // r_things.c (skin NAME)
|
||||
CV_RegisterVar(&cv_follower);
|
||||
CV_RegisterVar(&cv_followercolor);
|
||||
CV_RegisterVar(&cv_showfollowers);
|
||||
// secondary player (splitscreen)
|
||||
CV_RegisterVar(&cv_playername2);
|
||||
CV_RegisterVar(&cv_playercolor2);
|
||||
CV_RegisterVar(&cv_skin2);
|
||||
CV_RegisterVar(&cv_follower2);
|
||||
CV_RegisterVar(&cv_followercolor2);
|
||||
// third player
|
||||
CV_RegisterVar(&cv_playername3);
|
||||
CV_RegisterVar(&cv_playercolor3);
|
||||
CV_RegisterVar(&cv_skin3);
|
||||
CV_RegisterVar(&cv_follower3);
|
||||
CV_RegisterVar(&cv_followercolor3);
|
||||
// fourth player
|
||||
CV_RegisterVar(&cv_playername4);
|
||||
CV_RegisterVar(&cv_playercolor4);
|
||||
CV_RegisterVar(&cv_skin4);
|
||||
CV_RegisterVar(&cv_follower4);
|
||||
CV_RegisterVar(&cv_followercolor4);
|
||||
// preferred number of players
|
||||
CV_RegisterVar(&cv_splitplayers);
|
||||
|
||||
|
|
@ -1449,7 +1491,7 @@ static INT32 snacpending = 0, snac2pending = 0, snac3pending = 0, snac4pending =
|
|||
//
|
||||
static void SendNameAndColor(void)
|
||||
{
|
||||
XBOXSTATIC char buf[MAXPLAYERNAME+2];
|
||||
XBOXSTATIC char buf[MAXPLAYERNAME+3];
|
||||
char *p;
|
||||
|
||||
p = buf;
|
||||
|
|
@ -1474,10 +1516,13 @@ static void SendNameAndColor(void)
|
|||
CV_StealthSet(&cv_playercolor, cv_playercolor.defaultvalue);
|
||||
}
|
||||
|
||||
if (!strcmp(cv_playername.string, player_names[consoleplayer])
|
||||
&& cv_playercolor.value == players[consoleplayer].skincolor
|
||||
&& !strcmp(cv_skin.string, skins[players[consoleplayer].skin].name))
|
||||
return;
|
||||
// ditto for follower colour:
|
||||
if (!cv_followercolor.value)
|
||||
CV_StealthSet(&cv_followercolor, "Match"); // set it to "Match". I don't care about your stupidity!
|
||||
|
||||
// so like, this is sent before we even use anything like cvars or w/e so it's possible that follower is set to a pretty yikes value, so let's fix that before we send garbage that could crash the game:
|
||||
if (cv_follower.value > numfollowers-1 || cv_follower.value < -1)
|
||||
CV_StealthSet(&cv_follower, "-1");
|
||||
|
||||
// We'll handle it later if we're not playing.
|
||||
if (!Playing())
|
||||
|
|
@ -1496,6 +1541,10 @@ static void SendNameAndColor(void)
|
|||
if (players[consoleplayer].mo)
|
||||
players[consoleplayer].mo->color = players[consoleplayer].skincolor;
|
||||
|
||||
// Update follower for local games:
|
||||
if (cv_follower.value >= -1 && cv_follower.value != players[consoleplayer].followerskin)
|
||||
SetFollower(consoleplayer, cv_follower.value);
|
||||
|
||||
if (metalrecording)
|
||||
{ // Metal Sonic is Sonic, obviously.
|
||||
SetPlayerSkinByNum(consoleplayer, 0);
|
||||
|
|
@ -1559,6 +1608,8 @@ static void SendNameAndColor(void)
|
|||
WRITESTRINGN(p, cv_playername.zstring, MAXPLAYERNAME);
|
||||
WRITEUINT8(p, (UINT8)cv_playercolor.value);
|
||||
WRITEUINT8(p, (UINT8)cv_skin.value);
|
||||
WRITESINT8(p, (UINT8)cv_follower.value);
|
||||
WRITESINT8(p, (UINT8)cv_followercolor.value);
|
||||
SendNetXCmd(XD_NAMEANDCOLOR, buf, p - buf);
|
||||
}
|
||||
|
||||
|
|
@ -1566,7 +1617,7 @@ static void SendNameAndColor(void)
|
|||
static void SendNameAndColor2(void)
|
||||
{
|
||||
INT32 secondplaya = -1;
|
||||
XBOXSTATIC char buf[MAXPLAYERNAME+2];
|
||||
XBOXSTATIC char buf[MAXPLAYERNAME+3];
|
||||
char *p;
|
||||
|
||||
if (splitscreen < 1)
|
||||
|
|
@ -1602,6 +1653,14 @@ static void SendNameAndColor2(void)
|
|||
CV_StealthSet(&cv_playercolor2, cv_playercolor2.defaultvalue);
|
||||
}
|
||||
|
||||
// ditto for follower colour:
|
||||
if (!cv_followercolor2.value)
|
||||
CV_StealthSet(&cv_followercolor2, "Match"); // set it to "Match". I don't care about your stupidity!
|
||||
|
||||
// so like, this is sent before we even use anything like cvars or w/e so it's possible that follower is set to a pretty yikes value, so let's fix that before we send garbage that could crash the game:
|
||||
if (cv_follower2.value > numfollowers-1 || cv_follower2.value < -1)
|
||||
CV_StealthSet(&cv_follower2, "-1");
|
||||
|
||||
// We'll handle it later if we're not playing.
|
||||
if (!Playing())
|
||||
return;
|
||||
|
|
@ -1619,6 +1678,10 @@ static void SendNameAndColor2(void)
|
|||
if (players[secondplaya].mo)
|
||||
players[secondplaya].mo->color = players[secondplaya].skincolor;
|
||||
|
||||
// Update follower for local games:
|
||||
if (cv_follower2.value >= -1 && cv_follower2.value != players[secondplaya].followerskin)
|
||||
SetFollower(secondplaya, cv_follower2.value);
|
||||
|
||||
if ((foundskin = R_SkinAvailable(cv_skin2.string)) != -1)
|
||||
{
|
||||
//boolean notsame;
|
||||
|
|
@ -1675,13 +1738,15 @@ static void SendNameAndColor2(void)
|
|||
WRITESTRINGN(p, cv_playername2.zstring, MAXPLAYERNAME);
|
||||
WRITEUINT8(p, (UINT8)cv_playercolor2.value);
|
||||
WRITEUINT8(p, (UINT8)cv_skin2.value);
|
||||
WRITESINT8(p, (UINT8)cv_follower2.value);
|
||||
WRITESINT8(p, (UINT8)cv_followercolor2.value);
|
||||
SendNetXCmd2(XD_NAMEANDCOLOR, buf, p - buf);
|
||||
}
|
||||
|
||||
static void SendNameAndColor3(void)
|
||||
{
|
||||
INT32 thirdplaya = -1;
|
||||
XBOXSTATIC char buf[MAXPLAYERNAME+2];
|
||||
XBOXSTATIC char buf[MAXPLAYERNAME+3];
|
||||
char *p;
|
||||
|
||||
if (splitscreen < 2)
|
||||
|
|
@ -1706,6 +1771,10 @@ static void SendNameAndColor3(void)
|
|||
CV_StealthSetValue(&cv_playercolor3, skincolor_blueteam);
|
||||
}
|
||||
|
||||
// ditto for follower colour:
|
||||
if (!cv_followercolor3.value)
|
||||
CV_StealthSet(&cv_followercolor3, "Match"); // set it to "Match". I don't care about your stupidity!
|
||||
|
||||
// never allow the color "none"
|
||||
if (!cv_playercolor3.value)
|
||||
{
|
||||
|
|
@ -1717,6 +1786,10 @@ static void SendNameAndColor3(void)
|
|||
CV_StealthSet(&cv_playercolor3, cv_playercolor3.defaultvalue);
|
||||
}
|
||||
|
||||
// so like, this is sent before we even use anything like cvars or w/e so it's possible that follower is set to a pretty yikes value, so let's fix that before we send garbage that could crash the game:
|
||||
if (cv_follower3.value > numfollowers-1 || cv_follower3.value < -1)
|
||||
CV_StealthSet(&cv_follower3, "-1");
|
||||
|
||||
// We'll handle it later if we're not playing.
|
||||
if (!Playing())
|
||||
return;
|
||||
|
|
@ -1734,6 +1807,10 @@ static void SendNameAndColor3(void)
|
|||
if (players[thirdplaya].mo)
|
||||
players[thirdplaya].mo->color = players[thirdplaya].skincolor;
|
||||
|
||||
// Update follower for local games:
|
||||
if (cv_follower3.value >= -1 && cv_follower3.value != players[thirdplaya].followerskin)
|
||||
SetFollower(thirdplaya, cv_follower3.value);
|
||||
|
||||
if ((foundskin = R_SkinAvailable(cv_skin3.string)) != -1)
|
||||
{
|
||||
//boolean notsame;
|
||||
|
|
@ -1790,13 +1867,15 @@ static void SendNameAndColor3(void)
|
|||
WRITESTRINGN(p, cv_playername3.zstring, MAXPLAYERNAME);
|
||||
WRITEUINT8(p, (UINT8)cv_playercolor3.value);
|
||||
WRITEUINT8(p, (UINT8)cv_skin3.value);
|
||||
WRITESINT8(p, (UINT8)cv_follower3.value);
|
||||
WRITESINT8(p, (UINT8)cv_followercolor3.value);
|
||||
SendNetXCmd3(XD_NAMEANDCOLOR, buf, p - buf);
|
||||
}
|
||||
|
||||
static void SendNameAndColor4(void)
|
||||
{
|
||||
INT32 fourthplaya = -1;
|
||||
XBOXSTATIC char buf[MAXPLAYERNAME+2];
|
||||
XBOXSTATIC char buf[MAXPLAYERNAME+3];
|
||||
char *p;
|
||||
|
||||
if (splitscreen < 3)
|
||||
|
|
@ -1821,6 +1900,10 @@ static void SendNameAndColor4(void)
|
|||
CV_StealthSetValue(&cv_playercolor4, skincolor_blueteam);
|
||||
}
|
||||
|
||||
// ditto for follower colour:
|
||||
if (!cv_followercolor4.value)
|
||||
CV_StealthSet(&cv_followercolor4, "Match"); // set it to "Match". I don't care about your stupidity!
|
||||
|
||||
// never allow the color "none"
|
||||
if (!cv_playercolor4.value)
|
||||
{
|
||||
|
|
@ -1832,6 +1915,10 @@ static void SendNameAndColor4(void)
|
|||
CV_StealthSet(&cv_playercolor4, cv_playercolor4.defaultvalue);
|
||||
}
|
||||
|
||||
// so like, this is sent before we even use anything like cvars or w/e so it's possible that follower is set to a pretty yikes value, so let's fix that before we send garbage that could crash the game:
|
||||
if (cv_follower4.value > numfollowers-1 || cv_follower4.value < -1)
|
||||
CV_StealthSet(&cv_follower4, "-1");
|
||||
|
||||
// We'll handle it later if we're not playing.
|
||||
if (!Playing())
|
||||
return;
|
||||
|
|
@ -1849,6 +1936,10 @@ static void SendNameAndColor4(void)
|
|||
if (players[fourthplaya].mo)
|
||||
players[fourthplaya].mo->color = players[fourthplaya].skincolor;
|
||||
|
||||
// Update follower for local games:
|
||||
if (cv_follower4.value >= -1 && cv_follower4.value != players[fourthplaya].followerskin)
|
||||
SetFollower(fourthplaya, cv_follower4.value);
|
||||
|
||||
if ((foundskin = R_SkinAvailable(cv_skin4.string)) != -1)
|
||||
{
|
||||
//boolean notsame;
|
||||
|
|
@ -1905,6 +1996,8 @@ static void SendNameAndColor4(void)
|
|||
WRITESTRINGN(p, cv_playername4.zstring, MAXPLAYERNAME);
|
||||
WRITEUINT8(p, (UINT8)cv_playercolor4.value);
|
||||
WRITEUINT8(p, (UINT8)cv_skin4.value);
|
||||
WRITESINT8(p, (UINT8)cv_follower4.value);
|
||||
WRITESINT8(p, (UINT8)cv_followercolor4.value);
|
||||
SendNetXCmd4(XD_NAMEANDCOLOR, buf, p - buf);
|
||||
}
|
||||
|
||||
|
|
@ -1912,7 +2005,8 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
|
|||
{
|
||||
player_t *p = &players[playernum];
|
||||
char name[MAXPLAYERNAME+1];
|
||||
UINT8 color, skin;
|
||||
UINT8 color, skin, followercolor;
|
||||
SINT8 follower;
|
||||
|
||||
#ifdef PARANOIA
|
||||
if (playernum < 0 || playernum > MAXPLAYERS)
|
||||
|
|
@ -1936,6 +2030,8 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
|
|||
READSTRINGN(*cp, name, MAXPLAYERNAME);
|
||||
color = READUINT8(*cp);
|
||||
skin = READUINT8(*cp);
|
||||
follower = READSINT8(*cp);
|
||||
followercolor = READSINT8(*cp);
|
||||
|
||||
// set name
|
||||
if (strcasecmp(player_names[playernum], name) != 0)
|
||||
|
|
@ -1995,6 +2091,13 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
|
|||
}
|
||||
else
|
||||
SetPlayerSkinByNum(playernum, skin);
|
||||
|
||||
// set follower colour:
|
||||
// Don't bother doing garbage and kicking if we receive None, this is both silly and a waste of time, this will be handled properly in P_HandleFollower.
|
||||
p->followercolor = followercolor;
|
||||
|
||||
// set follower
|
||||
SetFollower(playernum, follower);
|
||||
}
|
||||
|
||||
void SendWeaponPref(void)
|
||||
|
|
@ -2815,6 +2918,7 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r
|
|||
}
|
||||
|
||||
chmappending++;
|
||||
|
||||
if (netgame)
|
||||
WRITEUINT32(buf_p, M_RandomizedSeed()); // random seed
|
||||
SendNetXCmd(XD_MAP, buf, buf_p - buf);
|
||||
|
|
@ -6034,6 +6138,192 @@ static void Name4_OnChange(void)
|
|||
SendNameAndColor4();
|
||||
}
|
||||
|
||||
// sends the follower change for players
|
||||
static void Follower_OnChange(void)
|
||||
{
|
||||
char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1];
|
||||
INT32 num;
|
||||
char set[10]; // This isn't Lua and mixed declarations in the middle of code make caveman compilers scream.
|
||||
|
||||
// there is a slight chance that we will actually use a string instead so...
|
||||
// let's investigate the string...
|
||||
strcpy(str, cv_follower.string);
|
||||
strcpy(cpy, cv_follower.string);
|
||||
strlwr(str);
|
||||
if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright...
|
||||
{
|
||||
if (stricmp(cpy, "None") == 0)
|
||||
{
|
||||
CV_StealthSet(&cv_follower, "-1");
|
||||
|
||||
if (!Playing())
|
||||
return; // don't send anything there.
|
||||
|
||||
SendNameAndColor();
|
||||
return;
|
||||
}
|
||||
|
||||
num = R_FollowerAvailable(str);
|
||||
|
||||
if (num == -1) // that's an error.
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str);
|
||||
|
||||
sprintf(set, "%d", num);
|
||||
CV_StealthSet(&cv_follower, set); // set it to a number. It's easier for us to send later :)
|
||||
}
|
||||
|
||||
if (!Playing())
|
||||
return; // don't send anything there.
|
||||
|
||||
SendNameAndColor();
|
||||
}
|
||||
|
||||
// About the same as Color_OnChange but for followers.
|
||||
static void Followercolor_OnChange(void)
|
||||
{
|
||||
|
||||
if (!Playing())
|
||||
return; // do whatever you want if you aren't in the game or don't have a follower.
|
||||
|
||||
if (!P_PlayerMoving(consoleplayer))
|
||||
{
|
||||
// Color change menu scrolling fix is no longer necessary
|
||||
SendNameAndColor();
|
||||
}
|
||||
}
|
||||
|
||||
// repeat for the 3 other players
|
||||
|
||||
static void Follower2_OnChange(void)
|
||||
{
|
||||
char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1];
|
||||
if (!Playing() || !splitscreen)
|
||||
return; // do whatever you want
|
||||
|
||||
strcpy(str, cv_follower2.string);
|
||||
strcpy(cpy, cv_follower2.string);
|
||||
strlwr(str);
|
||||
if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright...
|
||||
{
|
||||
|
||||
if (stricmp(cpy, "None") == 0)
|
||||
{
|
||||
CV_StealthSet(&cv_follower2, "-1");
|
||||
SendNameAndColor2();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
INT32 num = R_FollowerAvailable(str);
|
||||
char set[10];
|
||||
if (num == -1) // that's an error.
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str);
|
||||
|
||||
sprintf(set, "%d", num);
|
||||
CV_StealthSet(&cv_follower2, set); // set it to a number. It's easier for us to send later :)
|
||||
}
|
||||
SendNameAndColor2();
|
||||
}
|
||||
|
||||
static void Followercolor2_OnChange(void)
|
||||
{
|
||||
|
||||
if (!Playing())
|
||||
return; // do whatever you want if you aren't in the game or don't have a follower.
|
||||
|
||||
if (!P_PlayerMoving(g_localplayers[1]))
|
||||
{
|
||||
// Color change menu scrolling fix is no longer necessary
|
||||
SendNameAndColor2();
|
||||
}
|
||||
}
|
||||
|
||||
static void Follower3_OnChange(void)
|
||||
{
|
||||
char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1];
|
||||
if (!Playing() || !splitscreen)
|
||||
return; // do whatever you want
|
||||
|
||||
strcpy(str, cv_follower3.string);
|
||||
strcpy(cpy, cv_follower3.string);
|
||||
strlwr(str);
|
||||
if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright...
|
||||
{
|
||||
|
||||
if (stricmp(cpy, "None") == 0)
|
||||
{
|
||||
CV_StealthSet(&cv_follower3, "-1");
|
||||
SendNameAndColor3();
|
||||
return;
|
||||
}
|
||||
|
||||
INT32 num = R_FollowerAvailable(str);
|
||||
char set[10];
|
||||
if (num == -1) // that's an error.
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str);
|
||||
|
||||
sprintf(set, "%d", num);
|
||||
CV_StealthSet(&cv_follower3, set); // set it to a number. It's easier for us to send later :)
|
||||
}
|
||||
SendNameAndColor3();
|
||||
}
|
||||
|
||||
static void Followercolor3_OnChange(void)
|
||||
{
|
||||
|
||||
if (!Playing())
|
||||
return; // do whatever you want if you aren't in the game or don't have a follower.
|
||||
|
||||
if (!P_PlayerMoving(g_localplayers[2]))
|
||||
{
|
||||
// Color change menu scrolling fix is no longer necessary
|
||||
SendNameAndColor3();
|
||||
}
|
||||
}
|
||||
|
||||
static void Follower4_OnChange(void)
|
||||
{
|
||||
char str[SKINNAMESIZE+1], cpy[SKINNAMESIZE+1];
|
||||
if (!Playing() || !splitscreen)
|
||||
return; // do whatever you want
|
||||
|
||||
strcpy(str, cv_follower4.string);
|
||||
strcpy(cpy, cv_follower4.string);
|
||||
strlwr(str);
|
||||
if (stricmp(cpy,"0") !=0 && !atoi(cpy)) // yep, that's a string alright...
|
||||
{
|
||||
|
||||
if (stricmp(cpy, "None") == 0)
|
||||
{
|
||||
CV_StealthSet(&cv_follower4, "-1");
|
||||
SendNameAndColor4();
|
||||
return;
|
||||
}
|
||||
|
||||
INT32 num = R_FollowerAvailable(str);
|
||||
char set[10];
|
||||
if (num == -1) // that's an error.
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found\n"), str);
|
||||
|
||||
sprintf(set, "%d", num);
|
||||
CV_StealthSet(&cv_follower4, set); // set it to a number. It's easier for us to send later :)
|
||||
}
|
||||
SendNameAndColor4();
|
||||
}
|
||||
|
||||
static void Followercolor4_OnChange(void)
|
||||
{
|
||||
|
||||
if (!Playing())
|
||||
return; // do whatever you want if you aren't in the game or don't have a follower.
|
||||
|
||||
if (!P_PlayerMoving(g_localplayers[3]))
|
||||
{
|
||||
// Color change menu scrolling fix is no longer necessary
|
||||
SendNameAndColor4();
|
||||
}
|
||||
}
|
||||
|
||||
/** 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>
|
||||
|
|
|
|||
|
|
@ -21,18 +21,23 @@
|
|||
extern consvar_t cv_playername;
|
||||
extern consvar_t cv_playercolor;
|
||||
extern consvar_t cv_skin;
|
||||
extern consvar_t cv_follower;
|
||||
extern consvar_t cv_showfollowers;
|
||||
// secondary splitscreen player
|
||||
extern consvar_t cv_playername2;
|
||||
extern consvar_t cv_playercolor2;
|
||||
extern consvar_t cv_skin2;
|
||||
extern consvar_t cv_follower2;
|
||||
// third splitscreen player
|
||||
extern consvar_t cv_playername3;
|
||||
extern consvar_t cv_playercolor3;
|
||||
extern consvar_t cv_skin3;
|
||||
extern consvar_t cv_follower3;
|
||||
// fourth splitscreen player
|
||||
extern consvar_t cv_playername4;
|
||||
extern consvar_t cv_playercolor4;
|
||||
extern consvar_t cv_skin4;
|
||||
extern consvar_t cv_follower4;
|
||||
// preferred number of players
|
||||
extern consvar_t cv_splitplayers;
|
||||
|
||||
|
|
|
|||
|
|
@ -513,6 +513,12 @@ typedef struct player_s
|
|||
// SRB2kart
|
||||
UINT8 kartspeed; // Kart speed stat between 1 and 9
|
||||
UINT8 kartweight; // Kart weight stat between 1 and 9
|
||||
|
||||
INT32 followerskin; // Kart: This player's follower "skin"
|
||||
boolean followerready; // Kart: Used to know when we can have a follower or not. (This is set on the first NameAndColor follower update)
|
||||
UINT8 followercolor; // Kart: Used to store the follower colour the player wishes to use
|
||||
mobj_t *follower; // Kart: This is the follower object we have. (If any)
|
||||
|
||||
//
|
||||
|
||||
UINT32 charflags; // Extra abilities/settings for skins (combinable stuff)
|
||||
|
|
|
|||
302
src/dehacked.c
302
src/dehacked.c
|
|
@ -34,6 +34,7 @@
|
|||
#include "lua_script.h"
|
||||
#include "lua_hook.h"
|
||||
#include "d_clisrv.h"
|
||||
#include "r_things.h" // for followers
|
||||
|
||||
#include "m_cond.h"
|
||||
|
||||
|
|
@ -674,6 +675,271 @@ static void readfreeslots(MYFILE *f)
|
|||
Z_Free(s);
|
||||
}
|
||||
|
||||
// This here is our current only way to make followers.
|
||||
INT32 numfollowers = 0;
|
||||
|
||||
static void readfollower(MYFILE *f)
|
||||
{
|
||||
char *s;
|
||||
char *word, *word2, dname[SKINNAMESIZE+1];
|
||||
char *tmp;
|
||||
char testname[SKINNAMESIZE];
|
||||
|
||||
boolean nameset;
|
||||
INT32 fallbackstate = 0;
|
||||
INT32 res;
|
||||
INT32 i;
|
||||
|
||||
if (numfollowers > MAXSKINS)
|
||||
{
|
||||
deh_warning("Error: Too many followers, cannot add anymore.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
||||
|
||||
// Ready the default variables for followers. We will overwrite them as we go! We won't set the name or states RIGHT HERE as this is handled down instead.
|
||||
followers[numfollowers].scale = FRACUNIT;
|
||||
followers[numfollowers].bubblescale = 0; // No bubble by default
|
||||
followers[numfollowers].atangle = 230;
|
||||
followers[numfollowers].dist = 32; // changed from 16 to 32 to better account for ogl models
|
||||
followers[numfollowers].height = 16;
|
||||
followers[numfollowers].zoffs = 32;
|
||||
followers[numfollowers].horzlag = 2;
|
||||
followers[numfollowers].vertlag = 6;
|
||||
followers[numfollowers].bobspeed = TICRATE*2;
|
||||
followers[numfollowers].bobamp = 4;
|
||||
followers[numfollowers].hitconfirmtime = TICRATE;
|
||||
followers[numfollowers].defaultcolor = 1;
|
||||
|
||||
do
|
||||
{
|
||||
if (myfgets(s, MAXLINELEN, f))
|
||||
{
|
||||
if (s[0] == '\n')
|
||||
break;
|
||||
|
||||
tmp = strchr(s, '#');
|
||||
if (tmp)
|
||||
*tmp = '\0';
|
||||
if (s == tmp)
|
||||
continue; // Skip comment lines, but don't break.
|
||||
|
||||
word = strtok(s, " ");
|
||||
if (word)
|
||||
strupr(word);
|
||||
else
|
||||
break;
|
||||
|
||||
word2 = strtok(NULL, " = ");
|
||||
|
||||
if (!word2)
|
||||
break;
|
||||
|
||||
if (word2[strlen(word2)-1] == '\n')
|
||||
word2[strlen(word2)-1] = '\0';
|
||||
|
||||
if (fastcmp(word, "NAME"))
|
||||
{
|
||||
DEH_WriteUndoline(word, va("%s", followers[numfollowers].name), UNDO_NONE);
|
||||
strcpy(followers[numfollowers].name, word2);
|
||||
nameset = true;
|
||||
}
|
||||
else if (fastcmp(word, "DEFAULTCOLOR"))
|
||||
{
|
||||
DEH_WriteUndoline(word, va("%d", followers[numfollowers].defaultcolor), UNDO_NONE);
|
||||
followers[numfollowers].defaultcolor = (UINT8)get_number(word2);
|
||||
}
|
||||
|
||||
else if (fastcmp(word, "SCALE"))
|
||||
{
|
||||
DEH_WriteUndoline(word, va("%d", followers[numfollowers].scale), UNDO_NONE);
|
||||
followers[numfollowers].scale = get_number(word2);
|
||||
}
|
||||
else if (fastcmp(word, "BUBBLESCALE"))
|
||||
{
|
||||
DEH_WriteUndoline(word, va("%d", followers[numfollowers].bubblescale), UNDO_NONE);
|
||||
followers[numfollowers].bubblescale = get_number(word2);
|
||||
}
|
||||
else if (fastcmp(word, "ATANGLE"))
|
||||
{
|
||||
DEH_WriteUndoline(word, va("%d", followers[numfollowers].atangle), UNDO_NONE);
|
||||
followers[numfollowers].atangle = (INT32)atoi(word2);
|
||||
}
|
||||
else if (fastcmp(word, "HORZLAG"))
|
||||
{
|
||||
DEH_WriteUndoline(word, va("%d", followers[numfollowers].horzlag), UNDO_NONE);
|
||||
followers[numfollowers].horzlag = (INT32)atoi(word2);
|
||||
}
|
||||
else if (fastcmp(word, "VERTLAG"))
|
||||
{
|
||||
DEH_WriteUndoline(word, va("%d", followers[numfollowers].vertlag), UNDO_NONE);
|
||||
followers[numfollowers].vertlag = (INT32)atoi(word2);
|
||||
}
|
||||
else if (fastcmp(word, "BOBSPEED"))
|
||||
{
|
||||
DEH_WriteUndoline(word, va("%d", followers[numfollowers].bobspeed), UNDO_NONE);
|
||||
followers[numfollowers].bobspeed = (INT32)atoi(word2);
|
||||
}
|
||||
else if (fastcmp(word, "BOBAMP"))
|
||||
{
|
||||
DEH_WriteUndoline(word, va("%d", followers[numfollowers].bobamp), UNDO_NONE);
|
||||
followers[numfollowers].bobamp = (INT32)atoi(word2);
|
||||
}
|
||||
else if (fastcmp(word, "ZOFFSET") || (fastcmp(word, "ZOFFS")))
|
||||
{
|
||||
DEH_WriteUndoline(word, va("%d", followers[numfollowers].zoffs), UNDO_NONE);
|
||||
followers[numfollowers].zoffs = (INT32)atoi(word2);
|
||||
}
|
||||
else if (fastcmp(word, "DISTANCE") || (fastcmp(word, "DIST")))
|
||||
{
|
||||
DEH_WriteUndoline(word, va("%d", followers[numfollowers].dist), UNDO_NONE);
|
||||
followers[numfollowers].dist = (INT32)atoi(word2);
|
||||
}
|
||||
else if (fastcmp(word, "HEIGHT"))
|
||||
{
|
||||
DEH_WriteUndoline(word, va("%d", followers[numfollowers].height), UNDO_NONE);
|
||||
followers[numfollowers].height = (INT32)atoi(word2);
|
||||
}
|
||||
else if (fastcmp(word, "IDLESTATE"))
|
||||
{
|
||||
if (word2)
|
||||
strupr(word2);
|
||||
DEH_WriteUndoline(word, va("%d", followers[numfollowers].idlestate), UNDO_NONE);
|
||||
followers[numfollowers].idlestate = get_number(word2);
|
||||
fallbackstate = followers[numfollowers].idlestate;
|
||||
}
|
||||
else if (fastcmp(word, "FOLLOWSTATE"))
|
||||
{
|
||||
if (word2)
|
||||
strupr(word2);
|
||||
DEH_WriteUndoline(word, va("%d", followers[numfollowers].followstate), UNDO_NONE);
|
||||
followers[numfollowers].followstate = get_number(word2);
|
||||
}
|
||||
else if (fastcmp(word, "HURTSTATE"))
|
||||
{
|
||||
if (word2)
|
||||
strupr(word2);
|
||||
DEH_WriteUndoline(word, va("%d", followers[numfollowers].hurtstate), UNDO_NONE);
|
||||
followers[numfollowers].hurtstate = get_number(word2);
|
||||
}
|
||||
else if (fastcmp(word, "LOSESTATE"))
|
||||
{
|
||||
if (word2)
|
||||
strupr(word2);
|
||||
DEH_WriteUndoline(word, va("%d", followers[numfollowers].losestate), UNDO_NONE);
|
||||
followers[numfollowers].losestate = get_number(word2);
|
||||
}
|
||||
else if (fastcmp(word, "WINSTATE"))
|
||||
{
|
||||
if (word2)
|
||||
strupr(word2);
|
||||
DEH_WriteUndoline(word, va("%d", followers[numfollowers].winstate), UNDO_NONE);
|
||||
followers[numfollowers].winstate = get_number(word2);
|
||||
}
|
||||
else if (fastcmp(word, "HITSTATE") || (fastcmp(word, "HITCONFIRMSTATE")))
|
||||
{
|
||||
if (word2)
|
||||
strupr(word2);
|
||||
DEH_WriteUndoline(word, va("%d", followers[numfollowers].hitconfirmstate), UNDO_NONE);
|
||||
followers[numfollowers].hitconfirmstate = get_number(word2);
|
||||
}
|
||||
else if (fastcmp(word, "HITTIME") || (fastcmp(word, "HITCONFIRMTIME")))
|
||||
{
|
||||
DEH_WriteUndoline(word, va("%d", followers[numfollowers].hitconfirmtime), UNDO_NONE);
|
||||
followers[numfollowers].hitconfirmtime = (INT32)atoi(word2);
|
||||
}
|
||||
else
|
||||
deh_warning("Follower %d: unknown word '%s'", numfollowers, word);
|
||||
}
|
||||
} while (!myfeof(f)); // finish when the line is empty
|
||||
|
||||
if (!nameset) // well this is problematic.
|
||||
{
|
||||
strcpy(followers[numfollowers].name, va("Follower%d", numfollowers)); // this is lazy, so what
|
||||
}
|
||||
|
||||
// set skin name (this is just the follower's name in lowercases):
|
||||
// but before we do, let's... actually check if another follower isn't doing the same shit...
|
||||
|
||||
strcpy(testname, followers[numfollowers].name);
|
||||
|
||||
// lower testname for skin checks...
|
||||
strlwr(testname);
|
||||
res = R_FollowerAvailable(testname);
|
||||
if (res > -1) // yikes, someone else has stolen our name already
|
||||
{
|
||||
INT32 startlen = strlen(testname);
|
||||
char cpy[2];
|
||||
//deh_warning("There was already a follower with the same name. (%s)", testname); This warning probably isn't necessary anymore?
|
||||
sprintf(cpy, "%d", numfollowers);
|
||||
memcpy(&testname[startlen], cpy, 2);
|
||||
// in that case, we'll be very lazy and copy numfollowers to the end of our skin name.
|
||||
}
|
||||
|
||||
strcpy(followers[numfollowers].skinname, testname);
|
||||
strcpy(dname, followers[numfollowers].skinname); // display name, just used for printing succesful stuff or errors later down the line.
|
||||
|
||||
// now that the skin name is ready, post process the actual name to turn the underscores into spaces!
|
||||
for (i = 0; followers[numfollowers].name[i]; i++)
|
||||
{
|
||||
if (followers[numfollowers].name[i] == '_')
|
||||
followers[numfollowers].name[i] = ' ';
|
||||
}
|
||||
|
||||
// fallbacks for variables
|
||||
// Print a warning if the variable is on a weird value and set it back to the minimum available if that's the case.
|
||||
#define FALLBACK(field, field2, threshold, set) \
|
||||
if (followers[numfollowers].field < threshold) \
|
||||
{ \
|
||||
followers[numfollowers].field = set; \
|
||||
deh_warning("Follower '%s': Value for '%s' is too low! Minimum should be %d. Value was overwritten to %d.", dname, field2, set, set); \
|
||||
} \
|
||||
|
||||
FALLBACK(dist, "DIST", 0, 0);
|
||||
FALLBACK(height, "HEIGHT", 1, 1);
|
||||
FALLBACK(zoffs, "ZOFFS", 0, 0);
|
||||
FALLBACK(horzlag, "HORZLAG", 1, 1);
|
||||
FALLBACK(vertlag, "VERTLAG", 1, 1);
|
||||
FALLBACK(bobamp, "BOBAMP", 0, 0);
|
||||
FALLBACK(bobspeed, "BOBSPEED", 0, 0);
|
||||
FALLBACK(hitconfirmtime, "HITCONFIRMTIME", 1, 1);
|
||||
FALLBACK(scale, "SCALE", 1, 1); // No null/negative scale
|
||||
FALLBACK(bubblescale, "BUBBLESCALE", 0, 0); // No negative scale
|
||||
|
||||
// Special case for color I suppose
|
||||
if (followers[numfollowers].defaultcolor < 0 || followers[numfollowers].defaultcolor > MAXSKINCOLORS-1)
|
||||
{
|
||||
followers[numfollowers].defaultcolor = 1;
|
||||
deh_warning("Follower \'%s\': Value for 'color' should be between 1 and %d.\n", dname, MAXSKINCOLORS-1);
|
||||
}
|
||||
|
||||
#undef FALLBACK
|
||||
|
||||
// also check if we forgot states. If we did, we will set any missing state to the follower's idlestate.
|
||||
// Print a warning in case we don't have a fallback and set the state to S_INVISIBLE (rather than S_NULL) if unavailable.
|
||||
|
||||
#define NOSTATE(field, field2) \
|
||||
if (!followers[numfollowers].field) \
|
||||
{ \
|
||||
followers[numfollowers].field = fallbackstate ? fallbackstate : S_INVISIBLE; \
|
||||
if (!fallbackstate) \
|
||||
deh_warning("Follower '%s' is missing state definition for '%s', no idlestate fallback was found", dname, field2); \
|
||||
} \
|
||||
|
||||
NOSTATE(idlestate, "IDLESTATE");
|
||||
NOSTATE(followstate, "FOLLOWSTATE");
|
||||
NOSTATE(hurtstate, "HURTSTATE");
|
||||
NOSTATE(losestate, "LOSESTATE");
|
||||
NOSTATE(winstate, "WINSTATE");
|
||||
NOSTATE(hitconfirmstate, "HITCONFIRMSTATE");
|
||||
#undef NOSTATE
|
||||
|
||||
CONS_Printf("Added follower '%s'\n", dname);
|
||||
numfollowers++; // add 1 follower
|
||||
Z_Free(s);
|
||||
}
|
||||
|
||||
static void readthing(MYFILE *f, INT32 num)
|
||||
{
|
||||
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
|
||||
|
|
@ -3418,6 +3684,13 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
|
|||
// This is not a major mod.
|
||||
continue;
|
||||
}
|
||||
else if (fastcmp(word, "FOLLOWER"))
|
||||
{
|
||||
readfollower(f); // at the same time this will be our only way to ADD followers for now. Yikes.
|
||||
DEH_WriteUndoline(word, "", UNDO_HEADER);
|
||||
// This is not a major mod either.
|
||||
continue; // continue so that we don't error.
|
||||
}
|
||||
word2 = strtok(NULL, " ");
|
||||
if (fastcmp(word, "CHARACTER"))
|
||||
{
|
||||
|
|
@ -7207,6 +7480,31 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
|
|||
"S_OPAQUESMOKE4",
|
||||
"S_OPAQUESMOKE5",
|
||||
|
||||
"S_FOLLOWERBUBBLE_FRONT",
|
||||
"S_FOLLOWERBUBBLE_BACK",
|
||||
|
||||
"S_GCHAOIDLE",
|
||||
"S_GCHAOFLY",
|
||||
"S_GCHAOSAD1",
|
||||
"S_GCHAOSAD2",
|
||||
"S_GCHAOSAD3",
|
||||
"S_GCHAOSAD4",
|
||||
"S_GCHAOHAPPY1",
|
||||
"S_GCHAOHAPPY2",
|
||||
"S_GCHAOHAPPY3",
|
||||
"S_GCHAOHAPPY4",
|
||||
|
||||
"S_CHEESEIDLE",
|
||||
"S_CHEESEFLY",
|
||||
"S_CHEESESAD1",
|
||||
"S_CHEESESAD2",
|
||||
"S_CHEESESAD3",
|
||||
"S_CHEESESAD4",
|
||||
"S_CHEESEHAPPY1",
|
||||
"S_CHEESEHAPPY2",
|
||||
"S_CHEESEHAPPY3",
|
||||
"S_CHEESEHAPPY4",
|
||||
|
||||
"S_RINGDEBT",
|
||||
"S_RINGSPARKS1",
|
||||
"S_RINGSPARKS2",
|
||||
|
|
@ -8074,6 +8372,10 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
|
|||
"MT_BATTLECAPSULE",
|
||||
"MT_BATTLECAPSULE_PIECE",
|
||||
|
||||
"MT_FOLLOWER",
|
||||
"MT_FOLLOWERBUBBLE_FRONT",
|
||||
"MT_FOLLOWERBUBBLE_BACK",
|
||||
|
||||
"MT_WATERTRAIL",
|
||||
"MT_WATERTRAILUNDERLAY",
|
||||
|
||||
|
|
|
|||
108
src/g_game.c
108
src/g_game.c
|
|
@ -2570,6 +2570,10 @@ void G_PlayerReborn(INT32 player)
|
|||
// SRB2kart
|
||||
UINT8 kartspeed;
|
||||
UINT8 kartweight;
|
||||
boolean followerready;
|
||||
INT32 followerskin;
|
||||
UINT8 followercolor;
|
||||
mobj_t *follower; // old follower, will probably be removed by the time we're dead but you never know.
|
||||
//
|
||||
INT32 charflags;
|
||||
INT32 pflags;
|
||||
|
|
@ -2630,6 +2634,10 @@ void G_PlayerReborn(INT32 player)
|
|||
// SRB2kart
|
||||
kartspeed = players[player].kartspeed;
|
||||
kartweight = players[player].kartweight;
|
||||
follower = players[player].follower;
|
||||
followerready = players[player].followerready;
|
||||
followercolor = players[player].followercolor;
|
||||
followerskin = players[player].followerskin;
|
||||
//
|
||||
charflags = players[player].charflags;
|
||||
|
||||
|
|
@ -2684,6 +2692,9 @@ void G_PlayerReborn(INT32 player)
|
|||
wanted = players[player].kartstuff[k_wanted];
|
||||
}
|
||||
|
||||
// Obliterate follower from existence
|
||||
P_SetTarget(&players[player].follower, NULL);
|
||||
|
||||
memcpy(&respawn, &players[player].respawn, sizeof (respawn));
|
||||
|
||||
p = &players[player];
|
||||
|
|
@ -2740,6 +2751,16 @@ void G_PlayerReborn(INT32 player)
|
|||
|
||||
memcpy(&p->respawn, &respawn, sizeof (p->respawn));
|
||||
|
||||
if (follower)
|
||||
P_RemoveMobj(follower);
|
||||
|
||||
p->followerready = followerready;
|
||||
p->followerskin = followerskin;
|
||||
p->followercolor = followercolor;
|
||||
//p->follower = NULL; // respawn a new one with you, it looks better.
|
||||
// ^ Not necessary anyway since it will be respawned regardless considering it doesn't exist anymore.
|
||||
|
||||
|
||||
// Don't do anything immediately
|
||||
p->pflags |= PF_USEDOWN;
|
||||
p->pflags |= PF_ATTACKDOWN;
|
||||
|
|
@ -3207,7 +3228,7 @@ void G_AddPlayer(INT32 playernum)
|
|||
p->jointime = 0;
|
||||
p->playerstate = PST_REBORN;
|
||||
|
||||
demo_extradata[playernum] |= DXD_PLAYSTATE|DXD_COLOR|DXD_NAME|DXD_SKIN; // Set everything
|
||||
demo_extradata[playernum] |= DXD_PLAYSTATE|DXD_COLOR|DXD_NAME|DXD_SKIN|DXD_FOLLOWER; // Set everything
|
||||
}
|
||||
|
||||
void G_ExitLevel(void)
|
||||
|
|
@ -4936,6 +4957,25 @@ void G_ReadDemoExtraData(void)
|
|||
// Name
|
||||
M_Memcpy(player_names[p],demo_p,16);
|
||||
demo_p += 16;
|
||||
}
|
||||
if (extradata & DXD_FOLLOWER)
|
||||
{
|
||||
// Set our follower
|
||||
M_Memcpy(name, demo_p, 16);
|
||||
demo_p += 16;
|
||||
SetPlayerFollower(p, name);
|
||||
|
||||
// Follower's color
|
||||
M_Memcpy(name, demo_p, 16);
|
||||
demo_p += 16;
|
||||
for (i = 0; i < MAXSKINCOLORS; i++)
|
||||
if (!stricmp(KartColor_Names[i], name)) // SRB2kart
|
||||
{
|
||||
players[p].followercolor = i;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
if (extradata & DXD_PLAYSTATE)
|
||||
{
|
||||
|
|
@ -5039,6 +5079,7 @@ void G_WriteDemoExtraData(void)
|
|||
|
||||
WRITEUINT8(demo_p, skins[players[i].skin].kartspeed);
|
||||
WRITEUINT8(demo_p, skins[players[i].skin].kartweight);
|
||||
|
||||
}
|
||||
if (demo_extradata[i] & DXD_COLOR)
|
||||
{
|
||||
|
|
@ -5056,6 +5097,21 @@ void G_WriteDemoExtraData(void)
|
|||
M_Memcpy(demo_p,name,16);
|
||||
demo_p += 16;
|
||||
}
|
||||
if (demo_extradata[i] & DXD_FOLLOWER)
|
||||
{
|
||||
// write follower
|
||||
memset(name, 0, 16);
|
||||
strncpy(name, followers[players[i].followerskin].skinname, 16);
|
||||
M_Memcpy(demo_p, name, 16);
|
||||
demo_p += 16;
|
||||
|
||||
// write follower color
|
||||
memset(name, 0, 16);
|
||||
strncpy(name, Followercolor_cons_t[players[i].followercolor].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match"
|
||||
M_Memcpy(demo_p,name,16);
|
||||
demo_p += 16;
|
||||
|
||||
}
|
||||
if (demo_extradata[i] & DXD_PLAYSTATE)
|
||||
{
|
||||
demo_writerng = 1;
|
||||
|
|
@ -5656,6 +5712,8 @@ void G_GhostTicker(void)
|
|||
g->p += 16; // Same tbh
|
||||
if (ziptic & DXD_NAME)
|
||||
g->p += 16; // yea
|
||||
if (ziptic & DXD_FOLLOWER)
|
||||
g->p += 32; // ok (32 because there's both the skin and the colour)
|
||||
if (ziptic & DXD_PLAYSTATE && READUINT8(g->p) != DXD_PST_PLAYING)
|
||||
I_Error("Ghost is not a record attack ghost"); //@TODO lmao don't blow up like this
|
||||
}
|
||||
|
|
@ -6394,6 +6452,12 @@ void G_BeginRecording(void)
|
|||
CV_SaveNetVars(&demo_p, true);
|
||||
|
||||
// Now store some info for each in-game player
|
||||
|
||||
// Lat' 12/05/19: Do note that for the first game you load, everything that gets saved here is total garbage;
|
||||
// The name will always be Player <n>, the skin sonic, the color None and the follower 0. This is only correct on subsequent games.
|
||||
// In the case of said first game, the skin and the likes are updated with Got_NameAndColor, which are then saved in extradata for the demo with DXD_SKIN in r_things.c for instance.
|
||||
|
||||
|
||||
for (p = 0; p < MAXPLAYERS; p++) {
|
||||
if (playeringame[p]) {
|
||||
player = &players[p];
|
||||
|
|
@ -6418,6 +6482,25 @@ void G_BeginRecording(void)
|
|||
M_Memcpy(demo_p,name,16);
|
||||
demo_p += 16;
|
||||
|
||||
// Save follower's skin name
|
||||
// PS: We must check for 'follower' to determine if the followerskin is valid. It's going to be 0 if we don't have a follower, but 0 is also absolutely a valid follower!
|
||||
// Doesn't really matter if the follower mobj is valid so long as it exists in a way or another.
|
||||
|
||||
memset(name, 0, 16);
|
||||
if (player->follower)
|
||||
strncpy(name, followers[player->followerskin].skinname, 16);
|
||||
else
|
||||
strncpy(name, "None", 16); // Say we don't have one, then.
|
||||
|
||||
M_Memcpy(demo_p,name,16);
|
||||
demo_p += 16;
|
||||
|
||||
// Save follower's colour
|
||||
memset(name, 0, 16);
|
||||
strncpy(name, Followercolor_cons_t[player->followercolor].strvalue, 16); // Not KartColor_Names because followercolor has extra values such as "Match"
|
||||
M_Memcpy(demo_p, name, 16);
|
||||
demo_p += 16;
|
||||
|
||||
// Score, since Kart uses this to determine where you start on the map
|
||||
WRITEUINT32(demo_p, player->score);
|
||||
|
||||
|
|
@ -7017,7 +7100,7 @@ void G_DoPlayDemo(char *defdemoname)
|
|||
{
|
||||
UINT8 i, p;
|
||||
lumpnum_t l;
|
||||
char skin[17],color[17],*n,*pdemoname;
|
||||
char skin[17],color[17],follower[17],*n,*pdemoname;
|
||||
UINT8 version,subversion;
|
||||
UINT32 randseed;
|
||||
char msg[1024];
|
||||
|
|
@ -7031,6 +7114,7 @@ void G_DoPlayDemo(char *defdemoname)
|
|||
|
||||
skin[16] = '\0';
|
||||
color[16] = '\0';
|
||||
follower[16] = '\0';
|
||||
|
||||
// No demo name means we're restarting the current demo
|
||||
if (defdemoname == NULL)
|
||||
|
|
@ -7345,6 +7429,23 @@ void G_DoPlayDemo(char *defdemoname)
|
|||
break;
|
||||
}
|
||||
|
||||
// Follower
|
||||
M_Memcpy(follower, demo_p, 16);
|
||||
demo_p += 16;
|
||||
SetPlayerFollower(p, follower);
|
||||
|
||||
// Follower colour
|
||||
M_Memcpy(color, demo_p, 16);
|
||||
demo_p += 16;
|
||||
for (i = 0; i < MAXSKINCOLORS +2; i++) // +2 because of Match and Opposite
|
||||
{
|
||||
if (!stricmp(Followercolor_cons_t[i].strvalue, color))
|
||||
{
|
||||
players[p].followercolor = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Score, since Kart uses this to determine where you start on the map
|
||||
players[p].score = READUINT32(demo_p);
|
||||
|
||||
|
|
@ -7574,6 +7675,9 @@ void G_AddGhost(char *defdemoname)
|
|||
M_Memcpy(color, p, 16);
|
||||
p += 16;
|
||||
|
||||
// Follower data was here, skip it, we don't care about it for ghosts.
|
||||
p += 32; // followerskin (16) + followercolor (16)
|
||||
|
||||
p += 4; // score
|
||||
p += 2; // powerlevel
|
||||
|
||||
|
|
|
|||
|
|
@ -226,6 +226,7 @@ extern UINT8 demo_writerng;
|
|||
#define DXD_NAME 0x04 // name changed
|
||||
#define DXD_COLOR 0x08 // color changed
|
||||
#define DXD_PLAYSTATE 0x10 // state changed between playing, spectating, or not in-game
|
||||
#define DXD_FOLLOWER 0x20 // follower was changed
|
||||
|
||||
#define DXD_PST_PLAYING 0x01
|
||||
#define DXD_PST_SPECTATING 0x02
|
||||
|
|
|
|||
120
src/info.c
120
src/info.c
|
|
@ -53,6 +53,7 @@ char sprnames[NUMSPRITES + 1][5] =
|
|||
"SRBB","SRBC","SRBD","SRBE","SRBF","SRBG","SRBH","SRBI","SRBJ","SRBK",
|
||||
"SRBL","SRBM","SRBN","SRBO",
|
||||
//SRB2kart Sprites
|
||||
|
||||
"RNDM","RPOP","SGNS","FAST","DSHR","BOST","BOSM","KFRE","KINV","KINF",
|
||||
"WIPD","DRIF","BDRF","DUST","DRWS","RSHE","FITM","BANA","ORBN","JAWZ","SSMN",
|
||||
"KRBM","BHOG","BHBM","SPBM","THNS","BUBS","BWVE",
|
||||
|
|
@ -69,8 +70,9 @@ char sprnames[NUMSPRITES + 1][5] =
|
|||
"ICEB","CNDL","DOCH","DUCK","GTRE","CHES","CHIM","DRGN","LZMN","PGSS",
|
||||
"ZTCH","MKMA","MKMP","RTCH","BOWL","BOWH","BRRL","BRRR","HRSE","TOAH",
|
||||
"BFRT","OFRT","RFRT","PFRT","ASPK","HBST","HBSO","HBSF","WBLZ","WBLN",
|
||||
|
||||
"FWRK","MXCL","RGSP","DRAF","GRES","OTFG","DBOS","EGOO","WTRL","XMS4",
|
||||
"XMS5","VIEW"
|
||||
"XMS5","FBUB","GCHA","CHEZ","VIEW"
|
||||
};
|
||||
|
||||
// Doesn't work with g++, needs actionf_p1 (don't modify this comment)
|
||||
|
|
@ -3461,6 +3463,37 @@ state_t states[NUMSTATES] =
|
|||
{SPR_SMOK, 3, 7, {NULL}, 0, 0, S_OPAQUESMOKE5}, // S_OPAQUESMOKE4
|
||||
{SPR_SMOK, 4, 8, {NULL}, 0, 0, S_NULL}, // S_OPAQUESMOKE5
|
||||
|
||||
|
||||
// followers:
|
||||
|
||||
// bubble
|
||||
{SPR_FBUB, 11|FF_ANIMATE|FF_TRANS70|FF_FULLBRIGHT, -1, {NULL}, 10, 3, S_FOLLOWERBUBBLE_FRONT}, // S_FOLLOWERBUBBLE_FRONT
|
||||
{SPR_FBUB, FF_ANIMATE|0|FF_FULLBRIGHT, -1, {NULL}, 10, 3, S_FOLLOWERBUBBLE_BACK}, // S_FOLLOWERBUBBLE_BACK
|
||||
|
||||
// generic chao:
|
||||
{SPR_GCHA, FF_ANIMATE, -1, {NULL}, 1, 4, S_GCHAOIDLE}, //S_GCHAOIDLE
|
||||
{SPR_GCHA, 2|FF_ANIMATE, -1, {NULL}, 1, 2, S_GCHAOFLY}, //S_GCHAOFLY
|
||||
{SPR_GCHA, 7, 5, {NULL}, 0, 0, S_GCHAOSAD2}, //S_GCHAOSAD1
|
||||
{SPR_GCHA, 8, 3, {NULL}, 0, 0, S_GCHAOSAD3}, //S_GCHAOSAD2
|
||||
{SPR_GCHA, 9, 6, {NULL}, 0, 0, S_GCHAOSAD4}, //S_GCHAOSAD3
|
||||
{SPR_GCHA, 8, 3, {NULL}, 0, 0, S_GCHAOSAD1}, //S_GCHAOSAD4
|
||||
{SPR_GCHA, 4, 8, {NULL}, 0, 0, S_GCHAOHAPPY2}, //S_GCHAOHAPPY1
|
||||
{SPR_GCHA, 5, 4, {NULL}, 0, 0, S_GCHAOHAPPY3}, //S_GCHAOHAPPY2
|
||||
{SPR_GCHA, 6, 8, {NULL}, 0, 0, S_GCHAOHAPPY4}, //S_GCHAOHAPPY3
|
||||
{SPR_GCHA, 5, 4, {NULL}, 0, 0, S_GCHAOHAPPY1}, //S_GCHAOHAPPY4
|
||||
|
||||
// cheese:
|
||||
{SPR_CHEZ, FF_ANIMATE, -1, {NULL}, 1, 4, S_CHEESEIDLE}, //S_CHEESEIDLE
|
||||
{SPR_CHEZ, 2|FF_ANIMATE, -1, {NULL}, 1, 2, S_CHEESEFLY}, //S_CHEESEFLY
|
||||
{SPR_CHEZ, 7, 5, {NULL}, 0, 0, S_CHEESESAD2}, //S_CHEESESAD1
|
||||
{SPR_CHEZ, 8, 3, {NULL}, 0, 0, S_CHEESESAD3}, //S_CHEESESAD2
|
||||
{SPR_CHEZ, 9, 6, {NULL}, 0, 0, S_CHEESESAD4}, //S_CHEESESAD3
|
||||
{SPR_CHEZ, 8, 3, {NULL}, 0, 0, S_CHEESESAD1}, //S_CHEESESAD4
|
||||
{SPR_CHEZ, 4, 8, {NULL}, 0, 0, S_CHEESEHAPPY2}, //S_CHEESEHAPPY1
|
||||
{SPR_CHEZ, 5, 4, {NULL}, 0, 0, S_CHEESEHAPPY3}, //S_CHEESEHAPPY2
|
||||
{SPR_CHEZ, 6, 8, {NULL}, 0, 0, S_CHEESEHAPPY4}, //S_CHEESEHAPPY3
|
||||
{SPR_CHEZ, 5, 4, {NULL}, 0, 0, S_CHEESEHAPPY1}, //S_CHEESEHAPPY4
|
||||
|
||||
{SPR_MXCL, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_RINGDEBT
|
||||
|
||||
{SPR_RGSP, FF_PAPERSPRITE|FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_RINGSPARKS2}, // S_RINGSPARKS1
|
||||
|
|
@ -20553,6 +20586,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
|||
S_NULL // raisestate
|
||||
},
|
||||
|
||||
|
||||
{ // MT_RINGSPARKS
|
||||
-1, // doomednum
|
||||
S_RINGSPARKS1, // spawnstate
|
||||
|
|
@ -20796,6 +20830,90 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
|||
S_NULL // raisestate
|
||||
},
|
||||
|
||||
{ // MT_FOLLOWER
|
||||
-1, // doomednum
|
||||
S_INVISIBLE, // spawnstate
|
||||
1000, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_None, // seesound
|
||||
8, // reactiontime
|
||||
sfx_None, // attacksound
|
||||
S_NULL, // painstate
|
||||
0, // painchance
|
||||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_NULL, // deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_None, // deathsound
|
||||
0, // speed
|
||||
8<<FRACBITS, // radius
|
||||
16<<FRACBITS, // height
|
||||
1, // display offset
|
||||
100, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
|
||||
MF_NOCLIPTHING|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT, // flags
|
||||
S_NULL, // raisestate
|
||||
},
|
||||
|
||||
{ // MT_FOLLOWERBUBBLE_FRONT
|
||||
-1, // doomednum
|
||||
S_FOLLOWERBUBBLE_FRONT, // spawnstate
|
||||
1000, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_None, // seesound
|
||||
8, // reactiontime
|
||||
sfx_None, // attacksound
|
||||
S_NULL, // painstate
|
||||
0, // painchance
|
||||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_NULL, // deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_None, // deathsound
|
||||
0, // speed
|
||||
8<<FRACBITS, // radius
|
||||
16<<FRACBITS, // height
|
||||
2, // display offset
|
||||
100, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
|
||||
MF_NOCLIPTHING|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT, // flags
|
||||
S_NULL, // raisestate
|
||||
},
|
||||
|
||||
{ // MT_FOLLOWERBUBBLE_BACK
|
||||
-1, // doomednum
|
||||
S_FOLLOWERBUBBLE_BACK, // spawnstate
|
||||
1000, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_None, // seesound
|
||||
8, // reactiontime
|
||||
sfx_None, // attacksound
|
||||
S_NULL, // painstate
|
||||
0, // painchance
|
||||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_NULL, // deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_None, // deathsound
|
||||
0, // speed
|
||||
8<<FRACBITS, // radius
|
||||
16<<FRACBITS, // height
|
||||
-2, // display offset
|
||||
100, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
|
||||
MF_NOCLIPTHING|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT, // flags
|
||||
S_NULL, // raisestate
|
||||
},
|
||||
|
||||
{ // MT_WATERTRAIL
|
||||
-1, // doomednum
|
||||
S_WATERTRAIL1, // spawnstate
|
||||
|
|
|
|||
38
src/info.h
38
src/info.h
|
|
@ -804,6 +804,10 @@ typedef enum sprite
|
|||
SPR_XMS4,
|
||||
SPR_XMS5,
|
||||
|
||||
SPR_FBUB, // follower bubble
|
||||
SPR_GCHA, // follower: generic chao
|
||||
SPR_CHEZ, // follower: cheese
|
||||
|
||||
// First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later
|
||||
SPR_VIEW,
|
||||
|
||||
|
|
@ -4152,6 +4156,36 @@ typedef enum state
|
|||
S_OPAQUESMOKE4,
|
||||
S_OPAQUESMOKE5,
|
||||
|
||||
// followers:
|
||||
|
||||
// bubble:
|
||||
S_FOLLOWERBUBBLE_FRONT,
|
||||
S_FOLLOWERBUBBLE_BACK,
|
||||
|
||||
// generic chao:
|
||||
S_GCHAOIDLE,
|
||||
S_GCHAOFLY,
|
||||
S_GCHAOSAD1,
|
||||
S_GCHAOSAD2,
|
||||
S_GCHAOSAD3,
|
||||
S_GCHAOSAD4,
|
||||
S_GCHAOHAPPY1,
|
||||
S_GCHAOHAPPY2,
|
||||
S_GCHAOHAPPY3,
|
||||
S_GCHAOHAPPY4,
|
||||
|
||||
// cheese:
|
||||
S_CHEESEIDLE,
|
||||
S_CHEESEFLY,
|
||||
S_CHEESESAD1,
|
||||
S_CHEESESAD2,
|
||||
S_CHEESESAD3,
|
||||
S_CHEESESAD4,
|
||||
S_CHEESEHAPPY1,
|
||||
S_CHEESEHAPPY2,
|
||||
S_CHEESEHAPPY3,
|
||||
S_CHEESEHAPPY4,
|
||||
|
||||
S_RINGDEBT,
|
||||
S_RINGSPARKS1,
|
||||
S_RINGSPARKS2,
|
||||
|
|
@ -5036,6 +5070,10 @@ typedef enum mobj_type
|
|||
MT_BATTLECAPSULE,
|
||||
MT_BATTLECAPSULE_PIECE,
|
||||
|
||||
MT_FOLLOWER,
|
||||
MT_FOLLOWERBUBBLE_FRONT,
|
||||
MT_FOLLOWERBUBBLE_BACK,
|
||||
|
||||
MT_WATERTRAIL,
|
||||
MT_WATERTRAILUNDERLAY,
|
||||
|
||||
|
|
|
|||
23
src/k_kart.c
23
src/k_kart.c
|
|
@ -1573,6 +1573,22 @@ void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master)
|
|||
mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP4)|(master->eflags & MFE_DRAWONLYFORP4);
|
||||
}
|
||||
|
||||
// same as above, but does not adjust Z height when flipping
|
||||
void K_GenericExtraFlagsNoZAdjust(mobj_t *mo, mobj_t *master)
|
||||
{
|
||||
// flipping
|
||||
mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP)|(master->eflags & MFE_VERTICALFLIP);
|
||||
mo->flags2 = (mo->flags2 & ~MF2_OBJECTFLIP)|(master->flags2 & MF2_OBJECTFLIP);
|
||||
|
||||
// visibility (usually for hyudoro)
|
||||
mo->flags2 = (mo->flags2 & ~MF2_DONTDRAW)|(master->flags2 & MF2_DONTDRAW);
|
||||
mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP1)|(master->eflags & MFE_DRAWONLYFORP1);
|
||||
mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP2)|(master->eflags & MFE_DRAWONLYFORP2);
|
||||
mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP3)|(master->eflags & MFE_DRAWONLYFORP3);
|
||||
mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP4)|(master->eflags & MFE_DRAWONLYFORP4);
|
||||
}
|
||||
|
||||
|
||||
void K_SpawnDashDustRelease(player_t *player)
|
||||
{
|
||||
fixed_t newx;
|
||||
|
|
@ -1899,6 +1915,13 @@ void K_PlayPainSound(mobj_t *source)
|
|||
|
||||
void K_PlayHitEmSound(mobj_t *source)
|
||||
{
|
||||
|
||||
if (source->player->follower)
|
||||
{
|
||||
follower_t fl = followers[source->player->followerskin];
|
||||
source->player->follower->movecount = fl.hitconfirmtime; // movecount is used to play the hitconfirm animation for followers.
|
||||
}
|
||||
|
||||
if (cv_kartvoices.value)
|
||||
S_StartSound(source, sfx_khitem);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid)
|
|||
void K_KartPainEnergyFling(player_t *player);
|
||||
void K_FlipFromObject(mobj_t *mo, mobj_t *master);
|
||||
void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master);
|
||||
void K_GenericExtraFlagsNoZAdjust(mobj_t *mo, mobj_t *master);
|
||||
void K_SpawnDashDustRelease(player_t *player);
|
||||
void K_KartMoveAnimation(player_t *player);
|
||||
void K_KartPlayerHUDUpdate(player_t *player);
|
||||
|
|
|
|||
|
|
@ -239,6 +239,14 @@ static int player_get(lua_State *L)
|
|||
lua_pushinteger(L, plr->kartspeed);
|
||||
else if (fastcmp(field,"kartweight"))
|
||||
lua_pushinteger(L, plr->kartweight);
|
||||
else if (fastcmp(field,"followerskin"))
|
||||
lua_pushinteger(L, plr->followerskin);
|
||||
else if (fastcmp(field,"followerready"))
|
||||
lua_pushboolean(L, plr->followerready);
|
||||
else if (fastcmp(field,"followercolor"))
|
||||
lua_pushinteger(L, plr->followercolor);
|
||||
else if (fastcmp(field,"follower"))
|
||||
LUA_PushUserdata(L, plr->follower, META_MOBJ);
|
||||
//
|
||||
else if (fastcmp(field,"charflags"))
|
||||
lua_pushinteger(L, plr->charflags);
|
||||
|
|
@ -368,6 +376,7 @@ static int player_get(lua_State *L)
|
|||
lua_pushinteger(L, plr->awayviewtics);
|
||||
else if (fastcmp(field,"awayviewaiming"))
|
||||
lua_pushangle(L, plr->awayviewaiming);
|
||||
|
||||
else if (fastcmp(field,"spectator"))
|
||||
lua_pushboolean(L, plr->spectator);
|
||||
else if (fastcmp(field,"bot"))
|
||||
|
|
@ -483,6 +492,14 @@ static int player_set(lua_State *L)
|
|||
plr->kartspeed = (UINT8)luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"kartweight"))
|
||||
plr->kartweight = (UINT8)luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"followerskin"))
|
||||
plr->followerskin = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"followercolor"))
|
||||
plr->followercolor = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"followerready"))
|
||||
plr->followerready = luaL_checkboolean(L, 3);
|
||||
else if (fastcmp(field,"follower")) // it's probably best we don't allow the follower mobj to change.
|
||||
return NOSET;
|
||||
//
|
||||
else if (fastcmp(field,"charflags"))
|
||||
plr->charflags = (UINT32)luaL_checkinteger(L, 3);
|
||||
|
|
|
|||
217
src/m_menu.c
217
src/m_menu.c
|
|
@ -158,6 +158,7 @@ INT16 startmap; // Mario, NiGHTS, or just a plain old normal game?
|
|||
|
||||
static INT16 itemOn = 1; // menu item skull is on, Hack by Tails 09-18-2002
|
||||
static INT16 skullAnimCounter = 10; // skull animation counter
|
||||
static tic_t followertimer = 0; // Used for smooth follower floating
|
||||
|
||||
static UINT8 setupcontrolplayer;
|
||||
static INT32 (*setupcontrols)[2]; // pointer to the gamecontrols of the player being edited
|
||||
|
|
@ -1032,6 +1033,7 @@ static menuitem_t MP_PlayerSetupMenu[] =
|
|||
{
|
||||
{IT_KEYHANDLER | IT_STRING, NULL, "Name", M_HandleSetupMultiPlayer, 0},
|
||||
{IT_KEYHANDLER | IT_STRING, NULL, "Character", M_HandleSetupMultiPlayer, 16}, // Tails 01-18-2001
|
||||
{IT_KEYHANDLER | IT_STRING, NULL, "Follower", M_HandleSetupMultiPlayer, 26},
|
||||
{IT_KEYHANDLER | IT_STRING, NULL, "Color", M_HandleSetupMultiPlayer, 152},
|
||||
};
|
||||
|
||||
|
|
@ -1430,24 +1432,27 @@ enum
|
|||
|
||||
static menuitem_t OP_HUDOptionsMenu[] =
|
||||
{
|
||||
{IT_STRING | IT_CVAR, NULL, "Show HUD (F3)", &cv_showhud, 10},
|
||||
{IT_STRING | IT_CVAR | IT_CV_SLIDER,
|
||||
NULL, "HUD Visibility", &cv_translucenthud, 20},
|
||||
|
||||
{IT_STRING | IT_SUBMENU, NULL, "Online HUD options...",&OP_ChatOptionsDef, 35},
|
||||
{IT_STRING | IT_CVAR, NULL, "Background Glass", &cons_backcolor, 45},
|
||||
{IT_STRING | IT_CVAR, NULL, "Show Followers", &cv_showfollowers, 10},
|
||||
|
||||
{IT_STRING | IT_CVAR, NULL, "Show HUD (F3)", &cv_showhud, 20},
|
||||
{IT_STRING | IT_CVAR | IT_CV_SLIDER,
|
||||
NULL, "HUD Visibility", &cv_translucenthud, 30},
|
||||
|
||||
{IT_STRING | IT_SUBMENU, NULL, "Online HUD options...",&OP_ChatOptionsDef, 45},
|
||||
{IT_STRING | IT_CVAR, NULL, "Background Glass", &cons_backcolor, 55},
|
||||
|
||||
{IT_STRING | IT_CVAR | IT_CV_SLIDER,
|
||||
NULL, "Minimap Visibility", &cv_kartminimap, 60},
|
||||
{IT_STRING | IT_CVAR, NULL, "Speedometer Display", &cv_kartspeedometer, 70},
|
||||
{IT_STRING | IT_CVAR, NULL, "Show \"CHECK\"", &cv_kartcheck, 80},
|
||||
NULL, "Minimap Visibility", &cv_kartminimap, 70},
|
||||
{IT_STRING | IT_CVAR, NULL, "Speedometer Display", &cv_kartspeedometer, 80},
|
||||
{IT_STRING | IT_CVAR, NULL, "Show \"CHECK\"", &cv_kartcheck, 90},
|
||||
|
||||
{IT_STRING | IT_CVAR, NULL, "Menu Highlights", &cons_menuhighlight, 95},
|
||||
{IT_STRING | IT_CVAR, NULL, "Menu Highlights", &cons_menuhighlight, 105},
|
||||
// highlight info - (GOOD HIGHLIGHT, WARNING HIGHLIGHT) - 105 (see M_DrawHUDOptions)
|
||||
|
||||
{IT_STRING | IT_CVAR, NULL, "Console Text Size", &cv_constextsize, 120},
|
||||
{IT_STRING | IT_CVAR, NULL, "Console Text Size", &cv_constextsize, 130},
|
||||
|
||||
{IT_STRING | IT_CVAR, NULL, "Show \"FOCUS LOST\"", &cv_showfocuslost, 135},
|
||||
{IT_STRING | IT_CVAR, NULL, "Show \"FOCUS LOST\"", &cv_showfocuslost, 145},
|
||||
};
|
||||
|
||||
// Ok it's still called chatoptions but we'll put ping display in here to be clean
|
||||
|
|
@ -3336,6 +3341,8 @@ void M_Ticker(void)
|
|||
if (--skullAnimCounter <= 0)
|
||||
skullAnimCounter = 8;
|
||||
|
||||
followertimer++;
|
||||
|
||||
if (currentMenu == &PlaybackMenuDef)
|
||||
{
|
||||
if (playback_enterheld > 0)
|
||||
|
|
@ -9312,9 +9319,15 @@ static void M_HandleConnectIP(INT32 choice)
|
|||
// ========================
|
||||
// Tails 03-02-2002
|
||||
|
||||
// used for skin display on player setup menu
|
||||
static INT32 multi_tics;
|
||||
static state_t *multi_state;
|
||||
|
||||
// used for follower display on player setup menu
|
||||
static INT32 follower_tics;
|
||||
static UINT32 follower_frame; // used for FF_ANIMATE garbo
|
||||
static state_t *follower_state;
|
||||
|
||||
// this is set before entering the MultiPlayer setup menu,
|
||||
// for either player 1 or 2
|
||||
static char setupm_name[MAXPLAYERNAME+1];
|
||||
|
|
@ -9322,8 +9335,10 @@ static player_t *setupm_player;
|
|||
static consvar_t *setupm_cvskin;
|
||||
static consvar_t *setupm_cvcolor;
|
||||
static consvar_t *setupm_cvname;
|
||||
static consvar_t *setupm_cvfollower;
|
||||
static INT32 setupm_fakeskin;
|
||||
static INT32 setupm_fakecolor;
|
||||
static INT32 setupm_fakefollower; // -1 is for none, our followers start at 0
|
||||
|
||||
static void M_DrawSetupMultiPlayerMenu(void)
|
||||
{
|
||||
|
|
@ -9341,6 +9356,7 @@ static void M_DrawSetupMultiPlayerMenu(void)
|
|||
UINT8 i;
|
||||
const UINT8 *flashcol = V_GetStringColormap(highlightflags);
|
||||
INT32 statx, staty;
|
||||
char *fname;
|
||||
|
||||
mx = MP_PlayerSetupDef.x;
|
||||
my = MP_PlayerSetupDef.y;
|
||||
|
|
@ -9372,11 +9388,31 @@ static void M_DrawSetupMultiPlayerMenu(void)
|
|||
'\x1D' | highlightflags, false); // right arrow
|
||||
}
|
||||
|
||||
// draw follower string
|
||||
fname = malloc(SKINNAMESIZE+1);
|
||||
|
||||
if (setupm_fakefollower == -1)
|
||||
strcpy(fname, "None");
|
||||
else
|
||||
strcpy(fname, followers[setupm_fakefollower].name);
|
||||
|
||||
st = V_StringWidth(fname, 0);
|
||||
V_DrawString(BASEVIDWIDTH - mx - st, my + 26,
|
||||
((MP_PlayerSetupMenu[2].status & IT_TYPE) == IT_SPACE ? V_TRANSLUCENT : 0)|highlightflags|V_ALLOWLOWERCASE,
|
||||
fname);
|
||||
if (itemOn == 2)
|
||||
{
|
||||
V_DrawCharacter(BASEVIDWIDTH - mx - 10 - st - (skullAnimCounter/5), my + 26,
|
||||
'\x1C' | highlightflags, false); // left arrow
|
||||
V_DrawCharacter(BASEVIDWIDTH - mx + 2 + (skullAnimCounter/5), my + 26,
|
||||
'\x1D' | highlightflags, false); // right arrow
|
||||
}
|
||||
|
||||
// draw the name of the color you have chosen
|
||||
// Just so people don't go thinking that "Default" is Green.
|
||||
st = V_StringWidth(KartColor_Names[setupm_fakecolor], 0);
|
||||
V_DrawString(BASEVIDWIDTH - mx - st, my + 152, highlightflags|V_ALLOWLOWERCASE, KartColor_Names[setupm_fakecolor]); // SRB2kart
|
||||
if (itemOn == 2)
|
||||
if (itemOn == 3)
|
||||
{
|
||||
V_DrawCharacter(BASEVIDWIDTH - mx - 10 - st - (skullAnimCounter/5), my + 152,
|
||||
'\x1C' | highlightflags, false); // left arrow
|
||||
|
|
@ -9524,7 +9560,7 @@ static void M_DrawSetupMultiPlayerMenu(void)
|
|||
|
||||
sprframe = &sprdef->spriteframes[frame];
|
||||
patch = W_CachePatchNum(sprframe->lumppat[1], PU_CACHE);
|
||||
if (sprframe->flip & 1) // Only for first sprite
|
||||
if (sprframe->flip & 2) // Only for first sprite
|
||||
flags |= V_FLIP; // This sprite is left/right flipped!
|
||||
|
||||
// draw box around guy
|
||||
|
|
@ -9545,9 +9581,88 @@ static void M_DrawSetupMultiPlayerMenu(void)
|
|||
else
|
||||
V_DrawMappedPatch(mx+43, my+131, flags, patch, colormap);
|
||||
}
|
||||
|
||||
// draw their follower if there is one
|
||||
if (setupm_fakefollower > -1 && setupm_fakefollower < numfollowers)
|
||||
{
|
||||
// animate the follower
|
||||
|
||||
if (--follower_tics <= 0)
|
||||
{
|
||||
|
||||
// FF_ANIMATE; cycle through FRAMES and get back afterwards. This will be prominent amongst followers hence why it's being supported here.
|
||||
if (follower_state->frame & FF_ANIMATE)
|
||||
{
|
||||
follower_frame++;
|
||||
follower_tics = follower_state->var2;
|
||||
if (follower_frame > (follower_state->frame & FF_FRAMEMASK) + follower_state->var1) // that's how it works, right?
|
||||
follower_frame = follower_state->frame & FF_FRAMEMASK;
|
||||
}
|
||||
else
|
||||
{
|
||||
st = follower_state->nextstate;
|
||||
if (st != S_NULL)
|
||||
follower_state = &states[st];
|
||||
follower_tics = follower_state->tics;
|
||||
if (follower_tics == -1)
|
||||
follower_tics = 15; // er, what?
|
||||
// get spritedef:
|
||||
follower_frame = follower_state->frame & FF_FRAMEMASK;
|
||||
}
|
||||
}
|
||||
sprdef = &sprites[follower_state->sprite];
|
||||
|
||||
// draw the follower
|
||||
|
||||
if (follower_frame >= sprdef->numframes)
|
||||
follower_frame = 0; // frame doesn't exist, we went beyond it... what?
|
||||
sprframe = &sprdef->spriteframes[follower_frame];
|
||||
patch = W_CachePatchNum(sprframe->lumppat[1], PU_CACHE);
|
||||
if (sprframe->flip & 2) // Only for first sprite
|
||||
flags |= V_FLIP; // This sprite is left/right flipped!
|
||||
|
||||
// @TODO: Reminder that followers on the menu right now do NOT support the 'followercolor' command, considering this whole menu is getting remade anyway, I see no point in incorporating it in right now.
|
||||
|
||||
// draw follower sprite
|
||||
if (setupm_fakecolor) // inverse should never happen
|
||||
{
|
||||
|
||||
// Fake the follower's in game appearance by now also applying some of its variables! coolio, eh?
|
||||
follower_t fl = followers[setupm_fakefollower]; // shortcut for our sanity
|
||||
// smooth floating, totally not stolen from rocket sneakers.
|
||||
const fixed_t pi = (22<<FRACBITS) / 7; // loose approximation, this doesn't need to be incredibly precise
|
||||
fixed_t sine = fl.bobamp * FINESINE((((8*pi*(fl.bobspeed)) * followertimer)>>ANGLETOFINESHIFT) & FINEMASK);
|
||||
|
||||
UINT8 *colormap = R_GetTranslationColormap(-1, setupm_fakecolor, 0);
|
||||
V_DrawFixedPatch((mx+65)*FRACUNIT, (my+131-fl.zoffs)*FRACUNIT+sine, fl.scale, flags, patch, colormap);
|
||||
Z_Free(colormap);
|
||||
}
|
||||
}
|
||||
|
||||
#undef charw
|
||||
}
|
||||
|
||||
// follower state update. This is its own function so that it's at least somewhat clean
|
||||
static void M_GetFollowerState(void)
|
||||
{
|
||||
|
||||
if (setupm_fakefollower <= -1 || setupm_fakefollower > numfollowers-1) // yikes, there's none!
|
||||
return;
|
||||
// ^ we don't actually need to set anything since it won't be displayed anyway.
|
||||
|
||||
//followertimer = 0; // reset timer. not like it'll overflow anytime soon but whatever.
|
||||
|
||||
// set follower state
|
||||
follower_state = &states[followers[setupm_fakefollower].followstate];
|
||||
|
||||
if (follower_state->frame & FF_ANIMATE)
|
||||
follower_tics = follower_state->var2; // support for FF_ANIMATE
|
||||
else
|
||||
follower_tics = follower_state->tics;
|
||||
|
||||
follower_frame = follower_state->frame & FF_FRAMEMASK;
|
||||
}
|
||||
|
||||
// Handle 1P/2P MP Setup
|
||||
static void M_HandleSetupMultiPlayer(INT32 choice)
|
||||
{
|
||||
|
|
@ -9575,7 +9690,13 @@ static void M_HandleSetupMultiPlayer(INT32 choice)
|
|||
S_StartSound(NULL,sfx_menu1); // Tails
|
||||
setupm_fakeskin--;
|
||||
}
|
||||
else if (itemOn == 2) // player color
|
||||
else if (itemOn == 2) // follower
|
||||
{
|
||||
S_StartSound(NULL,sfx_menu1);
|
||||
setupm_fakefollower--;
|
||||
M_GetFollowerState(); // update follower state
|
||||
}
|
||||
else if (itemOn == 3) // player color
|
||||
{
|
||||
S_StartSound(NULL,sfx_menu1); // Tails
|
||||
setupm_fakecolor--;
|
||||
|
|
@ -9587,8 +9708,15 @@ static void M_HandleSetupMultiPlayer(INT32 choice)
|
|||
{
|
||||
S_StartSound(NULL,sfx_menu1); // Tails
|
||||
setupm_fakeskin++;
|
||||
M_GetFollowerState(); // update follower state
|
||||
}
|
||||
else if (itemOn == 2) // player color
|
||||
else if (itemOn == 2) // follower
|
||||
{
|
||||
S_StartSound(NULL,sfx_menu1);
|
||||
setupm_fakefollower++;
|
||||
M_GetFollowerState();
|
||||
}
|
||||
else if (itemOn == 3) // player color
|
||||
{
|
||||
S_StartSound(NULL,sfx_menu1); // Tails
|
||||
setupm_fakecolor++;
|
||||
|
|
@ -9608,7 +9736,12 @@ static void M_HandleSetupMultiPlayer(INT32 choice)
|
|||
setupm_name[l-1] =0;
|
||||
}
|
||||
}
|
||||
else if (itemOn == 2)
|
||||
else if (itemOn == 2) // follower
|
||||
{
|
||||
S_StartSound(NULL,sfx_menu1);
|
||||
setupm_fakefollower = -1;
|
||||
}
|
||||
else if (itemOn == 3)
|
||||
{
|
||||
UINT8 col = skins[setupm_fakeskin].prefcolor;
|
||||
if (setupm_fakecolor != col)
|
||||
|
|
@ -9646,6 +9779,18 @@ static void M_HandleSetupMultiPlayer(INT32 choice)
|
|||
if (setupm_fakeskin > numskins-1)
|
||||
setupm_fakeskin = 0;
|
||||
|
||||
// check followers:
|
||||
if (setupm_fakefollower < -1)
|
||||
{
|
||||
setupm_fakefollower = numfollowers-1;
|
||||
M_GetFollowerState(); // update follower state
|
||||
}
|
||||
if (setupm_fakefollower > numfollowers-1)
|
||||
{
|
||||
setupm_fakefollower = -1;
|
||||
M_GetFollowerState(); // update follower state
|
||||
}
|
||||
|
||||
// check color
|
||||
if (setupm_fakecolor < 1)
|
||||
setupm_fakecolor = MAXSKINCOLORS-1;
|
||||
|
|
@ -9668,6 +9813,7 @@ static void M_SetupMultiPlayer(INT32 choice)
|
|||
|
||||
multi_state = &states[mobjinfo[MT_PLAYER].seestate];
|
||||
multi_tics = multi_state->tics;
|
||||
|
||||
strcpy(setupm_name, cv_playername.string);
|
||||
|
||||
// set for player 1
|
||||
|
|
@ -9675,6 +9821,15 @@ static void M_SetupMultiPlayer(INT32 choice)
|
|||
setupm_cvskin = &cv_skin;
|
||||
setupm_cvcolor = &cv_playercolor;
|
||||
setupm_cvname = &cv_playername;
|
||||
setupm_cvfollower = &cv_follower;
|
||||
|
||||
setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value
|
||||
|
||||
// yikes, we don't want none of that...
|
||||
if (setupm_fakefollower > numfollowers-1)
|
||||
setupm_fakefollower = -1;
|
||||
|
||||
M_GetFollowerState(); // update follower state
|
||||
|
||||
// For whatever reason this doesn't work right if you just use ->value
|
||||
setupm_fakeskin = R_SkinAvailable(setupm_cvskin->string);
|
||||
|
|
@ -9706,6 +9861,15 @@ static void M_SetupMultiPlayer2(INT32 choice)
|
|||
setupm_cvskin = &cv_skin2;
|
||||
setupm_cvcolor = &cv_playercolor2;
|
||||
setupm_cvname = &cv_playername2;
|
||||
setupm_cvfollower = &cv_follower2;
|
||||
|
||||
setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value
|
||||
|
||||
// yikes, we don't want none of that...
|
||||
if (setupm_fakefollower > numfollowers-1)
|
||||
setupm_fakefollower = -1;
|
||||
|
||||
M_GetFollowerState(); // update follower state
|
||||
|
||||
// For whatever reason this doesn't work right if you just use ->value
|
||||
setupm_fakeskin = R_SkinAvailable(setupm_cvskin->string);
|
||||
|
|
@ -9737,6 +9901,15 @@ static void M_SetupMultiPlayer3(INT32 choice)
|
|||
setupm_cvskin = &cv_skin3;
|
||||
setupm_cvcolor = &cv_playercolor3;
|
||||
setupm_cvname = &cv_playername3;
|
||||
setupm_cvfollower = &cv_follower3;
|
||||
|
||||
setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value
|
||||
|
||||
// yikes, we don't want none of that...
|
||||
if (setupm_fakefollower > numfollowers-1)
|
||||
setupm_fakefollower = -1;
|
||||
|
||||
M_GetFollowerState(); // update follower state
|
||||
|
||||
// For whatever reason this doesn't work right if you just use ->value
|
||||
setupm_fakeskin = R_SkinAvailable(setupm_cvskin->string);
|
||||
|
|
@ -9768,6 +9941,15 @@ static void M_SetupMultiPlayer4(INT32 choice)
|
|||
setupm_cvskin = &cv_skin4;
|
||||
setupm_cvcolor = &cv_playercolor4;
|
||||
setupm_cvname = &cv_playername4;
|
||||
setupm_cvfollower = &cv_follower4;
|
||||
|
||||
setupm_fakefollower = atoi(setupm_cvfollower->string); // update fake follower value
|
||||
|
||||
// yikes, we don't want none of that...
|
||||
if (setupm_fakefollower > numfollowers-1)
|
||||
setupm_fakefollower = -1;
|
||||
|
||||
M_GetFollowerState(); // update follower state
|
||||
|
||||
// For whatever reason this doesn't work right if you just use ->value
|
||||
setupm_fakeskin = R_SkinAvailable(setupm_cvskin->string);
|
||||
|
|
@ -9800,6 +9982,7 @@ static boolean M_QuitMultiPlayerMenu(void)
|
|||
// you know what? always putting these in the buffer won't hurt anything.
|
||||
COM_BufAddText (va("%s \"%s\"\n",setupm_cvskin->name,skins[setupm_fakeskin].name));
|
||||
COM_BufAddText (va("%s %d\n",setupm_cvcolor->name,setupm_fakecolor));
|
||||
COM_BufAddText (va("%s %d\n",setupm_cvfollower->name,setupm_fakefollower));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -10683,7 +10866,7 @@ static void M_DrawHUDOptions(void)
|
|||
const char *str1 = " Warning highlight";
|
||||
const char *str2 = ",";
|
||||
const char *str3 = "Good highlight";
|
||||
INT32 x = BASEVIDWIDTH - currentMenu->x + 2, y = currentMenu->y + 105;
|
||||
INT32 x = BASEVIDWIDTH - currentMenu->x + 2, y = currentMenu->y + 115;
|
||||
INT32 w0 = V_StringWidth(str0, 0), w1 = V_StringWidth(str1, 0), w2 = V_StringWidth(str2, 0), w3 = V_StringWidth(str3, 0);
|
||||
|
||||
M_DrawGenericMenu();
|
||||
|
|
|
|||
23
src/p_mobj.c
23
src/p_mobj.c
|
|
@ -6495,6 +6495,27 @@ void P_MobjThinker(mobj_t *mobj)
|
|||
#endif
|
||||
switch (mobj->type)
|
||||
{
|
||||
case MT_FOLLOWER:
|
||||
// small thinker for follower:
|
||||
// We cleanse ourselves from existence if our target player doesn't exist for whatever reason. (generally players leaving)
|
||||
if (!mobj->target || P_MobjWasRemoved(mobj->target) || !mobj->target->player || mobj->target->player->spectator || mobj->target->player->followerskin < 0)
|
||||
{
|
||||
// Remove possible hnext list (bubble)
|
||||
mobj_t *bub = mobj->hnext;
|
||||
mobj_t *tmp;
|
||||
|
||||
while (bub && !P_MobjWasRemoved(bub))
|
||||
{
|
||||
tmp = bub->hnext;
|
||||
P_RemoveMobj(bub);
|
||||
bub = tmp;
|
||||
}
|
||||
|
||||
P_RemoveMobj(mobj);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
case MT_HOOP:
|
||||
if (mobj->fuse > 1)
|
||||
P_MoveHoop(mobj);
|
||||
|
|
@ -11870,6 +11891,8 @@ void P_SpawnPlayer(INT32 playernum)
|
|||
p->awayviewmobj = NULL;
|
||||
p->awayviewtics = 0;
|
||||
|
||||
P_SetTarget(&p->follower, NULL); // cleanse follower from existence
|
||||
|
||||
// set the scale to the mobj's destscale so settings get correctly set. if we don't, they sometimes don't.
|
||||
if (cv_kartdebugshrink.value && !modeattacking && !p->bot)
|
||||
mobj->destscale = 6*mobj->destscale/8;
|
||||
|
|
|
|||
|
|
@ -57,10 +57,11 @@ typedef enum
|
|||
{
|
||||
// RFLAGPOINT = 0x01,
|
||||
// BFLAGPOINT = 0x02,
|
||||
CAPSULE = 0x04,
|
||||
AWAYVIEW = 0x08,
|
||||
FIRSTAXIS = 0x10,
|
||||
SECONDAXIS = 0x20,
|
||||
CAPSULE = 4,
|
||||
AWAYVIEW = 8,
|
||||
FIRSTAXIS = 16,
|
||||
SECONDAXIS = 32,
|
||||
FOLLOWER = 64,
|
||||
} player_saveflags;
|
||||
|
||||
//
|
||||
|
|
@ -218,6 +219,9 @@ static void P_NetArchivePlayers(void)
|
|||
if (players[i].axis2)
|
||||
flags |= SECONDAXIS;
|
||||
|
||||
if (players[i].follower)
|
||||
flags |= FOLLOWER;
|
||||
|
||||
WRITEINT16(save_p, players[i].lastsidehit);
|
||||
WRITEINT16(save_p, players[i].lastlinehit);
|
||||
|
||||
|
|
@ -249,6 +253,14 @@ static void P_NetArchivePlayers(void)
|
|||
// SRB2kart
|
||||
WRITEUINT8(save_p, players[i].kartspeed);
|
||||
WRITEUINT8(save_p, players[i].kartweight);
|
||||
|
||||
WRITEUINT8(save_p, players[i].followerskin);
|
||||
WRITEUINT8(save_p, players[i].followerready); // booleans are really just numbers eh??
|
||||
WRITEUINT8(save_p, players[i].followercolor);
|
||||
if (flags & FOLLOWER)
|
||||
WRITEUINT32(save_p, players[i].follower->mobjnum);
|
||||
|
||||
|
||||
//
|
||||
|
||||
for (j = 0; j < NUMKARTSTUFF; j++)
|
||||
|
|
@ -435,6 +447,13 @@ static void P_NetUnArchivePlayers(void)
|
|||
// SRB2kart
|
||||
players[i].kartspeed = READUINT8(save_p);
|
||||
players[i].kartweight = READUINT8(save_p);
|
||||
|
||||
players[i].followerskin = READUINT8(save_p);
|
||||
players[i].followerready = READUINT8(save_p);
|
||||
players[i].followercolor = READUINT8(save_p);
|
||||
if (flags & FOLLOWER)
|
||||
players[i].follower = (mobj_t *)(size_t)READUINT32(save_p);
|
||||
|
||||
//
|
||||
|
||||
for (j = 0; j < NUMKARTSTUFF; j++)
|
||||
|
|
@ -3130,6 +3149,7 @@ static void P_RelinkPointers(void)
|
|||
if (!(mobj->itnext = P_FindNewPosition(temp)))
|
||||
CONS_Debug(DBG_GAMELOGIC, "itnext not found on %d\n", mobj->type);
|
||||
}
|
||||
|
||||
if (mobj->player)
|
||||
{
|
||||
if (mobj->player->capsule)
|
||||
|
|
@ -3160,6 +3180,13 @@ static void P_RelinkPointers(void)
|
|||
if (!P_SetTarget(&mobj->player->awayviewmobj, P_FindNewPosition(temp)))
|
||||
CONS_Debug(DBG_GAMELOGIC, "awayviewmobj not found on %d\n", mobj->type);
|
||||
}
|
||||
if (mobj->player->follower)
|
||||
{
|
||||
temp = (UINT32)(size_t)mobj->player->follower;
|
||||
mobj->player->follower = NULL;
|
||||
if (!P_SetTarget(&mobj->player->follower, P_FindNewPosition(temp)))
|
||||
CONS_Debug(DBG_GAMELOGIC, "follower not found on %d\n", mobj->type);
|
||||
}
|
||||
if (mobj->player->nextwaypoint)
|
||||
{
|
||||
temp = (UINT32)(size_t)mobj->player->nextwaypoint;
|
||||
|
|
|
|||
|
|
@ -2460,6 +2460,9 @@ static void P_LevelInitStuff(void)
|
|||
|
||||
// and this stupid flag as a result
|
||||
players[i].pflags &= ~PF_TRANSFERTOCLOSEST;
|
||||
|
||||
// Wipe follower from existence to avoid crashes
|
||||
players[i].follower = NULL;
|
||||
}
|
||||
|
||||
// SRB2Kart: map load variables
|
||||
|
|
|
|||
283
src/p_user.c
283
src/p_user.c
|
|
@ -45,6 +45,7 @@
|
|||
// SRB2kart
|
||||
#include "m_cond.h" // M_UpdateUnlockablesAndExtraEmblems
|
||||
#include "k_kart.h"
|
||||
#include "k_color.h" // KartColor_Opposite
|
||||
#include "console.h" // CON_LogMessage
|
||||
#include "k_respawn.h"
|
||||
#include "k_bot.h"
|
||||
|
|
@ -8407,6 +8408,285 @@ void P_DoTimeOver(player_t *player)
|
|||
exitcountdown = 5*TICRATE;
|
||||
}
|
||||
|
||||
/* set follower state with our weird hacks
|
||||
the reason we do this is to avoid followers ever using actions (majormods, yikes!)
|
||||
without having to touch p_mobj.c.
|
||||
so we give it 1more tic and change the state when tic == 1 instead of 0
|
||||
cool beans?
|
||||
cool beans.
|
||||
*/
|
||||
static void P_SetFollowerState(mobj_t *f, INT32 state)
|
||||
{
|
||||
|
||||
if (!f || P_MobjWasRemoved(f))
|
||||
return; // safety net
|
||||
|
||||
// No, do NOT set the follower to S_NULL. Set it to S_INVISIBLE.
|
||||
if (state == S_NULL)
|
||||
{
|
||||
state = S_INVISIBLE;
|
||||
f->threshold = 1; // Threshold = 1 means stop doing anything related to setting states, so that we don't get out of S_INVISIBLE
|
||||
}
|
||||
|
||||
// extravalue2 stores the last "first state" we used.
|
||||
// because states default to idlestates, if we use an animation that uses an "ongoing" state line, don't reset it!
|
||||
// this prevents it from looking very dumb
|
||||
if (state == f->extravalue2)
|
||||
return;
|
||||
|
||||
// we will save the state into extravalue2.
|
||||
f->extravalue2 = state;
|
||||
|
||||
P_SetMobjStateNF(f, state);
|
||||
if (f->state->tics > 0)
|
||||
f->tics++;
|
||||
}
|
||||
|
||||
//
|
||||
//P_HandleFollower
|
||||
//
|
||||
//Handle the follower's spawning and moving along with the player. Do note that some of the stuff like the removal if a player doesn't exist anymore is handled in MT_FOLLOWER's thinker.
|
||||
static void P_HandleFollower(player_t *player)
|
||||
{
|
||||
follower_t fl;
|
||||
angle_t an;
|
||||
fixed_t zoffs;
|
||||
fixed_t sx, sy, sz;
|
||||
UINT8 color;
|
||||
|
||||
fixed_t bubble; // bubble scale (0 if no bubble)
|
||||
mobj_t *bmobj; // temp bubble mobj
|
||||
|
||||
|
||||
if (!player->followerready)
|
||||
return; // we aren't ready to perform anything follower related yet.
|
||||
|
||||
// How about making sure our follower exists and is added before trying to spawn it n' all?
|
||||
if (player->followerskin > numfollowers-1 || player->followerskin < -1)
|
||||
{
|
||||
//CONS_Printf("Follower skin invlaid. Setting to -1.\n");
|
||||
player->followerskin = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
// don't do anything if we can't have a follower to begin with. (It gets removed under those conditions)
|
||||
if (player->spectator)
|
||||
return;
|
||||
if (player->followerskin < 0)
|
||||
return;
|
||||
// Before we do anything, let's be sure of where we're supposed to be
|
||||
fl = followers[player->followerskin];
|
||||
|
||||
an = player->mo->angle + (fl.atangle)*ANG1; // it's aproximative but it really doesn't matter in the grand scheme of things...
|
||||
zoffs = (fl.zoffs)*FRACUNIT;
|
||||
bubble = fl.bubblescale; // 0 if no bubble to spawn.
|
||||
|
||||
// do you like angle maths? I certainly don't...
|
||||
sx = player->mo->x + FixedMul((player->mo->scale*fl.dist), FINECOSINE((an)>>ANGLETOFINESHIFT));
|
||||
sy = player->mo->y + FixedMul((player->mo->scale*fl.dist), FINESINE((an)>>ANGLETOFINESHIFT));
|
||||
|
||||
// for the z coordinate, don't be a doof like Steel and forget that MFE_VERTICALFLIP exists :P
|
||||
sz = player->mo->z + FixedMul(player->mo->scale, zoffs)*P_MobjFlip(player->mo);
|
||||
if (player->mo->eflags & MFE_VERTICALFLIP)
|
||||
sz += fl.height*player->mo->scale;
|
||||
|
||||
// finally, add a cool floating effect to the z height.
|
||||
// not stolen from k_kart I swear!!
|
||||
const fixed_t pi = (22<<FRACBITS) / 7; // loose approximation, this doesn't need to be incredibly precise
|
||||
fixed_t sine = fl.bobamp * FINESINE((((8*pi*(fl.bobspeed)) * leveltime)>>ANGLETOFINESHIFT) & FINEMASK);
|
||||
sz += FixedMul(player->mo->scale, sine)*P_MobjFlip(player->mo);
|
||||
|
||||
// Set follower colour
|
||||
|
||||
switch (player->followercolor)
|
||||
{
|
||||
case MAXSKINCOLORS: // "Match"
|
||||
color = player->skincolor;
|
||||
break;
|
||||
case MAXSKINCOLORS+1: // "Opposite"
|
||||
color = KartColor_Opposite[player->skincolor*2];
|
||||
break;
|
||||
default:
|
||||
|
||||
color = player->followercolor;
|
||||
if (!color || color > MAXSKINCOLORS+2) // Make sure this isn't garbage
|
||||
color = player->skincolor; // "Match" as fallback.
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!player->follower) // follower doesn't exist / isn't valid
|
||||
{
|
||||
//CONS_Printf("Spawning follower...\n");
|
||||
// so let's spawn one!
|
||||
P_SetTarget(&player->follower, P_SpawnMobj(sx, sy, sz, MT_FOLLOWER));
|
||||
P_SetFollowerState(player->follower, fl.idlestate);
|
||||
P_SetTarget(&player->follower->target, player->mo); // we need that to know when we need to disappear
|
||||
player->follower->angle = player->mo->angle;
|
||||
|
||||
// This is safe to only spawn it here, the follower is removed then respawned when switched.
|
||||
if (bubble)
|
||||
{
|
||||
bmobj = P_SpawnMobj(player->follower->x, player->follower->y, player->follower->z, MT_FOLLOWERBUBBLE_FRONT);
|
||||
P_SetTarget(&player->follower->hnext, bmobj);
|
||||
P_SetTarget(&bmobj->target, player->follower); // Used to know if we have to despawn at some point.
|
||||
|
||||
bmobj = P_SpawnMobj(player->follower->x, player->follower->y, player->follower->z, MT_FOLLOWERBUBBLE_BACK);
|
||||
P_SetTarget(&player->follower->hnext->hnext, bmobj); // this seems absolutely stupid, I know, but this will make updating the momentums/flags of these a bit easier.
|
||||
P_SetTarget(&bmobj->target, player->follower); // Ditto
|
||||
}
|
||||
|
||||
player->follower->extravalue1 = 0; // extravalue1 is used to know what "state set" to use.
|
||||
/*
|
||||
0 = idle
|
||||
1 = forwards
|
||||
2 = hurt
|
||||
3 = win
|
||||
4 = lose
|
||||
5 = hitconfirm (< this one uses ->movecount as timer to know when to end, and goes back to normal states afterwards, unless hurt)
|
||||
*/
|
||||
}
|
||||
else // follower exists, woo!
|
||||
{
|
||||
|
||||
// Safety net (2)
|
||||
|
||||
if (P_MobjWasRemoved(player->follower))
|
||||
{
|
||||
P_SetTarget(&player->follower, NULL); // Remove this and respawn one, don't crash the game if Lua decides to P_RemoveMobj this thing.
|
||||
return;
|
||||
}
|
||||
|
||||
// first of all, handle states following the same model as above:
|
||||
if (player->follower->tics == 1)
|
||||
P_SetFollowerState(player->follower, player->follower->state->nextstate);
|
||||
|
||||
// move the follower next to us (yes, this is really basic maths but it looks pretty damn clean in practice)!
|
||||
player->follower->momx = (sx - player->follower->x)/fl.horzlag;
|
||||
player->follower->momy = (sy - player->follower->y)/fl.horzlag;
|
||||
player->follower->momz = (sz - player->follower->z)/fl.vertlag;
|
||||
player->follower->angle = player->mo->angle;
|
||||
|
||||
if (player->mo->colorized)
|
||||
player->follower->color = player->mo->color;
|
||||
else
|
||||
player->follower->color = color;
|
||||
|
||||
player->follower->colorized = player->mo->colorized;
|
||||
|
||||
P_SetScale(player->follower, FixedMul(fl.scale, player->mo->scale));
|
||||
K_GenericExtraFlagsNoZAdjust(player->follower, player->mo); // Not K_MatchGenericExtraFlag because the Z adjust it has only works properly if master & mo have the same Z height.
|
||||
|
||||
// For comeback in battle.
|
||||
player->follower->flags2 = (player->follower->flags2 & ~MF2_SHADOW)|(player->mo->flags2 & MF2_SHADOW);
|
||||
|
||||
// Make the follower invisible if we no contest'd rather than removing it. No one will notice the diff seriously.
|
||||
// Also make the follower invisible if we choose not to have it displayed because it isn't ours. (also quick hacky check for f12)
|
||||
if (player->pflags & PF_TIMEOVER || (!cv_showfollowers.value && (!P_IsDisplayPlayer(player) || displayplayers[0] != consoleplayer) ))
|
||||
player->follower->flags2 |= MF2_DONTDRAW;
|
||||
|
||||
if (player->speed && (player->follower->momx || player->follower->momy))
|
||||
player->follower->angle = R_PointToAngle2(0, 0, player->follower->momx, player->follower->momy);
|
||||
// if we're moving let's make the angle the direction we're moving towards. This is to avoid drifting / reverse looking awkward.
|
||||
// Make sure the follower itself is also moving however, otherwise we'll be facing angle 0
|
||||
|
||||
// Finally, if the follower has bubbles, move them, set their scale, etc....
|
||||
// This is what I meant earlier by it being easier, now we can just use this weird lil loop to get the job done!
|
||||
|
||||
bmobj = player->follower->hnext; // will be NULL if there's no bubble
|
||||
|
||||
while (bmobj && !P_MobjWasRemoved(bmobj))
|
||||
{
|
||||
// match follower's momentums and (e)flags(2).
|
||||
bmobj->momx = player->follower->momx;
|
||||
bmobj->momy = player->follower->momy;
|
||||
bmobj->momz = player->follower->momz;
|
||||
|
||||
P_SetScale(bmobj, FixedMul(bubble, player->mo->scale));
|
||||
K_GenericExtraFlagsNoZAdjust(bmobj, player->follower);
|
||||
bmobj->flags2 = (player->follower->flags2 & ~MF2_SHADOW)|(player->mo->flags2 & MF2_SHADOW);
|
||||
|
||||
if (player->follower->threshold) // threshold means the follower was "despawned" with S_NULL (is actually just set to S_INVISIBLE)
|
||||
P_SetMobjState(bmobj, S_INVISIBLE); // sooooo... let's do the same!
|
||||
|
||||
bmobj = bmobj->hnext; // switch to other bubble layer or exit
|
||||
}
|
||||
|
||||
|
||||
if (player->follower->threshold)
|
||||
return; // Threshold means the follower was "despanwed" with S_NULL.
|
||||
|
||||
// However with how the code is factored, this is just a special case of S_INVISBLE to avoid having to add other player variables.
|
||||
|
||||
|
||||
// handle follower animations. Could probably be better...
|
||||
// hurt or dead
|
||||
if (player->kartstuff[k_spinouttimer] || player->mo->state == &states[S_KART_SPIN] || player->mo->health <= 0)
|
||||
{
|
||||
player->follower->movecount = 0; // cancel hit confirm.
|
||||
player->follower->angle = player->frameangle; // spin out
|
||||
if (player->follower->extravalue1 != 2)
|
||||
{
|
||||
player->follower->extravalue1 = 2;
|
||||
P_SetFollowerState(player->follower, fl.hurtstate);
|
||||
}
|
||||
if (player->mo->health <= 0) // if dead, follow the player's z momentum exactly so they both look like they die at the same speed.
|
||||
player->follower->momz = player->mo->momz;
|
||||
}
|
||||
else if (player->follower->movecount)
|
||||
{
|
||||
if (player->follower->extravalue1 != 5)
|
||||
{
|
||||
player->follower->extravalue1 = 5;
|
||||
P_SetFollowerState(player->follower, fl.hitconfirmstate);
|
||||
}
|
||||
player->follower->movecount--;
|
||||
}
|
||||
else if (player->speed > 10*player->mo->scale) // animation for moving fast enough
|
||||
{
|
||||
|
||||
if (player->follower->extravalue1 != 1)
|
||||
{
|
||||
player->follower->extravalue1 = 1;
|
||||
P_SetFollowerState(player->follower, fl.followstate);
|
||||
}
|
||||
}
|
||||
else // animations when nearly still. This includes winning and losing.
|
||||
{
|
||||
if (player->follower->extravalue1 != 0)
|
||||
{
|
||||
|
||||
if (player->exiting) // win/ loss animations
|
||||
{
|
||||
if (K_IsPlayerLosing(player)) // L
|
||||
{
|
||||
if (player->follower->extravalue1 != 4)
|
||||
{
|
||||
player->follower->extravalue1 = 4;
|
||||
P_SetFollowerState(player->follower, fl.losestate);
|
||||
}
|
||||
}
|
||||
else // W
|
||||
{
|
||||
if (player->follower->extravalue1 != 3)
|
||||
{
|
||||
player->follower->extravalue1 = 3;
|
||||
P_SetFollowerState(player->follower, fl.winstate);
|
||||
}
|
||||
}
|
||||
}
|
||||
else // normal standstill
|
||||
{
|
||||
player->follower->extravalue1 = 0;
|
||||
P_SetFollowerState(player->follower, fl.idlestate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// P_PlayerThink
|
||||
//
|
||||
|
|
@ -8461,6 +8741,9 @@ void P_PlayerThink(player_t *player)
|
|||
player->awayviewtics = 0; // reset to zero
|
||||
}
|
||||
|
||||
// Run followes here. We need them to run even when we're dead to follow through what we're doing.
|
||||
P_HandleFollower(player);
|
||||
|
||||
if (player->flashcount)
|
||||
player->flashcount--;
|
||||
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ extern INT16 color8to16[256]; // remap color index to highcolor
|
|||
extern INT16 *hicolormaps; // remap high colors to high colors..
|
||||
|
||||
extern CV_PossibleValue_t Color_cons_t[];
|
||||
extern CV_PossibleValue_t Followercolor_cons_t[]; // follower colours table, not a duplicate because of the "Match" option.
|
||||
|
||||
// Load TEXTURE1/TEXTURE2/PNAMES definitions, create lookup tables
|
||||
void R_LoadTextures(void);
|
||||
|
|
|
|||
|
|
@ -147,6 +147,7 @@ static UINT8** translationtablecache[TT_CACHE_SIZE] = {NULL};
|
|||
// SKINCOLOR DEFINITIONS HAVE BEEN MOVED TO K_KART.C
|
||||
|
||||
CV_PossibleValue_t Color_cons_t[MAXSKINCOLORS+1];
|
||||
CV_PossibleValue_t Followercolor_cons_t[MAXSKINCOLORS+3]; // +3 to account for "Match", "Opposite" & NULL
|
||||
|
||||
/** \brief The R_InitTranslationTables
|
||||
|
||||
|
|
|
|||
|
|
@ -2891,7 +2891,12 @@ void R_DrawMasked(void)
|
|||
//
|
||||
// ==========================================================================
|
||||
|
||||
// We can assume those are tied to skins somewhat, hence why they're defined here.
|
||||
INT32 numskins = 0;
|
||||
follower_t followers[MAXSKINS];
|
||||
// default followers are defined in SOC_FLWR in followers.kart / gfx.kart (depending on what exe this is, at this point)
|
||||
|
||||
|
||||
skin_t skins[MAXSKINS];
|
||||
// FIXTHIS: don't work because it must be inistilised before the config load
|
||||
//#define SKINVALUES
|
||||
|
|
@ -2954,6 +2959,19 @@ INT32 R_SkinAvailable(const char *name)
|
|||
return -1;
|
||||
}
|
||||
|
||||
// same thing but for followers:
|
||||
INT32 R_FollowerAvailable(const char *name)
|
||||
{
|
||||
INT32 i;
|
||||
|
||||
for (i = 0; i < numfollowers; i++)
|
||||
{
|
||||
if (stricmp(followers[i].skinname,name)==0)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// network code calls this when a 'skin change' is received
|
||||
boolean SetPlayerSkin(INT32 playernum, const char *skinname)
|
||||
{
|
||||
|
|
@ -2979,13 +2997,37 @@ boolean SetPlayerSkin(INT32 playernum, const char *skinname)
|
|||
return false;
|
||||
}
|
||||
|
||||
// Again, same thing but for followers;
|
||||
boolean SetPlayerFollower(INT32 playernum, const char *skinname)
|
||||
{
|
||||
INT32 i;
|
||||
player_t *player = &players[playernum];
|
||||
|
||||
for (i = 0; i < numfollowers; i++)
|
||||
{
|
||||
// search in the skin list
|
||||
if (stricmp(followers[i].skinname, skinname) == 0)
|
||||
{
|
||||
SetFollower(playernum, i);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (P_IsLocalPlayer(player))
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Follower '%s' not found.\n"), skinname);
|
||||
else if(server || IsPlayerAdmin(consoleplayer))
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Player %d (%s) follower '%s' not found\n"), playernum, player_names[playernum], skinname);
|
||||
|
||||
SetFollower(playernum, -1); // reminder that -1 is nothing
|
||||
return false;
|
||||
}
|
||||
|
||||
// Same as SetPlayerSkin, but uses the skin #.
|
||||
// network code calls this when a 'skin change' is received
|
||||
void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum)
|
||||
{
|
||||
player_t *player = &players[playernum];
|
||||
skin_t *skin = &skins[skinnum];
|
||||
|
||||
if (skinnum >= 0 && skinnum < numskins) // Make sure it exists!
|
||||
{
|
||||
player->skin = skinnum;
|
||||
|
|
@ -3016,6 +3058,7 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum)
|
|||
if (player->mo)
|
||||
P_SetScale(player->mo, player->mo->scale);
|
||||
|
||||
// for replays: We have changed our skin mid-game; let the game know so it can do the same in the replay!
|
||||
demo_extradata[playernum] |= DXD_SKIN;
|
||||
|
||||
return;
|
||||
|
|
@ -3028,6 +3071,53 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum)
|
|||
SetPlayerSkinByNum(playernum, 0); // not found put the sonic skin
|
||||
}
|
||||
|
||||
// you get the drill, now we do the same for followers:
|
||||
void SetFollower(INT32 playernum, INT32 skinnum)
|
||||
{
|
||||
player_t *player = &players[playernum];
|
||||
mobj_t *bub;
|
||||
mobj_t *tmp;
|
||||
|
||||
player->followerready = true; // we are ready to perform follower related actions in the player thinker, now.
|
||||
if (skinnum >= -1 && skinnum <= numfollowers) // Make sure it exists!
|
||||
{
|
||||
/*
|
||||
We don't spawn the follower here since it'll be easier to handle all of it in the Player thinker itself.
|
||||
However, we will despawn it right here if there's any to make it easy for the player thinker to replace it or delete it.
|
||||
*/
|
||||
if (player->follower && skinnum != player->followerskin) // this is also called when we change colour so don't respawn the follower unless we changed skins
|
||||
{
|
||||
|
||||
// Remove follower's possible hnext list (bubble)
|
||||
bub = player->follower->hnext;
|
||||
|
||||
while (bub && !P_MobjWasRemoved(bub))
|
||||
{
|
||||
tmp = bub->hnext;
|
||||
P_RemoveMobj(bub);
|
||||
bub = tmp;
|
||||
}
|
||||
|
||||
P_RemoveMobj(player->follower);
|
||||
P_SetTarget(&player->follower, NULL);
|
||||
}
|
||||
|
||||
player->followerskin = skinnum;
|
||||
//CONS_Printf("Updated player follower num\n");
|
||||
|
||||
// for replays: We have changed our follower mid-game; let the game know so it can do the same in the replay!
|
||||
demo_extradata[playernum] |= DXD_FOLLOWER;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (P_IsLocalPlayer(player))
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Follower %d not found\n"), skinnum);
|
||||
else if(server || IsPlayerAdmin(consoleplayer))
|
||||
CONS_Alert(CONS_WARNING, "Player %d (%s) follower %d not found\n", playernum, player_names[playernum], skinnum);
|
||||
SetFollower(playernum, -1); // Not found, then set -1 (nothing) as our follower.
|
||||
}
|
||||
|
||||
//
|
||||
// Add skins from a pwad, each skin preceded by 'S_SKIN' marker
|
||||
//
|
||||
|
|
|
|||
|
|
@ -103,6 +103,45 @@ typedef struct
|
|||
} skin_t;
|
||||
|
||||
extern CV_PossibleValue_t Forceskin_cons_t[];
|
||||
//
|
||||
// for followers.
|
||||
//
|
||||
// We'll define these here because they're really just a mobj that'll follow some rules behind a player
|
||||
//
|
||||
typedef struct follower_s
|
||||
{
|
||||
char skinname[SKINNAMESIZE+1]; // Skin Name. This is what to refer to when asking the commands anything.
|
||||
char name[SKINNAMESIZE+1]; // Name. This is used for the menus. We'll just follow the same rules as skins for this.
|
||||
|
||||
UINT8 defaultcolor; // default color for menus.
|
||||
|
||||
fixed_t scale; // Scale relative to the player's.
|
||||
fixed_t bubblescale; // Bubble scale relative to the player scale. If not set, no bubble will spawn (default)
|
||||
|
||||
// some position shenanigans:
|
||||
INT32 atangle; // angle the object will be at around the player. The object itself will always face the same direction as the player.
|
||||
INT32 dist; // distance relative to the player. (In a circle)
|
||||
INT32 height; // height of the follower, this is mostly important for Z flipping.
|
||||
INT32 zoffs; // Z offset relative to the player's height. Cannot be negative.
|
||||
|
||||
// movement options
|
||||
|
||||
INT32 horzlag; // Lag for X/Y displacement. Default is 2. Must be > 0 because we divide by this number.
|
||||
INT32 vertlag; // not Vert from Neptunia lagging, this is for Z displacement lag Default is 6. Must be > 0 because we divide by this number.
|
||||
INT32 bobamp; // Bob amplitude. Default is 4.
|
||||
INT32 bobspeed; // Arbitrary modifier for bobbing speed, default is TICRATE*2 (70).
|
||||
|
||||
// from there on out, everything is STATES to allow customization
|
||||
// these are only set once when the action is performed and are then free to animate however they want.
|
||||
|
||||
INT32 idlestate; // state when the player is at a standstill
|
||||
INT32 followstate; // state when the player is moving
|
||||
INT32 hurtstate; // state when the player is being hurt
|
||||
INT32 winstate; // state when the player has won
|
||||
INT32 losestate; // state when the player has lost
|
||||
INT32 hitconfirmstate; // state for hit confirm
|
||||
INT32 hitconfirmtime; // time to keep the above playing for
|
||||
} follower_t;
|
||||
|
||||
// -----------
|
||||
// NOT SKINS STUFF !
|
||||
|
|
@ -199,12 +238,18 @@ typedef struct drawnode_s
|
|||
|
||||
extern INT32 numskins;
|
||||
extern skin_t skins[MAXSKINS];
|
||||
extern INT32 numfollowers;
|
||||
extern follower_t followers[MAXSKINS]; // again, use the same rules as skins, no reason not to.
|
||||
|
||||
boolean SetPlayerSkin(INT32 playernum,const char *skinname);
|
||||
void SetPlayerSkinByNum(INT32 playernum,INT32 skinnum); // Tails 03-16-2002
|
||||
INT32 R_SkinAvailable(const char *name);
|
||||
void R_AddSkins(UINT16 wadnum);
|
||||
|
||||
INT32 R_FollowerAvailable(const char *name);
|
||||
boolean SetPlayerFollower(INT32 playernum,const char *skinname);
|
||||
void SetFollower(INT32 playernum,INT32 skinnum);
|
||||
|
||||
#ifdef DELFILE
|
||||
void R_DelSkins(UINT16 wadnum);
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue