mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Merge branch 'queue-skin' into 'master'
Queued skin/color changes See merge request KartKrew/Kart!2452
This commit is contained in:
commit
71588f490f
47 changed files with 1292 additions and 1309 deletions
|
|
@ -1271,7 +1271,7 @@ bool CallFunc_EndPrintBold(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM
|
|||
bool CallFunc_PlayerTeam(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
|
||||
{
|
||||
auto info = &static_cast<Thread *>(thread)->info;
|
||||
UINT8 teamID = 0;
|
||||
UINT8 teamID = TEAM_UNASSIGNED;
|
||||
|
||||
(void)argV;
|
||||
(void)argC;
|
||||
|
|
@ -1280,7 +1280,7 @@ bool CallFunc_PlayerTeam(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::
|
|||
&& (info->mo != NULL && P_MobjWasRemoved(info->mo) == false)
|
||||
&& (info->mo->player != NULL))
|
||||
{
|
||||
teamID = info->mo->player->ctfteam;
|
||||
teamID = info->mo->player->team;
|
||||
}
|
||||
|
||||
thread->dataStk.push(teamID);
|
||||
|
|
|
|||
|
|
@ -597,9 +597,6 @@ consvar_t cv_sleep = Server("cpusleep", "1").min_max(0, 1000/TICRATE);
|
|||
// There's a separate section for netvars that don't save...
|
||||
//
|
||||
|
||||
void AutoBalance_OnChange(void);
|
||||
consvar_t cv_autobalance = NetVar("autobalance", "Off").on_off().onchange(AutoBalance_OnChange);
|
||||
|
||||
consvar_t cv_blamecfail = NetVar("blamecfail", "Off").on_off();
|
||||
|
||||
// Speed of file downloading (in packets per tic)
|
||||
|
|
@ -628,10 +625,6 @@ consvar_t cv_noticedownload = NetVar("noticedownload", "Off").on_off();
|
|||
consvar_t cv_pingtimeout = NetVar("maxdelaytimeout", "10").min_max(8, 120);
|
||||
consvar_t cv_resynchattempts = NetVar("resynchattempts", "2").min_max(1, 20, {{0, "No"}});
|
||||
|
||||
void TeamScramble_OnChange(void);
|
||||
consvar_t cv_scrambleonchange = NetVar("scrambleonchange", "Off").values({{0, "Off"}, {1, "Random"}, {2, "Points"}});
|
||||
consvar_t cv_teamscramble = NetVar("teamscramble", "Off").values({{0, "Off"}, {1, "Random"}, {2, "Points"}}).onchange_noinit(TeamScramble_OnChange);
|
||||
|
||||
consvar_t cv_showjoinaddress = NetVar("showjoinaddress", "Off").on_off();
|
||||
consvar_t cv_zvote_delay = NetVar("zvote_delay", "20").values(CV_Unsigned);
|
||||
consvar_t cv_zvote_length = NetVar("zvote_length", "20").values(CV_Unsigned);
|
||||
|
|
@ -736,6 +729,8 @@ consvar_t cv_kartfrantic = UnsavedNetVar("franticitems", "Off").on_off().onchang
|
|||
void KartSpeed_OnChange(void);
|
||||
consvar_t cv_kartspeed = UnsavedNetVar("gamespeed", "Auto Gear").values(kartspeed_cons_t).onchange_noinit(KartSpeed_OnChange);
|
||||
|
||||
consvar_t cv_teamplay = UnsavedNetVar("teamplay", "Off").on_off();
|
||||
|
||||
consvar_t cv_kartusepwrlv = UnsavedNetVar("usepwrlv", "Yes").yes_no();
|
||||
|
||||
void LiveStudioAudience_OnChange(void);
|
||||
|
|
@ -997,9 +992,6 @@ consvar_t cv_dummyspectate = MenuDummy("dummyspectate", "Spectator").values({{0,
|
|||
extern CV_PossibleValue_t dummystaff_cons_t[];
|
||||
consvar_t cv_dummystaff = MenuDummy("dummystaff", "0").values(dummystaff_cons_t);
|
||||
|
||||
consvar_t cv_dummyteam = MenuDummy("dummyteam", "Spectator").values({{0, "Spectator"}, {1, "Red"}, {2, "Blue"}});
|
||||
|
||||
|
||||
//
|
||||
// lastprofile
|
||||
//
|
||||
|
|
|
|||
|
|
@ -1145,28 +1145,32 @@ static void SV_SendPlayerInfo(INT32 node)
|
|||
//No, don't do that, you fuckface.
|
||||
memset(netbuffer->u.playerinfo[i].address, 0, 4);
|
||||
|
||||
if (G_GametypeHasTeams())
|
||||
if (players[i].spectator)
|
||||
{
|
||||
if (!players[i].ctfteam)
|
||||
netbuffer->u.playerinfo[i].team = 255;
|
||||
else
|
||||
netbuffer->u.playerinfo[i].team = (UINT8)players[i].ctfteam;
|
||||
netbuffer->u.playerinfo[i].team = 255;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (players[i].spectator)
|
||||
netbuffer->u.playerinfo[i].team = 255;
|
||||
if (G_GametypeHasTeams())
|
||||
{
|
||||
if (players[i].team == TEAM_UNASSIGNED)
|
||||
{
|
||||
netbuffer->u.playerinfo[i].team = 255;
|
||||
}
|
||||
else
|
||||
{
|
||||
netbuffer->u.playerinfo[i].team = players[i].team;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
netbuffer->u.playerinfo[i].team = 0;
|
||||
}
|
||||
}
|
||||
|
||||
netbuffer->u.playerinfo[i].score = LONG(players[i].score);
|
||||
netbuffer->u.playerinfo[i].timeinserver = SHORT((UINT16)(players[i].jointime / TICRATE));
|
||||
netbuffer->u.playerinfo[i].skin = (UINT8)(players[i].skin
|
||||
#ifdef DEVELOP // it's safe to do this only because PLAYERINFO isn't read by the game itself
|
||||
% 3
|
||||
#endif
|
||||
);
|
||||
netbuffer->u.playerinfo[i].skin = (UINT8)(players[i].skin);
|
||||
|
||||
// Extra data
|
||||
netbuffer->u.playerinfo[i].data = 0; //players[i].skincolor;
|
||||
|
|
|
|||
|
|
@ -357,7 +357,7 @@ struct plrconfig
|
|||
UINT16 color;
|
||||
UINT32 pflags;
|
||||
UINT32 score;
|
||||
UINT8 ctfteam;
|
||||
UINT8 team;
|
||||
} ATTRPACK;
|
||||
|
||||
struct filesneededconfig_pak
|
||||
|
|
|
|||
687
src/d_netcmd.c
687
src/d_netcmd.c
|
|
@ -102,7 +102,8 @@ static void Got_Addfilecmd(const UINT8 **cp, INT32 playernum);
|
|||
static void Got_Pause(const UINT8 **cp, INT32 playernum);
|
||||
static void Got_RandomSeed(const UINT8 **cp, INT32 playernum);
|
||||
static void Got_RunSOCcmd(const UINT8 **cp, INT32 playernum);
|
||||
static void Got_Teamchange(const UINT8 **cp, INT32 playernum);
|
||||
static void Got_Spectate(const UINT8 **cp, INT32 playernum);
|
||||
static void Got_TeamChange(const UINT8 **cp, INT32 playernum);
|
||||
static void Got_Clearscores(const UINT8 **cp, INT32 playernum);
|
||||
static void Got_DiscordInfo(const UINT8 **cp, INT32 playernum);
|
||||
static void Got_ScheduleTaskcmd(const UINT8 **cp, INT32 playernum);
|
||||
|
|
@ -155,11 +156,6 @@ static void Command_ExitLevel_f(void);
|
|||
static void Command_Showmap_f(void);
|
||||
static void Command_Mapmd5_f(void);
|
||||
|
||||
static void Command_Teamchange_f(void);
|
||||
static void Command_Teamchange2_f(void);
|
||||
static void Command_Teamchange3_f(void);
|
||||
static void Command_Teamchange4_f(void);
|
||||
|
||||
static void Command_ServerTeamChange_f(void);
|
||||
|
||||
static void Command_Clearscores_f(void);
|
||||
|
|
@ -293,7 +289,7 @@ const char *netxcmdnames[MAXNETXCMD - 1] =
|
|||
"ADDFILE", // XD_ADDFILE
|
||||
"PAUSE", // XD_PAUSE
|
||||
"ADDPLAYER", // XD_ADDPLAYER
|
||||
"TEAMCHANGE", // XD_TEAMCHANGE
|
||||
"SPECTATE", // XD_SPECTATE
|
||||
"CLEARSCORES", // XD_CLEARSCORES
|
||||
"VERIFIED", // XD_VERIFIED
|
||||
"RANDOMSEED", // XD_RANDOMSEED
|
||||
|
|
@ -325,6 +321,7 @@ const char *netxcmdnames[MAXNETXCMD - 1] =
|
|||
"MAPQUEUE", // XD_MAPQUEUE
|
||||
"CALLZVOTE", // XD_CALLZVOTE
|
||||
"SETZVOTE", // XD_SETZVOTE
|
||||
"TEAMCHANGE", // XD_TEAMCHANGE
|
||||
};
|
||||
|
||||
// =========================================================================
|
||||
|
|
@ -388,7 +385,8 @@ void D_RegisterServerCommands(void)
|
|||
COM_AddCommand("motd", Command_MotD_f);
|
||||
RegisterNetXCmd(XD_SETMOTD, Got_MotD_f); // For remote admin
|
||||
|
||||
RegisterNetXCmd(XD_TEAMCHANGE, Got_Teamchange);
|
||||
RegisterNetXCmd(XD_SPECTATE, Got_Spectate);
|
||||
RegisterNetXCmd(XD_TEAMCHANGE, Got_TeamChange);
|
||||
COM_AddCommand("serverchangeteam", Command_ServerTeamChange_f);
|
||||
|
||||
RegisterNetXCmd(XD_CLEARSCORES, Got_Clearscores);
|
||||
|
|
@ -504,11 +502,6 @@ void D_RegisterClientCommands(void)
|
|||
if (dedicated)
|
||||
return;
|
||||
|
||||
COM_AddCommand("changeteam", Command_Teamchange_f);
|
||||
COM_AddCommand("changeteam2", Command_Teamchange2_f);
|
||||
COM_AddCommand("changeteam3", Command_Teamchange3_f);
|
||||
COM_AddCommand("changeteam4", Command_Teamchange4_f);
|
||||
|
||||
COM_AddCommand("invite", Command_Invite_f);
|
||||
COM_AddCommand("cancelinvite", Command_CancelInvite_f);
|
||||
COM_AddCommand("acceptinvite", Command_AcceptInvite_f);
|
||||
|
|
@ -996,11 +989,6 @@ static void SendNameAndColor(const UINT8 n)
|
|||
cv_follower[n].value = -1;
|
||||
}
|
||||
|
||||
if (sendColor == SKINCOLOR_NONE)
|
||||
{
|
||||
sendColor = skins[cv_skin[n].value].prefcolor;
|
||||
}
|
||||
|
||||
if (sendFollowerColor == SKINCOLOR_NONE)
|
||||
{
|
||||
if (cv_follower[n].value >= 0)
|
||||
|
|
@ -1015,11 +1003,11 @@ static void SendNameAndColor(const UINT8 n)
|
|||
|
||||
// Don't send if everything was identical.
|
||||
if (!strcmp(cv_playername[n].string, player_names[playernum])
|
||||
&& sendColor == player->skincolor
|
||||
&& !stricmp(cv_skin[n].string, skins[player->skin].name)
|
||||
&& sendColor == player->prefcolor
|
||||
&& !stricmp(cv_skin[n].string, skins[player->prefskin].name)
|
||||
&& !stricmp(cv_follower[n].string,
|
||||
(player->followerskin < 0 ? "None" : followers[player->followerskin].name))
|
||||
&& sendFollowerColor == player->followercolor)
|
||||
(player->preffollower < 0 ? "None" : followers[player->preffollower].name))
|
||||
&& sendFollowerColor == player->preffollowercolor)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -1123,7 +1111,6 @@ static void Got_NameAndColor(const UINT8 **cp, INT32 playernum)
|
|||
UINT16 color, followercolor;
|
||||
UINT8 skin;
|
||||
INT16 follower;
|
||||
SINT8 localplayer = -1;
|
||||
UINT8 i;
|
||||
|
||||
#ifdef PARANOIA
|
||||
|
|
@ -1145,8 +1132,6 @@ static void Got_NameAndColor(const UINT8 **cp, INT32 playernum)
|
|||
I_Error("snacpending[%d] negative!", i);
|
||||
}
|
||||
#endif
|
||||
|
||||
localplayer = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1164,95 +1149,26 @@ static void Got_NameAndColor(const UINT8 **cp, INT32 playernum)
|
|||
SetPlayerName(playernum, name);
|
||||
}
|
||||
|
||||
// set color
|
||||
p->skincolor = color % numskincolors;
|
||||
if (p->mo)
|
||||
p->mo->color = (UINT16)p->skincolor;
|
||||
demo_extradata[playernum] |= DXD_COLOR;
|
||||
|
||||
// normal player colors
|
||||
if (server && !P_IsMachineLocalPlayer(p))
|
||||
// queue the rest for next round
|
||||
p->prefcolor = color % numskincolors;
|
||||
if (K_ColorUsable(p->prefcolor, false, false) == false)
|
||||
{
|
||||
boolean kick = false;
|
||||
|
||||
// don't allow inaccessible colors
|
||||
if (K_ColorUsable(p->skincolor, false, false) == false)
|
||||
{
|
||||
kick = true;
|
||||
}
|
||||
|
||||
if (kick)
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Illegal color change received from %s, color: %d)\n"), player_names[playernum], p->skincolor);
|
||||
SendKick(playernum, KICK_MSG_CON_FAIL);
|
||||
return;
|
||||
}
|
||||
p->prefcolor = SKINCOLOR_NONE;
|
||||
}
|
||||
|
||||
// set skin
|
||||
if (cv_forceskin.value >= 0 && K_CanChangeRules(true)) // Server wants everyone to use the same player
|
||||
p->prefskin = skin;
|
||||
p->preffollowercolor = followercolor;
|
||||
p->preffollower = follower;
|
||||
|
||||
if (
|
||||
(p->jointime <= 1) // Just entered
|
||||
|| (cv_restrictskinchange.value == 0 // Not restricted
|
||||
&& !Y_IntermissionPlayerLock()) // Not start of intermission
|
||||
)
|
||||
{
|
||||
const INT32 forcedskin = cv_forceskin.value;
|
||||
SetPlayerSkinByNum(playernum, forcedskin);
|
||||
|
||||
if (localplayer != -1)
|
||||
CV_StealthSet(&cv_skin[localplayer], skins[forcedskin].name);
|
||||
// update preferences immediately
|
||||
G_UpdatePlayerPreferences(p);
|
||||
}
|
||||
else
|
||||
{
|
||||
UINT8 oldskin = players[playernum].skin;
|
||||
|
||||
SetPlayerSkinByNum(playernum, skin);
|
||||
|
||||
// The following is a miniature subset of Got_Teamchange.
|
||||
if ((gamestate == GS_LEVEL) // In a level?
|
||||
&& (players[playernum].jointime > 1) // permit on join
|
||||
&& (leveltime > introtime) // permit during intro turnaround
|
||||
&& (players[playernum].skin != oldskin)) // a skin change actually happened?
|
||||
{
|
||||
players[playernum].roundconditions.switched_skin = true;
|
||||
|
||||
if (
|
||||
cv_restrictskinchange.value // Skin changes are restricted?
|
||||
&& G_GametypeHasSpectators() // not a spectator...
|
||||
&& players[playernum].spectator == false // ...but could be?
|
||||
)
|
||||
{
|
||||
for (i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
if (i == playernum)
|
||||
continue;
|
||||
if (!playeringame[i])
|
||||
continue;
|
||||
if (players[i].spectator)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
if (i != MAXPLAYERS // Someone on your server who isn't you?
|
||||
&& LUA_HookTeamSwitch(&players[playernum], 0, false, false, false)) // fiiiine, lua can except it
|
||||
{
|
||||
P_DamageMobj(players[playernum].mo, NULL, NULL, 1, DMG_SPECTATOR);
|
||||
|
||||
if (players[i].spectator)
|
||||
{
|
||||
HU_AddChatText(va("\x82*%s became a spectator.", player_names[playernum]), false);
|
||||
|
||||
FinalisePlaystateChange(playernum);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 K_HandleFollower.
|
||||
p->followercolor = followercolor;
|
||||
|
||||
// set follower
|
||||
K_SetFollowerByNum(playernum, follower);
|
||||
}
|
||||
|
||||
enum {
|
||||
|
|
@ -3477,127 +3393,13 @@ static void Got_Clearscores(const UINT8 **cp, INT32 playernum)
|
|||
CONS_Printf(M_GetText("Scores have been reset by the server.\n"));
|
||||
}
|
||||
|
||||
// Team changing functions
|
||||
static void HandleTeamChangeCommand(UINT8 localplayer)
|
||||
{
|
||||
const char *commandname = NULL;
|
||||
changeteam_union NetPacket;
|
||||
boolean error = false;
|
||||
UINT16 usvalue;
|
||||
NetPacket.value.l = NetPacket.value.b = 0;
|
||||
|
||||
switch (localplayer)
|
||||
{
|
||||
case 0:
|
||||
commandname = "changeteam";
|
||||
break;
|
||||
default:
|
||||
commandname = va("changeteam%d", localplayer+1);
|
||||
break;
|
||||
}
|
||||
|
||||
// 0 1
|
||||
// changeteam <color>
|
||||
|
||||
if (COM_Argc() <= 1)
|
||||
{
|
||||
if (G_GametypeHasTeams())
|
||||
CONS_Printf(M_GetText("%s <team>: switch to a new team (%s)\n"), commandname, "red, blue or spectator");
|
||||
else if (G_GametypeHasSpectators())
|
||||
CONS_Printf(M_GetText("%s <team>: switch to a new team (%s)\n"), commandname, "spectator or playing");
|
||||
else
|
||||
CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (G_GametypeHasTeams())
|
||||
{
|
||||
if (!strcasecmp(COM_Argv(1), "red") || !strcasecmp(COM_Argv(1), "1"))
|
||||
NetPacket.packet.newteam = 1;
|
||||
else if (!strcasecmp(COM_Argv(1), "blue") || !strcasecmp(COM_Argv(1), "2"))
|
||||
NetPacket.packet.newteam = 2;
|
||||
else if (!strcasecmp(COM_Argv(1), "spectator") || !strcasecmp(COM_Argv(1), "0"))
|
||||
NetPacket.packet.newteam = 0;
|
||||
else
|
||||
error = true;
|
||||
}
|
||||
else if (G_GametypeHasSpectators())
|
||||
{
|
||||
if (!strcasecmp(COM_Argv(1), "spectator") || !strcasecmp(COM_Argv(1), "0"))
|
||||
NetPacket.packet.newteam = 0;
|
||||
else if (!strcasecmp(COM_Argv(1), "playing") || !strcasecmp(COM_Argv(1), "1"))
|
||||
NetPacket.packet.newteam = 3;
|
||||
else
|
||||
error = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (error)
|
||||
{
|
||||
if (G_GametypeHasTeams())
|
||||
CONS_Printf(M_GetText("%s <team>: switch to a new team (%s)\n"), commandname, "red, blue or spectator");
|
||||
else if (G_GametypeHasSpectators())
|
||||
CONS_Printf(M_GetText("%s <team>: switch to a new team (%s)\n"), commandname, "spectator or playing");
|
||||
return;
|
||||
}
|
||||
|
||||
if (players[g_localplayers[localplayer]].spectator)
|
||||
error = !(NetPacket.packet.newteam || (players[g_localplayers[localplayer]].pflags & PF_WANTSTOJOIN)); // :lancer:
|
||||
else if (G_GametypeHasTeams())
|
||||
error = (NetPacket.packet.newteam == players[g_localplayers[localplayer]].ctfteam);
|
||||
else if (G_GametypeHasSpectators() && !players[g_localplayers[localplayer]].spectator)
|
||||
error = (NetPacket.packet.newteam == 3);
|
||||
#ifdef PARANOIA
|
||||
else
|
||||
I_Error("Invalid gametype after initial checks!");
|
||||
#endif
|
||||
|
||||
if (error)
|
||||
{
|
||||
CONS_Alert(CONS_NOTICE, M_GetText("You're already on that team!\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!cv_allowteamchange.value && NetPacket.packet.newteam) // allow swapping to spectator even in locked teams.
|
||||
{
|
||||
CONS_Alert(CONS_NOTICE, M_GetText("The server is not allowing team changes at the moment.\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
usvalue = SHORT(NetPacket.value.l|NetPacket.value.b);
|
||||
SendNetXCmdForPlayer(localplayer, XD_TEAMCHANGE, &usvalue, sizeof(usvalue));
|
||||
}
|
||||
|
||||
static void Command_Teamchange_f(void)
|
||||
{
|
||||
HandleTeamChangeCommand(0);
|
||||
}
|
||||
|
||||
static void Command_Teamchange2_f(void)
|
||||
{
|
||||
HandleTeamChangeCommand(1);
|
||||
}
|
||||
|
||||
static void Command_Teamchange3_f(void)
|
||||
{
|
||||
HandleTeamChangeCommand(2);
|
||||
}
|
||||
|
||||
static void Command_Teamchange4_f(void)
|
||||
{
|
||||
HandleTeamChangeCommand(3);
|
||||
}
|
||||
|
||||
static void Command_ServerTeamChange_f(void)
|
||||
{
|
||||
changeteam_union NetPacket;
|
||||
boolean error = false;
|
||||
UINT16 usvalue;
|
||||
NetPacket.value.l = NetPacket.value.b = 0;
|
||||
UINT8 buf[2];
|
||||
UINT8 *p = buf;
|
||||
|
||||
UINT8 new_team = TEAM_UNASSIGNED;
|
||||
UINT8 player_num = consoleplayer;
|
||||
|
||||
if (!(server || (IsPlayerAdmin(consoleplayer))))
|
||||
{
|
||||
|
|
@ -3605,96 +3407,63 @@ static void Command_ServerTeamChange_f(void)
|
|||
return;
|
||||
}
|
||||
|
||||
if (G_GametypeHasTeams() == false)
|
||||
{
|
||||
CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used currently.\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
// 0 1 2
|
||||
// serverchangeteam <playernum> <team>
|
||||
|
||||
if (COM_Argc() < 3)
|
||||
{
|
||||
if (G_GametypeHasTeams())
|
||||
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "red, blue or spectator");
|
||||
else if (G_GametypeHasSpectators())
|
||||
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "spectator or playing");
|
||||
else
|
||||
CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n"));
|
||||
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "orange, blue, or auto");
|
||||
return;
|
||||
}
|
||||
|
||||
if (G_GametypeHasTeams())
|
||||
if (!strcasecmp(COM_Argv(2), "orange") || !strcasecmp(COM_Argv(2), "1"))
|
||||
{
|
||||
if (!strcasecmp(COM_Argv(2), "red") || !strcasecmp(COM_Argv(2), "1"))
|
||||
NetPacket.packet.newteam = 1;
|
||||
else if (!strcasecmp(COM_Argv(2), "blue") || !strcasecmp(COM_Argv(2), "2"))
|
||||
NetPacket.packet.newteam = 2;
|
||||
else if (!strcasecmp(COM_Argv(2), "spectator") || !strcasecmp(COM_Argv(2), "0"))
|
||||
NetPacket.packet.newteam = 0;
|
||||
else
|
||||
error = true;
|
||||
new_team = TEAM_ORANGE;
|
||||
}
|
||||
else if (G_GametypeHasSpectators())
|
||||
else if (!strcasecmp(COM_Argv(2), "blue") || !strcasecmp(COM_Argv(2), "2"))
|
||||
{
|
||||
if (!strcasecmp(COM_Argv(2), "spectator") || !strcasecmp(COM_Argv(2), "0"))
|
||||
NetPacket.packet.newteam = 0;
|
||||
else if (!strcasecmp(COM_Argv(2), "playing") || !strcasecmp(COM_Argv(2), "1"))
|
||||
NetPacket.packet.newteam = 3;
|
||||
else
|
||||
error = true;
|
||||
new_team = TEAM_BLUE;
|
||||
}
|
||||
else if (!strcasecmp(COM_Argv(2), "auto") || !strcasecmp(COM_Argv(2), "0"))
|
||||
{
|
||||
new_team = TEAM_UNASSIGNED;
|
||||
}
|
||||
else
|
||||
{
|
||||
CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n"));
|
||||
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "orange, blue, or auto");
|
||||
return;
|
||||
}
|
||||
|
||||
if (error)
|
||||
player_num = atoi(COM_Argv(1));
|
||||
|
||||
if (playeringame[player_num] == false)
|
||||
{
|
||||
if (G_GametypeHasTeams())
|
||||
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "red, blue or spectator");
|
||||
else if (G_GametypeHasSpectators())
|
||||
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "spectator or playing");
|
||||
CONS_Alert(CONS_NOTICE, M_GetText("There is no player %d!\n"), player_num);
|
||||
return;
|
||||
}
|
||||
|
||||
NetPacket.packet.playernum = atoi(COM_Argv(1));
|
||||
|
||||
if (!playeringame[NetPacket.packet.playernum])
|
||||
{
|
||||
CONS_Alert(CONS_NOTICE, M_GetText("There is no player %d!\n"), NetPacket.packet.playernum);
|
||||
return;
|
||||
}
|
||||
|
||||
if (G_GametypeHasTeams())
|
||||
{
|
||||
if (NetPacket.packet.newteam == players[NetPacket.packet.playernum].ctfteam ||
|
||||
(players[NetPacket.packet.playernum].spectator && !NetPacket.packet.newteam))
|
||||
error = true;
|
||||
}
|
||||
else if (G_GametypeHasSpectators())
|
||||
{
|
||||
if ((players[NetPacket.packet.playernum].spectator && !NetPacket.packet.newteam) ||
|
||||
(!players[NetPacket.packet.playernum].spectator && NetPacket.packet.newteam == 3))
|
||||
error = true;
|
||||
}
|
||||
#ifdef PARANOIA
|
||||
else
|
||||
I_Error("Invalid gametype after initial checks!");
|
||||
#endif
|
||||
|
||||
if (error)
|
||||
if (new_team == players[player_num].team)
|
||||
{
|
||||
CONS_Alert(CONS_NOTICE, M_GetText("That player is already on that team!\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
NetPacket.packet.verification = true; // This signals that it's a server change
|
||||
WRITEUINT8(p, new_team);
|
||||
WRITEUINT8(p, player_num);
|
||||
|
||||
usvalue = SHORT(NetPacket.value.l|NetPacket.value.b);
|
||||
SendNetXCmd(XD_TEAMCHANGE, &usvalue, sizeof(usvalue));
|
||||
SendNetXCmd(XD_TEAMCHANGE, &buf, p - buf);
|
||||
}
|
||||
|
||||
void P_SetPlayerSpectator(INT32 playernum)
|
||||
{
|
||||
//Make sure you're in the right gametype.
|
||||
if (!G_GametypeHasTeams() && !G_GametypeHasSpectators())
|
||||
if (!G_GametypeHasSpectators())
|
||||
return;
|
||||
|
||||
// Don't duplicate efforts.
|
||||
|
|
@ -3703,148 +3472,105 @@ void P_SetPlayerSpectator(INT32 playernum)
|
|||
|
||||
players[playernum].spectator = true;
|
||||
players[playernum].pflags &= ~PF_WANTSTOJOIN;
|
||||
G_AssignTeam(&players[playernum], TEAM_UNASSIGNED);
|
||||
|
||||
players[playernum].playerstate = PST_REBORN;
|
||||
}
|
||||
|
||||
//todo: This and the other teamchange functions are getting too long and messy. Needs cleaning.
|
||||
static void Got_Teamchange(const UINT8 **cp, INT32 playernum)
|
||||
static void Got_Spectate(const UINT8 **cp, INT32 playernum)
|
||||
{
|
||||
changeteam_union NetPacket;
|
||||
boolean error = false, wasspectator = false;
|
||||
NetPacket.value.l = NetPacket.value.b = READINT16(*cp);
|
||||
UINT8 edit_player = READUINT8(*cp);
|
||||
UINT8 desired_state = READUINT8(*cp);
|
||||
|
||||
if (!G_GametypeHasTeams() && !G_GametypeHasSpectators()) //Make sure you're in the right gametype.
|
||||
if (playeringame[edit_player] == false)
|
||||
{
|
||||
// this should never happen unless the client is hacked/buggy
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (playernum != playerconsole[edit_player]
|
||||
&& playernum != serverplayer
|
||||
&& IsPlayerAdmin(playernum) == false)
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Illegal spectate command received from player %s\n"), player_names[playernum]);
|
||||
if (server)
|
||||
{
|
||||
SendKick(playernum, KICK_MSG_CON_FAIL);
|
||||
}
|
||||
|
||||
if (NetPacket.packet.verification) // Special marker that the server sent the request
|
||||
{
|
||||
if (playernum != serverplayer && (!IsPlayerAdmin(playernum)))
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]);
|
||||
if (server)
|
||||
SendKick(playernum, KICK_MSG_CON_FAIL);
|
||||
return;
|
||||
}
|
||||
playernum = NetPacket.packet.playernum;
|
||||
}
|
||||
|
||||
// Prevent multiple changes in one go.
|
||||
if (players[playernum].spectator && !(players[playernum].pflags & PF_WANTSTOJOIN) && !NetPacket.packet.newteam)
|
||||
return;
|
||||
else if (G_GametypeHasTeams())
|
||||
{
|
||||
if (NetPacket.packet.newteam && (NetPacket.packet.newteam == (unsigned)players[playernum].ctfteam))
|
||||
return;
|
||||
}
|
||||
else if (G_GametypeHasSpectators())
|
||||
{
|
||||
if (!players[playernum].spectator && NetPacket.packet.newteam == 3)
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (playernum != serverplayer && (!IsPlayerAdmin(playernum)))
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]);
|
||||
if (server)
|
||||
SendKick(playernum, KICK_MSG_CON_FAIL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't switch team, just go away, please, go awaayyyy, aaauuauugghhhghgh
|
||||
if (!LUA_HookTeamSwitch(&players[playernum], NetPacket.packet.newteam, players[playernum].spectator, NetPacket.packet.autobalance, NetPacket.packet.scrambled))
|
||||
return;
|
||||
|
||||
//Make sure that the right team number is sent. Keep in mind that normal clients cannot change to certain teams in certain gametypes.
|
||||
#ifdef PARANOIA
|
||||
if (!G_GametypeHasTeams() && !G_GametypeHasSpectators())
|
||||
I_Error("Invalid gametype after initial checks!");
|
||||
#endif
|
||||
|
||||
if (!cv_allowteamchange.value)
|
||||
if (G_GametypeHasSpectators() == false)
|
||||
{
|
||||
if (!NetPacket.packet.verification && NetPacket.packet.newteam)
|
||||
error = true; //Only admin can change status, unless changing to spectator.
|
||||
}
|
||||
|
||||
if (server && ((NetPacket.packet.newteam < 0 || NetPacket.packet.newteam > 3) || error))
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]);
|
||||
SendKick(playernum, KICK_MSG_CON_FAIL);
|
||||
return;
|
||||
}
|
||||
|
||||
//Safety first!
|
||||
// (not respawning spectators here...)
|
||||
wasspectator = (players[playernum].spectator == true);
|
||||
player_t *const player = &players[edit_player];
|
||||
|
||||
if (!wasspectator)
|
||||
// Safety first!
|
||||
const boolean was_spectator = (player->spectator == true);
|
||||
if (was_spectator == false)
|
||||
{
|
||||
if (gamestate == GS_LEVEL && players[playernum].mo)
|
||||
if (gamestate == GS_LEVEL && player->mo != NULL)
|
||||
{
|
||||
// The following will call P_SetPlayerSpectator if successful
|
||||
P_DamageMobj(players[playernum].mo, NULL, NULL, 1, DMG_SPECTATOR);
|
||||
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_SPECTATOR);
|
||||
}
|
||||
|
||||
//...but because the above could return early under some contexts, we try again here
|
||||
P_SetPlayerSpectator(playernum);
|
||||
P_SetPlayerSpectator(edit_player);
|
||||
HU_AddChatText(va("\x82*%s became a spectator.", player_names[edit_player]), false);
|
||||
}
|
||||
|
||||
//Now that we've done our error checking and killed the player
|
||||
//if necessary, put the player on the correct team/status.
|
||||
|
||||
// This serves us in both teamchange contexts.
|
||||
if (NetPacket.packet.newteam != 0)
|
||||
if (desired_state != 0)
|
||||
{
|
||||
players[playernum].pflags |= PF_WANTSTOJOIN;
|
||||
player->pflags |= PF_WANTSTOJOIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
players[playernum].pflags &= ~PF_WANTSTOJOIN;
|
||||
player->pflags &= ~PF_WANTSTOJOIN;
|
||||
}
|
||||
|
||||
if (G_GametypeHasTeams())
|
||||
if (gamestate != GS_LEVEL || was_spectator == true)
|
||||
{
|
||||
// This one is, of course, specific.
|
||||
players[playernum].ctfteam = NetPacket.packet.newteam;
|
||||
}
|
||||
|
||||
if (NetPacket.packet.autobalance)
|
||||
{
|
||||
if (NetPacket.packet.newteam == 1)
|
||||
CONS_Printf(M_GetText("%s was autobalanced to the %c%s%c.\n"), player_names[playernum], '\x85', M_GetText("Red Team"), '\x80');
|
||||
else if (NetPacket.packet.newteam == 2)
|
||||
CONS_Printf(M_GetText("%s was autobalanced to the %c%s%c.\n"), player_names[playernum], '\x84', M_GetText("Blue Team"), '\x80');
|
||||
}
|
||||
else if (NetPacket.packet.scrambled)
|
||||
{
|
||||
if (NetPacket.packet.newteam == 1)
|
||||
CONS_Printf(M_GetText("%s was scrambled to the %c%s%c.\n"), player_names[playernum], '\x85', M_GetText("Red Team"), '\x80');
|
||||
else if (NetPacket.packet.newteam == 2)
|
||||
CONS_Printf(M_GetText("%s was scrambled to the %c%s%c.\n"), player_names[playernum], '\x84', M_GetText("Blue Team"), '\x80');
|
||||
}
|
||||
else if (NetPacket.packet.newteam == 1)
|
||||
{
|
||||
CONS_Printf(M_GetText("%s switched to the %c%s%c.\n"), player_names[playernum], '\x85', M_GetText("Red Team"), '\x80');
|
||||
}
|
||||
else if (NetPacket.packet.newteam == 2)
|
||||
{
|
||||
CONS_Printf(M_GetText("%s switched to the %c%s%c.\n"), player_names[playernum], '\x84', M_GetText("Blue Team"), '\x80');
|
||||
}
|
||||
else if (NetPacket.packet.newteam == 0 && !wasspectator)
|
||||
HU_AddChatText(va("\x82*%s became a spectator.", player_names[playernum]), false); // "entered the game" text was moved to P_SpectatorJoinGame
|
||||
|
||||
if (gamestate != GS_LEVEL || wasspectator == true)
|
||||
return;
|
||||
}
|
||||
|
||||
FinalisePlaystateChange(playernum);
|
||||
FinalisePlaystateChange(edit_player);
|
||||
}
|
||||
|
||||
static void Got_TeamChange(const UINT8 **cp, INT32 playernum)
|
||||
{
|
||||
UINT8 new_team = READUINT8(*cp);
|
||||
UINT8 edit_player = READUINT8(*cp);
|
||||
|
||||
if (playernum != serverplayer && IsPlayerAdmin(playernum) == false)
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]);
|
||||
if (server)
|
||||
{
|
||||
SendKick(playernum, KICK_MSG_CON_FAIL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (G_GametypeHasTeams() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (new_team >= TEAM__MAX)
|
||||
{
|
||||
new_team = TEAM_UNASSIGNED;
|
||||
}
|
||||
|
||||
player_t *const player = &players[edit_player];
|
||||
G_AssignTeam(player, new_team);
|
||||
|
||||
if (player->team == TEAM_UNASSIGNED)
|
||||
{
|
||||
// auto assign
|
||||
G_AutoAssignTeam(player);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -5430,22 +5156,6 @@ void D_GameTypeChanged(INT32 lastgametype)
|
|||
if (oldgt && newgt && (lastgametype != gametype))
|
||||
CONS_Printf(M_GetText("Gametype was changed from %s to %s\n"), oldgt, newgt);
|
||||
}
|
||||
|
||||
// don't retain teams in other modes or between changes from ctf to team match.
|
||||
// also, stop any and all forms of team scrambling that might otherwise take place.
|
||||
if (G_GametypeHasTeams())
|
||||
{
|
||||
INT32 i;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
if (playeringame[i])
|
||||
players[i].ctfteam = 0;
|
||||
|
||||
if (server || (IsPlayerAdmin(consoleplayer)))
|
||||
{
|
||||
CV_StealthSetValue(&cv_teamscramble, 0);
|
||||
teamscramble = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Gravity_OnChange(void);
|
||||
|
|
@ -5480,165 +5190,6 @@ void SoundTest_OnChange(void)
|
|||
S_StartSound(NULL, cv_soundtest.value);
|
||||
}
|
||||
|
||||
void AutoBalance_OnChange(void);
|
||||
void AutoBalance_OnChange(void)
|
||||
{
|
||||
autobalance = (INT16)cv_autobalance.value;
|
||||
}
|
||||
|
||||
void TeamScramble_OnChange(void);
|
||||
void TeamScramble_OnChange(void)
|
||||
{
|
||||
INT16 i = 0, j = 0, playercount = 0;
|
||||
boolean repick = true;
|
||||
INT32 blue = 0, red = 0;
|
||||
INT32 maxcomposition = 0;
|
||||
INT16 newteam = 0;
|
||||
INT32 retries = 0;
|
||||
boolean success = false;
|
||||
|
||||
// Don't trigger outside level or intermission!
|
||||
if (!(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING))
|
||||
return;
|
||||
|
||||
if (!cv_teamscramble.value)
|
||||
teamscramble = 0;
|
||||
|
||||
if (!G_GametypeHasTeams() && (server || IsPlayerAdmin(consoleplayer)))
|
||||
{
|
||||
CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n"));
|
||||
CV_StealthSetValue(&cv_teamscramble, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// If a team scramble is already in progress, do not allow another one to be started!
|
||||
if (teamscramble)
|
||||
return;
|
||||
|
||||
retryscramble:
|
||||
|
||||
// Clear related global variables. These will get used again in p_tick.c/y_inter.c as the teams are scrambled.
|
||||
memset(&scrambleplayers, 0, sizeof(scrambleplayers));
|
||||
memset(&scrambleteams, 0, sizeof(scrambleplayers));
|
||||
scrambletotal = scramblecount = 0;
|
||||
blue = red = maxcomposition = newteam = playercount = 0;
|
||||
repick = true;
|
||||
|
||||
// Put each player's node in the array.
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i] && !players[i].spectator)
|
||||
{
|
||||
scrambleplayers[playercount] = i;
|
||||
playercount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (playercount < 2)
|
||||
{
|
||||
CV_StealthSetValue(&cv_teamscramble, 0);
|
||||
return; // Don't scramble one or zero players.
|
||||
}
|
||||
|
||||
// Randomly place players on teams.
|
||||
if (cv_teamscramble.value == 1)
|
||||
{
|
||||
maxcomposition = playercount / 2;
|
||||
|
||||
// Now randomly assign players to teams.
|
||||
// If the teams get out of hand, assign the rest to the other team.
|
||||
for (i = 0; i < playercount; i++)
|
||||
{
|
||||
if (repick)
|
||||
newteam = (INT16)((M_RandomByte() % 2) + 1);
|
||||
|
||||
// One team has the most players they can get, assign the rest to the other team.
|
||||
if (red == maxcomposition || blue == maxcomposition)
|
||||
{
|
||||
if (red == maxcomposition)
|
||||
newteam = 2;
|
||||
else //if (blue == maxcomposition)
|
||||
newteam = 1;
|
||||
|
||||
repick = false;
|
||||
}
|
||||
|
||||
scrambleteams[i] = newteam;
|
||||
|
||||
if (newteam == 1)
|
||||
red++;
|
||||
else
|
||||
blue++;
|
||||
}
|
||||
}
|
||||
else if (cv_teamscramble.value == 2) // Same as before, except split teams based on current score.
|
||||
{
|
||||
// Now, sort the array based on points scored.
|
||||
for (i = 1; i < playercount; i++)
|
||||
{
|
||||
for (j = i; j < playercount; j++)
|
||||
{
|
||||
INT16 tempplayer = 0;
|
||||
|
||||
if ((players[scrambleplayers[i-1]].score > players[scrambleplayers[j]].score))
|
||||
{
|
||||
tempplayer = scrambleplayers[i-1];
|
||||
scrambleplayers[i-1] = scrambleplayers[j];
|
||||
scrambleplayers[j] = tempplayer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now assign players to teams based on score. Scramble in pairs.
|
||||
// If there is an odd number, one team will end up with the unlucky slob who has no points. =(
|
||||
for (i = 0; i < playercount; i++)
|
||||
{
|
||||
if (repick)
|
||||
{
|
||||
newteam = (INT16)((M_RandomByte() % 2) + 1);
|
||||
repick = false;
|
||||
}
|
||||
// (i != 2) means it does ABBABABA, instead of ABABABAB.
|
||||
// Team A gets 1st, 4th, 6th, 8th.
|
||||
// Team B gets 2nd, 3rd, 5th, 7th.
|
||||
// So 1st on one team, 2nd/3rd on the other, then alternates afterwards.
|
||||
// Sounds strange on paper, but works really well in practice!
|
||||
else if (i != 2)
|
||||
{
|
||||
// We will only randomly pick the team for the first guy.
|
||||
// Otherwise, just alternate back and forth, distributing players.
|
||||
newteam = 3 - newteam;
|
||||
}
|
||||
|
||||
scrambleteams[i] = newteam;
|
||||
}
|
||||
}
|
||||
|
||||
// Check to see if our random selection actually
|
||||
// changed anybody. If not, we run through and try again.
|
||||
for (i = 0; i < playercount; i++)
|
||||
{
|
||||
if (players[scrambleplayers[i]].ctfteam != scrambleteams[i])
|
||||
success = true;
|
||||
}
|
||||
|
||||
if (!success && retries < 5)
|
||||
{
|
||||
retries++;
|
||||
goto retryscramble; //try again
|
||||
}
|
||||
|
||||
// Display a witty message, but only during scrambles specifically triggered by an admin.
|
||||
if (cv_teamscramble.value)
|
||||
{
|
||||
scrambletotal = playercount;
|
||||
teamscramble = (INT16)cv_teamscramble.value;
|
||||
|
||||
if (!(gamestate == GS_INTERMISSION && cv_scrambleonchange.value))
|
||||
CONS_Printf(M_GetText("Teams will be scrambled next round.\n"));
|
||||
}
|
||||
}
|
||||
|
||||
static void Command_Showmap_f(void)
|
||||
{
|
||||
if (gamestate == GS_LEVEL)
|
||||
|
|
|
|||
|
|
@ -57,10 +57,6 @@ extern UINT32 timelimitintics, extratimeintics, secretextratime;
|
|||
extern UINT32 g_pointlimit;
|
||||
extern consvar_t cv_allowexitlevel;
|
||||
|
||||
extern consvar_t cv_autobalance;
|
||||
extern consvar_t cv_teamscramble;
|
||||
extern consvar_t cv_scrambleonchange;
|
||||
|
||||
extern consvar_t cv_netstat;
|
||||
|
||||
extern consvar_t cv_countdowntime;
|
||||
|
|
@ -83,6 +79,7 @@ extern consvar_t cv_karthorns;
|
|||
extern consvar_t cv_kartbot;
|
||||
extern consvar_t cv_karteliminatelast;
|
||||
extern consvar_t cv_thunderdome;
|
||||
extern consvar_t cv_teamplay;
|
||||
extern consvar_t cv_kartusepwrlv;
|
||||
#ifdef DEVELOP
|
||||
extern consvar_t cv_kartencoremap;
|
||||
|
|
@ -155,7 +152,7 @@ typedef enum
|
|||
XD_ADDFILE, // 8
|
||||
XD_PAUSE, // 9
|
||||
XD_ADDPLAYER, // 10
|
||||
XD_TEAMCHANGE, // 11
|
||||
XD_SPECTATE, // 11
|
||||
XD_CLEARSCORES, // 12
|
||||
XD_VERIFIED, // 13
|
||||
XD_RANDOMSEED, // 14
|
||||
|
|
@ -187,52 +184,13 @@ typedef enum
|
|||
XD_MAPQUEUE, // 38
|
||||
XD_CALLZVOTE, // 39
|
||||
XD_SETZVOTE, // 40
|
||||
XD_TEAMCHANGE, // 41
|
||||
|
||||
MAXNETXCMD
|
||||
} netxcmd_t;
|
||||
|
||||
extern const char *netxcmdnames[MAXNETXCMD - 1];
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma pack(1)
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4214)
|
||||
#endif
|
||||
|
||||
//Packet composition for Command_TeamChange_f() ServerTeamChange, etc.
|
||||
//bitwise structs make packing bits a little easier, but byte alignment harder?
|
||||
//todo: decide whether to make the other netcommands conform, or just get rid of this experiment.
|
||||
struct changeteam_packet_t {
|
||||
UINT32 playernum : 5; // value 0 to 31
|
||||
UINT32 newteam : 5; // value 0 to 31
|
||||
UINT32 verification : 1; // value 0 to 1
|
||||
UINT32 autobalance : 1; // value 0 to 1
|
||||
UINT32 scrambled : 1; // value 0 to 1
|
||||
} ATTRPACK;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(default : 4214)
|
||||
#endif
|
||||
|
||||
struct changeteam_value_t {
|
||||
UINT16 l; // liitle endian
|
||||
UINT16 b; // big enian
|
||||
} ATTRPACK;
|
||||
|
||||
//Since we do not want other files/modules to know about this data buffer we union it here with a Short Int.
|
||||
//Other files/modules will hand the INT16 back to us and we will decode it here.
|
||||
//We don't have to use a union, but we would then send four bytes instead of two.
|
||||
typedef union {
|
||||
changeteam_packet_t packet;
|
||||
changeteam_value_t value;
|
||||
} ATTRPACK changeteam_union;
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
// add game commands, needs cleanup
|
||||
void D_RegisterServerCommands(void);
|
||||
void D_RegisterClientCommands(void);
|
||||
|
|
|
|||
|
|
@ -671,6 +671,11 @@ struct player_t
|
|||
UINT8 carry;
|
||||
UINT16 dye;
|
||||
|
||||
INT32 prefskin; // Queued skin change
|
||||
UINT16 prefcolor; // Queued color change
|
||||
INT32 preffollower; // Queued follower change
|
||||
UINT16 preffollowercolor; // Queued follower color change
|
||||
|
||||
// SRB2kart stuff
|
||||
INT32 karthud[NUMKARTHUD];
|
||||
|
||||
|
|
@ -678,12 +683,19 @@ struct player_t
|
|||
UINT8 position; // Used for Kart positions, mostly for deterministic stuff
|
||||
UINT8 oldposition; // Used for taunting when you pass someone
|
||||
UINT8 positiondelay; // Used for position number, so it can grow when passing
|
||||
|
||||
UINT8 teamposition; // Position, but only against other teams -- not your own.
|
||||
UINT8 teamimportance; // Opposite of team position x2, with +1 for being in 1st.
|
||||
|
||||
UINT32 distancetofinish;
|
||||
UINT32 distancetofinishprev;
|
||||
|
||||
UINT32 lastpickupdistance; // Anti item set farming
|
||||
UINT8 lastpickuptype;
|
||||
|
||||
waypoint_t *currentwaypoint;
|
||||
waypoint_t *nextwaypoint;
|
||||
|
||||
respawnvars_t respawn; // Respawn info
|
||||
mobj_t *ringShooter; // DEZ respawner object
|
||||
tic_t airtime; // Used to track just air time, but has evolved over time into a general "karted" timer. Rename this variable?
|
||||
|
|
@ -926,7 +938,7 @@ struct player_t
|
|||
INT32 cheatchecknum; // The number of the last cheatcheck you hit
|
||||
INT32 checkpointId; // Players respawn here, objects/checkpoint.cpp
|
||||
|
||||
UINT8 ctfteam; // 0 == Spectator, 1 == Red, 2 == Blue
|
||||
UINT8 team; // 0 == Spectator, 1 == Red, 2 == Blue
|
||||
|
||||
UINT8 checkskip; // Skipping checkpoints? Oh no no no
|
||||
|
||||
|
|
|
|||
|
|
@ -3551,22 +3551,6 @@ void readmaincfg(MYFILE *f, boolean mainfile)
|
|||
COM_BufInsertText(W_CacheLumpNum(lumpnum, PU_CACHE));
|
||||
}
|
||||
}
|
||||
else if (fastcmp(word, "REDTEAM"))
|
||||
{
|
||||
skincolor_redteam = (UINT16)get_number(word2);
|
||||
}
|
||||
else if (fastcmp(word, "BLUETEAM"))
|
||||
{
|
||||
skincolor_blueteam = (UINT16)get_number(word2);
|
||||
}
|
||||
else if (fastcmp(word, "REDRING"))
|
||||
{
|
||||
skincolor_redring = (UINT16)get_number(word2);
|
||||
}
|
||||
else if (fastcmp(word, "BLUERING"))
|
||||
{
|
||||
skincolor_bluering = (UINT16)get_number(word2);
|
||||
}
|
||||
else if (fastcmp(word, "INVULNTICS"))
|
||||
{
|
||||
invulntics = (UINT16)get_number(word2);
|
||||
|
|
|
|||
|
|
@ -544,6 +544,7 @@ typedef enum
|
|||
DBG_LUA = 0x00000800,
|
||||
DBG_RNG = 0x00001000,
|
||||
DBG_DEMO = 0x00002000,
|
||||
DBG_TEAMS = 0x00004000,
|
||||
} debugFlags_t;
|
||||
|
||||
struct debugFlagNames_s
|
||||
|
|
|
|||
|
|
@ -277,9 +277,6 @@ extern UINT8 tutorialchallenge;
|
|||
#define TUTORIALSKIP_FAILED 1
|
||||
#define TUTORIALSKIP_INPROGRESS 2
|
||||
|
||||
// CTF colors.
|
||||
extern UINT16 skincolor_redteam, skincolor_blueteam, skincolor_redring, skincolor_bluering;
|
||||
|
||||
extern boolean exitfadestarted;
|
||||
|
||||
struct scene_t
|
||||
|
|
@ -762,8 +759,24 @@ extern INT32 nummaprings; //keep track of spawned rings/coins
|
|||
extern UINT8 nummapspraycans;
|
||||
extern UINT16 numchallengedestructibles;
|
||||
|
||||
extern UINT32 bluescore; ///< Blue Team Scores
|
||||
extern UINT32 redscore; ///< Red Team Scores
|
||||
// Teamplay
|
||||
typedef enum
|
||||
{
|
||||
TEAM_UNASSIGNED = 0,
|
||||
TEAM_ORANGE,
|
||||
TEAM_BLUE,
|
||||
TEAM__MAX
|
||||
} team_e;
|
||||
|
||||
struct teaminfo_t
|
||||
{
|
||||
const char *name;
|
||||
skincolornum_t color;
|
||||
UINT32 chat_color;
|
||||
};
|
||||
|
||||
extern teaminfo_t g_teaminfo[TEAM__MAX];
|
||||
extern UINT32 g_teamscores[TEAM__MAX];
|
||||
|
||||
// Eliminates unnecessary searching.
|
||||
extern boolean CheckForBustableBlocks;
|
||||
|
|
@ -845,19 +858,12 @@ extern struct maplighting
|
|||
angle_t angle;
|
||||
} maplighting;
|
||||
|
||||
//for CTF balancing
|
||||
extern INT16 autobalance;
|
||||
extern INT16 teamscramble;
|
||||
extern INT16 scrambleplayers[MAXPLAYERS]; //for CTF team scramble
|
||||
extern INT16 scrambleteams[MAXPLAYERS]; //for CTF team scramble
|
||||
extern INT16 scrambletotal; //for CTF team scramble
|
||||
extern INT16 scramblecount; //for CTF team scramble
|
||||
|
||||
// SRB2kart
|
||||
extern UINT8 numlaps;
|
||||
extern UINT8 gamespeed;
|
||||
extern boolean franticitems;
|
||||
extern boolean encoremode, prevencoremode;
|
||||
extern boolean g_teamplay;
|
||||
|
||||
extern tic_t wantedcalcdelay;
|
||||
extern tic_t itemCooldowns[NUMKARTITEMS - 1];
|
||||
|
|
@ -891,8 +897,7 @@ extern tic_t gametic;
|
|||
|
||||
// Player spawn spots.
|
||||
extern mapthing_t *playerstarts[MAXPLAYERS]; // Cooperative
|
||||
extern mapthing_t *bluectfstarts[MAXPLAYERS]; // CTF
|
||||
extern mapthing_t *redctfstarts[MAXPLAYERS]; // CTF
|
||||
extern mapthing_t *teamstarts[TEAM__MAX][MAXPLAYERS]; // Teamplay
|
||||
extern mapthing_t *faultstart; // Kart Fault
|
||||
|
||||
#define TUBEWAYPOINTSEQUENCESIZE 256
|
||||
|
|
|
|||
473
src/g_game.c
473
src/g_game.c
|
|
@ -182,11 +182,6 @@ char * podiummap = NULL; // map to load for podium
|
|||
char * tutorialchallengemap = NULL; // map to load for tutorial skip
|
||||
UINT8 tutorialchallenge = TUTORIALSKIP_NONE;
|
||||
|
||||
UINT16 skincolor_redteam = SKINCOLOR_RED;
|
||||
UINT16 skincolor_blueteam = SKINCOLOR_BLUE;
|
||||
UINT16 skincolor_redring = SKINCOLOR_RASPBERRY;
|
||||
UINT16 skincolor_bluering = SKINCOLOR_PERIWINKLE;
|
||||
|
||||
boolean exitfadestarted = false;
|
||||
|
||||
cutscene_t *cutscenes[128];
|
||||
|
|
@ -222,7 +217,7 @@ INT32 luabanks[NUM_LUABANKS];
|
|||
// Temporary holding place for nights data for the current map
|
||||
//nightsdata_t ntemprecords;
|
||||
|
||||
UINT32 bluescore, redscore; // CTF and Team Match team scores
|
||||
UINT32 g_teamscores[TEAM__MAX];
|
||||
|
||||
// ring count... for PERFECT!
|
||||
INT32 nummaprings = 0;
|
||||
|
|
@ -289,13 +284,6 @@ fixed_t mapobjectscale;
|
|||
|
||||
struct maplighting maplighting;
|
||||
|
||||
INT16 autobalance; //for CTF team balance
|
||||
INT16 teamscramble; //for CTF team scramble
|
||||
INT16 scrambleplayers[MAXPLAYERS]; //for CTF team scramble
|
||||
INT16 scrambleteams[MAXPLAYERS]; //for CTF team scramble
|
||||
INT16 scrambletotal; //for CTF team scramble
|
||||
INT16 scramblecount; //for CTF team scramble
|
||||
|
||||
// SRB2Kart
|
||||
// Cvars that we don't want changed mid-game
|
||||
UINT8 numlaps; // Removed from Cvar hell
|
||||
|
|
@ -304,6 +292,10 @@ boolean encoremode = false; // Encore Mode currently enabled?
|
|||
boolean prevencoremode;
|
||||
boolean franticitems; // Frantic items currently enabled?
|
||||
|
||||
// Server wants to enable teams?
|
||||
// (Certain gametypes can override this -- prefer using G_GametypeHasTeams().)
|
||||
boolean g_teamplay;
|
||||
|
||||
// Voting system
|
||||
UINT16 g_voteLevels[4][2]; // Levels that were rolled by the host
|
||||
SINT8 g_votes[VOTE_TOTAL]; // Each player's vote
|
||||
|
|
@ -1559,7 +1551,7 @@ boolean G_CouldView(INT32 playernum)
|
|||
// SRB2Kart: we have no team-based modes, YET...
|
||||
if (G_GametypeHasTeams())
|
||||
{
|
||||
if (players[consoleplayer].ctfteam && player->ctfteam != players[consoleplayer].ctfteam)
|
||||
if (players[consoleplayer].spectator == false && player->team != players[consoleplayer].team)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1792,6 +1784,73 @@ void G_FixCamera(UINT8 view)
|
|||
R_ResetViewInterpolation(view);
|
||||
}
|
||||
|
||||
void G_UpdatePlayerPreferences(player_t *const player)
|
||||
{
|
||||
if (demo.playback)
|
||||
return;
|
||||
|
||||
// set skin
|
||||
INT32 new_skin = player->prefskin;
|
||||
if (K_CanChangeRules(true) == true && cv_forceskin.value >= 0)
|
||||
{
|
||||
// Server wants everyone to use the same player
|
||||
new_skin = cv_forceskin.value;
|
||||
}
|
||||
|
||||
if (player->skin != new_skin)
|
||||
{
|
||||
SetPlayerSkinByNum(player - players, new_skin);
|
||||
}
|
||||
|
||||
// set color
|
||||
UINT16 new_color = player->prefcolor;
|
||||
if (new_color == SKINCOLOR_NONE)
|
||||
{
|
||||
new_color = skins[player->skin].prefcolor;
|
||||
}
|
||||
|
||||
if (G_GametypeHasTeams() == true && player->team != TEAM_UNASSIGNED)
|
||||
{
|
||||
new_color = g_teaminfo[player->team].color;
|
||||
}
|
||||
|
||||
if (player->skincolor != new_color)
|
||||
{
|
||||
player->skincolor = new_color;
|
||||
K_KartResetPlayerColor(player);
|
||||
}
|
||||
|
||||
// set follower
|
||||
if (player->followerskin != player->preffollower)
|
||||
{
|
||||
K_SetFollowerByNum(player - players, player->preffollower);
|
||||
}
|
||||
|
||||
// set follower color
|
||||
if (player->followercolor != player->preffollowercolor)
|
||||
{
|
||||
// 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 K_HandleFollower.
|
||||
player->followercolor = player->preffollowercolor;
|
||||
}
|
||||
}
|
||||
|
||||
void G_UpdateAllPlayerPreferences(void)
|
||||
{
|
||||
INT32 i;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i] == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
G_UpdatePlayerPreferences(&players[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// G_Ticker
|
||||
// Make ticcmd_ts for the players.
|
||||
|
|
@ -1885,6 +1944,13 @@ void G_Ticker(boolean run)
|
|||
K_UpdateAllPlayerPositions();
|
||||
}
|
||||
}
|
||||
else if (Playing() && !Y_IntermissionPlayerLock())
|
||||
{
|
||||
if (run)
|
||||
{
|
||||
G_UpdateAllPlayerPreferences();
|
||||
}
|
||||
}
|
||||
|
||||
P_MapEnd();
|
||||
|
||||
|
|
@ -2136,7 +2202,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
|
||||
INT32 pflags;
|
||||
|
||||
UINT8 ctfteam;
|
||||
UINT8 team;
|
||||
|
||||
INT32 cheatchecknum;
|
||||
INT32 exiting;
|
||||
|
|
@ -2200,6 +2266,11 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
|
||||
tic_t laptime[LAP__MAX];
|
||||
|
||||
UINT16 prefcolor;
|
||||
INT32 prefskin;
|
||||
UINT16 preffollowercolor;
|
||||
INT32 preffollower;
|
||||
|
||||
INT32 i;
|
||||
|
||||
// This needs to be first, to permit it to wipe extra information
|
||||
|
|
@ -2212,7 +2283,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
|
||||
score = players[player].score;
|
||||
lives = players[player].lives;
|
||||
ctfteam = players[player].ctfteam;
|
||||
team = players[player].team;
|
||||
|
||||
splitscreenindex = players[player].splitscreenindex;
|
||||
spectator = players[player].spectator;
|
||||
|
|
@ -2223,6 +2294,11 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
skincolor = players[player].skincolor;
|
||||
skin = players[player].skin;
|
||||
|
||||
prefcolor = players[player].prefcolor;
|
||||
prefskin = players[player].prefskin;
|
||||
preffollower = players[player].preffollower;
|
||||
preffollowercolor = players[player].preffollowercolor;
|
||||
|
||||
if (betweenmaps)
|
||||
{
|
||||
fakeskin = MAXSKINS;
|
||||
|
|
@ -2463,7 +2539,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
p->roundscore = roundscore;
|
||||
p->lives = lives;
|
||||
p->pflags = pflags;
|
||||
p->ctfteam = ctfteam;
|
||||
p->team = team;
|
||||
p->jointime = jointime;
|
||||
p->splitscreenindex = splitscreenindex;
|
||||
p->spectator = spectator;
|
||||
|
|
@ -2477,6 +2553,11 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
p->skincolor = skincolor;
|
||||
p->skin = skin;
|
||||
|
||||
p->prefcolor = prefcolor;
|
||||
p->prefskin = prefskin;
|
||||
p->preffollower = preffollower;
|
||||
p->preffollowercolor = preffollowercolor;
|
||||
|
||||
p->fakeskin = fakeskin;
|
||||
p->kartspeed = kartspeed;
|
||||
p->kartweight = kartweight;
|
||||
|
|
@ -2559,36 +2640,17 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
//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.
|
||||
|
||||
if (G_GametypeHasTeams() == true
|
||||
&& p->team == TEAM_UNASSIGNED
|
||||
&& p->spectator == false)
|
||||
{
|
||||
// No team?
|
||||
G_AutoAssignTeam(p);
|
||||
}
|
||||
|
||||
p->playerstate = PST_LIVE;
|
||||
p->panim = PA_STILL; // standing animation
|
||||
|
||||
// Check to make sure their color didn't change somehow...
|
||||
if (G_GametypeHasTeams())
|
||||
{
|
||||
if (p->ctfteam == 1 && p->skincolor != skincolor_redteam)
|
||||
{
|
||||
for (i = 0; i <= splitscreen; i++)
|
||||
{
|
||||
if (p == &players[g_localplayers[i]])
|
||||
{
|
||||
CV_SetValue(&cv_playercolor[i], skincolor_redteam);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (p->ctfteam == 2 && p->skincolor != skincolor_blueteam)
|
||||
{
|
||||
for (i = 0; i <= splitscreen; i++)
|
||||
{
|
||||
if (p == &players[g_localplayers[i]])
|
||||
{
|
||||
CV_SetValue(&cv_playercolor[i], skincolor_blueteam);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (p->spectator == false && !betweenmaps)
|
||||
{
|
||||
if (enteredGame == true)
|
||||
|
|
@ -2698,56 +2760,78 @@ void G_MovePlayerToSpawnOrCheatcheck(INT32 playernum)
|
|||
|
||||
mapthing_t *G_FindTeamStart(INT32 playernum)
|
||||
{
|
||||
const boolean doprints = P_IsPartyPlayer(&players[playernum]);
|
||||
INT32 i,j;
|
||||
const boolean do_prints = P_IsPartyPlayer(&players[playernum]);
|
||||
INT32 i, j;
|
||||
|
||||
if (!numredctfstarts && !numbluectfstarts) //why even bother, eh?
|
||||
for (i = 0; i < TEAM__MAX; i++)
|
||||
{
|
||||
if ((gametyperules & GTR_TEAMSTARTS) && doprints)
|
||||
CONS_Alert(CONS_WARNING, M_GetText("No CTF starts in this map!\n"));
|
||||
if (numteamstarts[i] > 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == TEAM__MAX)
|
||||
{
|
||||
// No team starts are counted?
|
||||
// Why even bother, eh?
|
||||
|
||||
if (do_prints == true && (gametyperules & GTR_TEAMSTARTS) == GTR_TEAMSTARTS)
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, M_GetText("No team starts in this map!\n"));
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((!players[playernum].ctfteam && numredctfstarts && (!numbluectfstarts || P_RandomChance(PR_PLAYERSTARTS, FRACUNIT/2))) || players[playernum].ctfteam == 1) //red
|
||||
UINT8 use_team = players[playernum].team;
|
||||
if (players[playernum].spectator == true)
|
||||
{
|
||||
if (!numredctfstarts)
|
||||
// Spawn at any team start as a spectator.
|
||||
i = P_RandomKey(PR_PLAYERSTARTS, TEAM__MAX);
|
||||
|
||||
for (j = 0; j < TEAM__MAX; j++)
|
||||
{
|
||||
if (doprints)
|
||||
CONS_Alert(CONS_WARNING, M_GetText("No Red Team starts in this map!\n"));
|
||||
return NULL;
|
||||
if (numteamstarts[i] > 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
i++;
|
||||
if (i >= TEAM__MAX)
|
||||
{
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < 32; j++)
|
||||
use_team = i;
|
||||
}
|
||||
|
||||
if (numteamstarts[use_team] <= 0)
|
||||
{
|
||||
if (do_prints == true)
|
||||
{
|
||||
i = P_RandomKey(PR_PLAYERSTARTS, numredctfstarts);
|
||||
if (G_CheckSpot(playernum, redctfstarts[i]))
|
||||
return redctfstarts[i];
|
||||
CONS_Alert(CONS_WARNING, M_GetText("No %s Team starts in this map!\n"), g_teaminfo[use_team].name);
|
||||
}
|
||||
|
||||
if (doprints)
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Could not spawn at any Red Team starts!\n"));
|
||||
return NULL;
|
||||
}
|
||||
else if (!players[playernum].ctfteam || players[playernum].ctfteam == 2) //blue
|
||||
{
|
||||
if (!numbluectfstarts)
|
||||
{
|
||||
if (doprints)
|
||||
CONS_Alert(CONS_WARNING, M_GetText("No Blue Team starts in this map!\n"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (j = 0; j < 32; j++)
|
||||
for (j = 0; j < 32; j++)
|
||||
{
|
||||
i = P_RandomKey(PR_PLAYERSTARTS, numteamstarts[use_team]);
|
||||
|
||||
if (G_CheckSpot(playernum, teamstarts[use_team][i]))
|
||||
{
|
||||
i = P_RandomKey(PR_PLAYERSTARTS, numbluectfstarts);
|
||||
if (G_CheckSpot(playernum, bluectfstarts[i]))
|
||||
return bluectfstarts[i];
|
||||
return teamstarts[use_team][i];
|
||||
}
|
||||
if (doprints)
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Could not spawn at any Blue Team starts!\n"));
|
||||
return NULL;
|
||||
}
|
||||
//should never be reached but it gets stuff to shut up
|
||||
|
||||
if (do_prints == true)
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Could not spawn at any %s Team starts!\n"), g_teaminfo[use_team].name);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -2977,7 +3061,7 @@ mapthing_t *G_FindMapStart(INT32 playernum)
|
|||
|
||||
// -- CTF --
|
||||
// Order: CTF->DM->Race
|
||||
else if ((gametyperules & GTR_TEAMSTARTS) && players[playernum].ctfteam)
|
||||
else if ((gametyperules & GTR_TEAMSTARTS) && players[playernum].spectator == false)
|
||||
spawnpoint = G_FindTeamStartOrFallback(playernum);
|
||||
|
||||
// -- DM/Tag/CTF-spectator/etc --
|
||||
|
|
@ -3089,7 +3173,7 @@ void G_SpectatePlayerOnJoin(INT32 playernum)
|
|||
// This is only ever called shortly after the above.
|
||||
// That calls CL_ClearPlayer, so spectator is false by default
|
||||
|
||||
if (!netgame && !G_GametypeHasTeams() && !G_GametypeHasSpectators())
|
||||
if (!netgame && !G_GametypeHasSpectators())
|
||||
return;
|
||||
|
||||
// These are handled automatically elsewhere
|
||||
|
|
@ -3219,14 +3303,6 @@ void G_FinishExitLevel(void)
|
|||
gameaction = ga_completed;
|
||||
lastdraw = true;
|
||||
|
||||
// If you want your teams scrambled on map change, start the process now.
|
||||
// The teams will scramble at the start of the next round.
|
||||
if (cv_scrambleonchange.value && G_GametypeHasTeams())
|
||||
{
|
||||
if (server)
|
||||
CV_SetValue(&cv_teamscramble, cv_scrambleonchange.value);
|
||||
}
|
||||
|
||||
CON_LogMessage(M_GetText("The round has ended.\n"));
|
||||
|
||||
// Remove CEcho text on round end.
|
||||
|
|
@ -3525,19 +3601,20 @@ boolean G_GametypeAllowsRetrying(void)
|
|||
//
|
||||
boolean G_GametypeHasTeams(void)
|
||||
{
|
||||
if (gametyperules & GTR_TEAMS)
|
||||
const UINT32 rules = (gametyperules & (GTR_TEAMS|GTR_NOTEAMS));
|
||||
if (rules == GTR_TEAMS)
|
||||
{
|
||||
// Teams forced on by this gametype
|
||||
return true;
|
||||
}
|
||||
else if (gametyperules & GTR_NOTEAMS)
|
||||
else if (rules == GTR_NOTEAMS)
|
||||
{
|
||||
// Teams forced off by this gametype
|
||||
return false;
|
||||
}
|
||||
|
||||
// Teams are determined by the "teamplay" modifier!
|
||||
return false; // teamplay
|
||||
// Teams are determined by the server's preference!
|
||||
return g_teamplay;
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -4719,9 +4796,13 @@ static void G_DoCompleted(void)
|
|||
{
|
||||
Y_StartIntermission();
|
||||
}
|
||||
else if (grandprixinfo.gp == true)
|
||||
else
|
||||
{
|
||||
K_UpdateGPRank(&grandprixinfo.rank);
|
||||
Y_MidIntermission();
|
||||
if (grandprixinfo.gp == true)
|
||||
{
|
||||
K_UpdateGPRank(&grandprixinfo.rank);
|
||||
}
|
||||
}
|
||||
|
||||
G_UpdateVisited();
|
||||
|
|
@ -5255,9 +5336,14 @@ void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skippr
|
|||
}
|
||||
|
||||
// Clear a bunch of variables
|
||||
redscore = bluescore = lastmap = 0;
|
||||
lastmap = 0;
|
||||
racecountdown = exitcountdown = musiccountdown = mapreset = exitfadestarted = 0;
|
||||
|
||||
for (i = 0; i < TEAM__MAX; i++)
|
||||
{
|
||||
g_teamscores[i] = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
players[i].playerstate = PST_REBORN;
|
||||
|
|
@ -5730,3 +5816,204 @@ INT32 G_TicsToMilliseconds(tic_t tics)
|
|||
{
|
||||
return (INT32)((tics%TICRATE) * (1000.00f/TICRATE));
|
||||
}
|
||||
|
||||
teaminfo_t g_teaminfo[TEAM__MAX] =
|
||||
{
|
||||
// TEAM_UNASSIGNED
|
||||
// These values should not be reached most of the time,
|
||||
// but it is a necessary evil for this to exist.
|
||||
{
|
||||
"Unassigned",
|
||||
SKINCOLOR_NONE,
|
||||
0,
|
||||
},
|
||||
// TEAM_ORANGE
|
||||
{
|
||||
"Orange",
|
||||
SKINCOLOR_TANGERINE,
|
||||
V_ORANGEMAP,
|
||||
},
|
||||
// TEAM_BLUE
|
||||
{
|
||||
"Blue",
|
||||
SKINCOLOR_SAPPHIRE,
|
||||
V_BLUEMAP,
|
||||
},
|
||||
};
|
||||
|
||||
void G_AssignTeam(player_t *const p, UINT8 new_team)
|
||||
{
|
||||
if (p->team != new_team)
|
||||
{
|
||||
CONS_Debug(DBG_TEAMS, "%s >> Changed from team %s to team %s.\n", player_names[p - players], g_teaminfo[p->team].name, g_teaminfo[new_team].name);
|
||||
}
|
||||
|
||||
p->team = new_team;
|
||||
|
||||
if (new_team && p->skincolor != g_teaminfo[new_team].color)
|
||||
{
|
||||
p->skincolor = g_teaminfo[new_team].color;
|
||||
if (G_GamestateUsesLevel())
|
||||
{
|
||||
K_KartResetPlayerColor(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean G_SameTeam(const player_t *a, const player_t *b)
|
||||
{
|
||||
if (a == NULL || b == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (G_GametypeHasTeams() == true)
|
||||
{
|
||||
if (a->team == TEAM_UNASSIGNED || b->team == TEAM_UNASSIGNED)
|
||||
{
|
||||
// Unassigned is not a real team.
|
||||
// Treat them as lone wolves.
|
||||
return false;
|
||||
}
|
||||
|
||||
// You share a team!
|
||||
return (a->team == b->team);
|
||||
}
|
||||
|
||||
// Free for all.
|
||||
return false;
|
||||
}
|
||||
|
||||
UINT8 G_CountTeam(UINT8 team)
|
||||
{
|
||||
UINT8 count = 0;
|
||||
|
||||
for (UINT8 i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i] == false || players[i].spectator == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (players[i].team == team)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void G_AutoAssignTeam(player_t *const p)
|
||||
{
|
||||
if (G_GametypeHasTeams() == false)
|
||||
{
|
||||
CONS_Debug(DBG_TEAMS, "%s >> Teams are disabled.\n", player_names[p - players]);
|
||||
G_AssignTeam(p, TEAM_UNASSIGNED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (p->spectator == true)
|
||||
{
|
||||
CONS_Debug(DBG_TEAMS, "%s >> Why are you giving a spectator a team?\n", player_names[p - players]);
|
||||
G_AssignTeam(p, TEAM_UNASSIGNED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (p->team != TEAM_UNASSIGNED)
|
||||
{
|
||||
CONS_Debug(DBG_TEAMS, "%s >> Already assigned a team.\n", player_names[p - players]);
|
||||
return;
|
||||
}
|
||||
|
||||
const UINT8 orange_count = G_CountTeam(TEAM_ORANGE);
|
||||
const UINT8 blue_count = G_CountTeam(TEAM_BLUE);
|
||||
|
||||
if (orange_count == blue_count)
|
||||
{
|
||||
CONS_Debug(DBG_TEAMS, "%s >> Team assigned randomly.\n", player_names[p - players]);
|
||||
G_AssignTeam(p, (P_Random(PR_TEAMS) & 1) ? TEAM_BLUE : TEAM_ORANGE);
|
||||
return;
|
||||
}
|
||||
|
||||
CONS_Debug(DBG_TEAMS, "%s >> Team imbalance.\n", player_names[p - players]);
|
||||
|
||||
if (blue_count < orange_count)
|
||||
{
|
||||
G_AssignTeam(p, TEAM_BLUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
G_AssignTeam(p, TEAM_ORANGE);
|
||||
}
|
||||
}
|
||||
|
||||
void G_AddTeamScore(UINT8 team, INT32 amount, player_t *source)
|
||||
{
|
||||
if (team == TEAM_UNASSIGNED || G_GametypeHasTeams() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((gametyperules & GTR_POINTLIMIT) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if 1
|
||||
if (amount <= 0)
|
||||
{
|
||||
// Don't allow players to intentionally
|
||||
// tank the team score. Might not be necessary?
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
(void)source; // Just included in case we need the scorer later.
|
||||
|
||||
// Don't underflow.
|
||||
// Don't go above MAXSCORE.
|
||||
if (amount < 0 && (UINT32)-amount > g_teamscores[team])
|
||||
{
|
||||
g_teamscores[team] = 0;
|
||||
}
|
||||
else if (g_teamscores[team] + amount < MAXSCORE)
|
||||
{
|
||||
if (g_teamscores[team] < g_pointlimit
|
||||
&& g_pointlimit <= g_teamscores[team] + amount)
|
||||
{
|
||||
INT32 i;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
player_t *const p = &players[i];
|
||||
|
||||
if (playeringame[i] == false || p->spectator == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (p->team == team)
|
||||
{
|
||||
HU_DoTitlecardCEchoForDuration(p, "K.O. READY!", true, 5*TICRATE/2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_teamscores[team] += amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_teamscores[team] = MAXSCORE;
|
||||
}
|
||||
}
|
||||
|
||||
UINT32 G_TeamOrIndividualScore(const player_t *player)
|
||||
{
|
||||
if (G_GametypeHasTeams() == true && player->team != TEAM_UNASSIGNED)
|
||||
{
|
||||
return g_teamscores[player->team];
|
||||
}
|
||||
|
||||
return player->roundscore;
|
||||
}
|
||||
|
||||
|
|
|
|||
10
src/g_game.h
10
src/g_game.h
|
|
@ -234,6 +234,9 @@ void G_UpdateTimeStickerMedals(UINT16 map, boolean showownrecord);
|
|||
void G_TickTimeStickerMedals(void);
|
||||
void G_UpdateRecords(void);
|
||||
|
||||
void G_UpdatePlayerPreferences(player_t *const player);
|
||||
void G_UpdateAllPlayerPreferences(void);
|
||||
|
||||
void G_Ticker(boolean run);
|
||||
boolean G_Responder(event_t *ev);
|
||||
|
||||
|
|
@ -290,6 +293,13 @@ void G_AddMapToBuffer(UINT16 map);
|
|||
|
||||
void G_UpdateVisited(void);
|
||||
|
||||
boolean G_SameTeam(const player_t *a, const player_t *b);
|
||||
UINT8 G_CountTeam(UINT8 team);
|
||||
void G_AssignTeam(player_t *const p, UINT8 new_team);
|
||||
void G_AutoAssignTeam(player_t *const p);
|
||||
void G_AddTeamScore(UINT8 team, INT32 amount, player_t *source);
|
||||
UINT32 G_TeamOrIndividualScore(const player_t *player);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -621,7 +621,7 @@ static void Command_Sayteam_f(void)
|
|||
return;
|
||||
}
|
||||
|
||||
if (G_GametypeHasTeams()) // revert to normal say if we don't have teams in this gametype.
|
||||
if (G_GametypeHasTeams()) // revert to normal say if we don't have teams in this gametype.
|
||||
DoSayPacketFromCommand(-1, 1, 0);
|
||||
else
|
||||
DoSayPacketFromCommand(0, 1, 0);
|
||||
|
|
@ -779,16 +779,8 @@ static void Got_Saycmd(const UINT8 **p, INT32 playernum)
|
|||
}
|
||||
else if (target == -1) // say team
|
||||
{
|
||||
if (players[playernum].ctfteam == 1)
|
||||
{
|
||||
// red text
|
||||
cstart = textcolor = "\x85";
|
||||
}
|
||||
else
|
||||
{
|
||||
// blue text
|
||||
cstart = textcolor = "\x84";
|
||||
}
|
||||
sprintf(color_prefix, "%c", '\x80' + (g_teaminfo[ players[playernum].team ].chat_color >> V_CHARCOLORSHIFT));
|
||||
cstart = textcolor = color_prefix;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -1593,12 +1585,7 @@ static void HU_DrawChat(void)
|
|||
if (teamtalk)
|
||||
{
|
||||
talk = ttalk;
|
||||
#if 0
|
||||
if (players[consoleplayer].ctfteam == 1)
|
||||
t = '\0x85'; // Red
|
||||
else if (players[consoleplayer].ctfteam == 2)
|
||||
t = '\0x84'; // Blue
|
||||
#endif
|
||||
//t = '\x80' + (g_teaminfo[ players[consoleplayer].team ].chat_color >> V_CHARCOLORSHIFT);
|
||||
}
|
||||
|
||||
typelines = 1;
|
||||
|
|
@ -2569,8 +2556,6 @@ static void HU_DrawRankings(void)
|
|||
|
||||
completed[i] = true;
|
||||
|
||||
standings.character[standings.numplayers] = players[i].skin;
|
||||
standings.color[standings.numplayers] = players[i].skincolor;
|
||||
standings.pos[standings.numplayers] = players[i].position;
|
||||
|
||||
#define strtime standings.strval[standings.numplayers]
|
||||
|
|
@ -2608,6 +2593,8 @@ static void HU_DrawRankings(void)
|
|||
standings.numplayers++;
|
||||
}
|
||||
|
||||
standings.halfway = (standings.numplayers-1)/2;
|
||||
|
||||
// Returns early if there's no players to draw
|
||||
Y_PlayerStandingsDrawer(&standings, 0);
|
||||
|
||||
|
|
|
|||
|
|
@ -985,7 +985,16 @@ boolean K_EndBattleRound(player_t *victor)
|
|||
if (gametyperules & GTR_POINTLIMIT)
|
||||
{
|
||||
// Lock the winner in before the round ends.
|
||||
|
||||
// TODO: a "won the round" bool used for sorting
|
||||
// position / intermission, so we aren't completely
|
||||
// clobbering the individual scoring.
|
||||
victor->roundscore = 100;
|
||||
|
||||
if (G_GametypeHasTeams() == true && victor->team != TEAM_UNASSIGNED)
|
||||
{
|
||||
g_teamscores[victor->team] = 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -174,16 +174,20 @@ void K_SetBot(UINT8 newplayernum, UINT8 skinnum, UINT8 difficulty, botStyle_e st
|
|||
break;
|
||||
}
|
||||
}
|
||||
players[newplayernum].skincolor = color;
|
||||
K_SetNameForBot(newplayernum, realname);
|
||||
|
||||
SetPlayerSkinByNum(newplayernum, skinnum);
|
||||
K_SetNameForBot(newplayernum, realname);
|
||||
|
||||
for (UINT8 i = 0; i < PWRLV_NUMTYPES; i++)
|
||||
{
|
||||
clientpowerlevels[newplayernum][i] = 0;
|
||||
}
|
||||
|
||||
players[newplayernum].prefcolor = color;
|
||||
players[newplayernum].prefskin = skinnum;
|
||||
players[newplayernum].preffollower = -1;
|
||||
players[newplayernum].preffollowercolor = SKINCOLOR_NONE;
|
||||
G_UpdatePlayerPreferences(&players[newplayernum]);
|
||||
|
||||
if (netgame)
|
||||
{
|
||||
HU_AddChatText(va("\x82*Bot %d has been added to the game", newplayernum+1), false);
|
||||
|
|
@ -630,6 +634,12 @@ static UINT32 K_BotRubberbandDistance(const player_t *player)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (G_SameTeam(player, &players[i]) == true)
|
||||
{
|
||||
// Don't consider friendlies with your rubberbanding.
|
||||
continue;
|
||||
}
|
||||
|
||||
// First check difficulty levels, then score, then settle it with port priority!
|
||||
if (player->botvars.difficulty < players[i].botvars.difficulty)
|
||||
{
|
||||
|
|
@ -716,6 +726,12 @@ fixed_t K_BotRubberband(const player_t *player)
|
|||
continue;
|
||||
}
|
||||
|
||||
// Don't rubberband to friendlies...
|
||||
if (G_SameTeam(player, &players[i]) == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Only rubberband up to players.
|
||||
if (players[i].bot)
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ static boolean K_BotUseItemNearPlayer(const player_t *player, ticcmd_t *cmd, fix
|
|||
|
||||
if (target->mo == NULL || P_MobjWasRemoved(target->mo)
|
||||
|| player == target || target->spectator
|
||||
|| G_SameTeam(player, target)
|
||||
|| target->flashing)
|
||||
{
|
||||
continue;
|
||||
|
|
@ -144,6 +145,7 @@ static player_t *K_PlayerNearSpot(const player_t *player, fixed_t x, fixed_t y,
|
|||
|
||||
if (target->mo == NULL || P_MobjWasRemoved(target->mo)
|
||||
|| player == target || target->spectator
|
||||
|| G_SameTeam(player, target)
|
||||
|| target->flashing)
|
||||
{
|
||||
continue;
|
||||
|
|
@ -222,6 +224,7 @@ static player_t *K_PlayerInCone(const player_t *player, fixed_t radius, UINT16 c
|
|||
|
||||
if (target->mo == NULL || P_MobjWasRemoved(target->mo)
|
||||
|| player == target || target->spectator
|
||||
|| G_SameTeam(player, target)
|
||||
|| target->flashing
|
||||
|| !P_CheckSight(player->mo, target->mo))
|
||||
{
|
||||
|
|
@ -1142,28 +1145,31 @@ static void K_BotItemJawz(const player_t *player, ticcmd_t *cmd)
|
|||
&& players[lastTarg].mo != NULL
|
||||
&& P_MobjWasRemoved(players[lastTarg].mo) == false)
|
||||
{
|
||||
mobj_t *targMo = players[lastTarg].mo;
|
||||
mobj_t *mobj = NULL, *next = NULL;
|
||||
boolean targettedAlready = false;
|
||||
|
||||
target = &players[lastTarg];
|
||||
|
||||
// Make sure no other Jawz are targetting this player.
|
||||
for (mobj = trackercap; mobj; mobj = next)
|
||||
if (G_SameTeam(player, target) == false)
|
||||
{
|
||||
next = mobj->itnext;
|
||||
mobj_t *targMo = players[lastTarg].mo;
|
||||
mobj_t *mobj = NULL, *next = NULL;
|
||||
boolean targettedAlready = false;
|
||||
|
||||
if (mobj->type == MT_JAWZ && mobj->target == targMo)
|
||||
// Make sure no other Jawz are targetting this player.
|
||||
for (mobj = trackercap; mobj; mobj = next)
|
||||
{
|
||||
targettedAlready = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
next = mobj->itnext;
|
||||
|
||||
if (targettedAlready == false)
|
||||
{
|
||||
K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * snipeMul);
|
||||
throwdir = 1;
|
||||
if (mobj->type == MT_JAWZ && mobj->target == targMo)
|
||||
{
|
||||
targettedAlready = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (targettedAlready == false)
|
||||
{
|
||||
K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * snipeMul);
|
||||
throwdir = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1253,6 +1259,7 @@ static void K_BotItemBubble(const player_t *player, ticcmd_t *cmd)
|
|||
|
||||
if (target->mo == NULL || P_MobjWasRemoved(target->mo)
|
||||
|| player == target || target->spectator
|
||||
|| G_SameTeam(player, target)
|
||||
|| target->flashing)
|
||||
{
|
||||
continue;
|
||||
|
|
@ -1528,6 +1535,7 @@ static void K_BotItemInstashield(const player_t *player, ticcmd_t *cmd)
|
|||
if (P_MobjWasRemoved(target->mo) == true
|
||||
|| player == target
|
||||
|| target->spectator == true
|
||||
|| G_SameTeam(player, target) == true
|
||||
|| target->flashing != 0)
|
||||
{
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -353,13 +353,14 @@ static void K_AddDodgeObject(mobj_t *thing, UINT8 side, UINT8 weight)
|
|||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static boolean K_PlayerAttackSteer(mobj_t *thing, UINT8 side, UINT8 weight, boolean attackCond, boolean dodgeCond)
|
||||
static boolean K_PlayerAttackSteer(mobj_t *thing, boolean friendly_fire, UINT8 side, UINT8 weight, boolean attackCond, boolean dodgeCond)
|
||||
|
||||
Checks two conditions to determine if the object should be
|
||||
attacked or dodged.
|
||||
|
||||
Input Arguments:-
|
||||
thing - Object to move towards/away from.
|
||||
friendly_fire - If the attack would be against a friendly player.
|
||||
side - Which side -- 0 for left, 1 for right
|
||||
weight - How important this object is.
|
||||
attackCond - If this is true, and dodgeCond isn't, then we go towards the object.
|
||||
|
|
@ -368,8 +369,15 @@ static void K_AddDodgeObject(mobj_t *thing, UINT8 side, UINT8 weight)
|
|||
Return:-
|
||||
true if either condition is successful.
|
||||
--------------------------------------------------*/
|
||||
static boolean K_PlayerAttackSteer(mobj_t *thing, UINT8 side, UINT8 weight, boolean attackCond, boolean dodgeCond)
|
||||
static boolean K_PlayerAttackSteer(mobj_t *thing, boolean friendly_fire, UINT8 side, UINT8 weight, boolean attackCond, boolean dodgeCond)
|
||||
{
|
||||
if (friendly_fire == true && attackCond == true && dodgeCond == false)
|
||||
{
|
||||
// Dodge, don't attack.
|
||||
attackCond = false;
|
||||
dodgeCond = true;
|
||||
}
|
||||
|
||||
if (attackCond == true && dodgeCond == false)
|
||||
{
|
||||
K_AddAttackObject(thing, side, weight);
|
||||
|
|
@ -547,9 +555,11 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
|
|||
&& !thing->player->hyudorotimer
|
||||
&& !g_nudgeSearch.botmo->player->hyudorotimer)
|
||||
{
|
||||
const boolean same_team = G_SameTeam(g_nudgeSearch.botmo->player, thing->player);
|
||||
|
||||
// There REALLY ought to be a better way to handle this logic, right?!
|
||||
// Squishing
|
||||
if (K_PlayerAttackSteer(thing, side, 20,
|
||||
if (K_PlayerAttackSteer(thing, same_team, side, 20,
|
||||
K_IsBigger(g_nudgeSearch.botmo, thing),
|
||||
K_IsBigger(thing, g_nudgeSearch.botmo)
|
||||
))
|
||||
|
|
@ -557,7 +567,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
|
|||
break;
|
||||
}
|
||||
// Invincibility
|
||||
else if (K_PlayerAttackSteer(thing, side, 20,
|
||||
else if (K_PlayerAttackSteer(thing, same_team, side, 20,
|
||||
g_nudgeSearch.botmo->player->invincibilitytimer,
|
||||
thing->player->invincibilitytimer
|
||||
))
|
||||
|
|
@ -565,7 +575,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
|
|||
break;
|
||||
}
|
||||
// Lightning Shield
|
||||
else if (K_PlayerAttackSteer(thing, side, 20,
|
||||
else if (K_PlayerAttackSteer(thing, same_team, side, 20,
|
||||
g_nudgeSearch.botmo->player->itemtype == KITEM_LIGHTNINGSHIELD,
|
||||
thing->player->itemtype == KITEM_LIGHTNINGSHIELD
|
||||
))
|
||||
|
|
@ -573,7 +583,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
|
|||
break;
|
||||
}
|
||||
// Bubble Shield
|
||||
else if (K_PlayerAttackSteer(thing, side, 20,
|
||||
else if (K_PlayerAttackSteer(thing, same_team, side, 20,
|
||||
g_nudgeSearch.botmo->player->itemtype == KITEM_BUBBLESHIELD,
|
||||
thing->player->itemtype == KITEM_BUBBLESHIELD
|
||||
))
|
||||
|
|
@ -581,7 +591,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
|
|||
break;
|
||||
}
|
||||
// Flame Shield
|
||||
else if (K_PlayerAttackSteer(thing, side, 20,
|
||||
else if (K_PlayerAttackSteer(thing, same_team, side, 20,
|
||||
g_nudgeSearch.botmo->player->itemtype == KITEM_FLAMESHIELD,
|
||||
thing->player->itemtype == KITEM_FLAMESHIELD
|
||||
))
|
||||
|
|
@ -589,7 +599,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
|
|||
break;
|
||||
}
|
||||
// Has held item shield
|
||||
else if (K_PlayerAttackSteer(thing, side, 20,
|
||||
else if (K_PlayerAttackSteer(thing, same_team, side, 20,
|
||||
(thing->player->itemflags & (IF_ITEMOUT|IF_EGGMANOUT)),
|
||||
(g_nudgeSearch.botmo->player->itemflags & (IF_ITEMOUT|IF_EGGMANOUT))
|
||||
))
|
||||
|
|
@ -597,7 +607,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
|
|||
break;
|
||||
}
|
||||
// Ring Sting
|
||||
else if (K_PlayerAttackSteer(thing, side, 20,
|
||||
else if (K_PlayerAttackSteer(thing, same_team, side, 20,
|
||||
thing->player->rings <= 0,
|
||||
g_nudgeSearch.botmo->player->rings <= 0
|
||||
))
|
||||
|
|
@ -620,7 +630,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
|
|||
weightdiff = ourweight - theirweight;
|
||||
}
|
||||
|
||||
if (weightdiff > mapobjectscale)
|
||||
if (weightdiff > mapobjectscale && same_team == false)
|
||||
{
|
||||
K_AddAttackObject(thing, side, 20);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -791,10 +791,14 @@ void K_RetireBots(void)
|
|||
bot->botvars.difficulty = newDifficulty;
|
||||
bot->botvars.diffincrease = 0;
|
||||
|
||||
SetPlayerSkinByNum(i, skinnum);
|
||||
bot->skincolor = skins[skinnum].prefcolor;
|
||||
K_SetNameForBot(i, skins[skinnum].realname);
|
||||
|
||||
bot->prefskin = skinnum;
|
||||
bot->prefcolor = skins[skinnum].prefcolor;
|
||||
bot->preffollower = -1;
|
||||
bot->preffollowercolor = SKINCOLOR_NONE;
|
||||
G_UpdatePlayerPreferences(bot);
|
||||
|
||||
bot->score = 0;
|
||||
bot->pflags &= ~PF_NOCONTEST;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2428,7 +2428,7 @@ struct PositionFacesInfo
|
|||
void draw_4p_battle(int x, int y, INT32 flags);
|
||||
|
||||
player_t* top() const { return &players[rankplayer[0]]; }
|
||||
UINT32 top_score() const { return top()->roundscore; }
|
||||
UINT32 top_score() const { return G_TeamOrIndividualScore( top() ); }
|
||||
|
||||
bool near_goal() const
|
||||
{
|
||||
|
|
@ -2548,7 +2548,7 @@ void PositionFacesInfo::draw_1p()
|
|||
}
|
||||
|
||||
// Draw GOAL
|
||||
bool skull = g_pointlimit && (g_pointlimit <= stplyr->roundscore);
|
||||
bool skull = g_pointlimit && (g_pointlimit <= G_TeamOrIndividualScore(stplyr));
|
||||
INT32 height = i*18;
|
||||
INT32 GOAL_Y = Y-height;
|
||||
|
||||
|
|
@ -3035,6 +3035,34 @@ INT32 K_GetTransFlagFromFixed(fixed_t value)
|
|||
}
|
||||
}
|
||||
|
||||
static void K_drawKartTeamScores(void)
|
||||
{
|
||||
if (G_GametypeHasTeams() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (INT32 i = TEAM_UNASSIGNED+1; i < TEAM__MAX; i++)
|
||||
{
|
||||
INT32 x = BASEVIDWIDTH/2;
|
||||
|
||||
x += -12 + (24 * (i - 1));
|
||||
|
||||
V_DrawCenteredString(x, 5, g_teaminfo[i].chat_color, va("%d", g_teamscores[i]));
|
||||
|
||||
if (stplyr->team == i)
|
||||
{
|
||||
UINT32 individual_score = stplyr->teamimportance;
|
||||
if (gametyperules & GTR_POINTLIMIT)
|
||||
{
|
||||
individual_score = stplyr->roundscore;
|
||||
}
|
||||
|
||||
V_DrawCenteredString(x, 15, g_teaminfo[i].chat_color, va("+%d", individual_score));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void K_drawKartLaps(void)
|
||||
{
|
||||
INT32 splitflags = V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN;
|
||||
|
|
@ -6720,7 +6748,14 @@ void K_drawKartHUD(void)
|
|||
K_drawKartEmeralds();
|
||||
}
|
||||
else if (!islonesome && !K_Cooperative())
|
||||
{
|
||||
K_DrawKartPositionNum(stplyr->position);
|
||||
}
|
||||
}
|
||||
|
||||
if (G_GametypeHasTeams() == true)
|
||||
{
|
||||
K_drawKartTeamScores();
|
||||
}
|
||||
|
||||
if (LUA_HudEnabled(hud_gametypeinfo))
|
||||
|
|
|
|||
175
src/k_kart.c
175
src/k_kart.c
|
|
@ -4030,6 +4030,12 @@ angle_t K_MomentumAngleReal(const mobj_t *mo)
|
|||
// Scale amp rewards for crab bucketing. Play ambitiously!
|
||||
boolean K_PvPAmpReward(UINT32 award, player_t *attacker, player_t *defender)
|
||||
{
|
||||
if (G_SameTeam(attacker, defender) == true)
|
||||
{
|
||||
// Do not reward amps for friendly fire.
|
||||
return 0;
|
||||
}
|
||||
|
||||
UINT32 epsilon = FixedMul(2048/4, mapobjectscale); // How close is close enough that full reward seems fair, even if you're technically ahead?
|
||||
UINT32 range = FixedMul(2048, mapobjectscale);
|
||||
UINT32 atkdist = attacker->distancetofinish + epsilon;
|
||||
|
|
@ -4054,6 +4060,9 @@ void K_SpawnAmps(player_t *player, UINT8 amps, mobj_t *impact)
|
|||
if (gametyperules & GTR_SPHERES)
|
||||
return;
|
||||
|
||||
if (amps == 0)
|
||||
return;
|
||||
|
||||
UINT16 scaledamps = min(amps, amps * (10 + (9-player->kartspeed) - (9-player->kartweight)) / 10);
|
||||
|
||||
/*
|
||||
|
|
@ -4317,6 +4326,12 @@ void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UIN
|
|||
return;
|
||||
}
|
||||
|
||||
if (G_SameTeam(player, victim) == true)
|
||||
{
|
||||
// No farming points off of teammates, either.
|
||||
return;
|
||||
}
|
||||
|
||||
if (player->exiting)
|
||||
{
|
||||
// The round has already ended, don't mess with points
|
||||
|
|
@ -4347,7 +4362,7 @@ void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UIN
|
|||
}
|
||||
|
||||
// Check this before adding to player score
|
||||
if ((gametyperules & GTR_BUMPERS) && finishOff && g_pointlimit <= player->roundscore)
|
||||
if ((gametyperules & GTR_BUMPERS) && finishOff && g_pointlimit <= G_TeamOrIndividualScore(player))
|
||||
{
|
||||
K_EndBattleRound(player);
|
||||
|
||||
|
|
@ -8294,7 +8309,7 @@ mobj_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (G_GametypeHasTeams() && source != NULL && source->ctfteam == player->ctfteam)
|
||||
if (G_SameTeam(source, player) == false)
|
||||
{
|
||||
// Don't home in on teammates.
|
||||
continue;
|
||||
|
|
@ -10090,9 +10105,7 @@ void K_KartResetPlayerColor(player_t *player)
|
|||
|
||||
if (player->mo->health <= 0 || player->playerstate == PST_DEAD || (player->respawn.state == RESPAWNST_MOVE)) // Override everything
|
||||
{
|
||||
player->mo->colorized = (player->dye != 0);
|
||||
player->mo->color = player->dye ? player->dye : player->skincolor;
|
||||
goto finalise;
|
||||
goto base;
|
||||
}
|
||||
|
||||
if (player->eggmanexplode) // You're gonna diiiiie
|
||||
|
|
@ -10204,8 +10217,18 @@ void K_KartResetPlayerColor(player_t *player)
|
|||
goto finalise;
|
||||
}
|
||||
|
||||
player->mo->colorized = (player->dye != 0);
|
||||
player->mo->color = player->dye ? player->dye : player->skincolor;
|
||||
base:
|
||||
|
||||
if (player->dye)
|
||||
{
|
||||
player->mo->colorized = true;
|
||||
player->mo->color = player->dye;
|
||||
}
|
||||
else
|
||||
{
|
||||
player->mo->colorized = false;
|
||||
player->mo->color = player->skincolor;
|
||||
}
|
||||
|
||||
finalise:
|
||||
|
||||
|
|
@ -11740,13 +11763,18 @@ static void K_KartDrift(player_t *player, boolean onground)
|
|||
else
|
||||
player->pflags &= ~PF_BRAKEDRIFT;
|
||||
}
|
||||
|
||||
//
|
||||
// K_KartUpdatePosition
|
||||
//
|
||||
void K_KartUpdatePosition(player_t *player)
|
||||
{
|
||||
fixed_t position = 1;
|
||||
fixed_t oldposition = player->position;
|
||||
UINT8 position = 1;
|
||||
UINT8 oldposition = player->position;
|
||||
|
||||
UINT8 team_position = 1;
|
||||
UINT32 team_importance = 0;
|
||||
|
||||
fixed_t i;
|
||||
INT32 realplayers = 0;
|
||||
|
||||
|
|
@ -11755,6 +11783,8 @@ void K_KartUpdatePosition(player_t *player)
|
|||
// Ensure these are reset for spectators
|
||||
player->position = 0;
|
||||
player->positiondelay = 0;
|
||||
player->teamposition = 0;
|
||||
player->teamimportance = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -11779,23 +11809,40 @@ void K_KartUpdatePosition(player_t *player)
|
|||
|
||||
realplayers++;
|
||||
|
||||
const boolean same_team = G_SameTeam(player, &players[i]);
|
||||
|
||||
#define increment_position(condition) \
|
||||
if (condition) \
|
||||
{ \
|
||||
position++; \
|
||||
if (!same_team) \
|
||||
{ \
|
||||
team_position++; \
|
||||
} \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
if (!same_team) \
|
||||
{ \
|
||||
team_importance++; \
|
||||
} \
|
||||
}
|
||||
|
||||
if (gametyperules & GTR_CIRCUIT)
|
||||
{
|
||||
if (player->exiting) // End of match standings
|
||||
{
|
||||
// Only time matters
|
||||
if (players[i].realtime < player->realtime)
|
||||
position++;
|
||||
increment_position(players[i].realtime < player->realtime)
|
||||
}
|
||||
else
|
||||
{
|
||||
// I'm a lap behind this player OR
|
||||
// My distance to the finish line is higher, so I'm behind
|
||||
if ((players[i].laps > player->laps)
|
||||
|| (players[i].distancetofinish < player->distancetofinish))
|
||||
{
|
||||
position++;
|
||||
}
|
||||
increment_position(
|
||||
(players[i].laps > player->laps)
|
||||
|| (players[i].distancetofinish < player->distancetofinish)
|
||||
)
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -11803,8 +11850,7 @@ void K_KartUpdatePosition(player_t *player)
|
|||
if (player->exiting) // End of match standings
|
||||
{
|
||||
// Only score matters
|
||||
if (players[i].roundscore > player->roundscore)
|
||||
position++;
|
||||
increment_position(players[i].roundscore > player->roundscore)
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -11814,26 +11860,25 @@ void K_KartUpdatePosition(player_t *player)
|
|||
// First compare all points
|
||||
if (players[i].roundscore > player->roundscore)
|
||||
{
|
||||
position++;
|
||||
increment_position(true)
|
||||
}
|
||||
else if (players[i].roundscore == player->roundscore)
|
||||
{
|
||||
// Emeralds are a tie breaker
|
||||
if (yourEmeralds > myEmeralds)
|
||||
{
|
||||
position++;
|
||||
increment_position(true)
|
||||
}
|
||||
else if (yourEmeralds == myEmeralds)
|
||||
{
|
||||
// Bumpers are the second tier tie breaker
|
||||
if (K_Bumpers(&players[i]) > K_Bumpers(player))
|
||||
{
|
||||
position++;
|
||||
}
|
||||
increment_position(K_Bumpers(&players[i]) > K_Bumpers(player))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef increment_position
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -11887,6 +11932,15 @@ void K_KartUpdatePosition(player_t *player)
|
|||
}
|
||||
|
||||
player->position = position;
|
||||
player->teamposition = team_position;
|
||||
|
||||
// "Team importance" is used for scoring
|
||||
// in gametypes without scoring / point limit.
|
||||
player->teamimportance = (team_importance * 2);
|
||||
if (position == 1)
|
||||
{
|
||||
player->teamimportance++;
|
||||
}
|
||||
}
|
||||
|
||||
void K_UpdateAllPlayerPositions(void)
|
||||
|
|
@ -11927,6 +11981,26 @@ void K_UpdateAllPlayerPositions(void)
|
|||
K_KartUpdatePosition(&players[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Team Race: Live update score.
|
||||
if (G_GametypeHasTeams() == true && (gametyperules & GTR_POINTLIMIT) == 0)
|
||||
{
|
||||
for (i = 0; i < TEAM__MAX; i++)
|
||||
{
|
||||
g_teamscores[i] = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
const player_t *player = &players[i];
|
||||
if (playeringame[i] == false || player->spectator == true || player->team == TEAM_UNASSIGNED)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
g_teamscores[player->team] += player->teamimportance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -14968,7 +15042,7 @@ UINT32 K_PointLimitForGametype(void)
|
|||
// counted.
|
||||
for (i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
if (D_IsPlayerHumanAndGaming(i))
|
||||
if (playeringame[i] == true && players[i].spectator == false)
|
||||
{
|
||||
ptsCap += 3;
|
||||
}
|
||||
|
|
@ -14978,6 +15052,43 @@ UINT32 K_PointLimitForGametype(void)
|
|||
{
|
||||
ptsCap = 16;
|
||||
}
|
||||
|
||||
if (G_GametypeHasTeams() == true)
|
||||
{
|
||||
// Scale up the point limit based on
|
||||
// the team sizes. Based upon the smallest
|
||||
// team, because it would make an uneven
|
||||
// fucked up 1v15 possible to win, even
|
||||
// if it was still unbalanced.
|
||||
const UINT32 old_ptsCap = ptsCap;
|
||||
UINT8 smallest_team = MAXPLAYERS;
|
||||
|
||||
for (i = TEAM_UNASSIGNED+1; i < TEAM__MAX; i++)
|
||||
{
|
||||
UINT8 countteam = G_CountTeam(i);
|
||||
smallest_team = min( smallest_team, countteam );
|
||||
}
|
||||
|
||||
if (smallest_team > 1)
|
||||
{
|
||||
UINT8 pts_accumulator = ptsCap / 2;
|
||||
for (i = 0; i < smallest_team - 1; i++)
|
||||
{
|
||||
if (pts_accumulator == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
ptsCap += pts_accumulator;
|
||||
pts_accumulator /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
CONS_Debug(
|
||||
DBG_TEAMS, "Team Battle: points cap increased from %u to %u. (team size is %u)\n",
|
||||
old_ptsCap, ptsCap, smallest_team
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return ptsCap;
|
||||
|
|
@ -15081,13 +15192,19 @@ fixed_t K_GetExpAdjustment(player_t *player)
|
|||
fixed_t exp_stablerate = 3*FRACUNIT/10; // how low is your placement before losing XP? 4*FRACUNIT/10 = top 40% of race will gain
|
||||
fixed_t result = 0;
|
||||
|
||||
INT32 live_players = 0;
|
||||
INT32 live_players = 0; // players we are competing against
|
||||
|
||||
for (INT32 i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].spectator || player == players+i)
|
||||
continue;
|
||||
|
||||
if (G_SameTeam(player, &players[i]) == true)
|
||||
{
|
||||
// You don't win/lose against your teammates.
|
||||
continue;
|
||||
}
|
||||
|
||||
live_players++;
|
||||
}
|
||||
|
||||
|
|
@ -15102,6 +15219,12 @@ fixed_t K_GetExpAdjustment(player_t *player)
|
|||
if (!playeringame[i] || players[i].spectator || player == players+i)
|
||||
continue;
|
||||
|
||||
if (G_SameTeam(player, &players[i]) == true)
|
||||
{
|
||||
// You don't win/lose against your teammates.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player->position < players[i].position)
|
||||
result += exp_power;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -477,15 +477,17 @@ extern menu_t OPTIONS_HUDOnlineDef;
|
|||
typedef enum
|
||||
{
|
||||
gopt_spacer0 = 0,
|
||||
gopt_gamespeed,
|
||||
gopt_teamplay,
|
||||
gopt_frantic,
|
||||
gopt_spacer1,
|
||||
gopt_gamespeed,
|
||||
gopt_encore,
|
||||
gopt_exitcountdown,
|
||||
gopt_spacer1,
|
||||
gopt_spacer2,
|
||||
gopt_timelimit,
|
||||
gopt_pointlimit,
|
||||
gopt_startingbumpers,
|
||||
gopt_spacer2,
|
||||
gopt_spacer3,
|
||||
gopt_itemtoggles
|
||||
} gopt_e;
|
||||
|
||||
|
|
|
|||
|
|
@ -245,6 +245,12 @@ void K_UpdatePowerLevels(player_t *player, UINT8 lap, boolean forfeit)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (G_SameTeam(player, &players[i]) == true)
|
||||
{
|
||||
// You don't win/lose against your teammates.
|
||||
continue;
|
||||
}
|
||||
|
||||
CONS_Debug(DBG_PWRLV, "%s VS %s:\n", player_names[playerNum], player_names[i]);
|
||||
|
||||
theirPower = clientpowerlevels[i][powerType];
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ int LUA_HookMapThingSpawn(mobj_t *, mapthing_t *);
|
|||
int LUA_HookFollowMobj(player_t *, mobj_t *);
|
||||
int LUA_HookPlayerCanDamage(player_t *, mobj_t *);
|
||||
void LUA_HookPlayerQuit(player_t *, kickreason_t);
|
||||
int LUA_HookTeamSwitch(player_t *, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble);
|
||||
//int LUA_HookTeamSwitch(player_t *, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble);
|
||||
int LUA_HookViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced);
|
||||
int LUA_HookSeenPlayer(player_t *player, player_t *seenfriend);
|
||||
|
||||
|
|
|
|||
|
|
@ -961,6 +961,7 @@ void LUA_HookPlayerQuit(player_t *plr, kickreason_t reason)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
int LUA_HookTeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble)
|
||||
{
|
||||
Hook_State hook;
|
||||
|
|
@ -975,6 +976,7 @@ int LUA_HookTeamSwitch(player_t *player, int newteam, boolean fromspectators, bo
|
|||
}
|
||||
return hook.status;
|
||||
}
|
||||
*/
|
||||
|
||||
int LUA_HookViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -226,6 +226,10 @@ static int player_get(lua_State *L)
|
|||
lua_pushinteger(L, plr->oldposition);
|
||||
else if (fastcmp(field,"positiondelay"))
|
||||
lua_pushinteger(L, plr->positiondelay);
|
||||
else if (fastcmp(field,"teamposition"))
|
||||
lua_pushinteger(L, plr->teamposition);
|
||||
else if (fastcmp(field,"teamimportance"))
|
||||
lua_pushinteger(L, plr->teamimportance);
|
||||
else if (fastcmp(field,"distancetofinish"))
|
||||
lua_pushinteger(L, plr->distancetofinish);
|
||||
else if (fastcmp(field,"distancetofinishprev"))
|
||||
|
|
@ -561,6 +565,14 @@ static int player_get(lua_State *L)
|
|||
lua_pushinteger(L, plr->followercolor);
|
||||
else if (fastcmp(field,"follower"))
|
||||
LUA_PushUserdata(L, plr->follower, META_MOBJ);
|
||||
else if (fastcmp(field,"prefskin"))
|
||||
lua_pushinteger(L, plr->prefskin);
|
||||
else if (fastcmp(field,"prefcolor"))
|
||||
lua_pushinteger(L, plr->prefcolor);
|
||||
else if (fastcmp(field,"preffollower"))
|
||||
lua_pushinteger(L, plr->preffollower);
|
||||
else if (fastcmp(field,"preffollowercolor"))
|
||||
lua_pushinteger(L, plr->preffollowercolor);
|
||||
//
|
||||
|
||||
// rideroids
|
||||
|
|
@ -675,8 +687,8 @@ static int player_get(lua_State *L)
|
|||
lua_pushinteger(L, plr->laps);
|
||||
else if (fastcmp(field,"latestlap"))
|
||||
lua_pushinteger(L, plr->latestlap);
|
||||
else if (fastcmp(field,"ctfteam"))
|
||||
lua_pushinteger(L, plr->ctfteam);
|
||||
else if (fastcmp(field,"team"))
|
||||
lua_pushinteger(L, plr->team);
|
||||
else if (fastcmp(field,"checkskip"))
|
||||
lua_pushinteger(L, plr->checkskip);
|
||||
else if (fastcmp(field,"cheatchecknum"))
|
||||
|
|
@ -820,6 +832,10 @@ static int player_set(lua_State *L)
|
|||
plr->oldposition = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"positiondelay"))
|
||||
plr->positiondelay = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"teamposition"))
|
||||
plr->teamposition = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"teamimportance"))
|
||||
plr->teamimportance = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"distancetofinish"))
|
||||
return NOSET;
|
||||
else if (fastcmp(field,"distancetofinishprev"))
|
||||
|
|
@ -1130,8 +1146,16 @@ static int player_set(lua_State *L)
|
|||
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,"follower"))
|
||||
return NOSET; // it's probably best we don't allow the follower mobj to change.
|
||||
else if (fastcmp(field,"prefskin"))
|
||||
return NOSET; // don't allow changing user preferences
|
||||
else if (fastcmp(field,"prefcolor"))
|
||||
return NOSET; // don't allow changing user preferences
|
||||
else if (fastcmp(field,"preffollower"))
|
||||
return NOSET; // don't allow changing user preferences
|
||||
else if (fastcmp(field,"preffollowercolor"))
|
||||
return NOSET; // don't allow changing user preferences
|
||||
|
||||
// time to add to the endless elseif list!!!!
|
||||
// rideroids
|
||||
|
|
@ -1252,8 +1276,8 @@ static int player_set(lua_State *L)
|
|||
plr->laps = (UINT8)luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"latestlap"))
|
||||
plr->latestlap = (UINT8)luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"ctfteam"))
|
||||
plr->ctfteam = (INT32)luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"team"))
|
||||
G_AssignTeam(plr, (UINT8)luaL_checkinteger(L, 3));
|
||||
else if (fastcmp(field,"checkskip"))
|
||||
plr->checkskip = (INT32)luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"cheatchecknum"))
|
||||
|
|
|
|||
|
|
@ -197,12 +197,6 @@ int LUA_PushGlobals(lua_State *L, const char *word)
|
|||
} else if (fastcmp(word,"paused")) {
|
||||
lua_pushboolean(L, paused);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"bluescore")) {
|
||||
lua_pushinteger(L, bluescore);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"redscore")) {
|
||||
lua_pushinteger(L, redscore);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"timelimit")) {
|
||||
lua_pushinteger(L, timelimitintics);
|
||||
return 1;
|
||||
|
|
@ -226,20 +220,6 @@ int LUA_PushGlobals(lua_State *L, const char *word)
|
|||
lua_pushstring(L, tutorialchallengemap);
|
||||
return 1;
|
||||
// end map vars
|
||||
// begin CTF colors
|
||||
} else if (fastcmp(word,"skincolor_redteam")) {
|
||||
lua_pushinteger(L, skincolor_redteam);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"skincolor_blueteam")) {
|
||||
lua_pushinteger(L, skincolor_blueteam);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"skincolor_redring")) {
|
||||
lua_pushinteger(L, skincolor_redring);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"skincolor_bluering")) {
|
||||
lua_pushinteger(L, skincolor_bluering);
|
||||
return 1;
|
||||
// end CTF colors
|
||||
// begin timers
|
||||
} else if (fastcmp(word,"invulntics")) {
|
||||
lua_pushinteger(L, invulntics);
|
||||
|
|
@ -351,6 +331,9 @@ int LUA_PushGlobals(lua_State *L, const char *word)
|
|||
} else if (fastcmp(word,"franticitems")) {
|
||||
lua_pushboolean(L, franticitems);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"teamplay")) {
|
||||
lua_pushboolean(L, g_teamplay);
|
||||
return 1;
|
||||
} else if (fastcmp(word,"wantedcalcdelay")) {
|
||||
lua_pushinteger(L, wantedcalcdelay);
|
||||
return 1;
|
||||
|
|
@ -392,19 +375,7 @@ int LUA_PushGlobals(lua_State *L, const char *word)
|
|||
// See the above.
|
||||
int LUA_WriteGlobals(lua_State *L, const char *word)
|
||||
{
|
||||
if (fastcmp(word, "redscore"))
|
||||
redscore = (UINT32)luaL_checkinteger(L, 2);
|
||||
else if (fastcmp(word, "bluescore"))
|
||||
bluescore = (UINT32)luaL_checkinteger(L, 2);
|
||||
else if (fastcmp(word, "skincolor_redteam"))
|
||||
skincolor_redteam = (UINT16)luaL_checkinteger(L, 2);
|
||||
else if (fastcmp(word, "skincolor_blueteam"))
|
||||
skincolor_blueteam = (UINT16)luaL_checkinteger(L, 2);
|
||||
else if (fastcmp(word, "skincolor_redring"))
|
||||
skincolor_redring = (UINT16)luaL_checkinteger(L, 2);
|
||||
else if (fastcmp(word, "skincolor_bluering"))
|
||||
skincolor_bluering = (UINT16)luaL_checkinteger(L, 2);
|
||||
else if (fastcmp(word, "gravity"))
|
||||
if (fastcmp(word, "gravity"))
|
||||
gravity = (fixed_t)luaL_checkinteger(L, 2);
|
||||
else if (fastcmp(word, "stoppedclock"))
|
||||
stoppedclock = luaL_checkboolean(L, 2);
|
||||
|
|
|
|||
|
|
@ -290,6 +290,8 @@ struct debugFlagNames_s const debug_flag_names[] =
|
|||
{"PowerLevel", DBG_PWRLV}, // alt name
|
||||
{"Demo", DBG_DEMO},
|
||||
{"Replay", DBG_DEMO}, // alt name
|
||||
{"Teams", DBG_TEAMS},
|
||||
{"Teamplay", DBG_TEAMS}, // alt name
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ typedef enum
|
|||
PROLDDEMO, // The number of RNG classes in versions that didn't write down how many RNG classes they had in their replays.
|
||||
|
||||
PR_ITEM_SPAWNER = PROLDDEMO, // Battle mode item spawners
|
||||
PR_TEAMS, // Teamplay shuffling
|
||||
|
||||
PRNUMSYNCED,
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,14 @@
|
|||
|
||||
menuitem_t OPTIONS_Gameplay[] =
|
||||
{
|
||||
{IT_HEADER, "Global...", NULL,
|
||||
NULL, {NULL}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_CVAR, "Teamplay", "Split the game between two teams!",
|
||||
NULL, {.cvar = &cv_teamplay}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_CVAR, "Frantic Items", "Make item odds crazier with more powerful items!",
|
||||
NULL, {.cvar = &cv_kartfrantic}, 0, 0},
|
||||
|
||||
{IT_HEADER, "Race...", NULL,
|
||||
NULL, {NULL}, 0, 0},
|
||||
|
|
@ -21,9 +29,6 @@ menuitem_t OPTIONS_Gameplay[] =
|
|||
{IT_STRING | IT_CVAR, "Game Speed", "Gear for the next map.",
|
||||
NULL, {.cvar = &cv_kartspeed}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_CVAR, "Frantic Items", "Make item odds crazier with more powerful items!",
|
||||
NULL, {.cvar = &cv_kartfrantic}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_CVAR, "Encore Mode", "Play in Encore Mode next map.",
|
||||
NULL, {.cvar = &cv_kartencore}, 0, 0},
|
||||
|
||||
|
|
|
|||
|
|
@ -753,6 +753,20 @@ static void M_HandleBackToChars(setup_player_t *p)
|
|||
static boolean M_HandleBeginningColors(setup_player_t *p)
|
||||
{
|
||||
p->mdepth = CSSTEP_COLORS;
|
||||
|
||||
if (Playing() && G_GametypeHasTeams())
|
||||
{
|
||||
size_t pnum = (p - setup_player);
|
||||
if (pnum <= splitscreen)
|
||||
{
|
||||
if (players[g_localplayers[pnum]].team != TEAM_UNASSIGNED)
|
||||
{
|
||||
p->color = g_teaminfo[players[g_localplayers[pnum]].team].color;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
M_NewPlayerColors(p);
|
||||
if (p->colors.listLen != 1)
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
/// \file menus/transient/pause-game.c
|
||||
/// \brief In-game/pause menus
|
||||
|
||||
#include "../../byteptr.h"
|
||||
#include "../../d_netcmd.h"
|
||||
#include "../../i_time.h"
|
||||
#include "../../k_menu.h"
|
||||
|
|
@ -478,50 +479,33 @@ void M_HandleSpectateToggle(INT32 choice)
|
|||
return;
|
||||
}
|
||||
|
||||
boolean tospectator = false;
|
||||
// Identify relevant spectator state of pausemenu.splitscreenfocusid.
|
||||
// See also M_DrawPause.
|
||||
|
||||
const UINT8 splitspecid =
|
||||
g_localplayers[pausemenu.splitscreenfocusid];
|
||||
|
||||
const UINT8 joingame = (
|
||||
players[splitspecid].spectator == true
|
||||
&& ((players[splitspecid].pflags & PF_WANTSTOJOIN) == 0)
|
||||
) ? 1 : 0;
|
||||
|
||||
if (joingame && !cv_allowteamchange.value)
|
||||
{
|
||||
// Identify relevant spectator state of pausemenu.splitscreenfocusid.
|
||||
// See also M_DrawPause.
|
||||
|
||||
const UINT8 splitspecid =
|
||||
g_localplayers[pausemenu.splitscreenfocusid];
|
||||
|
||||
tospectator = (
|
||||
players[splitspecid].spectator == false
|
||||
|| (players[splitspecid].pflags & PF_WANTSTOJOIN)
|
||||
);
|
||||
}
|
||||
|
||||
if (!tospectator && !cv_allowteamchange.value)
|
||||
{
|
||||
M_StartMessage("Team Change", M_GetText("The server is not allowing\nteam changes at this time.\n"), NULL, MM_NOTHING, NULL, NULL);
|
||||
M_StartMessage("Joining Play", M_GetText("The server is not allowing\njoining play at this time.\n"), NULL, MM_NOTHING, NULL, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
M_QuitPauseMenu(-1);
|
||||
|
||||
const char *destinationstate = tospectator ? "spectator" : "playing";
|
||||
// Send spectate
|
||||
UINT8 buf[2];
|
||||
UINT8 *p = buf;
|
||||
|
||||
// These console command names...
|
||||
if (pausemenu.splitscreenfocusid == 0)
|
||||
{
|
||||
COM_ImmedExecute(
|
||||
va(
|
||||
"changeteam %s",
|
||||
destinationstate
|
||||
)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
COM_ImmedExecute(
|
||||
va(
|
||||
"changeteam%u %s",
|
||||
pausemenu.splitscreenfocusid + 1,
|
||||
destinationstate
|
||||
)
|
||||
);
|
||||
}
|
||||
WRITEUINT8(p, splitspecid);
|
||||
WRITEUINT8(p, joingame);
|
||||
|
||||
SendNetXCmd(XD_SPECTATE, &buf, p - buf);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -727,6 +727,12 @@ void Controller::search()
|
|||
continue;
|
||||
}
|
||||
|
||||
// Do not target someone on the same team as our owner.
|
||||
if (G_SameTeam(player, source()->player) == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Target is already being hunted.
|
||||
if (player->flickyAttacker)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5128,10 +5128,7 @@ void A_OldRingExplode(mobj_t *actor) {
|
|||
|
||||
if (changecolor)
|
||||
{
|
||||
if (!(gametyperules & GTR_TEAMS))
|
||||
mo->color = actor->target->color; //copy color
|
||||
else if (actor->target->player->ctfteam == 2)
|
||||
mo->color = skincolor_bluering;
|
||||
P_ColorTeamMissile(mo, actor->target->player);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -5144,10 +5141,7 @@ void A_OldRingExplode(mobj_t *actor) {
|
|||
|
||||
if (changecolor)
|
||||
{
|
||||
if (!(gametyperules & GTR_TEAMS))
|
||||
mo->color = actor->target->color; //copy color
|
||||
else if (actor->target->player->ctfteam == 2)
|
||||
mo->color = skincolor_bluering;
|
||||
P_ColorTeamMissile(mo, actor->target->player);
|
||||
}
|
||||
|
||||
mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1);
|
||||
|
|
@ -5159,10 +5153,7 @@ void A_OldRingExplode(mobj_t *actor) {
|
|||
|
||||
if (changecolor)
|
||||
{
|
||||
if (!(gametyperules & GTR_TEAMS))
|
||||
mo->color = actor->target->color; //copy color
|
||||
else if (actor->target->player->ctfteam == 2)
|
||||
mo->color = skincolor_bluering;
|
||||
P_ColorTeamMissile(mo, actor->target->player);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,11 +49,6 @@
|
|||
#include "m_easing.h"
|
||||
#include "k_hud.h" // K_AddMessage
|
||||
|
||||
|
||||
// CTF player names
|
||||
#define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : ""
|
||||
#define CTFTEAMENDCODE(pl) pl->ctfteam ? "\x80" : ""
|
||||
|
||||
void P_ForceFeed(const player_t *player, INT32 attack, INT32 fade, tic_t duration, INT32 period)
|
||||
{
|
||||
BasicFF_t Basicfeed;
|
||||
|
|
@ -1510,13 +1505,15 @@ void P_CheckPointLimit(void)
|
|||
return;
|
||||
|
||||
// pointlimit is nonzero, check if it's been reached by this player
|
||||
if (G_GametypeHasTeams())
|
||||
if (G_GametypeHasTeams() == true)
|
||||
{
|
||||
// Just check both teams
|
||||
if (g_pointlimit <= redscore || g_pointlimit <= bluescore)
|
||||
for (i = 0; i < TEAM__MAX; i++)
|
||||
{
|
||||
if (server)
|
||||
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
|
||||
if (g_pointlimit <= g_teamscores[i])
|
||||
{
|
||||
P_DoAllPlayersExit(0, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -1529,10 +1526,7 @@ void P_CheckPointLimit(void)
|
|||
if (g_pointlimit <= players[i].roundscore)
|
||||
{
|
||||
P_DoAllPlayersExit(0, false);
|
||||
|
||||
/*if (server)
|
||||
SendNetXCmd(XD_EXITLEVEL, NULL, 0);*/
|
||||
return; // good thing we're leaving the function immediately instead of letting the loop get mangled!
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2506,12 +2500,11 @@ static boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj_t *sou
|
|||
if (source == target)
|
||||
return false;
|
||||
|
||||
if (G_GametypeHasTeams())
|
||||
{
|
||||
// Don't hurt your team, either!
|
||||
if (source->player->ctfteam == target->player->ctfteam)
|
||||
return false;
|
||||
}
|
||||
#if 0
|
||||
// Don't hurt your team, either!
|
||||
if (G_SameTeam(source->player, target->player) == true)
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
|||
61
src/p_mobj.c
61
src/p_mobj.c
|
|
@ -12168,34 +12168,13 @@ void P_SpawnPlayer(INT32 playernum)
|
|||
G_PlayerReborn(playernum, false);
|
||||
}
|
||||
|
||||
if (G_GametypeHasTeams())
|
||||
if (G_GametypeHasTeams() == true)
|
||||
{
|
||||
// If you're in a team game and you don't have a team assigned yet...
|
||||
if (!p->spectator && p->ctfteam == 0)
|
||||
if (p->spectator == false && p->team == TEAM_UNASSIGNED)
|
||||
{
|
||||
changeteam_union NetPacket;
|
||||
UINT16 usvalue;
|
||||
NetPacket.value.l = NetPacket.value.b = 0;
|
||||
|
||||
// Spawn as a spectator,
|
||||
// yes even in splitscreen mode
|
||||
p->spectator = true;
|
||||
|
||||
// but immediately send a team change packet.
|
||||
NetPacket.packet.playernum = playernum;
|
||||
NetPacket.packet.verification = true;
|
||||
NetPacket.packet.newteam = !(playernum&1) + 1;
|
||||
|
||||
usvalue = SHORT(NetPacket.value.l|NetPacket.value.b);
|
||||
SendNetXCmd(XD_TEAMCHANGE, &usvalue, sizeof(usvalue));
|
||||
G_AssignTeam(p, !(playernum & 1) + 1);
|
||||
}
|
||||
|
||||
// Fix team colors.
|
||||
// This code isn't being done right somewhere else. Oh well.
|
||||
if (p->ctfteam == 1)
|
||||
p->skincolor = skincolor_redteam;
|
||||
else if (p->ctfteam == 2)
|
||||
p->skincolor = skincolor_blueteam;
|
||||
}
|
||||
|
||||
if (leveltime > introtime && K_PodiumSequence() == false)
|
||||
|
|
@ -12605,23 +12584,23 @@ static boolean P_SpawnNonMobjMapThing(mapthing_t *mthing)
|
|||
}
|
||||
return true;
|
||||
}
|
||||
else if (mthing->type == 34) // Red CTF starts
|
||||
else if (mthing->type == 34) // Orange team starts
|
||||
{
|
||||
if (numredctfstarts < MAXPLAYERS)
|
||||
if (numteamstarts[TEAM_ORANGE] < MAXPLAYERS)
|
||||
{
|
||||
redctfstarts[numredctfstarts] = mthing;
|
||||
teamstarts[TEAM_ORANGE][numteamstarts[TEAM_ORANGE]] = mthing;
|
||||
mthing->type = 0;
|
||||
numredctfstarts++;
|
||||
numteamstarts[TEAM_ORANGE]++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (mthing->type == 35) // Blue CTF starts
|
||||
else if (mthing->type == 35) // Blue team starts
|
||||
{
|
||||
if (numbluectfstarts < MAXPLAYERS)
|
||||
if (numteamstarts[TEAM_BLUE] < MAXPLAYERS)
|
||||
{
|
||||
bluectfstarts[numbluectfstarts] = mthing;
|
||||
teamstarts[TEAM_BLUE][numteamstarts[TEAM_BLUE]] = mthing;
|
||||
mthing->type = 0;
|
||||
numbluectfstarts++;
|
||||
numteamstarts[TEAM_BLUE]++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -14909,17 +14888,19 @@ mobj_t *P_SpawnMissile(mobj_t *source, mobj_t *dest, mobjtype_t type)
|
|||
//
|
||||
void P_ColorTeamMissile(mobj_t *missile, player_t *source)
|
||||
{
|
||||
if (G_GametypeHasTeams())
|
||||
if (missile == NULL || source == NULL)
|
||||
{
|
||||
if (source->ctfteam == 2)
|
||||
missile->color = skincolor_bluering;
|
||||
else if (source->ctfteam == 1)
|
||||
missile->color = skincolor_redring;
|
||||
return;
|
||||
}
|
||||
|
||||
if (source->team > TEAM_UNASSIGNED && source->team < TEAM__MAX)
|
||||
{
|
||||
missile->color = g_teaminfo[source->team].color;
|
||||
}
|
||||
/*
|
||||
else
|
||||
missile->color = player->mo->color; //copy color
|
||||
*/
|
||||
{
|
||||
missile->color = source->skincolor;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
WRITEUINT16(save->p, players[i].flashpal);
|
||||
WRITEUINT16(save->p, players[i].flashcount);
|
||||
|
||||
WRITEUINT8(save->p, players[i].skincolor);
|
||||
WRITEUINT16(save->p, players[i].skincolor);
|
||||
WRITEINT32(save->p, players[i].skin);
|
||||
|
||||
for (j = 0; j < MAXAVAILABILITY; j++)
|
||||
|
|
@ -257,6 +257,12 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
|
||||
WRITEUINT8(save->p, players[i].fakeskin);
|
||||
WRITEUINT8(save->p, players[i].lastfakeskin);
|
||||
|
||||
WRITEUINT16(save->p, players[i].prefcolor);
|
||||
WRITEINT32(save->p, players[i].prefskin);
|
||||
WRITEUINT16(save->p, players[i].preffollowercolor);
|
||||
WRITEINT32(save->p, players[i].preffollower);
|
||||
|
||||
WRITEUINT32(save->p, players[i].score);
|
||||
WRITESINT8(save->p, players[i].lives);
|
||||
WRITESINT8(save->p, players[i].xtralife);
|
||||
|
|
@ -287,7 +293,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
WRITEINT32(save->p, players[i].cheatchecknum);
|
||||
WRITEINT32(save->p, players[i].checkpointId);
|
||||
|
||||
WRITEUINT8(save->p, players[i].ctfteam);
|
||||
WRITEUINT8(save->p, players[i].team);
|
||||
|
||||
WRITEUINT8(save->p, players[i].checkskip);
|
||||
|
||||
|
|
@ -424,6 +430,8 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
WRITEUINT8(save->p, players[i].position);
|
||||
WRITEUINT8(save->p, players[i].oldposition);
|
||||
WRITEUINT8(save->p, players[i].positiondelay);
|
||||
WRITEUINT8(save->p, players[i].teamposition);
|
||||
WRITEUINT8(save->p, players[i].teamimportance);
|
||||
WRITEUINT32(save->p, players[i].distancetofinish);
|
||||
WRITEUINT32(save->p, players[i].distancetofinishprev);
|
||||
WRITEUINT32(save->p, players[i].lastpickupdistance);
|
||||
|
|
@ -921,7 +929,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
players[i].flashpal = READUINT16(save->p);
|
||||
players[i].flashcount = READUINT16(save->p);
|
||||
|
||||
players[i].skincolor = READUINT8(save->p);
|
||||
players[i].skincolor = READUINT16(save->p);
|
||||
players[i].skin = READINT32(save->p);
|
||||
|
||||
for (j = 0; j < MAXAVAILABILITY; j++)
|
||||
|
|
@ -931,6 +939,12 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
|
||||
players[i].fakeskin = READUINT8(save->p);
|
||||
players[i].lastfakeskin = READUINT8(save->p);
|
||||
|
||||
players[i].prefcolor = READUINT16(save->p);
|
||||
players[i].prefskin = READINT32(save->p);
|
||||
players[i].preffollowercolor = READUINT16(save->p);
|
||||
players[i].preffollower = READINT32(save->p);
|
||||
|
||||
players[i].score = READUINT32(save->p);
|
||||
players[i].lives = READSINT8(save->p);
|
||||
players[i].xtralife = READSINT8(save->p); // Ring Extra Life counter
|
||||
|
|
@ -961,7 +975,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
players[i].cheatchecknum = READINT32(save->p);
|
||||
players[i].checkpointId = READINT32(save->p);
|
||||
|
||||
players[i].ctfteam = READUINT8(save->p); // 1 == Red, 2 == Blue
|
||||
players[i].team = READUINT8(save->p);
|
||||
|
||||
players[i].checkskip = READUINT8(save->p);
|
||||
|
||||
|
|
@ -1051,6 +1065,8 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
players[i].position = READUINT8(save->p);
|
||||
players[i].oldposition = READUINT8(save->p);
|
||||
players[i].positiondelay = READUINT8(save->p);
|
||||
players[i].teamposition = READUINT8(save->p);
|
||||
players[i].teamimportance = READUINT8(save->p);
|
||||
players[i].distancetofinish = READUINT32(save->p);
|
||||
players[i].distancetofinishprev = READUINT32(save->p);
|
||||
players[i].lastpickupdistance = READUINT32(save->p);
|
||||
|
|
@ -6610,28 +6626,13 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending)
|
|||
WRITEUINT8(save->p, globools);
|
||||
}
|
||||
|
||||
WRITEUINT32(save->p, bluescore);
|
||||
WRITEUINT32(save->p, redscore);
|
||||
|
||||
WRITEUINT16(save->p, skincolor_redteam);
|
||||
WRITEUINT16(save->p, skincolor_blueteam);
|
||||
WRITEUINT16(save->p, skincolor_redring);
|
||||
WRITEUINT16(save->p, skincolor_bluering);
|
||||
for (i = 0; i < TEAM__MAX; i++)
|
||||
{
|
||||
WRITEUINT32(save->p, g_teamscores[i]);
|
||||
}
|
||||
|
||||
WRITEINT32(save->p, modulothing);
|
||||
|
||||
WRITEINT16(save->p, autobalance);
|
||||
WRITEINT16(save->p, teamscramble);
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
WRITEINT16(save->p, scrambleplayers[i]);
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
WRITEINT16(save->p, scrambleteams[i]);
|
||||
|
||||
WRITEINT16(save->p, scrambletotal);
|
||||
WRITEINT16(save->p, scramblecount);
|
||||
|
||||
WRITEUINT32(save->p, racecountdown);
|
||||
WRITEUINT32(save->p, exitcountdown);
|
||||
|
||||
|
|
@ -6654,6 +6655,7 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending)
|
|||
WRITEUINT8(save->p, gamespeed);
|
||||
WRITEUINT8(save->p, numlaps);
|
||||
WRITEUINT8(save->p, franticitems);
|
||||
WRITEUINT8(save->p, g_teamplay);
|
||||
|
||||
WRITESINT8(save->p, speedscramble);
|
||||
WRITESINT8(save->p, encorescramble);
|
||||
|
|
@ -6816,28 +6818,13 @@ static boolean P_NetUnArchiveMisc(savebuffer_t *save, boolean reloading)
|
|||
stoppedclock = !!(globools & (1<<1));
|
||||
}
|
||||
|
||||
bluescore = READUINT32(save->p);
|
||||
redscore = READUINT32(save->p);
|
||||
|
||||
skincolor_redteam = READUINT16(save->p);
|
||||
skincolor_blueteam = READUINT16(save->p);
|
||||
skincolor_redring = READUINT16(save->p);
|
||||
skincolor_bluering = READUINT16(save->p);
|
||||
for (i = 0; i < TEAM__MAX; i++)
|
||||
{
|
||||
g_teamscores[i] = READUINT32(save->p);
|
||||
}
|
||||
|
||||
modulothing = READINT32(save->p);
|
||||
|
||||
autobalance = READINT16(save->p);
|
||||
teamscramble = READINT16(save->p);
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
scrambleplayers[i] = READINT16(save->p);
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
scrambleteams[i] = READINT16(save->p);
|
||||
|
||||
scrambletotal = READINT16(save->p);
|
||||
scramblecount = READINT16(save->p);
|
||||
|
||||
racecountdown = READUINT32(save->p);
|
||||
exitcountdown = READUINT32(save->p);
|
||||
|
||||
|
|
@ -6859,6 +6846,7 @@ static boolean P_NetUnArchiveMisc(savebuffer_t *save, boolean reloading)
|
|||
gamespeed = READUINT8(save->p);
|
||||
numlaps = READUINT8(save->p);
|
||||
franticitems = (boolean)READUINT8(save->p);
|
||||
g_teamplay = (boolean)READUINT8(save->p);
|
||||
|
||||
speedscramble = READSINT8(save->p);
|
||||
encorescramble = READSINT8(save->p);
|
||||
|
|
|
|||
105
src/p_setup.cpp
105
src/p_setup.cpp
|
|
@ -196,13 +196,12 @@ precipmobj_t **precipblocklinks;
|
|||
UINT8 *rejectmatrix;
|
||||
|
||||
// Maintain single and multi player starting spots.
|
||||
INT32 numdmstarts, numcoopstarts, numredctfstarts, numbluectfstarts;
|
||||
INT32 numdmstarts, numcoopstarts, numteamstarts[TEAM__MAX];
|
||||
INT32 numfaultstarts;
|
||||
|
||||
mapthing_t *deathmatchstarts[MAX_DM_STARTS];
|
||||
mapthing_t *playerstarts[MAXPLAYERS];
|
||||
mapthing_t *bluectfstarts[MAXPLAYERS];
|
||||
mapthing_t *redctfstarts[MAXPLAYERS];
|
||||
mapthing_t *teamstarts[TEAM__MAX][MAXPLAYERS];
|
||||
mapthing_t *faultstart;
|
||||
|
||||
// Global state for PartialAddWadFile/MultiSetupWadFiles
|
||||
|
|
@ -5508,7 +5507,7 @@ static void P_ConvertBinaryLinedefTypes(void)
|
|||
lines[i].args[0] = (lines[i].flags & ML_NOTBOUNCY) ? TMT_EACHTIMEENTERANDEXIT : TMT_EACHTIMEENTER;
|
||||
else
|
||||
lines[i].args[0] = TMT_CONTINUOUS;
|
||||
lines[i].args[1] = (lines[i].special > 310) ? TMT_BLUE : TMT_RED;
|
||||
lines[i].args[1] = (lines[i].special > 310) ? TMT_BLUE : TMT_ORANGE;
|
||||
lines[i].special = 309;
|
||||
break;
|
||||
case 313: //No more enemies - once
|
||||
|
|
@ -7684,6 +7683,7 @@ static void P_InitLevelSettings(void)
|
|||
const boolean multi_speed = (gametypes[gametype]->speed == KARTSPEED_AUTO);
|
||||
gamespeed = multi_speed ? KARTSPEED_EASY : gametypes[gametype]->speed;
|
||||
franticitems = false;
|
||||
g_teamplay = false;
|
||||
|
||||
if (K_PodiumSequence() == true)
|
||||
{
|
||||
|
|
@ -7730,6 +7730,7 @@ static void P_InitLevelSettings(void)
|
|||
gamespeed = (UINT8)cv_kartspeed.value;
|
||||
}
|
||||
franticitems = (boolean)cv_kartfrantic.value;
|
||||
g_teamplay = (boolean)cv_teamplay.value; // we will overwrite this later if there is not enough players
|
||||
}
|
||||
|
||||
memset(&battleovertime, 0, sizeof(struct battleovertime));
|
||||
|
|
@ -7780,16 +7781,12 @@ void P_RespawnThings(void)
|
|||
|
||||
static void P_ResetSpawnpoints(void)
|
||||
{
|
||||
UINT8 i;
|
||||
|
||||
numdmstarts = numredctfstarts = numbluectfstarts = 0;
|
||||
numfaultstarts = 0;
|
||||
faultstart = NULL;
|
||||
UINT8 i, j;
|
||||
|
||||
// reset the player starts
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
playerstarts[i] = bluectfstarts[i] = redctfstarts[i] = NULL;
|
||||
playerstarts[i] = NULL;
|
||||
|
||||
if (playeringame[i])
|
||||
{
|
||||
|
|
@ -7798,9 +7795,23 @@ static void P_ResetSpawnpoints(void)
|
|||
}
|
||||
}
|
||||
|
||||
numfaultstarts = 0;
|
||||
faultstart = NULL;
|
||||
|
||||
numdmstarts = 0;
|
||||
for (i = 0; i < MAX_DM_STARTS; i++)
|
||||
deathmatchstarts[i] = NULL;
|
||||
|
||||
for (i = 0; i < TEAM__MAX; i++)
|
||||
{
|
||||
numteamstarts[i] = 0;
|
||||
|
||||
for (j = 0; j < MAXPLAYERS; j++)
|
||||
{
|
||||
teamstarts[i][j] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
skyboxviewpnts[i] = skyboxcenterpnts[i] = NULL;
|
||||
}
|
||||
|
|
@ -7971,6 +7982,75 @@ static void P_InitCamera(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void P_ShuffleTeams(void)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (G_GametypeHasTeams() == false)
|
||||
{
|
||||
// Teams are not enabled, force to TEAM_UNASSIGNED
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
players[i].team = TEAM_UNASSIGNED;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// The following will sort TEAM_UNASSIGNED players at random.
|
||||
// In addition, you should know all players will have their team
|
||||
// unset every round unless certain conditions are met.
|
||||
// See Y_MidIntermission, G_InitNew (where resetplayer == true)
|
||||
|
||||
CONS_Debug(DBG_TEAMS, "Shuffling player teams...\n");
|
||||
|
||||
std::vector<UINT8> player_shuffle;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i] == false || players[i].spectator == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
player_shuffle.push_back(i);
|
||||
}
|
||||
|
||||
size_t n = player_shuffle.size();
|
||||
if (inDuel == true || n <= 2) // cv_teamplay_min.value
|
||||
{
|
||||
CONS_Debug(DBG_TEAMS, "Not enough players to support teams; forcing teamplay preference off.\n");
|
||||
|
||||
// Not enough players for teams.
|
||||
// Turn off the preference for this match.
|
||||
g_teamplay = false;
|
||||
|
||||
// But we may still be in a forced
|
||||
// teams gametype, so only return false
|
||||
// if our preference means anything.
|
||||
if (G_GametypeHasTeams() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (n > 1)
|
||||
{
|
||||
for (i = n - 1; i > 0; i--)
|
||||
{
|
||||
size_t j = P_RandomKey(PR_TEAMS, i + 1);
|
||||
|
||||
size_t temp = player_shuffle[i];
|
||||
player_shuffle[i] = player_shuffle[j];
|
||||
player_shuffle[j] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
G_AutoAssignTeam(&players[ player_shuffle[i] ]);
|
||||
}
|
||||
}
|
||||
|
||||
static void P_InitPlayers(void)
|
||||
{
|
||||
INT32 i, skin = -1, follower = -1;
|
||||
|
|
@ -7978,6 +8058,9 @@ static void P_InitPlayers(void)
|
|||
// Make sure objectplace is OFF when you first start the level!
|
||||
OP_ResetObjectplace();
|
||||
|
||||
// Update skins / colors between levels.
|
||||
G_UpdateAllPlayerPreferences();
|
||||
|
||||
// Are we forcing a character?
|
||||
if (gametype == GT_TUTORIAL)
|
||||
{
|
||||
|
|
@ -8900,6 +8983,8 @@ void P_PostLoadLevel(void)
|
|||
}
|
||||
}
|
||||
|
||||
P_ShuffleTeams();
|
||||
|
||||
K_TimerInit();
|
||||
|
||||
P_InitPlayers();
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ extern unsigned char mapmd5[16];
|
|||
// Player spawn spots for deathmatch.
|
||||
#define MAX_DM_STARTS 64
|
||||
extern mapthing_t *deathmatchstarts[MAX_DM_STARTS];
|
||||
extern INT32 numdmstarts, numcoopstarts, numredctfstarts, numbluectfstarts, numfaultstarts;
|
||||
extern INT32 numdmstarts, numcoopstarts, numteamstarts[TEAM__MAX], numfaultstarts;
|
||||
|
||||
extern boolean levelloading;
|
||||
extern boolean g_reloadinggamestate;
|
||||
|
|
|
|||
|
|
@ -1535,7 +1535,7 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
|
|||
// Only red/blue team members can activate this.
|
||||
if (!(actor && actor->player))
|
||||
return false;
|
||||
if (actor->player->ctfteam != ((triggerline->args[1] == TMT_RED) ? 1 : 2))
|
||||
if (actor->player->team != ((triggerline->args[1] == TMT_ORANGE) ? TEAM_ORANGE : TEAM_BLUE))
|
||||
return false;
|
||||
break;
|
||||
case 314:
|
||||
|
|
|
|||
|
|
@ -289,7 +289,7 @@ typedef enum
|
|||
|
||||
typedef enum
|
||||
{
|
||||
TMT_RED = 0,
|
||||
TMT_ORANGE = 0,
|
||||
TMT_BLUE = 1,
|
||||
} textmapteam_t;
|
||||
|
||||
|
|
|
|||
143
src/p_tick.c
143
src/p_tick.c
|
|
@ -585,146 +585,6 @@ static void P_RunThinkers(void)
|
|||
ps_acs_time = I_GetPreciseTime() - ps_acs_time;
|
||||
}
|
||||
|
||||
//
|
||||
// P_DoAutobalanceTeams()
|
||||
//
|
||||
// Determine if the teams are unbalanced, and if so, move a player to the other team.
|
||||
//
|
||||
static void P_DoAutobalanceTeams(void)
|
||||
{
|
||||
changeteam_union NetPacket;
|
||||
UINT16 usvalue;
|
||||
INT32 i=0;
|
||||
INT32 red=0, blue=0;
|
||||
INT32 redarray[MAXPLAYERS], bluearray[MAXPLAYERS];
|
||||
//INT32 redflagcarrier = 0, blueflagcarrier = 0;
|
||||
INT32 totalred = 0, totalblue = 0;
|
||||
|
||||
NetPacket.value.l = NetPacket.value.b = 0;
|
||||
memset(redarray, 0, sizeof(redarray));
|
||||
memset(bluearray, 0, sizeof(bluearray));
|
||||
|
||||
// Only do it if we have enough room in the net buffer to send it.
|
||||
// Otherwise, come back next time and try again.
|
||||
if (sizeof(usvalue) > GetFreeXCmdSize(0))
|
||||
return;
|
||||
|
||||
//We have to store the players in an array with the rest of their team.
|
||||
//We can then pick a random player to be forced to change teams.
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i] && players[i].ctfteam)
|
||||
{
|
||||
if (players[i].ctfteam == 1)
|
||||
{
|
||||
//if (!players[i].gotflag)
|
||||
{
|
||||
redarray[red] = i; //store the player's node.
|
||||
red++;
|
||||
}
|
||||
/*else
|
||||
redflagcarrier++;*/
|
||||
}
|
||||
else
|
||||
{
|
||||
//if (!players[i].gotflag)
|
||||
{
|
||||
bluearray[blue] = i; //store the player's node.
|
||||
blue++;
|
||||
}
|
||||
/*else
|
||||
blueflagcarrier++;*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
totalred = red;// + redflagcarrier;
|
||||
totalblue = blue;// + blueflagcarrier;
|
||||
|
||||
if ((abs(totalred - totalblue) > max(1, (totalred + totalblue) / 8)))
|
||||
{
|
||||
if (totalred > totalblue)
|
||||
{
|
||||
i = M_RandomKey(red);
|
||||
NetPacket.packet.newteam = 2;
|
||||
NetPacket.packet.playernum = redarray[i];
|
||||
NetPacket.packet.verification = true;
|
||||
NetPacket.packet.autobalance = true;
|
||||
|
||||
usvalue = SHORT(NetPacket.value.l|NetPacket.value.b);
|
||||
SendNetXCmd(XD_TEAMCHANGE, &usvalue, sizeof(usvalue));
|
||||
}
|
||||
else //if (totalblue > totalred)
|
||||
{
|
||||
i = M_RandomKey(blue);
|
||||
NetPacket.packet.newteam = 1;
|
||||
NetPacket.packet.playernum = bluearray[i];
|
||||
NetPacket.packet.verification = true;
|
||||
NetPacket.packet.autobalance = true;
|
||||
|
||||
usvalue = SHORT(NetPacket.value.l|NetPacket.value.b);
|
||||
SendNetXCmd(XD_TEAMCHANGE, &usvalue, sizeof(usvalue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// P_DoTeamscrambling()
|
||||
//
|
||||
// If a team scramble has been started, scramble one person from the
|
||||
// pre-made scramble array. Said array is created in TeamScramble_OnChange()
|
||||
//
|
||||
void P_DoTeamscrambling(void)
|
||||
{
|
||||
changeteam_union NetPacket;
|
||||
UINT16 usvalue;
|
||||
NetPacket.value.l = NetPacket.value.b = 0;
|
||||
|
||||
// Only do it if we have enough room in the net buffer to send it.
|
||||
// Otherwise, come back next time and try again.
|
||||
if (sizeof(usvalue) > GetFreeXCmdSize(0))
|
||||
return;
|
||||
|
||||
if (scramblecount < scrambletotal)
|
||||
{
|
||||
if (players[scrambleplayers[scramblecount]].ctfteam != scrambleteams[scramblecount])
|
||||
{
|
||||
NetPacket.packet.newteam = scrambleteams[scramblecount];
|
||||
NetPacket.packet.playernum = scrambleplayers[scramblecount];
|
||||
NetPacket.packet.verification = true;
|
||||
NetPacket.packet.scrambled = true;
|
||||
|
||||
usvalue = SHORT(NetPacket.value.l|NetPacket.value.b);
|
||||
SendNetXCmd(XD_TEAMCHANGE, &usvalue, sizeof(usvalue));
|
||||
}
|
||||
|
||||
scramblecount++; //Increment, and get to the next player when we come back here next time.
|
||||
}
|
||||
else
|
||||
CV_SetValue(&cv_teamscramble, 0);
|
||||
}
|
||||
|
||||
static inline void P_DoTeamStuff(void)
|
||||
{
|
||||
// Automatic team balance for CTF and team match
|
||||
if (leveltime % (TICRATE * 5) == 0) //only check once per five seconds for the sake of CPU conservation.
|
||||
{
|
||||
// Do not attempt to autobalance and scramble teams at the same time.
|
||||
// Only the server should execute this. No verified admins, please.
|
||||
if ((cv_autobalance.value && !cv_teamscramble.value) && cv_allowteamchange.value && server)
|
||||
P_DoAutobalanceTeams();
|
||||
}
|
||||
|
||||
// Team scramble code for team match and CTF.
|
||||
if ((leveltime % (TICRATE/7)) == 0)
|
||||
{
|
||||
// If we run out of time in the level, the beauty is that
|
||||
// the Y_Ticker() team scramble code will pick it up.
|
||||
if (cv_teamscramble.value && server)
|
||||
P_DoTeamscrambling();
|
||||
}
|
||||
}
|
||||
|
||||
static inline void P_DeviceRumbleTick(void)
|
||||
{
|
||||
UINT8 i;
|
||||
|
|
@ -1225,9 +1085,6 @@ void P_Ticker(boolean run)
|
|||
timeinmap++;
|
||||
}
|
||||
|
||||
if (G_GametypeHasTeams())
|
||||
P_DoTeamStuff();
|
||||
|
||||
if (run)
|
||||
{
|
||||
if (racecountdown > 1)
|
||||
|
|
|
|||
86
src/p_user.c
86
src/p_user.c
|
|
@ -73,6 +73,7 @@
|
|||
#include "k_hud.h" // K_AddMessage
|
||||
#include "m_easing.h"
|
||||
#include "acs/interface.h"
|
||||
#include "byteptr.h"
|
||||
|
||||
#ifdef HWRENDER
|
||||
#include "hardware/hw_light.h"
|
||||
|
|
@ -514,25 +515,44 @@ void P_GivePlayerLives(player_t *player, INT32 numlives)
|
|||
// Adds to the player's score
|
||||
void P_AddPlayerScore(player_t *player, INT32 amount)
|
||||
{
|
||||
if (!((gametyperules & GTR_POINTLIMIT)))
|
||||
if ((gametyperules & GTR_POINTLIMIT) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (player->exiting) // srb2kart
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const boolean teams = G_GametypeHasTeams();
|
||||
|
||||
// Don't underflow.
|
||||
// Don't go above MAXSCORE.
|
||||
if (amount < 0 && (UINT32)-amount > player->roundscore)
|
||||
{
|
||||
player->roundscore = 0;
|
||||
}
|
||||
else if (player->roundscore + amount < MAXSCORE)
|
||||
{
|
||||
if (player->roundscore < g_pointlimit && g_pointlimit <= player->roundscore + amount)
|
||||
if (player->roundscore < g_pointlimit
|
||||
&& g_pointlimit <= player->roundscore + amount
|
||||
&& teams == false) // We want the normal scoring function to update roundscore, but this notification will be done by G_AddTeamScore.
|
||||
{
|
||||
HU_DoTitlecardCEchoForDuration(player, "K.O. READY!", true, 5*TICRATE/2);
|
||||
}
|
||||
|
||||
player->roundscore += amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
player->roundscore = MAXSCORE;
|
||||
}
|
||||
|
||||
if (teams == true)
|
||||
{
|
||||
G_AddTeamScore(player->team, amount, player);
|
||||
}
|
||||
}
|
||||
|
||||
void P_PlayRinglossSound(mobj_t *source)
|
||||
|
|
@ -3742,49 +3762,9 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
|
|||
|
||||
boolean P_SpectatorJoinGame(player_t *player)
|
||||
{
|
||||
INT32 changeto = 0;
|
||||
const char *text = NULL;
|
||||
|
||||
// Team changing isn't allowed.
|
||||
if (!cv_allowteamchange.value)
|
||||
return false;
|
||||
|
||||
// Team changing in Team Match and CTF
|
||||
// Pressing fire assigns you to a team that needs players if allowed.
|
||||
// Partial code reproduction from p_tick.c autobalance code.
|
||||
// a surprise tool that will help us later...
|
||||
if (G_GametypeHasTeams() && player->ctfteam == 0)
|
||||
{
|
||||
INT32 z, numplayersred = 0, numplayersblue = 0;
|
||||
|
||||
//find a team by num players, score, or random if all else fails.
|
||||
for (z = 0; z < MAXPLAYERS; ++z)
|
||||
if (playeringame[z])
|
||||
{
|
||||
if (players[z].ctfteam == 1)
|
||||
++numplayersred;
|
||||
else if (players[z].ctfteam == 2)
|
||||
++numplayersblue;
|
||||
}
|
||||
// for z
|
||||
|
||||
if (numplayersblue > numplayersred)
|
||||
changeto = 1;
|
||||
else if (numplayersred > numplayersblue)
|
||||
changeto = 2;
|
||||
else if (bluescore > redscore)
|
||||
changeto = 1;
|
||||
else if (redscore > bluescore)
|
||||
changeto = 2;
|
||||
else
|
||||
changeto = (P_RandomFixed(PR_RULESCRAMBLE) & 1) + 1;
|
||||
|
||||
if (!LUA_HookTeamSwitch(player, changeto, true, false, false))
|
||||
return false;
|
||||
}
|
||||
|
||||
// no conditions that could cause the gamejoin to fail below this line
|
||||
|
||||
if (player->mo)
|
||||
{
|
||||
P_RemoveMobj(player->mo);
|
||||
|
|
@ -3793,7 +3773,7 @@ boolean P_SpectatorJoinGame(player_t *player)
|
|||
player->spectator = false;
|
||||
player->pflags &= ~PF_WANTSTOJOIN;
|
||||
player->spectatewait = 0;
|
||||
player->ctfteam = changeto;
|
||||
player->team = TEAM_UNASSIGNED; // We will auto-assign later.
|
||||
player->playerstate = PST_REBORN;
|
||||
player->enteredGame = true;
|
||||
|
||||
|
|
@ -3805,12 +3785,7 @@ boolean P_SpectatorJoinGame(player_t *player)
|
|||
}
|
||||
|
||||
// a surprise tool that will help us later...
|
||||
if (changeto == 1)
|
||||
text = va("\x82*%s switched to the %c%s%c team.\n", player_names[player-players], '\x85', "RED", '\x82');
|
||||
else if (changeto == 2)
|
||||
text = va("\x82*%s switched to the %c%s%c team.\n", player_names[player-players], '\x85', "BLU", '\x82');
|
||||
else
|
||||
text = va("\x82*%s entered the game.", player_names[player-players]);
|
||||
text = va("\x82*%s entered the game.", player_names[player-players]);
|
||||
|
||||
HU_AddChatText(text, false);
|
||||
return true; // no more player->mo, cannot continue.
|
||||
|
|
@ -4891,16 +4866,13 @@ void P_CheckRaceGriefing(player_t *player, boolean dopunishment)
|
|||
else
|
||||
{
|
||||
// Send spectate
|
||||
changeteam_union NetPacket;
|
||||
UINT16 usvalue;
|
||||
UINT8 buf[2];
|
||||
UINT8 *p = buf;
|
||||
|
||||
NetPacket.value.l = NetPacket.value.b = 0;
|
||||
NetPacket.packet.newteam = 0;
|
||||
NetPacket.packet.playernum = n;
|
||||
NetPacket.packet.verification = true;
|
||||
WRITEUINT8(p, n);
|
||||
WRITEUINT8(p, 0);
|
||||
|
||||
usvalue = SHORT(NetPacket.value.l|NetPacket.value.b);
|
||||
SendNetXCmd(XD_TEAMCHANGE, &usvalue, sizeof(usvalue));
|
||||
SendNetXCmd(XD_SPECTATE, &buf, p - buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -408,22 +408,6 @@ static void SetSkin(player_t *player, INT32 skinnum)
|
|||
player->kartweight = skin->kartweight;
|
||||
player->charflags = skin->flags;
|
||||
|
||||
#if 0
|
||||
if (!CV_CheatsEnabled() && !(netgame || multiplayer || demo.playback))
|
||||
{
|
||||
for (i = 0; i <= r_splitscreen; i++)
|
||||
{
|
||||
if (playernum == g_localplayers[i])
|
||||
{
|
||||
CV_StealthSetValue(&cv_playercolor[i], skin->prefcolor);
|
||||
}
|
||||
}
|
||||
|
||||
player->skincolor = skin->prefcolor;
|
||||
K_KartResetPlayerColor(player);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (player->followmobj)
|
||||
{
|
||||
P_RemoveMobj(player->followmobj);
|
||||
|
|
|
|||
|
|
@ -96,13 +96,11 @@ boolean ST_SameTeam(player_t *a, player_t *b)
|
|||
if (G_GametypeHasTeams() == true)
|
||||
{
|
||||
// You get team messages if you're on the same team.
|
||||
return (a->ctfteam == b->ctfteam);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not that everyone's not on the same team, but team messages go to normal chat if everyone's not in the same team.
|
||||
return true;
|
||||
return (a->team == b->team);
|
||||
}
|
||||
|
||||
// Not that everyone's not on the same team, but team messages go to normal chat if everyone's not in the same team.
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean st_stopped = true;
|
||||
|
|
|
|||
|
|
@ -144,6 +144,7 @@ TYPEDEF (unloaded_cupheader_t);
|
|||
TYPEDEF (exitcondition_t);
|
||||
TYPEDEF (darkness_t);
|
||||
TYPEDEF (musicfade_t);
|
||||
TYPEDEF (teaminfo_t);
|
||||
|
||||
// font.h
|
||||
TYPEDEF (font_t);
|
||||
|
|
|
|||
184
src/y_inter.cpp
184
src/y_inter.cpp
|
|
@ -103,6 +103,11 @@ static boolean Y_CanSkipIntermission(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
boolean Y_IntermissionPlayerLock(void)
|
||||
{
|
||||
return (gamestate == GS_INTERMISSION && data.rankingsmode == false);
|
||||
}
|
||||
|
||||
static void Y_UnloadData(void);
|
||||
|
||||
//
|
||||
|
|
@ -191,8 +196,6 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
|
|||
numplayersingame++;
|
||||
}
|
||||
|
||||
memset(data.color, 0, sizeof (data.color));
|
||||
memset(data.character, 0, sizeof (data.character));
|
||||
memset(completed, 0, sizeof (completed));
|
||||
data.numplayers = 0;
|
||||
data.showroundnum = false;
|
||||
|
|
@ -202,9 +205,63 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
|
|||
srb2::StandingsJson standings {};
|
||||
bool savestandings = (!rankingsmode && demo.recording);
|
||||
|
||||
// Team stratification (this code only barely supports more than 2 teams)
|
||||
data.winningteam = TEAM_UNASSIGNED;
|
||||
data.halfway = UINT8_MAX;
|
||||
|
||||
UINT8 countteam[TEAM__MAX];
|
||||
UINT8 smallestteam = UINT8_MAX;
|
||||
memset(countteam, 0, sizeof(countteam));
|
||||
|
||||
if (rankingsmode == 0 && G_GametypeHasTeams())
|
||||
{
|
||||
for (i = data.winningteam+1; i < TEAM__MAX; i++)
|
||||
{
|
||||
countteam[i] = G_CountTeam(i);
|
||||
|
||||
if (g_teamscores[data.winningteam] < g_teamscores[i])
|
||||
{
|
||||
data.winningteam = i;
|
||||
}
|
||||
|
||||
if (smallestteam > countteam[i])
|
||||
{
|
||||
smallestteam = countteam[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (countteam[data.winningteam])
|
||||
{
|
||||
data.halfway = countteam[data.winningteam] - 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j < numplayersingame; j++)
|
||||
{
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
i = 0;
|
||||
|
||||
if (data.winningteam != TEAM_UNASSIGNED)
|
||||
{
|
||||
for (; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].spectator || completed[i])
|
||||
continue;
|
||||
|
||||
if (players[i].team != data.winningteam)
|
||||
continue;
|
||||
|
||||
comparison(i);
|
||||
}
|
||||
|
||||
if (data.val[data.numplayers] == UINT32_MAX)
|
||||
{
|
||||
// Only run the un-teamed loop if everybody
|
||||
// on the winning team was previously placed
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].spectator || completed[i])
|
||||
continue;
|
||||
|
|
@ -217,9 +274,6 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
|
|||
completed[i] = true;
|
||||
data.grade[i] = K_PlayerTallyActive(&players[i]) ? players[i].tally.rank : GRADE_INVALID;
|
||||
|
||||
data.color[data.numplayers] = players[i].skincolor;
|
||||
data.character[data.numplayers] = players[i].skin;
|
||||
|
||||
if (data.numplayers && (data.val[data.numplayers] == data.val[data.numplayers-1]))
|
||||
{
|
||||
data.pos[data.numplayers] = data.pos[data.numplayers-1];
|
||||
|
|
@ -235,13 +289,33 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
|
|||
|
||||
if (!rankingsmode)
|
||||
{
|
||||
if ((powertype == PWRLV_DISABLED)
|
||||
&& !(players[i].pflags & PF_NOCONTEST)
|
||||
&& (data.pos[data.numplayers] < (numplayersingame + spectateGriefed)))
|
||||
// Online rank is handled further below in this file.
|
||||
if (powertype == PWRLV_DISABLED)
|
||||
{
|
||||
// Online rank is handled further below in this file.
|
||||
data.increase[i] = K_CalculateGPRankPoints(data.pos[data.numplayers], numplayersingame + spectateGriefed);
|
||||
players[i].score += data.increase[i];
|
||||
if (data.winningteam != TEAM_UNASSIGNED)
|
||||
{
|
||||
// TODO ASK TYRON
|
||||
if (smallestteam != 0
|
||||
&& players[i].team == data.winningteam)
|
||||
{
|
||||
data.increase[i] = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UINT8 pointgetters = numplayersingame + spectateGriefed;
|
||||
|
||||
if (data.pos[data.numplayers] < pointgetters
|
||||
&& !(players[i].pflags & PF_NOCONTEST))
|
||||
{
|
||||
data.increase[i] = K_CalculateGPRankPoints(data.pos[data.numplayers], pointgetters);
|
||||
}
|
||||
}
|
||||
|
||||
if (data.increase[i] > 0)
|
||||
{
|
||||
players[i].score += data.increase[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (savestandings)
|
||||
|
|
@ -249,8 +323,8 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
|
|||
srb2::StandingJson standing {};
|
||||
standing.ranking = data.pos[data.numplayers];
|
||||
standing.name = std::string(player_names[i]);
|
||||
standing.demoskin = data.character[data.numplayers];
|
||||
standing.skincolor = std::string(skincolors[data.color[data.numplayers]].name);
|
||||
standing.demoskin = players[i].skin;
|
||||
standing.skincolor = std::string(skincolors[players[i].skincolor].name);
|
||||
standing.timeorscore = data.val[data.numplayers];
|
||||
standings.standings.emplace_back(std::move(standing));
|
||||
}
|
||||
|
|
@ -290,6 +364,14 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
|
|||
data.numplayers++;
|
||||
}
|
||||
|
||||
if (data.numplayers <= 2
|
||||
|| data.halfway == UINT8_MAX
|
||||
|| data.halfway >= 8
|
||||
|| (data.numplayers - data.halfway) >= 8)
|
||||
{
|
||||
data.halfway = (data.numplayers-1)/2;
|
||||
}
|
||||
|
||||
if (savestandings)
|
||||
{
|
||||
srb2::write_current_demo_end_marker();
|
||||
|
|
@ -386,7 +468,19 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
|
|||
{
|
||||
data.mainplayer = i;
|
||||
|
||||
if (!(players[i].pflags & PF_NOCONTEST))
|
||||
if (data.winningteam != TEAM_UNASSIGNED
|
||||
&& players[i].team != TEAM_UNASSIGNED)
|
||||
{
|
||||
data.gotthrough = true;
|
||||
|
||||
snprintf(data.headerstring,
|
||||
sizeof data.headerstring,
|
||||
"%s TEAM",
|
||||
g_teaminfo[players[i].team].name);
|
||||
|
||||
data.showroundnum = true;
|
||||
}
|
||||
else if (!(players[i].pflags & PF_NOCONTEST))
|
||||
{
|
||||
data.gotthrough = true;
|
||||
|
||||
|
|
@ -506,11 +600,13 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset)
|
|||
x2 -= 9;
|
||||
}
|
||||
|
||||
if (standings->numplayers > 10)
|
||||
UINT8 halfway = standings->halfway;
|
||||
|
||||
if (halfway > 4)
|
||||
{
|
||||
yspacing--;
|
||||
}
|
||||
else if (standings->numplayers <= 6)
|
||||
else if (halfway <= 2)
|
||||
{
|
||||
yspacing++;
|
||||
if (verticalresults)
|
||||
|
|
@ -545,7 +641,7 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset)
|
|||
);
|
||||
|
||||
i = 0;
|
||||
UINT8 halfway = (standings->numplayers-1)/2;
|
||||
|
||||
if (doreverse)
|
||||
{
|
||||
i = standings->numplayers-1;
|
||||
|
|
@ -563,13 +659,13 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset)
|
|||
else
|
||||
{
|
||||
UINT8 *charcolormap = NULL;
|
||||
if (!R_CanShowSkinInDemo(standings->character[i]))
|
||||
if (!R_CanShowSkinInDemo(players[pnum].skin))
|
||||
{
|
||||
charcolormap = R_GetTranslationColormap(TC_BLINK, static_cast<skincolornum_t>(standings->color[i]), GTC_CACHE);
|
||||
charcolormap = R_GetTranslationColormap(TC_BLINK, static_cast<skincolornum_t>(players[pnum].skincolor), GTC_CACHE);
|
||||
}
|
||||
else if (standings->color[i] != SKINCOLOR_NONE)
|
||||
else
|
||||
{
|
||||
charcolormap = R_GetTranslationColormap(standings->character[i], static_cast<skincolornum_t>(standings->color[i]), GTC_CACHE);
|
||||
charcolormap = R_GetTranslationColormap(players[pnum].skin, static_cast<skincolornum_t>(players[pnum].skincolor), GTC_CACHE);
|
||||
}
|
||||
|
||||
if (standings->isduel)
|
||||
|
|
@ -588,7 +684,7 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset)
|
|||
|
||||
M_DrawCharacterSprite(
|
||||
duelx + 40, duely + 78,
|
||||
standings->character[i],
|
||||
players[pnum].skin,
|
||||
spr2,
|
||||
(datarightofcolumn ? 1 : 7),
|
||||
0,
|
||||
|
|
@ -640,7 +736,7 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset)
|
|||
|
||||
V_DrawRightAlignedThinString(x+13, y-2, 0, va("%d", standings->pos[i]));
|
||||
|
||||
if (standings->color[i] != SKINCOLOR_NONE)
|
||||
//if (players[pnum].skincolor != SKINCOLOR_NONE)
|
||||
{
|
||||
if ((players[pnum].pflags & PF_NOCONTEST) && players[pnum].bot)
|
||||
{
|
||||
|
|
@ -649,15 +745,15 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset)
|
|||
x+14, y-5,
|
||||
0,
|
||||
static_cast<patch_t*>(W_CachePatchName("MINIDEAD", PU_CACHE)),
|
||||
R_GetTranslationColormap(TC_DEFAULT, static_cast<skincolornum_t>(standings->color[i]), GTC_CACHE)
|
||||
R_GetTranslationColormap(TC_DEFAULT, static_cast<skincolornum_t>(players[pnum].skincolor), GTC_CACHE)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
charcolormap = R_GetTranslationColormap(standings->character[i], static_cast<skincolornum_t>(standings->color[i]), GTC_CACHE);
|
||||
charcolormap = R_GetTranslationColormap(players[pnum].skin, static_cast<skincolornum_t>(players[pnum].skincolor), GTC_CACHE);
|
||||
V_DrawMappedPatch(x+14, y-5, 0,
|
||||
R_CanShowSkinInDemo(standings->character[i]) ?
|
||||
faceprefix[standings->character[i]][FACE_MINIMAP] : kp_unknownminimap,
|
||||
R_CanShowSkinInDemo(players[pnum].skin) ?
|
||||
faceprefix[players[pnum].skin][FACE_MINIMAP] : kp_unknownminimap,
|
||||
charcolormap);
|
||||
}
|
||||
}
|
||||
|
|
@ -1820,13 +1916,15 @@ void Y_Ticker(void)
|
|||
|
||||
// Team scramble code for team match and CTF.
|
||||
// Don't do this if we're going to automatically scramble teams next round.
|
||||
/*if (G_GametypeHasTeams() && cv_teamscramble.value && !cv_scrambleonchange.value && server)
|
||||
/*
|
||||
if (G_GametypeHasTeams() && cv_teamscramble.value && !cv_scrambleonchange.value && server)
|
||||
{
|
||||
// If we run out of time in intermission, the beauty is that
|
||||
// the P_Ticker() team scramble code will pick it up.
|
||||
if ((intertic % (TICRATE/7)) == 0)
|
||||
P_DoTeamscrambling();
|
||||
}*/
|
||||
}
|
||||
*/
|
||||
|
||||
if ((timer < INFINITE_TIMER && --timer <= 0)
|
||||
|| (intertic == endtic))
|
||||
|
|
@ -1892,8 +1990,7 @@ void Y_Ticker(void)
|
|||
{
|
||||
if (!data.rankingsmode && sorttic != -1 && (intertic >= sorttic + 8))
|
||||
{
|
||||
// Anything with post-intermission consequences here should also occur in Y_EndIntermission.
|
||||
K_RetireBots();
|
||||
Y_MidIntermission();
|
||||
Y_CalculateMatchData(1, Y_CompareRank);
|
||||
}
|
||||
|
||||
|
|
@ -2345,6 +2442,27 @@ void Y_StartIntermission(void)
|
|||
|
||||
// ======
|
||||
|
||||
//
|
||||
// Y_MidIntermission
|
||||
//
|
||||
void Y_MidIntermission(void)
|
||||
{
|
||||
// Replacing bots that fail out of play
|
||||
K_RetireBots();
|
||||
|
||||
// If tournament play is not in action...
|
||||
if (roundqueue.position == 0)
|
||||
{
|
||||
// Unset player teams in anticipation of P_ShuffleTeams
|
||||
|
||||
UINT8 i;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
players[i].team = TEAM_UNASSIGNED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Y_EndIntermission
|
||||
//
|
||||
|
|
@ -2352,7 +2470,7 @@ void Y_EndIntermission(void)
|
|||
{
|
||||
if (!data.rankingsmode)
|
||||
{
|
||||
K_RetireBots();
|
||||
Y_MidIntermission();
|
||||
}
|
||||
|
||||
Y_UnloadData();
|
||||
|
|
|
|||
|
|
@ -25,18 +25,17 @@ typedef struct
|
|||
boolean showrank; // show rank-restricted queue entry at the end, if it exists
|
||||
boolean encore; // encore mode
|
||||
boolean isduel; // duel mode
|
||||
UINT8 winningteam; // teamplay
|
||||
boolean showroundnum; // round number
|
||||
|
||||
char headerstring[64]; // holds levelnames up to 64 characters
|
||||
|
||||
UINT8 numplayers; // Number of players being displayed
|
||||
UINT8 halfway; // Position at which column switches
|
||||
|
||||
SINT8 num[MAXPLAYERS]; // Player #
|
||||
UINT8 pos[MAXPLAYERS]; // player positions. used for ties
|
||||
|
||||
UINT8 character[MAXPLAYERS]; // Character #
|
||||
UINT16 color[MAXPLAYERS]; // Color #
|
||||
|
||||
UINT32 val[MAXPLAYERS]; // Gametype-specific value
|
||||
char strval[MAXPLAYERS][MAXPLAYERNAME+1];
|
||||
|
||||
|
|
@ -59,6 +58,7 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations,
|
|||
void Y_DrawIntermissionButton(INT32 startslide, INT32 through, boolean widescreen);
|
||||
|
||||
void Y_StartIntermission(void);
|
||||
void Y_MidIntermission(void);
|
||||
void Y_EndIntermission(void);
|
||||
|
||||
boolean Y_ShouldDoIntermission(void);
|
||||
|
|
@ -66,6 +66,8 @@ void Y_DetermineIntermissionType(void);
|
|||
|
||||
void Y_PlayIntermissionMusic(void);
|
||||
|
||||
boolean Y_IntermissionPlayerLock(void);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
int_none,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue