Merge branch 'queue-skin' into 'master'

Queued skin/color changes

See merge request KartKrew/Kart!2452
This commit is contained in:
Oni 2024-09-28 17:46:42 +00:00
commit 71588f490f
47 changed files with 1292 additions and 1309 deletions

View file

@ -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) bool CallFunc_PlayerTeam(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC)
{ {
auto info = &static_cast<Thread *>(thread)->info; auto info = &static_cast<Thread *>(thread)->info;
UINT8 teamID = 0; UINT8 teamID = TEAM_UNASSIGNED;
(void)argV; (void)argV;
(void)argC; (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 != NULL && P_MobjWasRemoved(info->mo) == false)
&& (info->mo->player != NULL)) && (info->mo->player != NULL))
{ {
teamID = info->mo->player->ctfteam; teamID = info->mo->player->team;
} }
thread->dataStk.push(teamID); thread->dataStk.push(teamID);

View file

@ -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... // 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(); consvar_t cv_blamecfail = NetVar("blamecfail", "Off").on_off();
// Speed of file downloading (in packets per tic) // 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_pingtimeout = NetVar("maxdelaytimeout", "10").min_max(8, 120);
consvar_t cv_resynchattempts = NetVar("resynchattempts", "2").min_max(1, 20, {{0, "No"}}); 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_showjoinaddress = NetVar("showjoinaddress", "Off").on_off();
consvar_t cv_zvote_delay = NetVar("zvote_delay", "20").values(CV_Unsigned); consvar_t cv_zvote_delay = NetVar("zvote_delay", "20").values(CV_Unsigned);
consvar_t cv_zvote_length = NetVar("zvote_length", "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); void KartSpeed_OnChange(void);
consvar_t cv_kartspeed = UnsavedNetVar("gamespeed", "Auto Gear").values(kartspeed_cons_t).onchange_noinit(KartSpeed_OnChange); 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(); consvar_t cv_kartusepwrlv = UnsavedNetVar("usepwrlv", "Yes").yes_no();
void LiveStudioAudience_OnChange(void); void LiveStudioAudience_OnChange(void);
@ -997,9 +992,6 @@ consvar_t cv_dummyspectate = MenuDummy("dummyspectate", "Spectator").values({{0,
extern CV_PossibleValue_t dummystaff_cons_t[]; extern CV_PossibleValue_t dummystaff_cons_t[];
consvar_t cv_dummystaff = MenuDummy("dummystaff", "0").values(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 // lastprofile
// //

View file

@ -1145,28 +1145,32 @@ static void SV_SendPlayerInfo(INT32 node)
//No, don't do that, you fuckface. //No, don't do that, you fuckface.
memset(netbuffer->u.playerinfo[i].address, 0, 4); 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;
netbuffer->u.playerinfo[i].team = 255;
else
netbuffer->u.playerinfo[i].team = (UINT8)players[i].ctfteam;
} }
else else
{ {
if (players[i].spectator) if (G_GametypeHasTeams())
netbuffer->u.playerinfo[i].team = 255; {
if (players[i].team == TEAM_UNASSIGNED)
{
netbuffer->u.playerinfo[i].team = 255;
}
else
{
netbuffer->u.playerinfo[i].team = players[i].team;
}
}
else else
{
netbuffer->u.playerinfo[i].team = 0; netbuffer->u.playerinfo[i].team = 0;
}
} }
netbuffer->u.playerinfo[i].score = LONG(players[i].score); netbuffer->u.playerinfo[i].score = LONG(players[i].score);
netbuffer->u.playerinfo[i].timeinserver = SHORT((UINT16)(players[i].jointime / TICRATE)); netbuffer->u.playerinfo[i].timeinserver = SHORT((UINT16)(players[i].jointime / TICRATE));
netbuffer->u.playerinfo[i].skin = (UINT8)(players[i].skin 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
);
// Extra data // Extra data
netbuffer->u.playerinfo[i].data = 0; //players[i].skincolor; netbuffer->u.playerinfo[i].data = 0; //players[i].skincolor;

View file

@ -357,7 +357,7 @@ struct plrconfig
UINT16 color; UINT16 color;
UINT32 pflags; UINT32 pflags;
UINT32 score; UINT32 score;
UINT8 ctfteam; UINT8 team;
} ATTRPACK; } ATTRPACK;
struct filesneededconfig_pak struct filesneededconfig_pak

View file

@ -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_Pause(const UINT8 **cp, INT32 playernum);
static void Got_RandomSeed(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_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_Clearscores(const UINT8 **cp, INT32 playernum);
static void Got_DiscordInfo(const UINT8 **cp, INT32 playernum); static void Got_DiscordInfo(const UINT8 **cp, INT32 playernum);
static void Got_ScheduleTaskcmd(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_Showmap_f(void);
static void Command_Mapmd5_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_ServerTeamChange_f(void);
static void Command_Clearscores_f(void); static void Command_Clearscores_f(void);
@ -293,7 +289,7 @@ const char *netxcmdnames[MAXNETXCMD - 1] =
"ADDFILE", // XD_ADDFILE "ADDFILE", // XD_ADDFILE
"PAUSE", // XD_PAUSE "PAUSE", // XD_PAUSE
"ADDPLAYER", // XD_ADDPLAYER "ADDPLAYER", // XD_ADDPLAYER
"TEAMCHANGE", // XD_TEAMCHANGE "SPECTATE", // XD_SPECTATE
"CLEARSCORES", // XD_CLEARSCORES "CLEARSCORES", // XD_CLEARSCORES
"VERIFIED", // XD_VERIFIED "VERIFIED", // XD_VERIFIED
"RANDOMSEED", // XD_RANDOMSEED "RANDOMSEED", // XD_RANDOMSEED
@ -325,6 +321,7 @@ const char *netxcmdnames[MAXNETXCMD - 1] =
"MAPQUEUE", // XD_MAPQUEUE "MAPQUEUE", // XD_MAPQUEUE
"CALLZVOTE", // XD_CALLZVOTE "CALLZVOTE", // XD_CALLZVOTE
"SETZVOTE", // XD_SETZVOTE "SETZVOTE", // XD_SETZVOTE
"TEAMCHANGE", // XD_TEAMCHANGE
}; };
// ========================================================================= // =========================================================================
@ -388,7 +385,8 @@ void D_RegisterServerCommands(void)
COM_AddCommand("motd", Command_MotD_f); COM_AddCommand("motd", Command_MotD_f);
RegisterNetXCmd(XD_SETMOTD, Got_MotD_f); // For remote admin 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); COM_AddCommand("serverchangeteam", Command_ServerTeamChange_f);
RegisterNetXCmd(XD_CLEARSCORES, Got_Clearscores); RegisterNetXCmd(XD_CLEARSCORES, Got_Clearscores);
@ -504,11 +502,6 @@ void D_RegisterClientCommands(void)
if (dedicated) if (dedicated)
return; 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("invite", Command_Invite_f);
COM_AddCommand("cancelinvite", Command_CancelInvite_f); COM_AddCommand("cancelinvite", Command_CancelInvite_f);
COM_AddCommand("acceptinvite", Command_AcceptInvite_f); COM_AddCommand("acceptinvite", Command_AcceptInvite_f);
@ -996,11 +989,6 @@ static void SendNameAndColor(const UINT8 n)
cv_follower[n].value = -1; cv_follower[n].value = -1;
} }
if (sendColor == SKINCOLOR_NONE)
{
sendColor = skins[cv_skin[n].value].prefcolor;
}
if (sendFollowerColor == SKINCOLOR_NONE) if (sendFollowerColor == SKINCOLOR_NONE)
{ {
if (cv_follower[n].value >= 0) if (cv_follower[n].value >= 0)
@ -1015,11 +1003,11 @@ static void SendNameAndColor(const UINT8 n)
// Don't send if everything was identical. // Don't send if everything was identical.
if (!strcmp(cv_playername[n].string, player_names[playernum]) if (!strcmp(cv_playername[n].string, player_names[playernum])
&& sendColor == player->skincolor && sendColor == player->prefcolor
&& !stricmp(cv_skin[n].string, skins[player->skin].name) && !stricmp(cv_skin[n].string, skins[player->prefskin].name)
&& !stricmp(cv_follower[n].string, && !stricmp(cv_follower[n].string,
(player->followerskin < 0 ? "None" : followers[player->followerskin].name)) (player->preffollower < 0 ? "None" : followers[player->preffollower].name))
&& sendFollowerColor == player->followercolor) && sendFollowerColor == player->preffollowercolor)
{ {
return; return;
} }
@ -1123,7 +1111,6 @@ static void Got_NameAndColor(const UINT8 **cp, INT32 playernum)
UINT16 color, followercolor; UINT16 color, followercolor;
UINT8 skin; UINT8 skin;
INT16 follower; INT16 follower;
SINT8 localplayer = -1;
UINT8 i; UINT8 i;
#ifdef PARANOIA #ifdef PARANOIA
@ -1145,8 +1132,6 @@ static void Got_NameAndColor(const UINT8 **cp, INT32 playernum)
I_Error("snacpending[%d] negative!", i); I_Error("snacpending[%d] negative!", i);
} }
#endif #endif
localplayer = i;
break; break;
} }
} }
@ -1164,95 +1149,26 @@ static void Got_NameAndColor(const UINT8 **cp, INT32 playernum)
SetPlayerName(playernum, name); SetPlayerName(playernum, name);
} }
// set color // queue the rest for next round
p->skincolor = color % numskincolors; p->prefcolor = color % numskincolors;
if (p->mo) if (K_ColorUsable(p->prefcolor, false, false) == false)
p->mo->color = (UINT16)p->skincolor;
demo_extradata[playernum] |= DXD_COLOR;
// normal player colors
if (server && !P_IsMachineLocalPlayer(p))
{ {
boolean kick = false; p->prefcolor = SKINCOLOR_NONE;
// 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;
}
} }
// set skin p->prefskin = skin;
if (cv_forceskin.value >= 0 && K_CanChangeRules(true)) // Server wants everyone to use the same player 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; // update preferences immediately
SetPlayerSkinByNum(playernum, forcedskin); G_UpdatePlayerPreferences(p);
if (localplayer != -1)
CV_StealthSet(&cv_skin[localplayer], skins[forcedskin].name);
} }
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 { 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")); 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) static void Command_ServerTeamChange_f(void)
{ {
changeteam_union NetPacket; UINT8 buf[2];
boolean error = false; UINT8 *p = buf;
UINT16 usvalue;
NetPacket.value.l = NetPacket.value.b = 0; UINT8 new_team = TEAM_UNASSIGNED;
UINT8 player_num = consoleplayer;
if (!(server || (IsPlayerAdmin(consoleplayer)))) if (!(server || (IsPlayerAdmin(consoleplayer))))
{ {
@ -3605,96 +3407,63 @@ static void Command_ServerTeamChange_f(void)
return; return;
} }
if (G_GametypeHasTeams() == false)
{
CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used currently.\n"));
return;
}
// 0 1 2 // 0 1 2
// serverchangeteam <playernum> <team> // serverchangeteam <playernum> <team>
if (COM_Argc() < 3) if (COM_Argc() < 3)
{ {
if (G_GametypeHasTeams()) CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "orange, blue, or auto");
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"));
return; 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")) new_team = TEAM_ORANGE;
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;
} }
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")) new_team = TEAM_BLUE;
NetPacket.packet.newteam = 0; }
else if (!strcasecmp(COM_Argv(2), "playing") || !strcasecmp(COM_Argv(2), "1")) else if (!strcasecmp(COM_Argv(2), "auto") || !strcasecmp(COM_Argv(2), "0"))
NetPacket.packet.newteam = 3; {
else new_team = TEAM_UNASSIGNED;
error = true;
} }
else 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; return;
} }
if (error) player_num = atoi(COM_Argv(1));
if (playeringame[player_num] == false)
{ {
if (G_GametypeHasTeams()) CONS_Alert(CONS_NOTICE, M_GetText("There is no player %d!\n"), player_num);
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");
return; return;
} }
NetPacket.packet.playernum = atoi(COM_Argv(1)); if (new_team == players[player_num].team)
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)
{ {
CONS_Alert(CONS_NOTICE, M_GetText("That player is already on that team!\n")); CONS_Alert(CONS_NOTICE, M_GetText("That player is already on that team!\n"));
return; 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, &buf, p - buf);
SendNetXCmd(XD_TEAMCHANGE, &usvalue, sizeof(usvalue));
} }
void P_SetPlayerSpectator(INT32 playernum) void P_SetPlayerSpectator(INT32 playernum)
{ {
//Make sure you're in the right gametype. //Make sure you're in the right gametype.
if (!G_GametypeHasTeams() && !G_GametypeHasSpectators()) if (!G_GametypeHasSpectators())
return; return;
// Don't duplicate efforts. // Don't duplicate efforts.
@ -3703,148 +3472,105 @@ void P_SetPlayerSpectator(INT32 playernum)
players[playernum].spectator = true; players[playernum].spectator = true;
players[playernum].pflags &= ~PF_WANTSTOJOIN; players[playernum].pflags &= ~PF_WANTSTOJOIN;
G_AssignTeam(&players[playernum], TEAM_UNASSIGNED);
players[playernum].playerstate = PST_REBORN; players[playernum].playerstate = PST_REBORN;
} }
//todo: This and the other teamchange functions are getting too long and messy. Needs cleaning. static void Got_Spectate(const UINT8 **cp, INT32 playernum)
static void Got_Teamchange(const UINT8 **cp, INT32 playernum)
{ {
changeteam_union NetPacket; UINT8 edit_player = READUINT8(*cp);
boolean error = false, wasspectator = false; UINT8 desired_state = READUINT8(*cp);
NetPacket.value.l = NetPacket.value.b = READINT16(*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 return;
CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]); }
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) if (server)
{
SendKick(playernum, KICK_MSG_CON_FAIL); 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; return;
} }
// Don't switch team, just go away, please, go awaayyyy, aaauuauugghhhghgh if (G_GametypeHasSpectators() == false)
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 (!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; return;
} }
//Safety first! player_t *const player = &players[edit_player];
// (not respawning spectators here...)
wasspectator = (players[playernum].spectator == true);
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 // 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 //...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 (desired_state != 0)
//if necessary, put the player on the correct team/status.
// This serves us in both teamchange contexts.
if (NetPacket.packet.newteam != 0)
{ {
players[playernum].pflags |= PF_WANTSTOJOIN; player->pflags |= PF_WANTSTOJOIN;
} }
else 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; 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)) if (oldgt && newgt && (lastgametype != gametype))
CONS_Printf(M_GetText("Gametype was changed from %s to %s\n"), oldgt, newgt); 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); void Gravity_OnChange(void);
@ -5480,165 +5190,6 @@ void SoundTest_OnChange(void)
S_StartSound(NULL, cv_soundtest.value); 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) static void Command_Showmap_f(void)
{ {
if (gamestate == GS_LEVEL) if (gamestate == GS_LEVEL)

View file

@ -57,10 +57,6 @@ extern UINT32 timelimitintics, extratimeintics, secretextratime;
extern UINT32 g_pointlimit; extern UINT32 g_pointlimit;
extern consvar_t cv_allowexitlevel; 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_netstat;
extern consvar_t cv_countdowntime; extern consvar_t cv_countdowntime;
@ -83,6 +79,7 @@ extern consvar_t cv_karthorns;
extern consvar_t cv_kartbot; extern consvar_t cv_kartbot;
extern consvar_t cv_karteliminatelast; extern consvar_t cv_karteliminatelast;
extern consvar_t cv_thunderdome; extern consvar_t cv_thunderdome;
extern consvar_t cv_teamplay;
extern consvar_t cv_kartusepwrlv; extern consvar_t cv_kartusepwrlv;
#ifdef DEVELOP #ifdef DEVELOP
extern consvar_t cv_kartencoremap; extern consvar_t cv_kartencoremap;
@ -155,7 +152,7 @@ typedef enum
XD_ADDFILE, // 8 XD_ADDFILE, // 8
XD_PAUSE, // 9 XD_PAUSE, // 9
XD_ADDPLAYER, // 10 XD_ADDPLAYER, // 10
XD_TEAMCHANGE, // 11 XD_SPECTATE, // 11
XD_CLEARSCORES, // 12 XD_CLEARSCORES, // 12
XD_VERIFIED, // 13 XD_VERIFIED, // 13
XD_RANDOMSEED, // 14 XD_RANDOMSEED, // 14
@ -187,52 +184,13 @@ typedef enum
XD_MAPQUEUE, // 38 XD_MAPQUEUE, // 38
XD_CALLZVOTE, // 39 XD_CALLZVOTE, // 39
XD_SETZVOTE, // 40 XD_SETZVOTE, // 40
XD_TEAMCHANGE, // 41
MAXNETXCMD MAXNETXCMD
} netxcmd_t; } netxcmd_t;
extern const char *netxcmdnames[MAXNETXCMD - 1]; 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 // add game commands, needs cleanup
void D_RegisterServerCommands(void); void D_RegisterServerCommands(void);
void D_RegisterClientCommands(void); void D_RegisterClientCommands(void);

View file

@ -671,6 +671,11 @@ struct player_t
UINT8 carry; UINT8 carry;
UINT16 dye; 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 // SRB2kart stuff
INT32 karthud[NUMKARTHUD]; INT32 karthud[NUMKARTHUD];
@ -678,12 +683,19 @@ struct player_t
UINT8 position; // Used for Kart positions, mostly for deterministic stuff UINT8 position; // Used for Kart positions, mostly for deterministic stuff
UINT8 oldposition; // Used for taunting when you pass someone UINT8 oldposition; // Used for taunting when you pass someone
UINT8 positiondelay; // Used for position number, so it can grow when passing 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 distancetofinish;
UINT32 distancetofinishprev; UINT32 distancetofinishprev;
UINT32 lastpickupdistance; // Anti item set farming UINT32 lastpickupdistance; // Anti item set farming
UINT8 lastpickuptype; UINT8 lastpickuptype;
waypoint_t *currentwaypoint; waypoint_t *currentwaypoint;
waypoint_t *nextwaypoint; waypoint_t *nextwaypoint;
respawnvars_t respawn; // Respawn info respawnvars_t respawn; // Respawn info
mobj_t *ringShooter; // DEZ respawner object 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? 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 cheatchecknum; // The number of the last cheatcheck you hit
INT32 checkpointId; // Players respawn here, objects/checkpoint.cpp 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 UINT8 checkskip; // Skipping checkpoints? Oh no no no

View file

@ -3551,22 +3551,6 @@ void readmaincfg(MYFILE *f, boolean mainfile)
COM_BufInsertText(W_CacheLumpNum(lumpnum, PU_CACHE)); 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")) else if (fastcmp(word, "INVULNTICS"))
{ {
invulntics = (UINT16)get_number(word2); invulntics = (UINT16)get_number(word2);

View file

@ -544,6 +544,7 @@ typedef enum
DBG_LUA = 0x00000800, DBG_LUA = 0x00000800,
DBG_RNG = 0x00001000, DBG_RNG = 0x00001000,
DBG_DEMO = 0x00002000, DBG_DEMO = 0x00002000,
DBG_TEAMS = 0x00004000,
} debugFlags_t; } debugFlags_t;
struct debugFlagNames_s struct debugFlagNames_s

View file

@ -277,9 +277,6 @@ extern UINT8 tutorialchallenge;
#define TUTORIALSKIP_FAILED 1 #define TUTORIALSKIP_FAILED 1
#define TUTORIALSKIP_INPROGRESS 2 #define TUTORIALSKIP_INPROGRESS 2
// CTF colors.
extern UINT16 skincolor_redteam, skincolor_blueteam, skincolor_redring, skincolor_bluering;
extern boolean exitfadestarted; extern boolean exitfadestarted;
struct scene_t struct scene_t
@ -762,8 +759,24 @@ extern INT32 nummaprings; //keep track of spawned rings/coins
extern UINT8 nummapspraycans; extern UINT8 nummapspraycans;
extern UINT16 numchallengedestructibles; extern UINT16 numchallengedestructibles;
extern UINT32 bluescore; ///< Blue Team Scores // Teamplay
extern UINT32 redscore; ///< Red Team Scores 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. // Eliminates unnecessary searching.
extern boolean CheckForBustableBlocks; extern boolean CheckForBustableBlocks;
@ -845,19 +858,12 @@ extern struct maplighting
angle_t angle; angle_t angle;
} maplighting; } 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 // SRB2kart
extern UINT8 numlaps; extern UINT8 numlaps;
extern UINT8 gamespeed; extern UINT8 gamespeed;
extern boolean franticitems; extern boolean franticitems;
extern boolean encoremode, prevencoremode; extern boolean encoremode, prevencoremode;
extern boolean g_teamplay;
extern tic_t wantedcalcdelay; extern tic_t wantedcalcdelay;
extern tic_t itemCooldowns[NUMKARTITEMS - 1]; extern tic_t itemCooldowns[NUMKARTITEMS - 1];
@ -891,8 +897,7 @@ extern tic_t gametic;
// Player spawn spots. // Player spawn spots.
extern mapthing_t *playerstarts[MAXPLAYERS]; // Cooperative extern mapthing_t *playerstarts[MAXPLAYERS]; // Cooperative
extern mapthing_t *bluectfstarts[MAXPLAYERS]; // CTF extern mapthing_t *teamstarts[TEAM__MAX][MAXPLAYERS]; // Teamplay
extern mapthing_t *redctfstarts[MAXPLAYERS]; // CTF
extern mapthing_t *faultstart; // Kart Fault extern mapthing_t *faultstart; // Kart Fault
#define TUBEWAYPOINTSEQUENCESIZE 256 #define TUBEWAYPOINTSEQUENCESIZE 256

View file

@ -182,11 +182,6 @@ char * podiummap = NULL; // map to load for podium
char * tutorialchallengemap = NULL; // map to load for tutorial skip char * tutorialchallengemap = NULL; // map to load for tutorial skip
UINT8 tutorialchallenge = TUTORIALSKIP_NONE; 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; boolean exitfadestarted = false;
cutscene_t *cutscenes[128]; cutscene_t *cutscenes[128];
@ -222,7 +217,7 @@ INT32 luabanks[NUM_LUABANKS];
// Temporary holding place for nights data for the current map // Temporary holding place for nights data for the current map
//nightsdata_t ntemprecords; //nightsdata_t ntemprecords;
UINT32 bluescore, redscore; // CTF and Team Match team scores UINT32 g_teamscores[TEAM__MAX];
// ring count... for PERFECT! // ring count... for PERFECT!
INT32 nummaprings = 0; INT32 nummaprings = 0;
@ -289,13 +284,6 @@ fixed_t mapobjectscale;
struct maplighting maplighting; 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 // SRB2Kart
// Cvars that we don't want changed mid-game // Cvars that we don't want changed mid-game
UINT8 numlaps; // Removed from Cvar hell UINT8 numlaps; // Removed from Cvar hell
@ -304,6 +292,10 @@ boolean encoremode = false; // Encore Mode currently enabled?
boolean prevencoremode; boolean prevencoremode;
boolean franticitems; // Frantic items currently enabled? 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 // Voting system
UINT16 g_voteLevels[4][2]; // Levels that were rolled by the host UINT16 g_voteLevels[4][2]; // Levels that were rolled by the host
SINT8 g_votes[VOTE_TOTAL]; // Each player's vote 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... // SRB2Kart: we have no team-based modes, YET...
if (G_GametypeHasTeams()) if (G_GametypeHasTeams())
{ {
if (players[consoleplayer].ctfteam && player->ctfteam != players[consoleplayer].ctfteam) if (players[consoleplayer].spectator == false && player->team != players[consoleplayer].team)
return false; return false;
} }
@ -1792,6 +1784,73 @@ void G_FixCamera(UINT8 view)
R_ResetViewInterpolation(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 // G_Ticker
// Make ticcmd_ts for the players. // Make ticcmd_ts for the players.
@ -1885,6 +1944,13 @@ void G_Ticker(boolean run)
K_UpdateAllPlayerPositions(); K_UpdateAllPlayerPositions();
} }
} }
else if (Playing() && !Y_IntermissionPlayerLock())
{
if (run)
{
G_UpdateAllPlayerPreferences();
}
}
P_MapEnd(); P_MapEnd();
@ -2136,7 +2202,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
INT32 pflags; INT32 pflags;
UINT8 ctfteam; UINT8 team;
INT32 cheatchecknum; INT32 cheatchecknum;
INT32 exiting; INT32 exiting;
@ -2200,6 +2266,11 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
tic_t laptime[LAP__MAX]; tic_t laptime[LAP__MAX];
UINT16 prefcolor;
INT32 prefskin;
UINT16 preffollowercolor;
INT32 preffollower;
INT32 i; INT32 i;
// This needs to be first, to permit it to wipe extra information // 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; score = players[player].score;
lives = players[player].lives; lives = players[player].lives;
ctfteam = players[player].ctfteam; team = players[player].team;
splitscreenindex = players[player].splitscreenindex; splitscreenindex = players[player].splitscreenindex;
spectator = players[player].spectator; spectator = players[player].spectator;
@ -2223,6 +2294,11 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
skincolor = players[player].skincolor; skincolor = players[player].skincolor;
skin = players[player].skin; skin = players[player].skin;
prefcolor = players[player].prefcolor;
prefskin = players[player].prefskin;
preffollower = players[player].preffollower;
preffollowercolor = players[player].preffollowercolor;
if (betweenmaps) if (betweenmaps)
{ {
fakeskin = MAXSKINS; fakeskin = MAXSKINS;
@ -2463,7 +2539,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
p->roundscore = roundscore; p->roundscore = roundscore;
p->lives = lives; p->lives = lives;
p->pflags = pflags; p->pflags = pflags;
p->ctfteam = ctfteam; p->team = team;
p->jointime = jointime; p->jointime = jointime;
p->splitscreenindex = splitscreenindex; p->splitscreenindex = splitscreenindex;
p->spectator = spectator; p->spectator = spectator;
@ -2477,6 +2553,11 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
p->skincolor = skincolor; p->skincolor = skincolor;
p->skin = skin; p->skin = skin;
p->prefcolor = prefcolor;
p->prefskin = prefskin;
p->preffollower = preffollower;
p->preffollowercolor = preffollowercolor;
p->fakeskin = fakeskin; p->fakeskin = fakeskin;
p->kartspeed = kartspeed; p->kartspeed = kartspeed;
p->kartweight = kartweight; 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. //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. // ^ 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->playerstate = PST_LIVE;
p->panim = PA_STILL; // standing animation 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 (p->spectator == false && !betweenmaps)
{ {
if (enteredGame == true) if (enteredGame == true)
@ -2698,56 +2760,78 @@ void G_MovePlayerToSpawnOrCheatcheck(INT32 playernum)
mapthing_t *G_FindTeamStart(INT32 playernum) mapthing_t *G_FindTeamStart(INT32 playernum)
{ {
const boolean doprints = P_IsPartyPlayer(&players[playernum]); const boolean do_prints = P_IsPartyPlayer(&players[playernum]);
INT32 i,j; INT32 i, j;
if (!numredctfstarts && !numbluectfstarts) //why even bother, eh? for (i = 0; i < TEAM__MAX; i++)
{ {
if ((gametyperules & GTR_TEAMSTARTS) && doprints) if (numteamstarts[i] > 0)
CONS_Alert(CONS_WARNING, M_GetText("No CTF starts in this map!\n")); {
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; 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) if (numteamstarts[i] > 0)
CONS_Alert(CONS_WARNING, M_GetText("No Red Team starts in this map!\n")); {
return NULL; 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); CONS_Alert(CONS_WARNING, M_GetText("No %s Team starts in this map!\n"), g_teaminfo[use_team].name);
if (G_CheckSpot(playernum, redctfstarts[i]))
return redctfstarts[i];
} }
if (doprints)
CONS_Alert(CONS_WARNING, M_GetText("Could not spawn at any Red Team starts!\n"));
return NULL; 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); return teamstarts[use_team][i];
if (G_CheckSpot(playernum, bluectfstarts[i]))
return bluectfstarts[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; return NULL;
} }
@ -2977,7 +3061,7 @@ mapthing_t *G_FindMapStart(INT32 playernum)
// -- CTF -- // -- CTF --
// Order: CTF->DM->Race // 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); spawnpoint = G_FindTeamStartOrFallback(playernum);
// -- DM/Tag/CTF-spectator/etc -- // -- DM/Tag/CTF-spectator/etc --
@ -3089,7 +3173,7 @@ void G_SpectatePlayerOnJoin(INT32 playernum)
// This is only ever called shortly after the above. // This is only ever called shortly after the above.
// That calls CL_ClearPlayer, so spectator is false by default // That calls CL_ClearPlayer, so spectator is false by default
if (!netgame && !G_GametypeHasTeams() && !G_GametypeHasSpectators()) if (!netgame && !G_GametypeHasSpectators())
return; return;
// These are handled automatically elsewhere // These are handled automatically elsewhere
@ -3219,14 +3303,6 @@ void G_FinishExitLevel(void)
gameaction = ga_completed; gameaction = ga_completed;
lastdraw = true; 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")); CON_LogMessage(M_GetText("The round has ended.\n"));
// Remove CEcho text on round end. // Remove CEcho text on round end.
@ -3525,19 +3601,20 @@ boolean G_GametypeAllowsRetrying(void)
// //
boolean G_GametypeHasTeams(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 // Teams forced on by this gametype
return true; return true;
} }
else if (gametyperules & GTR_NOTEAMS) else if (rules == GTR_NOTEAMS)
{ {
// Teams forced off by this gametype // Teams forced off by this gametype
return false; return false;
} }
// Teams are determined by the "teamplay" modifier! // Teams are determined by the server's preference!
return false; // teamplay return g_teamplay;
} }
// //
@ -4719,9 +4796,13 @@ static void G_DoCompleted(void)
{ {
Y_StartIntermission(); Y_StartIntermission();
} }
else if (grandprixinfo.gp == true) else
{ {
K_UpdateGPRank(&grandprixinfo.rank); Y_MidIntermission();
if (grandprixinfo.gp == true)
{
K_UpdateGPRank(&grandprixinfo.rank);
}
} }
G_UpdateVisited(); G_UpdateVisited();
@ -5255,9 +5336,14 @@ void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skippr
} }
// Clear a bunch of variables // Clear a bunch of variables
redscore = bluescore = lastmap = 0; lastmap = 0;
racecountdown = exitcountdown = musiccountdown = mapreset = exitfadestarted = 0; racecountdown = exitcountdown = musiccountdown = mapreset = exitfadestarted = 0;
for (i = 0; i < TEAM__MAX; i++)
{
g_teamscores[i] = 0;
}
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
players[i].playerstate = PST_REBORN; players[i].playerstate = PST_REBORN;
@ -5730,3 +5816,204 @@ INT32 G_TicsToMilliseconds(tic_t tics)
{ {
return (INT32)((tics%TICRATE) * (1000.00f/TICRATE)); 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;
}

View file

@ -234,6 +234,9 @@ void G_UpdateTimeStickerMedals(UINT16 map, boolean showownrecord);
void G_TickTimeStickerMedals(void); void G_TickTimeStickerMedals(void);
void G_UpdateRecords(void); void G_UpdateRecords(void);
void G_UpdatePlayerPreferences(player_t *const player);
void G_UpdateAllPlayerPreferences(void);
void G_Ticker(boolean run); void G_Ticker(boolean run);
boolean G_Responder(event_t *ev); boolean G_Responder(event_t *ev);
@ -290,6 +293,13 @@ void G_AddMapToBuffer(UINT16 map);
void G_UpdateVisited(void); 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 #ifdef __cplusplus
} // extern "C" } // extern "C"
#endif #endif

View file

@ -621,7 +621,7 @@ static void Command_Sayteam_f(void)
return; 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); DoSayPacketFromCommand(-1, 1, 0);
else else
DoSayPacketFromCommand(0, 1, 0); DoSayPacketFromCommand(0, 1, 0);
@ -779,16 +779,8 @@ static void Got_Saycmd(const UINT8 **p, INT32 playernum)
} }
else if (target == -1) // say team else if (target == -1) // say team
{ {
if (players[playernum].ctfteam == 1) sprintf(color_prefix, "%c", '\x80' + (g_teaminfo[ players[playernum].team ].chat_color >> V_CHARCOLORSHIFT));
{ cstart = textcolor = color_prefix;
// red text
cstart = textcolor = "\x85";
}
else
{
// blue text
cstart = textcolor = "\x84";
}
} }
else else
{ {
@ -1593,12 +1585,7 @@ static void HU_DrawChat(void)
if (teamtalk) if (teamtalk)
{ {
talk = ttalk; talk = ttalk;
#if 0 //t = '\x80' + (g_teaminfo[ players[consoleplayer].team ].chat_color >> V_CHARCOLORSHIFT);
if (players[consoleplayer].ctfteam == 1)
t = '\0x85'; // Red
else if (players[consoleplayer].ctfteam == 2)
t = '\0x84'; // Blue
#endif
} }
typelines = 1; typelines = 1;
@ -2569,8 +2556,6 @@ static void HU_DrawRankings(void)
completed[i] = true; completed[i] = true;
standings.character[standings.numplayers] = players[i].skin;
standings.color[standings.numplayers] = players[i].skincolor;
standings.pos[standings.numplayers] = players[i].position; standings.pos[standings.numplayers] = players[i].position;
#define strtime standings.strval[standings.numplayers] #define strtime standings.strval[standings.numplayers]
@ -2608,6 +2593,8 @@ static void HU_DrawRankings(void)
standings.numplayers++; standings.numplayers++;
} }
standings.halfway = (standings.numplayers-1)/2;
// Returns early if there's no players to draw // Returns early if there's no players to draw
Y_PlayerStandingsDrawer(&standings, 0); Y_PlayerStandingsDrawer(&standings, 0);

View file

@ -985,7 +985,16 @@ boolean K_EndBattleRound(player_t *victor)
if (gametyperules & GTR_POINTLIMIT) if (gametyperules & GTR_POINTLIMIT)
{ {
// Lock the winner in before the round ends. // 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; victor->roundscore = 100;
if (G_GametypeHasTeams() == true && victor->team != TEAM_UNASSIGNED)
{
g_teamscores[victor->team] = 100;
}
} }
} }

View file

@ -174,16 +174,20 @@ void K_SetBot(UINT8 newplayernum, UINT8 skinnum, UINT8 difficulty, botStyle_e st
break; break;
} }
} }
players[newplayernum].skincolor = color;
K_SetNameForBot(newplayernum, realname);
SetPlayerSkinByNum(newplayernum, skinnum); K_SetNameForBot(newplayernum, realname);
for (UINT8 i = 0; i < PWRLV_NUMTYPES; i++) for (UINT8 i = 0; i < PWRLV_NUMTYPES; i++)
{ {
clientpowerlevels[newplayernum][i] = 0; 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) if (netgame)
{ {
HU_AddChatText(va("\x82*Bot %d has been added to the game", newplayernum+1), false); 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; 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! // First check difficulty levels, then score, then settle it with port priority!
if (player->botvars.difficulty < players[i].botvars.difficulty) if (player->botvars.difficulty < players[i].botvars.difficulty)
{ {
@ -716,6 +726,12 @@ fixed_t K_BotRubberband(const player_t *player)
continue; continue;
} }
// Don't rubberband to friendlies...
if (G_SameTeam(player, &players[i]) == true)
{
continue;
}
#if 0 #if 0
// Only rubberband up to players. // Only rubberband up to players.
if (players[i].bot) if (players[i].bot)

View file

@ -89,6 +89,7 @@ static boolean K_BotUseItemNearPlayer(const player_t *player, ticcmd_t *cmd, fix
if (target->mo == NULL || P_MobjWasRemoved(target->mo) if (target->mo == NULL || P_MobjWasRemoved(target->mo)
|| player == target || target->spectator || player == target || target->spectator
|| G_SameTeam(player, target)
|| target->flashing) || target->flashing)
{ {
continue; 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) if (target->mo == NULL || P_MobjWasRemoved(target->mo)
|| player == target || target->spectator || player == target || target->spectator
|| G_SameTeam(player, target)
|| target->flashing) || target->flashing)
{ {
continue; 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) if (target->mo == NULL || P_MobjWasRemoved(target->mo)
|| player == target || target->spectator || player == target || target->spectator
|| G_SameTeam(player, target)
|| target->flashing || target->flashing
|| !P_CheckSight(player->mo, target->mo)) || !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 && players[lastTarg].mo != NULL
&& P_MobjWasRemoved(players[lastTarg].mo) == false) && P_MobjWasRemoved(players[lastTarg].mo) == false)
{ {
mobj_t *targMo = players[lastTarg].mo;
mobj_t *mobj = NULL, *next = NULL;
boolean targettedAlready = false;
target = &players[lastTarg]; target = &players[lastTarg];
// Make sure no other Jawz are targetting this player. if (G_SameTeam(player, target) == false)
for (mobj = trackercap; mobj; mobj = next)
{ {
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; next = mobj->itnext;
break;
}
}
if (targettedAlready == false) if (mobj->type == MT_JAWZ && mobj->target == targMo)
{ {
K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * snipeMul); targettedAlready = true;
throwdir = 1; 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) if (target->mo == NULL || P_MobjWasRemoved(target->mo)
|| player == target || target->spectator || player == target || target->spectator
|| G_SameTeam(player, target)
|| target->flashing) || target->flashing)
{ {
continue; continue;
@ -1528,6 +1535,7 @@ static void K_BotItemInstashield(const player_t *player, ticcmd_t *cmd)
if (P_MobjWasRemoved(target->mo) == true if (P_MobjWasRemoved(target->mo) == true
|| player == target || player == target
|| target->spectator == true || target->spectator == true
|| G_SameTeam(player, target) == true
|| target->flashing != 0) || target->flashing != 0)
{ {
continue; continue;

View file

@ -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 Checks two conditions to determine if the object should be
attacked or dodged. attacked or dodged.
Input Arguments:- Input Arguments:-
thing - Object to move towards/away from. 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 side - Which side -- 0 for left, 1 for right
weight - How important this object is. weight - How important this object is.
attackCond - If this is true, and dodgeCond isn't, then we go towards the object. 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:- Return:-
true if either condition is successful. 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) if (attackCond == true && dodgeCond == false)
{ {
K_AddAttackObject(thing, side, weight); K_AddAttackObject(thing, side, weight);
@ -547,9 +555,11 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
&& !thing->player->hyudorotimer && !thing->player->hyudorotimer
&& !g_nudgeSearch.botmo->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?! // There REALLY ought to be a better way to handle this logic, right?!
// Squishing // Squishing
if (K_PlayerAttackSteer(thing, side, 20, if (K_PlayerAttackSteer(thing, same_team, side, 20,
K_IsBigger(g_nudgeSearch.botmo, thing), K_IsBigger(g_nudgeSearch.botmo, thing),
K_IsBigger(thing, g_nudgeSearch.botmo) K_IsBigger(thing, g_nudgeSearch.botmo)
)) ))
@ -557,7 +567,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
break; break;
} }
// Invincibility // Invincibility
else if (K_PlayerAttackSteer(thing, side, 20, else if (K_PlayerAttackSteer(thing, same_team, side, 20,
g_nudgeSearch.botmo->player->invincibilitytimer, g_nudgeSearch.botmo->player->invincibilitytimer,
thing->player->invincibilitytimer thing->player->invincibilitytimer
)) ))
@ -565,7 +575,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
break; break;
} }
// Lightning Shield // 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, g_nudgeSearch.botmo->player->itemtype == KITEM_LIGHTNINGSHIELD,
thing->player->itemtype == KITEM_LIGHTNINGSHIELD thing->player->itemtype == KITEM_LIGHTNINGSHIELD
)) ))
@ -573,7 +583,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
break; break;
} }
// Bubble Shield // 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, g_nudgeSearch.botmo->player->itemtype == KITEM_BUBBLESHIELD,
thing->player->itemtype == KITEM_BUBBLESHIELD thing->player->itemtype == KITEM_BUBBLESHIELD
)) ))
@ -581,7 +591,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
break; break;
} }
// Flame Shield // 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, g_nudgeSearch.botmo->player->itemtype == KITEM_FLAMESHIELD,
thing->player->itemtype == KITEM_FLAMESHIELD thing->player->itemtype == KITEM_FLAMESHIELD
)) ))
@ -589,7 +599,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
break; break;
} }
// Has held item shield // 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)), (thing->player->itemflags & (IF_ITEMOUT|IF_EGGMANOUT)),
(g_nudgeSearch.botmo->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; break;
} }
// Ring Sting // Ring Sting
else if (K_PlayerAttackSteer(thing, side, 20, else if (K_PlayerAttackSteer(thing, same_team, side, 20,
thing->player->rings <= 0, thing->player->rings <= 0,
g_nudgeSearch.botmo->player->rings <= 0 g_nudgeSearch.botmo->player->rings <= 0
)) ))
@ -620,7 +630,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
weightdiff = ourweight - theirweight; weightdiff = ourweight - theirweight;
} }
if (weightdiff > mapobjectscale) if (weightdiff > mapobjectscale && same_team == false)
{ {
K_AddAttackObject(thing, side, 20); K_AddAttackObject(thing, side, 20);
} }

View file

@ -791,10 +791,14 @@ void K_RetireBots(void)
bot->botvars.difficulty = newDifficulty; bot->botvars.difficulty = newDifficulty;
bot->botvars.diffincrease = 0; bot->botvars.diffincrease = 0;
SetPlayerSkinByNum(i, skinnum);
bot->skincolor = skins[skinnum].prefcolor;
K_SetNameForBot(i, skins[skinnum].realname); 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->score = 0;
bot->pflags &= ~PF_NOCONTEST; bot->pflags &= ~PF_NOCONTEST;
} }

View file

@ -2428,7 +2428,7 @@ struct PositionFacesInfo
void draw_4p_battle(int x, int y, INT32 flags); void draw_4p_battle(int x, int y, INT32 flags);
player_t* top() const { return &players[rankplayer[0]]; } 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 bool near_goal() const
{ {
@ -2548,7 +2548,7 @@ void PositionFacesInfo::draw_1p()
} }
// Draw GOAL // Draw GOAL
bool skull = g_pointlimit && (g_pointlimit <= stplyr->roundscore); bool skull = g_pointlimit && (g_pointlimit <= G_TeamOrIndividualScore(stplyr));
INT32 height = i*18; INT32 height = i*18;
INT32 GOAL_Y = Y-height; 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) static void K_drawKartLaps(void)
{ {
INT32 splitflags = V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN; INT32 splitflags = V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN;
@ -6720,7 +6748,14 @@ void K_drawKartHUD(void)
K_drawKartEmeralds(); K_drawKartEmeralds();
} }
else if (!islonesome && !K_Cooperative()) else if (!islonesome && !K_Cooperative())
{
K_DrawKartPositionNum(stplyr->position); K_DrawKartPositionNum(stplyr->position);
}
}
if (G_GametypeHasTeams() == true)
{
K_drawKartTeamScores();
} }
if (LUA_HudEnabled(hud_gametypeinfo)) if (LUA_HudEnabled(hud_gametypeinfo))

View file

@ -4030,6 +4030,12 @@ angle_t K_MomentumAngleReal(const mobj_t *mo)
// Scale amp rewards for crab bucketing. Play ambitiously! // Scale amp rewards for crab bucketing. Play ambitiously!
boolean K_PvPAmpReward(UINT32 award, player_t *attacker, player_t *defender) 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 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 range = FixedMul(2048, mapobjectscale);
UINT32 atkdist = attacker->distancetofinish + epsilon; UINT32 atkdist = attacker->distancetofinish + epsilon;
@ -4054,6 +4060,9 @@ void K_SpawnAmps(player_t *player, UINT8 amps, mobj_t *impact)
if (gametyperules & GTR_SPHERES) if (gametyperules & GTR_SPHERES)
return; return;
if (amps == 0)
return;
UINT16 scaledamps = min(amps, amps * (10 + (9-player->kartspeed) - (9-player->kartweight)) / 10); 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; return;
} }
if (G_SameTeam(player, victim) == true)
{
// No farming points off of teammates, either.
return;
}
if (player->exiting) if (player->exiting)
{ {
// The round has already ended, don't mess with points // 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 // 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); K_EndBattleRound(player);
@ -8294,7 +8309,7 @@ mobj_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range)
continue; continue;
} }
if (G_GametypeHasTeams() && source != NULL && source->ctfteam == player->ctfteam) if (G_SameTeam(source, player) == false)
{ {
// Don't home in on teammates. // Don't home in on teammates.
continue; 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 if (player->mo->health <= 0 || player->playerstate == PST_DEAD || (player->respawn.state == RESPAWNST_MOVE)) // Override everything
{ {
player->mo->colorized = (player->dye != 0); goto base;
player->mo->color = player->dye ? player->dye : player->skincolor;
goto finalise;
} }
if (player->eggmanexplode) // You're gonna diiiiie if (player->eggmanexplode) // You're gonna diiiiie
@ -10204,8 +10217,18 @@ void K_KartResetPlayerColor(player_t *player)
goto finalise; goto finalise;
} }
player->mo->colorized = (player->dye != 0); base:
player->mo->color = player->dye ? player->dye : player->skincolor;
if (player->dye)
{
player->mo->colorized = true;
player->mo->color = player->dye;
}
else
{
player->mo->colorized = false;
player->mo->color = player->skincolor;
}
finalise: finalise:
@ -11740,13 +11763,18 @@ static void K_KartDrift(player_t *player, boolean onground)
else else
player->pflags &= ~PF_BRAKEDRIFT; player->pflags &= ~PF_BRAKEDRIFT;
} }
// //
// K_KartUpdatePosition // K_KartUpdatePosition
// //
void K_KartUpdatePosition(player_t *player) void K_KartUpdatePosition(player_t *player)
{ {
fixed_t position = 1; UINT8 position = 1;
fixed_t oldposition = player->position; UINT8 oldposition = player->position;
UINT8 team_position = 1;
UINT32 team_importance = 0;
fixed_t i; fixed_t i;
INT32 realplayers = 0; INT32 realplayers = 0;
@ -11755,6 +11783,8 @@ void K_KartUpdatePosition(player_t *player)
// Ensure these are reset for spectators // Ensure these are reset for spectators
player->position = 0; player->position = 0;
player->positiondelay = 0; player->positiondelay = 0;
player->teamposition = 0;
player->teamimportance = 0;
return; return;
} }
@ -11779,23 +11809,40 @@ void K_KartUpdatePosition(player_t *player)
realplayers++; 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 (gametyperules & GTR_CIRCUIT)
{ {
if (player->exiting) // End of match standings if (player->exiting) // End of match standings
{ {
// Only time matters // Only time matters
if (players[i].realtime < player->realtime) increment_position(players[i].realtime < player->realtime)
position++;
} }
else else
{ {
// I'm a lap behind this player OR // I'm a lap behind this player OR
// My distance to the finish line is higher, so I'm behind // My distance to the finish line is higher, so I'm behind
if ((players[i].laps > player->laps) increment_position(
|| (players[i].distancetofinish < player->distancetofinish)) (players[i].laps > player->laps)
{ || (players[i].distancetofinish < player->distancetofinish)
position++; )
}
} }
} }
else else
@ -11803,8 +11850,7 @@ void K_KartUpdatePosition(player_t *player)
if (player->exiting) // End of match standings if (player->exiting) // End of match standings
{ {
// Only score matters // Only score matters
if (players[i].roundscore > player->roundscore) increment_position(players[i].roundscore > player->roundscore)
position++;
} }
else else
{ {
@ -11814,26 +11860,25 @@ void K_KartUpdatePosition(player_t *player)
// First compare all points // First compare all points
if (players[i].roundscore > player->roundscore) if (players[i].roundscore > player->roundscore)
{ {
position++; increment_position(true)
} }
else if (players[i].roundscore == player->roundscore) else if (players[i].roundscore == player->roundscore)
{ {
// Emeralds are a tie breaker // Emeralds are a tie breaker
if (yourEmeralds > myEmeralds) if (yourEmeralds > myEmeralds)
{ {
position++; increment_position(true)
} }
else if (yourEmeralds == myEmeralds) else if (yourEmeralds == myEmeralds)
{ {
// Bumpers are the second tier tie breaker // Bumpers are the second tier tie breaker
if (K_Bumpers(&players[i]) > K_Bumpers(player)) increment_position(K_Bumpers(&players[i]) > K_Bumpers(player))
{
position++;
}
} }
} }
} }
} }
#undef increment_position
} }
} }
@ -11887,6 +11932,15 @@ void K_KartUpdatePosition(player_t *player)
} }
player->position = position; 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) void K_UpdateAllPlayerPositions(void)
@ -11927,6 +11981,26 @@ void K_UpdateAllPlayerPositions(void)
K_KartUpdatePosition(&players[i]); 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. // counted.
for (i = 0; i < MAXPLAYERS; ++i) for (i = 0; i < MAXPLAYERS; ++i)
{ {
if (D_IsPlayerHumanAndGaming(i)) if (playeringame[i] == true && players[i].spectator == false)
{ {
ptsCap += 3; ptsCap += 3;
} }
@ -14978,6 +15052,43 @@ UINT32 K_PointLimitForGametype(void)
{ {
ptsCap = 16; 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; 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 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; fixed_t result = 0;
INT32 live_players = 0; INT32 live_players = 0; // players we are competing against
for (INT32 i = 0; i < MAXPLAYERS; i++) for (INT32 i = 0; i < MAXPLAYERS; i++)
{ {
if (!playeringame[i] || players[i].spectator || player == players+i) if (!playeringame[i] || players[i].spectator || player == players+i)
continue; continue;
if (G_SameTeam(player, &players[i]) == true)
{
// You don't win/lose against your teammates.
continue;
}
live_players++; live_players++;
} }
@ -15102,6 +15219,12 @@ fixed_t K_GetExpAdjustment(player_t *player)
if (!playeringame[i] || players[i].spectator || player == players+i) if (!playeringame[i] || players[i].spectator || player == players+i)
continue; continue;
if (G_SameTeam(player, &players[i]) == true)
{
// You don't win/lose against your teammates.
continue;
}
if (player->position < players[i].position) if (player->position < players[i].position)
result += exp_power; result += exp_power;
} }

View file

@ -477,15 +477,17 @@ extern menu_t OPTIONS_HUDOnlineDef;
typedef enum typedef enum
{ {
gopt_spacer0 = 0, gopt_spacer0 = 0,
gopt_gamespeed, gopt_teamplay,
gopt_frantic, gopt_frantic,
gopt_spacer1,
gopt_gamespeed,
gopt_encore, gopt_encore,
gopt_exitcountdown, gopt_exitcountdown,
gopt_spacer1, gopt_spacer2,
gopt_timelimit, gopt_timelimit,
gopt_pointlimit, gopt_pointlimit,
gopt_startingbumpers, gopt_startingbumpers,
gopt_spacer2, gopt_spacer3,
gopt_itemtoggles gopt_itemtoggles
} gopt_e; } gopt_e;

View file

@ -245,6 +245,12 @@ void K_UpdatePowerLevels(player_t *player, UINT8 lap, boolean forfeit)
continue; 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]); CONS_Debug(DBG_PWRLV, "%s VS %s:\n", player_names[playerNum], player_names[i]);
theirPower = clientpowerlevels[i][powerType]; theirPower = clientpowerlevels[i][powerType];

View file

@ -143,7 +143,7 @@ int LUA_HookMapThingSpawn(mobj_t *, mapthing_t *);
int LUA_HookFollowMobj(player_t *, mobj_t *); int LUA_HookFollowMobj(player_t *, mobj_t *);
int LUA_HookPlayerCanDamage(player_t *, mobj_t *); int LUA_HookPlayerCanDamage(player_t *, mobj_t *);
void LUA_HookPlayerQuit(player_t *, kickreason_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_HookViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced);
int LUA_HookSeenPlayer(player_t *player, player_t *seenfriend); int LUA_HookSeenPlayer(player_t *player, player_t *seenfriend);

View file

@ -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) int LUA_HookTeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble)
{ {
Hook_State hook; Hook_State hook;
@ -975,6 +976,7 @@ int LUA_HookTeamSwitch(player_t *player, int newteam, boolean fromspectators, bo
} }
return hook.status; return hook.status;
} }
*/
int LUA_HookViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced) int LUA_HookViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced)
{ {

View file

@ -226,6 +226,10 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->oldposition); lua_pushinteger(L, plr->oldposition);
else if (fastcmp(field,"positiondelay")) else if (fastcmp(field,"positiondelay"))
lua_pushinteger(L, plr->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")) else if (fastcmp(field,"distancetofinish"))
lua_pushinteger(L, plr->distancetofinish); lua_pushinteger(L, plr->distancetofinish);
else if (fastcmp(field,"distancetofinishprev")) else if (fastcmp(field,"distancetofinishprev"))
@ -561,6 +565,14 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->followercolor); lua_pushinteger(L, plr->followercolor);
else if (fastcmp(field,"follower")) else if (fastcmp(field,"follower"))
LUA_PushUserdata(L, plr->follower, META_MOBJ); 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 // rideroids
@ -675,8 +687,8 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->laps); lua_pushinteger(L, plr->laps);
else if (fastcmp(field,"latestlap")) else if (fastcmp(field,"latestlap"))
lua_pushinteger(L, plr->latestlap); lua_pushinteger(L, plr->latestlap);
else if (fastcmp(field,"ctfteam")) else if (fastcmp(field,"team"))
lua_pushinteger(L, plr->ctfteam); lua_pushinteger(L, plr->team);
else if (fastcmp(field,"checkskip")) else if (fastcmp(field,"checkskip"))
lua_pushinteger(L, plr->checkskip); lua_pushinteger(L, plr->checkskip);
else if (fastcmp(field,"cheatchecknum")) else if (fastcmp(field,"cheatchecknum"))
@ -820,6 +832,10 @@ static int player_set(lua_State *L)
plr->oldposition = luaL_checkinteger(L, 3); plr->oldposition = luaL_checkinteger(L, 3);
else if (fastcmp(field,"positiondelay")) else if (fastcmp(field,"positiondelay"))
plr->positiondelay = luaL_checkinteger(L, 3); 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")) else if (fastcmp(field,"distancetofinish"))
return NOSET; return NOSET;
else if (fastcmp(field,"distancetofinishprev")) else if (fastcmp(field,"distancetofinishprev"))
@ -1130,8 +1146,16 @@ static int player_set(lua_State *L)
plr->followercolor = luaL_checkinteger(L, 3); plr->followercolor = luaL_checkinteger(L, 3);
else if (fastcmp(field,"followerready")) else if (fastcmp(field,"followerready"))
plr->followerready = luaL_checkboolean(L, 3); plr->followerready = luaL_checkboolean(L, 3);
else if (fastcmp(field,"follower")) // it's probably best we don't allow the follower mobj to change. else if (fastcmp(field,"follower"))
return NOSET; 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!!!! // time to add to the endless elseif list!!!!
// rideroids // rideroids
@ -1252,8 +1276,8 @@ static int player_set(lua_State *L)
plr->laps = (UINT8)luaL_checkinteger(L, 3); plr->laps = (UINT8)luaL_checkinteger(L, 3);
else if (fastcmp(field,"latestlap")) else if (fastcmp(field,"latestlap"))
plr->latestlap = (UINT8)luaL_checkinteger(L, 3); plr->latestlap = (UINT8)luaL_checkinteger(L, 3);
else if (fastcmp(field,"ctfteam")) else if (fastcmp(field,"team"))
plr->ctfteam = (INT32)luaL_checkinteger(L, 3); G_AssignTeam(plr, (UINT8)luaL_checkinteger(L, 3));
else if (fastcmp(field,"checkskip")) else if (fastcmp(field,"checkskip"))
plr->checkskip = (INT32)luaL_checkinteger(L, 3); plr->checkskip = (INT32)luaL_checkinteger(L, 3);
else if (fastcmp(field,"cheatchecknum")) else if (fastcmp(field,"cheatchecknum"))

View file

@ -197,12 +197,6 @@ int LUA_PushGlobals(lua_State *L, const char *word)
} else if (fastcmp(word,"paused")) { } else if (fastcmp(word,"paused")) {
lua_pushboolean(L, paused); lua_pushboolean(L, paused);
return 1; 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")) { } else if (fastcmp(word,"timelimit")) {
lua_pushinteger(L, timelimitintics); lua_pushinteger(L, timelimitintics);
return 1; return 1;
@ -226,20 +220,6 @@ int LUA_PushGlobals(lua_State *L, const char *word)
lua_pushstring(L, tutorialchallengemap); lua_pushstring(L, tutorialchallengemap);
return 1; return 1;
// end map vars // 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 // begin timers
} else if (fastcmp(word,"invulntics")) { } else if (fastcmp(word,"invulntics")) {
lua_pushinteger(L, invulntics); lua_pushinteger(L, invulntics);
@ -351,6 +331,9 @@ int LUA_PushGlobals(lua_State *L, const char *word)
} else if (fastcmp(word,"franticitems")) { } else if (fastcmp(word,"franticitems")) {
lua_pushboolean(L, franticitems); lua_pushboolean(L, franticitems);
return 1; return 1;
} else if (fastcmp(word,"teamplay")) {
lua_pushboolean(L, g_teamplay);
return 1;
} else if (fastcmp(word,"wantedcalcdelay")) { } else if (fastcmp(word,"wantedcalcdelay")) {
lua_pushinteger(L, wantedcalcdelay); lua_pushinteger(L, wantedcalcdelay);
return 1; return 1;
@ -392,19 +375,7 @@ int LUA_PushGlobals(lua_State *L, const char *word)
// See the above. // See the above.
int LUA_WriteGlobals(lua_State *L, const char *word) int LUA_WriteGlobals(lua_State *L, const char *word)
{ {
if (fastcmp(word, "redscore")) if (fastcmp(word, "gravity"))
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"))
gravity = (fixed_t)luaL_checkinteger(L, 2); gravity = (fixed_t)luaL_checkinteger(L, 2);
else if (fastcmp(word, "stoppedclock")) else if (fastcmp(word, "stoppedclock"))
stoppedclock = luaL_checkboolean(L, 2); stoppedclock = luaL_checkboolean(L, 2);

View file

@ -290,6 +290,8 @@ struct debugFlagNames_s const debug_flag_names[] =
{"PowerLevel", DBG_PWRLV}, // alt name {"PowerLevel", DBG_PWRLV}, // alt name
{"Demo", DBG_DEMO}, {"Demo", DBG_DEMO},
{"Replay", DBG_DEMO}, // alt name {"Replay", DBG_DEMO}, // alt name
{"Teams", DBG_TEAMS},
{"Teamplay", DBG_TEAMS}, // alt name
{NULL, 0} {NULL, 0}
}; };

View file

@ -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. 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_ITEM_SPAWNER = PROLDDEMO, // Battle mode item spawners
PR_TEAMS, // Teamplay shuffling
PRNUMSYNCED, PRNUMSYNCED,

View file

@ -14,6 +14,14 @@
menuitem_t OPTIONS_Gameplay[] = 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, {IT_HEADER, "Race...", NULL,
NULL, {NULL}, 0, 0}, NULL, {NULL}, 0, 0},
@ -21,9 +29,6 @@ menuitem_t OPTIONS_Gameplay[] =
{IT_STRING | IT_CVAR, "Game Speed", "Gear for the next map.", {IT_STRING | IT_CVAR, "Game Speed", "Gear for the next map.",
NULL, {.cvar = &cv_kartspeed}, 0, 0}, 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.", {IT_STRING | IT_CVAR, "Encore Mode", "Play in Encore Mode next map.",
NULL, {.cvar = &cv_kartencore}, 0, 0}, NULL, {.cvar = &cv_kartencore}, 0, 0},

View file

@ -753,6 +753,20 @@ static void M_HandleBackToChars(setup_player_t *p)
static boolean M_HandleBeginningColors(setup_player_t *p) static boolean M_HandleBeginningColors(setup_player_t *p)
{ {
p->mdepth = CSSTEP_COLORS; 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); M_NewPlayerColors(p);
if (p->colors.listLen != 1) if (p->colors.listLen != 1)
return true; return true;

View file

@ -11,6 +11,7 @@
/// \file menus/transient/pause-game.c /// \file menus/transient/pause-game.c
/// \brief In-game/pause menus /// \brief In-game/pause menus
#include "../../byteptr.h"
#include "../../d_netcmd.h" #include "../../d_netcmd.h"
#include "../../i_time.h" #include "../../i_time.h"
#include "../../k_menu.h" #include "../../k_menu.h"
@ -478,50 +479,33 @@ void M_HandleSpectateToggle(INT32 choice)
return; 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. M_StartMessage("Joining Play", M_GetText("The server is not allowing\njoining play at this time.\n"), NULL, MM_NOTHING, NULL, NULL);
// 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);
return; return;
} }
M_QuitPauseMenu(-1); M_QuitPauseMenu(-1);
const char *destinationstate = tospectator ? "spectator" : "playing"; // Send spectate
UINT8 buf[2];
UINT8 *p = buf;
// These console command names... WRITEUINT8(p, splitspecid);
if (pausemenu.splitscreenfocusid == 0) WRITEUINT8(p, joingame);
{
COM_ImmedExecute( SendNetXCmd(XD_SPECTATE, &buf, p - buf);
va(
"changeteam %s",
destinationstate
)
);
}
else
{
COM_ImmedExecute(
va(
"changeteam%u %s",
pausemenu.splitscreenfocusid + 1,
destinationstate
)
);
}
return; return;
} }

View file

@ -727,6 +727,12 @@ void Controller::search()
continue; 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. // Target is already being hunted.
if (player->flickyAttacker) if (player->flickyAttacker)
{ {

View file

@ -5128,10 +5128,7 @@ void A_OldRingExplode(mobj_t *actor) {
if (changecolor) if (changecolor)
{ {
if (!(gametyperules & GTR_TEAMS)) P_ColorTeamMissile(mo, actor->target->player);
mo->color = actor->target->color; //copy color
else if (actor->target->player->ctfteam == 2)
mo->color = skincolor_bluering;
} }
} }
@ -5144,10 +5141,7 @@ void A_OldRingExplode(mobj_t *actor) {
if (changecolor) if (changecolor)
{ {
if (!(gametyperules & GTR_TEAMS)) P_ColorTeamMissile(mo, actor->target->player);
mo->color = actor->target->color; //copy color
else if (actor->target->player->ctfteam == 2)
mo->color = skincolor_bluering;
} }
mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1); mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1);
@ -5159,10 +5153,7 @@ void A_OldRingExplode(mobj_t *actor) {
if (changecolor) if (changecolor)
{ {
if (!(gametyperules & GTR_TEAMS)) P_ColorTeamMissile(mo, actor->target->player);
mo->color = actor->target->color; //copy color
else if (actor->target->player->ctfteam == 2)
mo->color = skincolor_bluering;
} }
} }

View file

@ -49,11 +49,6 @@
#include "m_easing.h" #include "m_easing.h"
#include "k_hud.h" // K_AddMessage #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) void P_ForceFeed(const player_t *player, INT32 attack, INT32 fade, tic_t duration, INT32 period)
{ {
BasicFF_t Basicfeed; BasicFF_t Basicfeed;
@ -1510,13 +1505,15 @@ void P_CheckPointLimit(void)
return; return;
// pointlimit is nonzero, check if it's been reached by this player // pointlimit is nonzero, check if it's been reached by this player
if (G_GametypeHasTeams()) if (G_GametypeHasTeams() == true)
{ {
// Just check both teams for (i = 0; i < TEAM__MAX; i++)
if (g_pointlimit <= redscore || g_pointlimit <= bluescore)
{ {
if (server) if (g_pointlimit <= g_teamscores[i])
SendNetXCmd(XD_EXITLEVEL, NULL, 0); {
P_DoAllPlayersExit(0, false);
return;
}
} }
} }
else else
@ -1529,10 +1526,7 @@ void P_CheckPointLimit(void)
if (g_pointlimit <= players[i].roundscore) if (g_pointlimit <= players[i].roundscore)
{ {
P_DoAllPlayersExit(0, false); P_DoAllPlayersExit(0, false);
return;
/*if (server)
SendNetXCmd(XD_EXITLEVEL, NULL, 0);*/
return; // good thing we're leaving the function immediately instead of letting the loop get mangled!
} }
} }
} }
@ -2506,12 +2500,11 @@ static boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj_t *sou
if (source == target) if (source == target)
return false; return false;
if (G_GametypeHasTeams()) #if 0
{ // Don't hurt your team, either!
// Don't hurt your team, either! if (G_SameTeam(source->player, target->player) == true)
if (source->player->ctfteam == target->player->ctfteam) return false;
return false; #endif
}
} }
return true; return true;

View file

@ -12168,34 +12168,13 @@ void P_SpawnPlayer(INT32 playernum)
G_PlayerReborn(playernum, false); 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 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; G_AssignTeam(p, !(playernum & 1) + 1);
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));
} }
// 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) if (leveltime > introtime && K_PodiumSequence() == false)
@ -12605,23 +12584,23 @@ static boolean P_SpawnNonMobjMapThing(mapthing_t *mthing)
} }
return true; 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; mthing->type = 0;
numredctfstarts++; numteamstarts[TEAM_ORANGE]++;
} }
return true; 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; mthing->type = 0;
numbluectfstarts++; numteamstarts[TEAM_BLUE]++;
} }
return true; 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) void P_ColorTeamMissile(mobj_t *missile, player_t *source)
{ {
if (G_GametypeHasTeams()) if (missile == NULL || source == NULL)
{ {
if (source->ctfteam == 2) return;
missile->color = skincolor_bluering; }
else if (source->ctfteam == 1)
missile->color = skincolor_redring; if (source->team > TEAM_UNASSIGNED && source->team < TEAM__MAX)
{
missile->color = g_teaminfo[source->team].color;
} }
/*
else else
missile->color = player->mo->color; //copy color {
*/ missile->color = source->skincolor;
}
} }
// //

View file

@ -247,7 +247,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT16(save->p, players[i].flashpal); WRITEUINT16(save->p, players[i].flashpal);
WRITEUINT16(save->p, players[i].flashcount); WRITEUINT16(save->p, players[i].flashcount);
WRITEUINT8(save->p, players[i].skincolor); WRITEUINT16(save->p, players[i].skincolor);
WRITEINT32(save->p, players[i].skin); WRITEINT32(save->p, players[i].skin);
for (j = 0; j < MAXAVAILABILITY; j++) 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].fakeskin);
WRITEUINT8(save->p, players[i].lastfakeskin); 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); WRITEUINT32(save->p, players[i].score);
WRITESINT8(save->p, players[i].lives); WRITESINT8(save->p, players[i].lives);
WRITESINT8(save->p, players[i].xtralife); 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].cheatchecknum);
WRITEINT32(save->p, players[i].checkpointId); WRITEINT32(save->p, players[i].checkpointId);
WRITEUINT8(save->p, players[i].ctfteam); WRITEUINT8(save->p, players[i].team);
WRITEUINT8(save->p, players[i].checkskip); 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].position);
WRITEUINT8(save->p, players[i].oldposition); WRITEUINT8(save->p, players[i].oldposition);
WRITEUINT8(save->p, players[i].positiondelay); 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].distancetofinish);
WRITEUINT32(save->p, players[i].distancetofinishprev); WRITEUINT32(save->p, players[i].distancetofinishprev);
WRITEUINT32(save->p, players[i].lastpickupdistance); 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].flashpal = READUINT16(save->p);
players[i].flashcount = 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); players[i].skin = READINT32(save->p);
for (j = 0; j < MAXAVAILABILITY; j++) 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].fakeskin = READUINT8(save->p);
players[i].lastfakeskin = 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].score = READUINT32(save->p);
players[i].lives = READSINT8(save->p); players[i].lives = READSINT8(save->p);
players[i].xtralife = READSINT8(save->p); // Ring Extra Life counter 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].cheatchecknum = READINT32(save->p);
players[i].checkpointId = 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); 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].position = READUINT8(save->p);
players[i].oldposition = READUINT8(save->p); players[i].oldposition = READUINT8(save->p);
players[i].positiondelay = 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].distancetofinish = READUINT32(save->p);
players[i].distancetofinishprev = READUINT32(save->p); players[i].distancetofinishprev = READUINT32(save->p);
players[i].lastpickupdistance = 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); WRITEUINT8(save->p, globools);
} }
WRITEUINT32(save->p, bluescore); for (i = 0; i < TEAM__MAX; i++)
WRITEUINT32(save->p, redscore); {
WRITEUINT32(save->p, g_teamscores[i]);
WRITEUINT16(save->p, skincolor_redteam); }
WRITEUINT16(save->p, skincolor_blueteam);
WRITEUINT16(save->p, skincolor_redring);
WRITEUINT16(save->p, skincolor_bluering);
WRITEINT32(save->p, modulothing); 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, racecountdown);
WRITEUINT32(save->p, exitcountdown); WRITEUINT32(save->p, exitcountdown);
@ -6654,6 +6655,7 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending)
WRITEUINT8(save->p, gamespeed); WRITEUINT8(save->p, gamespeed);
WRITEUINT8(save->p, numlaps); WRITEUINT8(save->p, numlaps);
WRITEUINT8(save->p, franticitems); WRITEUINT8(save->p, franticitems);
WRITEUINT8(save->p, g_teamplay);
WRITESINT8(save->p, speedscramble); WRITESINT8(save->p, speedscramble);
WRITESINT8(save->p, encorescramble); WRITESINT8(save->p, encorescramble);
@ -6816,28 +6818,13 @@ static boolean P_NetUnArchiveMisc(savebuffer_t *save, boolean reloading)
stoppedclock = !!(globools & (1<<1)); stoppedclock = !!(globools & (1<<1));
} }
bluescore = READUINT32(save->p); for (i = 0; i < TEAM__MAX; i++)
redscore = READUINT32(save->p); {
g_teamscores[i] = READUINT32(save->p);
skincolor_redteam = READUINT16(save->p); }
skincolor_blueteam = READUINT16(save->p);
skincolor_redring = READUINT16(save->p);
skincolor_bluering = READUINT16(save->p);
modulothing = READINT32(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); racecountdown = READUINT32(save->p);
exitcountdown = READUINT32(save->p); exitcountdown = READUINT32(save->p);
@ -6859,6 +6846,7 @@ static boolean P_NetUnArchiveMisc(savebuffer_t *save, boolean reloading)
gamespeed = READUINT8(save->p); gamespeed = READUINT8(save->p);
numlaps = READUINT8(save->p); numlaps = READUINT8(save->p);
franticitems = (boolean)READUINT8(save->p); franticitems = (boolean)READUINT8(save->p);
g_teamplay = (boolean)READUINT8(save->p);
speedscramble = READSINT8(save->p); speedscramble = READSINT8(save->p);
encorescramble = READSINT8(save->p); encorescramble = READSINT8(save->p);

View file

@ -196,13 +196,12 @@ precipmobj_t **precipblocklinks;
UINT8 *rejectmatrix; UINT8 *rejectmatrix;
// Maintain single and multi player starting spots. // Maintain single and multi player starting spots.
INT32 numdmstarts, numcoopstarts, numredctfstarts, numbluectfstarts; INT32 numdmstarts, numcoopstarts, numteamstarts[TEAM__MAX];
INT32 numfaultstarts; INT32 numfaultstarts;
mapthing_t *deathmatchstarts[MAX_DM_STARTS]; mapthing_t *deathmatchstarts[MAX_DM_STARTS];
mapthing_t *playerstarts[MAXPLAYERS]; mapthing_t *playerstarts[MAXPLAYERS];
mapthing_t *bluectfstarts[MAXPLAYERS]; mapthing_t *teamstarts[TEAM__MAX][MAXPLAYERS];
mapthing_t *redctfstarts[MAXPLAYERS];
mapthing_t *faultstart; mapthing_t *faultstart;
// Global state for PartialAddWadFile/MultiSetupWadFiles // 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; lines[i].args[0] = (lines[i].flags & ML_NOTBOUNCY) ? TMT_EACHTIMEENTERANDEXIT : TMT_EACHTIMEENTER;
else else
lines[i].args[0] = TMT_CONTINUOUS; 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; lines[i].special = 309;
break; break;
case 313: //No more enemies - once case 313: //No more enemies - once
@ -7684,6 +7683,7 @@ static void P_InitLevelSettings(void)
const boolean multi_speed = (gametypes[gametype]->speed == KARTSPEED_AUTO); const boolean multi_speed = (gametypes[gametype]->speed == KARTSPEED_AUTO);
gamespeed = multi_speed ? KARTSPEED_EASY : gametypes[gametype]->speed; gamespeed = multi_speed ? KARTSPEED_EASY : gametypes[gametype]->speed;
franticitems = false; franticitems = false;
g_teamplay = false;
if (K_PodiumSequence() == true) if (K_PodiumSequence() == true)
{ {
@ -7730,6 +7730,7 @@ static void P_InitLevelSettings(void)
gamespeed = (UINT8)cv_kartspeed.value; gamespeed = (UINT8)cv_kartspeed.value;
} }
franticitems = (boolean)cv_kartfrantic.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)); memset(&battleovertime, 0, sizeof(struct battleovertime));
@ -7780,16 +7781,12 @@ void P_RespawnThings(void)
static void P_ResetSpawnpoints(void) static void P_ResetSpawnpoints(void)
{ {
UINT8 i; UINT8 i, j;
numdmstarts = numredctfstarts = numbluectfstarts = 0;
numfaultstarts = 0;
faultstart = NULL;
// reset the player starts // reset the player starts
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
playerstarts[i] = bluectfstarts[i] = redctfstarts[i] = NULL; playerstarts[i] = NULL;
if (playeringame[i]) 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++) for (i = 0; i < MAX_DM_STARTS; i++)
deathmatchstarts[i] = NULL; 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++) for (i = 0; i < 16; i++)
skyboxviewpnts[i] = skyboxcenterpnts[i] = NULL; 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) static void P_InitPlayers(void)
{ {
INT32 i, skin = -1, follower = -1; 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! // Make sure objectplace is OFF when you first start the level!
OP_ResetObjectplace(); OP_ResetObjectplace();
// Update skins / colors between levels.
G_UpdateAllPlayerPreferences();
// Are we forcing a character? // Are we forcing a character?
if (gametype == GT_TUTORIAL) if (gametype == GT_TUTORIAL)
{ {
@ -8900,6 +8983,8 @@ void P_PostLoadLevel(void)
} }
} }
P_ShuffleTeams();
K_TimerInit(); K_TimerInit();
P_InitPlayers(); P_InitPlayers();

View file

@ -30,7 +30,7 @@ extern unsigned char mapmd5[16];
// Player spawn spots for deathmatch. // Player spawn spots for deathmatch.
#define MAX_DM_STARTS 64 #define MAX_DM_STARTS 64
extern mapthing_t *deathmatchstarts[MAX_DM_STARTS]; 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 levelloading;
extern boolean g_reloadinggamestate; extern boolean g_reloadinggamestate;

View file

@ -1535,7 +1535,7 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
// Only red/blue team members can activate this. // Only red/blue team members can activate this.
if (!(actor && actor->player)) if (!(actor && actor->player))
return false; 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; return false;
break; break;
case 314: case 314:

View file

@ -289,7 +289,7 @@ typedef enum
typedef enum typedef enum
{ {
TMT_RED = 0, TMT_ORANGE = 0,
TMT_BLUE = 1, TMT_BLUE = 1,
} textmapteam_t; } textmapteam_t;

View file

@ -585,146 +585,6 @@ static void P_RunThinkers(void)
ps_acs_time = I_GetPreciseTime() - ps_acs_time; 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) static inline void P_DeviceRumbleTick(void)
{ {
UINT8 i; UINT8 i;
@ -1225,9 +1085,6 @@ void P_Ticker(boolean run)
timeinmap++; timeinmap++;
} }
if (G_GametypeHasTeams())
P_DoTeamStuff();
if (run) if (run)
{ {
if (racecountdown > 1) if (racecountdown > 1)

View file

@ -73,6 +73,7 @@
#include "k_hud.h" // K_AddMessage #include "k_hud.h" // K_AddMessage
#include "m_easing.h" #include "m_easing.h"
#include "acs/interface.h" #include "acs/interface.h"
#include "byteptr.h"
#ifdef HWRENDER #ifdef HWRENDER
#include "hardware/hw_light.h" #include "hardware/hw_light.h"
@ -514,25 +515,44 @@ void P_GivePlayerLives(player_t *player, INT32 numlives)
// Adds to the player's score // Adds to the player's score
void P_AddPlayerScore(player_t *player, INT32 amount) void P_AddPlayerScore(player_t *player, INT32 amount)
{ {
if (!((gametyperules & GTR_POINTLIMIT))) if ((gametyperules & GTR_POINTLIMIT) == 0)
{
return; return;
}
if (player->exiting) // srb2kart if (player->exiting) // srb2kart
{
return; return;
}
const boolean teams = G_GametypeHasTeams();
// Don't underflow. // Don't underflow.
// Don't go above MAXSCORE. // Don't go above MAXSCORE.
if (amount < 0 && (UINT32)-amount > player->roundscore) if (amount < 0 && (UINT32)-amount > player->roundscore)
{
player->roundscore = 0; player->roundscore = 0;
}
else if (player->roundscore + amount < MAXSCORE) 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); HU_DoTitlecardCEchoForDuration(player, "K.O. READY!", true, 5*TICRATE/2);
}
player->roundscore += amount; player->roundscore += amount;
} }
else else
{
player->roundscore = MAXSCORE; player->roundscore = MAXSCORE;
}
if (teams == true)
{
G_AddTeamScore(player->team, amount, player);
}
} }
void P_PlayRinglossSound(mobj_t *source) 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) boolean P_SpectatorJoinGame(player_t *player)
{ {
INT32 changeto = 0;
const char *text = NULL; 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 // no conditions that could cause the gamejoin to fail below this line
if (player->mo) if (player->mo)
{ {
P_RemoveMobj(player->mo); P_RemoveMobj(player->mo);
@ -3793,7 +3773,7 @@ boolean P_SpectatorJoinGame(player_t *player)
player->spectator = false; player->spectator = false;
player->pflags &= ~PF_WANTSTOJOIN; player->pflags &= ~PF_WANTSTOJOIN;
player->spectatewait = 0; player->spectatewait = 0;
player->ctfteam = changeto; player->team = TEAM_UNASSIGNED; // We will auto-assign later.
player->playerstate = PST_REBORN; player->playerstate = PST_REBORN;
player->enteredGame = true; player->enteredGame = true;
@ -3805,12 +3785,7 @@ boolean P_SpectatorJoinGame(player_t *player)
} }
// a surprise tool that will help us later... // a surprise tool that will help us later...
if (changeto == 1) text = va("\x82*%s entered the game.", player_names[player-players]);
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]);
HU_AddChatText(text, false); HU_AddChatText(text, false);
return true; // no more player->mo, cannot continue. return true; // no more player->mo, cannot continue.
@ -4891,16 +4866,13 @@ void P_CheckRaceGriefing(player_t *player, boolean dopunishment)
else else
{ {
// Send spectate // Send spectate
changeteam_union NetPacket; UINT8 buf[2];
UINT16 usvalue; UINT8 *p = buf;
NetPacket.value.l = NetPacket.value.b = 0; WRITEUINT8(p, n);
NetPacket.packet.newteam = 0; WRITEUINT8(p, 0);
NetPacket.packet.playernum = n;
NetPacket.packet.verification = true;
usvalue = SHORT(NetPacket.value.l|NetPacket.value.b); SendNetXCmd(XD_SPECTATE, &buf, p - buf);
SendNetXCmd(XD_TEAMCHANGE, &usvalue, sizeof(usvalue));
} }
} }
} }

View file

@ -408,22 +408,6 @@ static void SetSkin(player_t *player, INT32 skinnum)
player->kartweight = skin->kartweight; player->kartweight = skin->kartweight;
player->charflags = skin->flags; 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) if (player->followmobj)
{ {
P_RemoveMobj(player->followmobj); P_RemoveMobj(player->followmobj);

View file

@ -96,13 +96,11 @@ boolean ST_SameTeam(player_t *a, player_t *b)
if (G_GametypeHasTeams() == true) if (G_GametypeHasTeams() == true)
{ {
// You get team messages if you're on the same team. // You get team messages if you're on the same team.
return (a->ctfteam == b->ctfteam); return (a->team == b->team);
}
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;
} }
// 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; static boolean st_stopped = true;

View file

@ -144,6 +144,7 @@ TYPEDEF (unloaded_cupheader_t);
TYPEDEF (exitcondition_t); TYPEDEF (exitcondition_t);
TYPEDEF (darkness_t); TYPEDEF (darkness_t);
TYPEDEF (musicfade_t); TYPEDEF (musicfade_t);
TYPEDEF (teaminfo_t);
// font.h // font.h
TYPEDEF (font_t); TYPEDEF (font_t);

View file

@ -103,6 +103,11 @@ static boolean Y_CanSkipIntermission(void)
return false; return false;
} }
boolean Y_IntermissionPlayerLock(void)
{
return (gamestate == GS_INTERMISSION && data.rankingsmode == false);
}
static void Y_UnloadData(void); static void Y_UnloadData(void);
// //
@ -191,8 +196,6 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
numplayersingame++; numplayersingame++;
} }
memset(data.color, 0, sizeof (data.color));
memset(data.character, 0, sizeof (data.character));
memset(completed, 0, sizeof (completed)); memset(completed, 0, sizeof (completed));
data.numplayers = 0; data.numplayers = 0;
data.showroundnum = false; data.showroundnum = false;
@ -202,9 +205,63 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
srb2::StandingsJson standings {}; srb2::StandingsJson standings {};
bool savestandings = (!rankingsmode && demo.recording); 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 (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]) if (!playeringame[i] || players[i].spectator || completed[i])
continue; continue;
@ -217,9 +274,6 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
completed[i] = true; completed[i] = true;
data.grade[i] = K_PlayerTallyActive(&players[i]) ? players[i].tally.rank : GRADE_INVALID; 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])) if (data.numplayers && (data.val[data.numplayers] == data.val[data.numplayers-1]))
{ {
data.pos[data.numplayers] = data.pos[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 (!rankingsmode)
{ {
if ((powertype == PWRLV_DISABLED) // Online rank is handled further below in this file.
&& !(players[i].pflags & PF_NOCONTEST) if (powertype == PWRLV_DISABLED)
&& (data.pos[data.numplayers] < (numplayersingame + spectateGriefed)))
{ {
// Online rank is handled further below in this file. if (data.winningteam != TEAM_UNASSIGNED)
data.increase[i] = K_CalculateGPRankPoints(data.pos[data.numplayers], numplayersingame + spectateGriefed); {
players[i].score += data.increase[i]; // 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) if (savestandings)
@ -249,8 +323,8 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
srb2::StandingJson standing {}; srb2::StandingJson standing {};
standing.ranking = data.pos[data.numplayers]; standing.ranking = data.pos[data.numplayers];
standing.name = std::string(player_names[i]); standing.name = std::string(player_names[i]);
standing.demoskin = data.character[data.numplayers]; standing.demoskin = players[i].skin;
standing.skincolor = std::string(skincolors[data.color[data.numplayers]].name); standing.skincolor = std::string(skincolors[players[i].skincolor].name);
standing.timeorscore = data.val[data.numplayers]; standing.timeorscore = data.val[data.numplayers];
standings.standings.emplace_back(std::move(standing)); standings.standings.emplace_back(std::move(standing));
} }
@ -290,6 +364,14 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
data.numplayers++; 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) if (savestandings)
{ {
srb2::write_current_demo_end_marker(); srb2::write_current_demo_end_marker();
@ -386,7 +468,19 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32))
{ {
data.mainplayer = i; 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; data.gotthrough = true;
@ -506,11 +600,13 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset)
x2 -= 9; x2 -= 9;
} }
if (standings->numplayers > 10) UINT8 halfway = standings->halfway;
if (halfway > 4)
{ {
yspacing--; yspacing--;
} }
else if (standings->numplayers <= 6) else if (halfway <= 2)
{ {
yspacing++; yspacing++;
if (verticalresults) if (verticalresults)
@ -545,7 +641,7 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset)
); );
i = 0; i = 0;
UINT8 halfway = (standings->numplayers-1)/2;
if (doreverse) if (doreverse)
{ {
i = standings->numplayers-1; i = standings->numplayers-1;
@ -563,13 +659,13 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset)
else else
{ {
UINT8 *charcolormap = NULL; 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) if (standings->isduel)
@ -588,7 +684,7 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset)
M_DrawCharacterSprite( M_DrawCharacterSprite(
duelx + 40, duely + 78, duelx + 40, duely + 78,
standings->character[i], players[pnum].skin,
spr2, spr2,
(datarightofcolumn ? 1 : 7), (datarightofcolumn ? 1 : 7),
0, 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])); 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) 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, x+14, y-5,
0, 0,
static_cast<patch_t*>(W_CachePatchName("MINIDEAD", PU_CACHE)), 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 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, V_DrawMappedPatch(x+14, y-5, 0,
R_CanShowSkinInDemo(standings->character[i]) ? R_CanShowSkinInDemo(players[pnum].skin) ?
faceprefix[standings->character[i]][FACE_MINIMAP] : kp_unknownminimap, faceprefix[players[pnum].skin][FACE_MINIMAP] : kp_unknownminimap,
charcolormap); charcolormap);
} }
} }
@ -1820,13 +1916,15 @@ void Y_Ticker(void)
// Team scramble code for team match and CTF. // Team scramble code for team match and CTF.
// Don't do this if we're going to automatically scramble teams next round. // 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 // If we run out of time in intermission, the beauty is that
// the P_Ticker() team scramble code will pick it up. // the P_Ticker() team scramble code will pick it up.
if ((intertic % (TICRATE/7)) == 0) if ((intertic % (TICRATE/7)) == 0)
P_DoTeamscrambling(); P_DoTeamscrambling();
}*/ }
*/
if ((timer < INFINITE_TIMER && --timer <= 0) if ((timer < INFINITE_TIMER && --timer <= 0)
|| (intertic == endtic)) || (intertic == endtic))
@ -1892,8 +1990,7 @@ void Y_Ticker(void)
{ {
if (!data.rankingsmode && sorttic != -1 && (intertic >= sorttic + 8)) if (!data.rankingsmode && sorttic != -1 && (intertic >= sorttic + 8))
{ {
// Anything with post-intermission consequences here should also occur in Y_EndIntermission. Y_MidIntermission();
K_RetireBots();
Y_CalculateMatchData(1, Y_CompareRank); 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 // Y_EndIntermission
// //
@ -2352,7 +2470,7 @@ void Y_EndIntermission(void)
{ {
if (!data.rankingsmode) if (!data.rankingsmode)
{ {
K_RetireBots(); Y_MidIntermission();
} }
Y_UnloadData(); Y_UnloadData();

View file

@ -25,18 +25,17 @@ typedef struct
boolean showrank; // show rank-restricted queue entry at the end, if it exists boolean showrank; // show rank-restricted queue entry at the end, if it exists
boolean encore; // encore mode boolean encore; // encore mode
boolean isduel; // duel mode boolean isduel; // duel mode
UINT8 winningteam; // teamplay
boolean showroundnum; // round number boolean showroundnum; // round number
char headerstring[64]; // holds levelnames up to 64 characters char headerstring[64]; // holds levelnames up to 64 characters
UINT8 numplayers; // Number of players being displayed UINT8 numplayers; // Number of players being displayed
UINT8 halfway; // Position at which column switches
SINT8 num[MAXPLAYERS]; // Player # SINT8 num[MAXPLAYERS]; // Player #
UINT8 pos[MAXPLAYERS]; // player positions. used for ties UINT8 pos[MAXPLAYERS]; // player positions. used for ties
UINT8 character[MAXPLAYERS]; // Character #
UINT16 color[MAXPLAYERS]; // Color #
UINT32 val[MAXPLAYERS]; // Gametype-specific value UINT32 val[MAXPLAYERS]; // Gametype-specific value
char strval[MAXPLAYERS][MAXPLAYERNAME+1]; 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_DrawIntermissionButton(INT32 startslide, INT32 through, boolean widescreen);
void Y_StartIntermission(void); void Y_StartIntermission(void);
void Y_MidIntermission(void);
void Y_EndIntermission(void); void Y_EndIntermission(void);
boolean Y_ShouldDoIntermission(void); boolean Y_ShouldDoIntermission(void);
@ -66,6 +66,8 @@ void Y_DetermineIntermissionType(void);
void Y_PlayIntermissionMusic(void); void Y_PlayIntermissionMusic(void);
boolean Y_IntermissionPlayerLock(void);
typedef enum typedef enum
{ {
int_none, int_none,