diff --git a/src/console.c b/src/console.c index 422aded7..5a7605d0 100644 --- a/src/console.c +++ b/src/console.c @@ -1618,6 +1618,7 @@ void CON_Drawer(void) if (con_curlines > 0) CON_DrawConsole(); - else if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_CUTSCENE || gamestate == GS_CREDITS) + else if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_CUTSCENE || gamestate == GS_CREDITS + || gamestate == GS_VOTING) CON_DrawHudlines(); } diff --git a/src/d_clisrv.c b/src/d_clisrv.c index a71b6677..3d864e39 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2073,6 +2073,8 @@ static void CL_ConnectToServer(boolean viams) if (gamestate == GS_INTERMISSION) Y_EndIntermission(); // clean up intermission graphics etc + if (gamestate == GS_VOTING) + Y_EndVote(); DEBFILE(va("waiting %d nodes\n", doomcom->numnodes)); G_SetGamestate(GS_WAITINGPLAYERS); @@ -3396,6 +3398,8 @@ void SV_StopServer(void) if (gamestate == GS_INTERMISSION) Y_EndIntermission(); + if (gamestate == GS_VOTING) + Y_EndVote(); gamestate = wipegamestate = GS_NULL; localtextcmd[0] = 0; @@ -3514,7 +3518,7 @@ static void HandleConnect(SINT8 node) #ifdef JOININGAME if (nodewaiting[node]) { - if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) && newnode) + if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING) && newnode) { SV_SendSaveGame(node); // send a complete game state DEBFILE("send savegame\n"); @@ -3726,8 +3730,9 @@ static void HandlePacketFromAwayNode(SINT8 node) /// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook? /// Shouldn't them be downloaded even at intermission time? /// Also, according to HandleConnect, the server will send the savegame even during intermission... - if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* || - netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/) + if (netbuffer->u.servercfg.gamestate == GS_LEVEL + /*|| netbuffer->u.servercfg.gamestate == GS_INTERMISSION + || netbuffer->u.servercfg.gamestate == GS_VOTING*/) cl_mode = CL_DOWNLOADSAVEGAME; else #endif @@ -4324,7 +4329,7 @@ static INT16 Consistancy(void) } // I give up // Coop desynching enemies is painful - if (!G_PlatformGametype()) + if (!G_RaceGametype()) ret += P_GetRandSeed(); #ifdef MOBJCONSISTANCY diff --git a/src/d_main.c b/src/d_main.c index 441bb231..8548ac49 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -301,6 +301,8 @@ static void D_Display(void) else //if (intertype != int_coop) // Multiplayer wipedefindex = wipe_multinter_toblack; } + else if (gamestate == GS_VOTING) + wipedefindex = wipe_multinter_toblack; if (rendermode != render_none) { @@ -335,6 +337,12 @@ static void D_Display(void) HU_Drawer(); break; + case GS_VOTING: + Y_VoteDrawer(); + HU_Erase(); + HU_Drawer(); + break; + case GS_TIMEATTACK: break; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index dfd38794..f558858e 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -46,6 +46,7 @@ #include "m_cond.h" #include "m_anigif.h" #include "k_kart.h" // SRB2kart +#include "y_inter.h" #ifdef NETGAME_DEVMODE #define CV_RESTRICT CV_NETVAR @@ -61,6 +62,9 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum); static void Got_WeaponPref(UINT8 **cp, INT32 playernum); static void Got_Mapcmd(UINT8 **cp, INT32 playernum); static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum); +static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum); +static void Got_ModifyVotecmd(UINT8 **cp, INT32 playernum); +static void Got_PickVotecmd(UINT8 **cp, INT32 playernum); static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum); #ifdef DELFILE static void Got_Delfilecmd(UINT8 **cp, INT32 playernum); @@ -367,6 +371,9 @@ consvar_t cv_kartdebugitem = {"kartdebugitem", "0", CV_NETVAR|CV_CHEAT, kartdebu static CV_PossibleValue_t kartdebugamount_cons_t[] = {{1, "MIN"}, {255, "MAX"}, {0, NULL}}; consvar_t cv_kartdebugamount = {"kartdebugamount", "1", CV_NETVAR|CV_CHEAT, kartdebugamount_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +static CV_PossibleValue_t votetime_cons_t[] = {{10, "MIN"}, {3600, "MAX"}, {0, NULL}}; +consvar_t cv_votetime = {"votetime", "20", CV_NETVAR, votetime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; + static CV_PossibleValue_t cv_collideminimum_cons_t[] = {{1, "MIN"}, {16384, "MAX"}, {0, NULL}}; consvar_t cv_collideminimum = {"collide_minspeed", "25", CV_NETVAR, cv_collideminimum_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t cv_collidesoundnum_cons_t[] = {{1, "MIN"}, {1208, "MAX"}, {0, NULL}}; @@ -434,8 +441,8 @@ consvar_t cv_maxping = {"maxping", "0", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NUL static CV_PossibleValue_t inttime_cons_t[] = {{0, "MIN"}, {3600, "MAX"}, {0, NULL}}; consvar_t cv_inttime = {"inttime", "20", CV_NETVAR, inttime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -static CV_PossibleValue_t advancemap_cons_t[] = {{0, "Off"}, {1, "Next"}, {2, "Random"}, {0, NULL}}; -consvar_t cv_advancemap = {"advancemap", "Next", CV_NETVAR, advancemap_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +static CV_PossibleValue_t advancemap_cons_t[] = {{0, "Off"}, {1, "Next"}, {2, "Random"}, {3, "Vote"}, {0, NULL}}; +consvar_t cv_advancemap = {"advancemap", "Vote", CV_NETVAR, advancemap_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t playersforexit_cons_t[] = {{0, "One"}, {1, "All"}, {0, NULL}}; consvar_t cv_playersforexit = {"playersforexit", "One", CV_NETVAR, playersforexit_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -475,6 +482,9 @@ const char *netxcmdnames[MAXNETXCMD - 1] = "SETMOTD", "SUICIDE", "DEMOTED", + "SETUPVOTE", + "MODIFYVOTE", + "PICKVOTE", #ifdef HAVE_BLUA "LUACMD", "LUAVAR" @@ -508,6 +518,10 @@ void D_RegisterServerCommands(void) RegisterNetXCmd(XD_LUACMD, Got_Luacmd); #endif + RegisterNetXCmd(XD_SETUPVOTE, Got_SetupVotecmd); + RegisterNetXCmd(XD_MODIFYVOTE, Got_ModifyVotecmd); + RegisterNetXCmd(XD_PICKVOTE, Got_PickVotecmd); + // Remote Administration COM_AddCommand("password", Command_Changepassword_f); RegisterNetXCmd(XD_LOGIN, Got_Login); @@ -1945,6 +1959,60 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pultmode, boolean rese } } +void D_SetupVote(void) +{ + XBOXSTATIC char buf[8]; + char *p; + INT32 i; + + p = buf; + + for (i = 0; i < 4; i++) + { + if (i == 3) + WRITEUINT16(p, G_RandMap(G_TOLFlag(gametype), prevmap, true, false)); + else + WRITEUINT16(p, G_RandMap(G_TOLFlag(gametype), prevmap, false, false)); + } + + SendNetXCmd(XD_SETUPVOTE, buf, p - buf); +} + +void D_ModifyClientVote(INT8 voted) +{ + XBOXSTATIC UINT8 buf[1]; + buf[0] = (UINT8)(voted+1); + SendNetXCmd(XD_MODIFYVOTE, &buf, 1); +} + +void D_PickVote(void) +{ + XBOXSTATIC UINT8 buf[2]; + UINT8 temppicks[MAXPLAYERS]; + UINT8 templevels[MAXPLAYERS]; + UINT8 numvotes = 0, key = 0; + INT32 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + if (votes[i] != -1) + { + temppicks[numvotes] = (UINT8)i; + templevels[numvotes] = (UINT8)votes[i]; + numvotes++; + } + } + + key = M_RandomKey(numvotes); + + buf[0] = temppicks[key]; + buf[1] = templevels[key]; + + SendNetXCmd(XD_PICKVOTE, &buf, 2); +} + // Warp to map code. // Called either from map console command, or idclev cheat. // @@ -2193,7 +2261,7 @@ static void Command_Pause(void) if (cv_pause.value || server || (IsPlayerAdmin(consoleplayer))) { - if (modeattacking || !(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION)) + if (modeattacking || !(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING)) { CONS_Printf(M_GetText("You can't pause here.\n")); return; @@ -2262,13 +2330,13 @@ static void Command_Suicide(void) WRITEINT32(cp, consoleplayer); - if (!(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION)) + if (!(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING)) { CONS_Printf(M_GetText("You must be in a level to use this.\n")); return; } - /*if (!G_PlatformGametype()) // srb2kart: not necessary, suiciding makes you lose a balloon in battle, so it's not desirable to use as a way to escape a hit + /*if (!G_RaceGametype()) // srb2kart: not necessary, suiciding makes you lose a balloon in battle, so it's not desirable to use as a way to escape a hit { CONS_Printf(M_GetText("You may only use this in co-op, race, and competition!\n")); return; @@ -2289,7 +2357,7 @@ static void Got_Suicide(UINT8 **cp, INT32 playernum) INT32 suicideplayer = READINT32(*cp); // You can't suicide someone else. Nice try, there. - if (suicideplayer != playernum) // srb2kart: "|| (!G_PlatformGametype())" + if (suicideplayer != playernum) // srb2kart: "|| (!G_RaceGametype())" { CONS_Alert(CONS_WARNING, M_GetText("Illegal suicide command received from %s\n"), player_names[playernum]); if (server) @@ -4021,7 +4089,7 @@ void ItemFinder_OnChange(void) static void PointLimit_OnChange(void) { // Don't allow pointlimit in Single Player/Co-Op/Race! - if (server && Playing() && G_PlatformGametype()) + if (server && Playing() && G_RaceGametype()) { if (cv_pointlimit.value) CV_StealthSetValue(&cv_pointlimit, 0); @@ -4069,7 +4137,7 @@ UINT32 timelimitintics = 0; static void TimeLimit_OnChange(void) { // Don't allow timelimit in Single Player/Co-Op/Race! - if (server && Playing() && cv_timelimit.value != 0 && G_PlatformGametype()) + if (server && Playing() && cv_timelimit.value != 0 && G_RaceGametype()) { CV_SetValue(&cv_timelimit, 0); return; @@ -4172,7 +4240,7 @@ void D_GameTypeChanged(INT32 lastgametype) // reset timelimit and pointlimit in race/coop, prevent stupid cheats if (server) { - if (G_PlatformGametype()) + if (G_RaceGametype()) { if (cv_timelimit.value) CV_SetValue(&cv_timelimit, 0); @@ -4287,7 +4355,7 @@ static void TeamScramble_OnChange(void) boolean success = false; // Don't trigger outside level or intermission! - if (!(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION)) + if (!(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING)) return; if (!cv_teamscramble.value) @@ -4505,6 +4573,63 @@ static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum) G_ExitLevel(); } +static void Got_SetupVotecmd(UINT8 **cp, INT32 playernum) +{ + INT32 i; + + if (playernum != serverplayer && !IsPlayerAdmin(playernum)) + { + CONS_Alert(CONS_WARNING, M_GetText("Illegal vote setup received from %s\n"), player_names[playernum]); + if (server) + { + XBOXSTATIC UINT8 buf[2]; + + buf[0] = (UINT8)playernum; + buf[1] = KICK_MSG_CON_FAIL; + SendNetXCmd(XD_KICK, &buf, 2); + } + return; + } + + for (i = 0; i < 4; i++) + { + votelevels[i] = (INT16)READUINT16(*cp); + if (!mapheaderinfo[votelevels[i]]) + P_AllocMapHeader(votelevels[i]); + } + + G_SetGamestate(GS_VOTING); + Y_StartVote(); +} + +static void Got_ModifyVotecmd(UINT8 **cp, INT32 playernum) +{ + INT8 voted = READUINT8(*cp); + votes[playernum] = (INT8)(voted-1); +} + +static void Got_PickVotecmd(UINT8 **cp, INT32 playernum) +{ + INT8 pick = READUINT8(*cp); + INT8 level = READUINT8(*cp); + + if (playernum != serverplayer && !IsPlayerAdmin(playernum)) + { + CONS_Alert(CONS_WARNING, M_GetText("Illegal vote setup received from %s\n"), player_names[playernum]); + if (server) + { + XBOXSTATIC UINT8 buf[2]; + + buf[0] = (UINT8)playernum; + buf[1] = KICK_MSG_CON_FAIL; + SendNetXCmd(XD_KICK, &buf, 2); + } + return; + } + + Y_SetupVoteFinish((INT8)pick, (INT8)level); +} + /** Prints the number of the displayplayer. * * \todo Possibly remove this; it was useful for debugging at one point. @@ -4630,7 +4755,7 @@ void Command_ExitGame_f(void) void Command_Retry_f(void) { - if (!(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION)) + if (!(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING)) CONS_Printf(M_GetText("You must be in a level to use this.\n")); else if (netgame || multiplayer) CONS_Printf(M_GetText("This only works in single player.\n")); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index fb314b97..70ee6940 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -127,6 +127,8 @@ extern consvar_t cv_kartcomeback; extern consvar_t cv_kartmirror; extern consvar_t cv_speedometer; +extern consvar_t cv_votetime; + extern consvar_t cv_collideminimum; extern consvar_t cv_collidesoundnum; extern consvar_t cv_collidesounds; @@ -192,9 +194,12 @@ typedef enum XD_SETMOTD, // 19 XD_SUICIDE, // 20 XD_DEMOTED, // 21 + XD_SETUPVOTE, // 22 + XD_MODIFYVOTE, // 23 + XD_PICKVOTE, // 24 #ifdef HAVE_BLUA - XD_LUACMD, // 22 - XD_LUAVAR, // 23 + XD_LUACMD, // 25 + XD_LUAVAR, // 26 #endif MAXNETXCMD } netxcmd_t; @@ -249,6 +254,9 @@ void Command_ExitGame_f(void); void Command_Retry_f(void); void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore void D_MapChange(INT32 pmapnum, INT32 pgametype, boolean pultmode, boolean presetplayers, INT32 pdelay, boolean pskipprecutscene, boolean pfromlevelselect); +void D_SetupVote(void); +void D_ModifyClientVote(INT8 voted); +void D_PickVote(void); void ObjectPlace_OnChange(void); boolean IsPlayerAdmin(INT32 playernum); void SetAdminPlayer(INT32 playernum); diff --git a/src/d_player.h b/src/d_player.h index 28ee6d00..48e7fa3f 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -323,7 +323,6 @@ typedef enum k_balloon, // Number of balloons left k_comebackpoints, // Number of times you've bombed or gave an item to someone; once it's 3 it gets set back to 0 and you're given a balloon k_comebackmode, // 0 = bomb, 1 = item - k_comebackshowninfo,// Have you already seen the info screen before? NUMKARTSTUFF } kartstufftype_t; diff --git a/src/dehacked.c b/src/dehacked.c index 9c1f3aef..76d491a2 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -7418,8 +7418,7 @@ static const char *const KARTSTUFF_LIST[] = { "BALLOON", "COMEBACKPOINTS", - "COMEBACKMODE", - "COMEBACKSHOWNINFO" + "COMEBACKMODE" }; static const char *const HUDITEMS_LIST[] = { diff --git a/src/doomstat.h b/src/doomstat.h index a6bbab87..7d55f34f 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -444,8 +444,14 @@ extern UINT8 gamespeed; extern boolean franticitems; extern boolean mirrormode; extern boolean comeback; -extern tic_t curlap, bestlap; + extern boolean legitimateexit; +extern boolean comebackshowninfo; +extern tic_t curlap, bestlap; + +extern INT16 votelevels[4]; +extern INT8 votes[MAXPLAYERS]; +extern INT8 pickedvote; extern tic_t hidetime; diff --git a/src/g_game.c b/src/g_game.c index 6ace117e..fdf554c7 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -72,6 +72,7 @@ static void G_DoCompleted(void); static void G_DoStartContinue(void); static void G_DoContinued(void); static void G_DoWorldDone(void); +static void G_DoStartVote(void); char mapmusname[7]; // Music name UINT16 mapmusflags; // Track and reset bit @@ -240,14 +241,23 @@ INT16 scramblecount; //for CTF team scramble INT32 cheats; //for multiplayer cheat commands // SRB2Kart +// Cvars that we don't want changed mid-game UINT8 gamespeed; // Game's current speed (or difficulty, or cc, or etc); 0-2 for relaxed, standard, & turbo boolean mirrormode; // Mirror Mode currently enabled? boolean franticitems; // Frantic items currently enabled? boolean comeback; // Battle Mode's karma comeback is on/off -boolean legitimateexit; // Did this client actually finish the match? Calculated locally -tic_t curlap; // Current lap time, calculated locally -tic_t bestlap; // Best lap time, locally +// Voting system +INT16 votelevels[4]; // Levels that were rolled by the host +INT8 votes[MAXPLAYERS]; // Each player's vote +INT8 pickedvote; // What vote the host rolls + +// Client-sided variables (NEVER use in anything that needs to be synced with other players) +boolean legitimateexit; // Did this client actually finish the match? +boolean comebackshowninfo; // Have you already seen the "ATTACK OR PROTECT" message? +tic_t curlap; // Current lap time +tic_t bestlap; // Best lap time +static INT16 randmapbuffer[NUMMAPS]; // Buffer for maps RandMap is allowed to roll tic_t hidetime; @@ -406,18 +416,6 @@ consvar_t cv_useranalog3 = {"useranalog3", "Off", CV_SAVE|CV_CALL, CV_OnOff, Use consvar_t cv_useranalog4 = {"useranalog4", "Off", CV_SAVE|CV_CALL, CV_OnOff, UserAnalog4_OnChange, 0, NULL, NULL, 0, 0, NULL}; #endif -typedef enum -{ - AXISNONE = 0, - AXISTURN, - AXISMOVE, - AXISLOOK, - AXISSTRAFE, - AXISDEAD, //Axises that don't want deadzones - AXISFIRE, - AXISFIRENORMAL, -} axis_input_e; - #if defined (_WII) || defined (WMINPUT) consvar_t cv_turnaxis = {"joyaxis_turn", "LStick.X", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_moveaxis = {"joyaxis_move", "LStick.Y", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -1141,7 +1139,7 @@ static INT32 Joy4Axis(axis_input_e axissel) return retaxis; } -static boolean InputDown(INT32 gc, UINT8 p) +boolean InputDown(INT32 gc, UINT8 p) { switch (p) { @@ -1156,7 +1154,7 @@ static boolean InputDown(INT32 gc, UINT8 p) } } -static INT32 JoyAxis(axis_input_e axissel, UINT8 p) +INT32 JoyAxis(axis_input_e axissel, UINT8 p) { switch (p) { @@ -1751,6 +1749,8 @@ void G_DoLoadLevel(boolean resetplayer) if (gamestate == GS_INTERMISSION) Y_EndIntermission(); + if (gamestate == GS_VOTING) + Y_EndVote(); G_SetGamestate(GS_LEVEL); @@ -1857,7 +1857,7 @@ boolean G_Responder(event_t *ev) && (players[consoleplayer].pflags & PF_TAGIT) != (players[displayplayer].pflags & PF_TAGIT)) continue; } - else if (G_GametypeHasSpectators() && G_RingSlingerGametype()) + else if (G_GametypeHasSpectators() && G_BattleGametype()) { if (!players[consoleplayer].spectator) continue; @@ -1957,7 +1957,7 @@ boolean G_Responder(event_t *ev) else if (gamestate == GS_GAMEEND || gamestate == GS_EVALUATION || gamestate == GS_CREDITS) return true; - else if (gamestate == GS_INTERMISSION) + else if (gamestate == GS_INTERMISSION || gamestate == GS_VOTING) if (HU_Responder(ev)) return true; // chat ate the event @@ -2122,6 +2122,7 @@ void G_Ticker(boolean run) case ga_startcont: G_DoStartContinue(); break; case ga_continued: G_DoContinued(); break; case ga_worlddone: G_DoWorldDone(); break; + case ga_startvote: G_DoStartVote(); break; case ga_nothing: break; default: I_Error("gameaction = %d\n", gameaction); } @@ -2167,6 +2168,12 @@ void G_Ticker(boolean run) HU_Ticker(); break; + case GS_VOTING: + if (run) + Y_VoteTicker(); + HU_Ticker(); + break; + case GS_TIMEATTACK: break; @@ -2341,7 +2348,6 @@ void G_PlayerReborn(INT32 player) INT32 offroad; INT32 balloon; INT32 comebackpoints; - INT32 comebackshowninfo; score = players[player].score; lives = players[player].lives; @@ -2399,7 +2405,6 @@ void G_PlayerReborn(INT32 player) offroad = players[player].kartstuff[k_offroad]; balloon = players[player].kartstuff[k_balloon]; comebackpoints = players[player].kartstuff[k_comebackpoints]; - comebackshowninfo = players[player].kartstuff[k_comebackshowninfo]; p = &players[player]; memset(p, 0, sizeof (*p)); @@ -2459,7 +2464,6 @@ void G_PlayerReborn(INT32 player) p->kartstuff[k_balloon] = balloon; p->kartstuff[k_comebackpoints] = comebackpoints; - p->kartstuff[k_comebackshowninfo] = comebackshowninfo; p->kartstuff[k_comebacktimer] = comebacktime; // Don't do anything immediately @@ -2971,9 +2975,10 @@ void G_ExitLevel(void) // boolean G_IsSpecialStage(INT32 mapnum) { +#if 0 if (gametype == GT_COOP && modeattacking != ATTACKING_RECORD && mapnum >= sstage_start && mapnum <= sstage_end) return true; - +#endif return false; } @@ -2985,13 +2990,18 @@ boolean G_IsSpecialStage(INT32 mapnum) // boolean G_GametypeUsesLives(void) { - // Coop, Competitive + // SRB2kart NEEDS no lives +#if 0 + // Coop, Competitive if ((gametype == GT_COOP || gametype == GT_COMPETITION) && !modeattacking // No lives in Time Attack //&& !G_IsSpecialStage(gamemap) && !(maptol & TOL_NIGHTS)) // No lives in NiGHTS return true; return false; +#else + return false; +#endif } // @@ -3022,30 +3032,30 @@ boolean G_GametypeHasSpectators(void) } // -// G_RingSlingerGametype +// G_BattleGametype // -// Returns true if the current gametype supports firing rings. -// ANY gametype can be a ringslinger gametype, just flick a switch. +// Returns true in Battle gamemodes, previously was G_RingSlingerGametype. // -boolean G_RingSlingerGametype(void) +boolean G_BattleGametype(void) { - return ((gametype != GT_COOP && gametype != GT_COMPETITION && gametype != GT_RACE) || (cv_ringslinger.value)); + return (gametype == GT_MATCH); } // -// G_PlatformGametype +// G_RaceGametype // -// Returns true if a gametype is a more traditional platforming-type. +// Returns true in racing gamemodes, previously was G_PlatformGametype. // -boolean G_PlatformGametype(void) +boolean G_RaceGametype(void) { - return (gametype == GT_COOP || gametype == GT_RACE || gametype == GT_COMPETITION); + return (gametype == GT_RACE); //(gametype == GT_COOP || gametype == GT_RACE || gametype == GT_COMPETITION); } // // G_TagGametype // -// For Jazz's Tag/HnS modes that have a lot of special cases.. +// For Jazz's Tag/HnS modes that have a lot of special cases... +// SRB2Kart: do we actually want to add Kart tag later? :V // boolean G_TagGametype(void) { @@ -3074,6 +3084,24 @@ INT16 G_TOLFlag(INT32 pgametype) return INT16_MAX; } +static INT32 TOLMaps(INT16 tolflags) +{ + INT32 num = 0; + INT16 i; + + // Find all the maps that are ok and and put them in an array. + for (i = 0; i < NUMMAPS; i++) + { + if (!mapheaderinfo[i]) + continue; + + if ((mapheaderinfo[i]->typeoflevel & tolflags) == tolflags) + num++; + } + + return num; +} + /** Select a random map with the given typeoflevel flags. * If no map has those flags, this arbitrarily gives you map 1. * \param tolflags The typeoflevel flags to insist on. Other bits may @@ -3082,24 +3110,59 @@ INT16 G_TOLFlag(INT32 pgametype) * has those flags. * \author Graue */ -static INT16 RandMap(INT16 tolflags, INT16 pprevmap) +INT16 G_RandMap(INT16 tolflags, INT16 pprevmap, boolean dontadd, boolean ignorebuffer) { INT16 *okmaps = Z_Malloc(NUMMAPS * sizeof(INT16), PU_STATIC, NULL); INT32 numokmaps = 0; - INT16 ix; + INT16 ix, bufx; // Find all the maps that are ok and and put them in an array. for (ix = 0; ix < NUMMAPS; ix++) - if (mapheaderinfo[ix] && (mapheaderinfo[ix]->typeoflevel & tolflags) == tolflags - && ix != pprevmap // Don't pick the same map. - && (dedicated || !M_MapLocked(ix+1)) // Don't pick locked maps. - ) + { + boolean isokmap = true; + + if (!mapheaderinfo[ix]) + continue; + + if ((mapheaderinfo[ix]->typeoflevel & tolflags) != tolflags + || ix == pprevmap + || (M_MapLocked(ix+1) && !dedicated)) + isokmap = false; + + if (!ignorebuffer) + { + for (bufx = 0; bufx < NUMMAPS; bufx++) + { + if (randmapbuffer[bufx] == -1) // Rest of buffer SHOULD be empty + break; + if (ix == randmapbuffer[bufx]) + { + isokmap = false; + break; + } + } + } + + if (isokmap) okmaps[numokmaps++] = ix; + } if (numokmaps == 0) + { + if (!ignorebuffer) + return G_RandMap(tolflags, pprevmap, dontadd, true); // If there's no matches, (An incredibly silly function chain, buuut... :V) + ix = 0; // Sorry, none match. You get MAP01. + for (bufx = 0; bufx < NUMMAPS; bufx++) + randmapbuffer[bufx] = -1; // if we're having trouble finding a map we should probably clear it + } else + { ix = okmaps[M_RandomKey(numokmaps)]; + for (bufx = NUMMAPS; bufx > 0; bufx--) + randmapbuffer[bufx] = randmapbuffer[bufx-1]; + randmapbuffer[0] = ix; + } Z_Free(okmaps); @@ -3226,12 +3289,18 @@ static void G_DoCompleted(void) automapactive = false; + if (randmapbuffer[TOLMaps(G_TOLFlag(gametype))-4] != -1) // we're getting pretty full, so lets clear it + { + for (i = 0; i < NUMMAPS; i++) + randmapbuffer[i] = -1; + } + if (gametype != GT_COOP) { if (cv_advancemap.value == 0) // Stay on same map. nextmap = prevmap; else if (cv_advancemap.value == 2) // Go to random map. - nextmap = RandMap(G_TOLFlag(gametype), prevmap); + nextmap = G_RandMap(G_TOLFlag(gametype), prevmap, false, false); } // We are committed to this map now. @@ -3252,6 +3321,7 @@ static void G_DoCompleted(void) void G_AfterIntermission(void) { HU_ClearCEcho(); + //G_NextLevel(); if (mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking) // Start a custom cutscene. F_StartCustomCutscene(mapheaderinfo[gamemap-1]->cutscenenum-1, false, false); @@ -3272,7 +3342,11 @@ void G_AfterIntermission(void) // void G_NextLevel(void) { - gameaction = ga_worlddone; + if ((cv_advancemap.value == 3 && gamestate != GS_VOTING) + && !modeattacking && !skipstats && (multiplayer || netgame)) + gameaction = ga_startvote; + else + gameaction = ga_worlddone; } static void G_DoWorldDone(void) @@ -3290,6 +3364,16 @@ static void G_DoWorldDone(void) gameaction = ga_nothing; } +// +// G_DoStartVote +// +static void G_DoStartVote(void) +{ + if (server) + D_SetupVote(); + gameaction = ga_nothing; +} + // // G_UseContinue // @@ -3904,6 +3988,7 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean ultimatemode = false; legitimateexit = false; // SRB2Kart + comebackshowninfo = false; if (!demoplayback && !netgame) // Netgame sets random seed elsewhere, demo playback sets seed just before us! P_SetRandSeed(M_RandomizedSeed()); // Use a more "Random" random seed @@ -5878,6 +5963,8 @@ void G_StopDemo(void) if (gamestate == GS_INTERMISSION) Y_EndIntermission(); // cleanup + if (gamestate == GS_VOTING) + Y_EndVote(); G_SetGamestate(GS_NULL); wipegamestate = GS_NULL; diff --git a/src/g_game.h b/src/g_game.h index 832a8e0c..8ea9d8f6 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -62,6 +62,18 @@ extern consvar_t cv_sideaxis3,cv_turnaxis3,cv_moveaxis3,cv_lookaxis3,cv_fireaxis extern consvar_t cv_sideaxis4,cv_turnaxis4,cv_moveaxis4,cv_lookaxis4,cv_fireaxis4,cv_firenaxis4; extern consvar_t cv_ghost_besttime, cv_ghost_bestlap, cv_ghost_last, cv_ghost_guest, cv_ghost_staff; +typedef enum +{ + AXISNONE = 0, + AXISTURN, + AXISMOVE, + AXISLOOK, + AXISSTRAFE, + AXISDEAD, //Axises that don't want deadzones + AXISFIRE, + AXISFIRENORMAL, +} axis_input_e; + // mouseaiming (looking up/down with the mouse or keyboard) #define KB_LOOKSPEED (1<<25) #define MAXPLMOVE (50) @@ -80,6 +92,9 @@ ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n); INT16 G_ClipAimingPitch(INT32 *aiming); INT16 G_SoftwareClipAimingPitch(INT32 *aiming); +boolean InputDown(INT32 gc, UINT8 p); +INT32 JoyAxis(axis_input_e axissel, UINT8 p); + extern angle_t localangle, localangle2, localangle3, localangle4; extern INT32 localaiming, localaiming2, localaiming3, localaiming4; // should be an angle_t but signed extern boolean camspin, camspin2, camspin3, camspin4; // SRB2Kart @@ -166,8 +181,8 @@ boolean G_IsSpecialStage(INT32 mapnum); boolean G_GametypeUsesLives(void); boolean G_GametypeHasTeams(void); boolean G_GametypeHasSpectators(void); -boolean G_RingSlingerGametype(void); -boolean G_PlatformGametype(void); +boolean G_BattleGametype(void); +boolean G_RaceGametype(void); boolean G_TagGametype(void); void G_ExitLevel(void); void G_NextLevel(void); @@ -220,4 +235,6 @@ FUNCMATH INT32 G_TicsToMilliseconds(tic_t tics); // Don't split up TOL handling INT16 G_TOLFlag(INT32 pgametype); +INT16 G_RandMap(INT16 tolflags, INT16 pprevmap, boolean dontadd, boolean ignorebuffer); + #endif diff --git a/src/g_state.h b/src/g_state.h index 81548b7c..38ad4145 100644 --- a/src/g_state.h +++ b/src/g_state.h @@ -23,6 +23,7 @@ typedef enum // Fadable gamestates GS_LEVEL, // Playing, in a level. GS_INTERMISSION, // Gazing at the intermission screen. + GS_VOTING, // SRB2Kart: MP voting screen GS_CONTINUING, // continue screen GS_TITLESCREEN, // title screen @@ -47,6 +48,7 @@ typedef enum ga_worlddone, ga_startcont, ga_continued, + ga_startvote, } gameaction_t; extern gamestate_t gamestate; diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 7f7946af..3941e6c1 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -1186,7 +1186,8 @@ void HU_Drawer(void) if (!Playing() || gamestate == GS_INTERMISSION || gamestate == GS_CUTSCENE || gamestate == GS_CREDITS || gamestate == GS_EVALUATION - || gamestate == GS_GAMEEND) + || gamestate == GS_GAMEEND + || gamestate == GS_VOTING) // SRB2kart return; // draw multiplayer rankings diff --git a/src/k_kart.c b/src/k_kart.c index d7084ec2..c699ffa6 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -323,6 +323,7 @@ void K_RegisterKartStuff(void) CV_RegisterVar(&cv_kartcomeback); CV_RegisterVar(&cv_kartmirror); CV_RegisterVar(&cv_speedometer); + CV_RegisterVar(&cv_votetime); CV_RegisterVar(&cv_collideminimum); CV_RegisterVar(&cv_collidesoundnum); @@ -1080,8 +1081,8 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) else if (player->kartstuff[k_comebacktimer]) { player->kartstuff[k_comebacktimer]--; - if (player->kartstuff[k_balloon] <= 0 && player->kartstuff[k_comebacktimer] <= 0) - player->kartstuff[k_comebackshowninfo] = 1; + if (player == &players[consoleplayer] && player->kartstuff[k_balloon] <= 0 && player->kartstuff[k_comebacktimer] <= 0) + comebackshowninfo = true; // client has already seen the message } if (player->kartstuff[k_spinout] == 0 && player->kartstuff[k_spinouttimer] == 0 && player->powers[pw_flashing] == K_GetKartFlashing()) @@ -4469,7 +4470,7 @@ static void K_drawBattleFullscreen(void) ty += (BASEVIDHEIGHT/2); } - if (!stplyr->kartstuff[k_comebackshowninfo]) + if (!comebackshowninfo) V_DrawFixedPatch(x<actnum != 0) - V_DrawString(40, 28, V_YELLOWMAP, va("%s %d", mapheaderinfo[gamemap-1]->lvlttl, mapheaderinfo[gamemap-1]->actnum)); + if (mapheaderinfo[gamemap-1]->zonttl) + { + if (mapheaderinfo[gamemap-1]->actnum != 0) + V_DrawString(40, 28, V_YELLOWMAP, va("%s %s %d", mapheaderinfo[gamemap-1]->lvlttl, mapheaderinfo[gamemap-1]->zonttl, mapheaderinfo[gamemap-1]->actnum)); + else + V_DrawString(40, 28, V_YELLOWMAP, va("%s %s", mapheaderinfo[gamemap-1]->lvlttl, mapheaderinfo[gamemap-1]->zonttl)); + } else - V_DrawString(40, 28, V_YELLOWMAP, mapheaderinfo[gamemap-1]->lvlttl); + { + if (mapheaderinfo[gamemap-1]->actnum != 0) + V_DrawString(40, 28, V_YELLOWMAP, va("%s %d", mapheaderinfo[gamemap-1]->lvlttl, mapheaderinfo[gamemap-1]->actnum)); + else + V_DrawString(40, 28, V_YELLOWMAP, mapheaderinfo[gamemap-1]->lvlttl); + } // Set up the detail boxes. { @@ -6019,7 +6029,7 @@ static void M_ModeAttackEndGame(INT32 choice) (void)choice; G_CheckDemoStatus(); // Cancel recording - if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) + if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING) Command_ExitGame_f(); M_StartControlPanel(); diff --git a/src/p_enemy.c b/src/p_enemy.c index fd961993..fca6bf1c 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -760,7 +760,7 @@ static int P_RecycleCompare(const void *p1, const void *p2) player_t *player2 = &players[*(const UINT8 *)p2]; // Non-shooting gametypes - if (!G_PlatformGametype()) + if (!G_RaceGametype()) { // Invincibility. if (player1->powers[pw_invulnerability] > player2->powers[pw_invulnerability]) return -1; diff --git a/src/p_inter.c b/src/p_inter.c index ca7003ac..1a687d1f 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1567,7 +1567,7 @@ static void P_HitDeathMessages(player_t *player, mobj_t *inflictor, mobj_t *sour char targetname[MAXPLAYERNAME+4]; char sourcename[MAXPLAYERNAME+4]; - if (G_PlatformGametype()) + if (G_RaceGametype()) return; // Not in coop, etc. if (!player) @@ -1762,7 +1762,7 @@ void P_CheckTimeLimit(void) if (!(multiplayer || netgame)) return; - if (G_PlatformGametype()) + if (G_RaceGametype()) return; if (leveltime < timelimitintics) @@ -1875,7 +1875,7 @@ void P_CheckPointLimit(void) if (!(multiplayer || netgame)) return; - if (G_PlatformGametype()) + if (G_RaceGametype()) return; // pointlimit is nonzero, check if it's been reached by this player @@ -2692,7 +2692,7 @@ static inline boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj // In COOP/RACE/CHAOS, you can't hurt other players unless cv_friendlyfire is on // ...But in SRB2kart, you can! - //if (!cv_friendlyfire.value && (G_PlatformGametype())) + //if (!cv_friendlyfire.value && (G_RaceGametype())) // return false; // Tag handling @@ -3117,7 +3117,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) return false; // Invincible to fire objects - if (G_PlatformGametype() && source && source->player) + if (G_RaceGametype() && source && source->player) return false; // Don't get hurt by fire generated from friends. } diff --git a/src/p_map.c b/src/p_map.c index 8ec8d36d..8e8af278 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -1541,7 +1541,7 @@ static boolean PIT_CheckThing(mobj_t *thing) else if (thing->player->kartstuff[k_invincibilitytimer] && !tmthing->player->kartstuff[k_invincibilitytimer]) P_DamageMobj(tmthing, thing, thing, 1); - if (G_RingSlingerGametype() && (!G_GametypeHasTeams() || tmthing->player->ctfteam != thing->player->ctfteam)) + if (G_BattleGametype() && (!G_GametypeHasTeams() || tmthing->player->ctfteam != thing->player->ctfteam)) { if ((tmthing->player->powers[pw_invulnerability] || tmthing->player->powers[pw_super]) && !thing->player->powers[pw_super]) diff --git a/src/p_mobj.c b/src/p_mobj.c index 8cd50205..699c26be 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9515,10 +9515,7 @@ void P_SpawnPlayer(INT32 playernum) p->spectator = false; } else if (netgame && p->jointime < 1) - { - //p->spectator = true; - p->kartstuff[k_comebackshowninfo] = 0; - } + /*p->spectator = true*/; else if (multiplayer && !netgame) { // If you're in a team game and you don't have a team assigned yet... @@ -9973,7 +9970,7 @@ void P_SpawnMapThing(mapthing_t *mthing) return; } - if (!G_RingSlingerGametype() || !cv_specialrings.value) + if (!G_BattleGametype() || !cv_specialrings.value) if (P_WeaponOrPanel(i)) return; // Don't place weapons/panels in non-ringslinger modes @@ -10006,7 +10003,7 @@ void P_SpawnMapThing(mapthing_t *mthing) runemeraldmanager = true; } - if (!G_PlatformGametype()) // No enemies in match or CTF modes + if (!G_RaceGametype()) // No enemies in match or CTF modes if ((mobjinfo[i].flags & MF_ENEMY) || (mobjinfo[i].flags & MF_BOSS)) return; @@ -10061,7 +10058,7 @@ void P_SpawnMapThing(mapthing_t *mthing) } } - if (!G_PlatformGametype() && (i == MT_SIGN || i == MT_STARPOST)) + if (!G_RaceGametype() && (i == MT_SIGN || i == MT_STARPOST)) return; // Don't spawn exit signs or starposts in wrong game modes if (modeattacking) // Record Attack special stuff diff --git a/src/p_saveg.c b/src/p_saveg.c index 577d512d..eaa38813 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -3209,6 +3209,14 @@ static void P_NetArchiveMisc(void) WRITEUINT32(save_p, totalrings); WRITEINT16(save_p, lastmap); + for (i = 0; i < 4; i++) + WRITEINT16(save_p, votelevels[i]); + + for (i = 0; i < MAXPLAYERS; i++) + WRITESINT8(save_p, votes[i]); + + WRITESINT8(save_p, pickedvote); + WRITEUINT16(save_p, emeralds); WRITEUINT8(save_p, stagefailed); @@ -3292,6 +3300,14 @@ static inline boolean P_NetUnArchiveMisc(void) totalrings = READUINT32(save_p); lastmap = READINT16(save_p); + for (i = 0; i < 4; i++) + votelevels[i] = READINT16(save_p); + + for (i = 0; i < MAXPLAYERS; i++) + votes[i] = READSINT8(save_p); + + pickedvote = READSINT8(save_p); + emeralds = READUINT16(save_p); stagefailed = READUINT8(save_p); @@ -3385,6 +3401,8 @@ boolean P_LoadGame(INT16 mapoverride) { if (gamestate == GS_INTERMISSION) Y_EndIntermission(); + if (gamestate == GS_VOTING) + Y_EndVote(); G_SetGamestate(GS_NULL); // should be changed in P_UnArchiveMisc P_UnArchiveSPGame(mapoverride); diff --git a/src/p_setup.c b/src/p_setup.c index 371a70b7..cc707baf 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2810,7 +2810,7 @@ boolean P_SetupLevel(boolean skipprecip) // Start players with pity shields if possible players[i].pity = -1; - if (!G_PlatformGametype()) + if (!G_RaceGametype()) { players[i].mo = NULL; G_DoReborn(i); diff --git a/src/p_user.c b/src/p_user.c index 3849a6cd..86624bef 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -3311,7 +3311,7 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd) // SRB2kart - unused. P_SpawnPlayerMissile(player->mo, MT_FIREBALL, 0); S_StartSound(player->mo, sfx_mario7); } - else if (G_RingSlingerGametype() && (!G_TagGametype() || player->pflags & PF_TAGIT) + else if (G_BattleGametype() && (!G_TagGametype() || player->pflags & PF_TAGIT) && !player->weapondelay && !player->climbing && !(player->pflags & PF_ATTACKDOWN)) { @@ -7788,7 +7788,7 @@ void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius) if (mo->flags & MF_MONITOR) continue; // Monitors cannot be 'nuked'. - //if (!G_RingSlingerGametype() && mo->type == MT_PLAYER) + //if (!G_BattleGametype() && mo->type == MT_PLAYER) // continue; // Don't hurt players in Co-Op! if (abs(inflictor->x - mo->x) > radius || abs(inflictor->y - mo->y) > radius || abs(inflictor->z - mo->z) > radius) @@ -8015,7 +8015,7 @@ static void P_DeathThink(player_t *player) } // Force respawn if idle for more than 30 seconds in shooter modes. - if (player->deadtimer > 30*TICRATE && !G_PlatformGametype()) + if (player->deadtimer > 30*TICRATE && !G_RaceGametype()) player->playerstate = PST_REBORN; else if (player->lives > 0 && !G_IsSpecialStage(gamemap) && leveltime >= 140) // Don't allow "click to respawn" in special stages! { diff --git a/src/sounds.c b/src/sounds.c index a0cb10e8..5c029f89 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -521,7 +521,7 @@ sfxinfo_t S_sfx[NUMSFX] = {"kc36", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, {"kc37", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, {"kc38", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, - {"kc39", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, + {"kc39", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Voting beep {"kc3a", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, {"kc3b", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, {"kc3c", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, @@ -536,7 +536,7 @@ sfxinfo_t S_sfx[NUMSFX] = {"kc45", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, {"kc46", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, {"kc47", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, - {"kc48", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, + {"kc48", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Voting pick {"kc49", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, {"kc4a", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, {"kc4b", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, @@ -574,7 +574,7 @@ sfxinfo_t S_sfx[NUMSFX] = {"kc6c", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, {"kc6d", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, {"kc6e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, - + // SRB2kart - Skin sounds {"kwin", false, 64, 0, -1, NULL, 0, SKSWIN, -1, LUMPERROR}, {"klose", false, 64, 0, -1, NULL, 0, SKSLOSE, -1, LUMPERROR}, diff --git a/src/st_stuff.c b/src/st_stuff.c index 6a5bc796..7ddb50ef 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -177,7 +177,7 @@ hudinfo_t hudinfo[NUMHUDITEMS] = boolean ST_SameTeam(player_t *a, player_t *b) { // Just pipe team messages to everyone in co-op or race. - if (!G_RingSlingerGametype()) + if (!G_BattleGametype()) return true; // Spectator chat. @@ -1412,7 +1412,7 @@ static void ST_drawMatchHUD(void) // SRB2kart - unused. { INT32 offset = (BASEVIDWIDTH / 2) - (NUM_WEAPONS * 10); - if (!G_RingSlingerGametype()) + if (!G_BattleGametype()) return; if (G_TagGametype() && !(stplyr->pflags & PF_TAGIT)) @@ -1850,7 +1850,7 @@ static void ST_overlayDrawer(void) /* SRB2kart doesn't need this stuff, I think // If you are in overtime, put a big honkin' flashin' message on the screen. - if (G_RingSlingerGametype() && cv_overtime.value + if (G_BattleGametype() && cv_overtime.value && (leveltime > (timelimitintics + TICRATE/2)) && cv_timelimit.value && (leveltime/TICRATE % 2 == 0)) { if (splitscreen) @@ -1937,7 +1937,7 @@ static void ST_overlayDrawer(void) V_DrawCenteredString(BASEVIDWIDTH/2, STRINGY(116), 0, M_GetText("You cannot move while hiding.")); V_DrawCenteredString(BASEVIDWIDTH/2, STRINGY(132), 0, M_GetText("Press F12 to watch another player.")); } - /*else if (!G_PlatformGametype() && stplyr->playerstate == PST_DEAD && stplyr->lives) //Death overrides spectator text. + /*else if (!G_RaceGametype() && stplyr->playerstate == PST_DEAD && stplyr->lives) //Death overrides spectator text. { INT32 respawntime = cv_respawntime.value - stplyr->deadtimer/TICRATE; if (respawntime > 0 && !stplyr->spectator) @@ -1980,7 +1980,7 @@ void ST_Drawer(void) va("%s%s", G_GametypeHasTeams() ? ((seenplayer->ctfteam == 1) ? "\x85" : "\x84") : "", player_names[seenplayer-players])); else //if (cv_seenames.value == 3) V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 + 15, V_HUDTRANSHALF, - va("%s%s", !G_RingSlingerGametype() || (G_GametypeHasTeams() && players[consoleplayer].ctfteam == seenplayer->ctfteam) + va("%s%s", !G_BattleGametype() || (G_GametypeHasTeams() && players[consoleplayer].ctfteam == seenplayer->ctfteam) ? "\x83" : "\x85", player_names[seenplayer-players])); } #endif diff --git a/src/y_inter.c b/src/y_inter.c index 297f88a0..80a22bb8 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -36,6 +36,9 @@ #include "m_cond.h" // condition sets +#include "m_random.h" // P_RandomKey +#include "g_input.h" // PLAYER1INPUTDOWN + #ifdef HWRENDER #include "hardware/hw_main.h" #endif @@ -164,6 +167,34 @@ static void Y_CalculateMatchWinners(void); static void Y_FollowIntermission(void); static void Y_UnloadData(void); +// SRB2Kart: voting stuff + +typedef struct +{ + char str[40]; + patch_t *pic; +} y_votelvlinfo; + +typedef struct +{ + INT8 selection; + UINT8 delay; + UINT8 ranim; + UINT8 rtics; + UINT8 roffset; + UINT8 rsynctime; + UINT8 rendoff; +} y_voteclient; + +static y_votelvlinfo levelinfo[4]; +static y_voteclient voteclient; +static INT32 votetic; +static INT32 voteendtic = -1; +static patch_t *cursor = NULL; +static patch_t *randomlvl = NULL; + +static void Y_UnloadVoteData(void); + // Stuff copy+pasted from st_stuff.c static INT32 SCX(INT32 x) { @@ -2088,3 +2119,399 @@ static void Y_UnloadData(void) break; } } + +// SRB2Kart: Voting! + +// +// Y_VoteDrawer +// +// Draws the voting screen! +// +void Y_VoteDrawer(void) +{ + INT32 i, x, y = 0; + + if (rendermode == render_none) + return; + + if (votetic >= voteendtic && voteendtic != -1) + return; + + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + + if (widebgpatch && rendermode == render_soft && vid.width / vid.dupx > 320) + V_DrawScaledPatch(((vid.width/2) / vid.dupx) - (SHORT(widebgpatch->width)/2), + (vid.height / vid.dupy) - SHORT(widebgpatch->height), + V_SNAPTOTOP|V_SNAPTOLEFT, widebgpatch); + else + V_DrawScaledPatch(((vid.width/2) / vid.dupx) - (SHORT(bgpatch->width)/2), // Keep the width/height adjustments, for screens that are less wide than 320(?) + (vid.height / vid.dupy) - SHORT(bgpatch->height), + V_SNAPTOTOP|V_SNAPTOLEFT, bgpatch); + + y = 30; + for (i = 0; i < 4; i++) + { + char str[40]; + patch_t *pic; + + if (i == 3) + { + snprintf(str, sizeof str, "%.32s", "RANDOM"); + str[sizeof str - 1] = '\0'; + pic = randomlvl; + } + else + { + strcpy(str, levelinfo[i].str); + pic = levelinfo[i].pic; + } + + if (i == voteclient.selection) + { + if (votes[consoleplayer] == -1) + { + V_DrawScaledPatch(BASEVIDWIDTH-124, y+21, V_SNAPTORIGHT, cursor); + if (votetic % 4 > 1) + V_DrawFill(BASEVIDWIDTH-101, y-1, 82, 52, 120|V_SNAPTORIGHT); + else + V_DrawFill(BASEVIDWIDTH-101, y-1, 82, 52, 103|V_SNAPTORIGHT); + } + V_DrawSmallScaledPatch(BASEVIDWIDTH-100, y, V_SNAPTORIGHT, pic); + V_DrawRightAlignedThinString(BASEVIDWIDTH-20, 40+y, V_SNAPTORIGHT, str); + y += 55; + } + else + { + V_DrawTinyScaledPatch(BASEVIDWIDTH-60, y, V_SNAPTORIGHT, pic); + y += 30; + } + } + + x = 20; + y = 15; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (votes[i] != -1) + { + patch_t *pic; + + if (votes[i] == 3 && (i != pickedvote || voteendtic == -1)) + pic = randomlvl; + else + pic = levelinfo[votes[i]].pic; + + if (!timer && i == voteclient.ranim) + { + V_DrawScaledPatch(x-18, y+9, V_SNAPTOLEFT, cursor); + if (votetic % 4 > 1) + V_DrawFill(x-1, y-1, 42, 27, 120|V_SNAPTOLEFT); + else + V_DrawFill(x-1, y-1, 42, 27, 103|V_SNAPTOLEFT); + } + + V_DrawTinyScaledPatch(x, y, V_SNAPTOLEFT, pic); + + if (players[i].skincolor == 0) + V_DrawSmallScaledPatch(x+24, y+9, V_SNAPTOLEFT, faceprefix[players[i].skin]); + else + { + UINT8 *colormap = R_GetTranslationColormap(players[i].skin, players[i].skincolor, GTC_CACHE); + V_DrawSmallMappedPatch(x+24, y+9, V_SNAPTOLEFT, faceprefix[players[i].skin], colormap); + } + } + + if (splitscreen) // only 1p has a vote in splitscreen + break; + + y += 30; + + if (y > BASEVIDHEIGHT-38) + { + x += 100; + y = 15; + } + } + + //V_DrawScaledPatch(x, y, V_SNAPTOBOTTOM, pic); + + if (timer) + V_DrawCenteredString(BASEVIDWIDTH/2, 188, V_YELLOWMAP|V_SNAPTOBOTTOM, + va("Vote ends in %d seconds", timer/TICRATE)); +} + +// +// Y_VoteTicker +// +// Vote screen thinking :eggthinking: +// +void Y_VoteTicker(void) +{ + boolean pressed = false; + INT32 i; + + if (paused || P_AutoPause()) + return; + + votetic++; + + if (votetic == voteendtic) + { + Y_UnloadVoteData(); // Y_EndVote resets voteendtic too early apparently, causing the game to try to render patches that we just unloaded... + Y_FollowIntermission(); + return; + } + + for (i = 0; i < MAXPLAYERS; i++) // Correct votes as early as possible, before they're processed by the game at all + { + if (!playeringame[i] || players[i].spectator) + votes[i] = -1; + else if (pickedvote != -1 && votes[i] == -1 && !splitscreen) + votes[i] = 3; // Slow people get random + } + + if (server && votes[pickedvote] == -1) // Uh oh! The person who got picked left! Recalculate, quick! + D_PickVote(); + + if (!votetic) + S_ChangeMusicInternal("vote", true); + + if (timer) + timer--; + + if (voteclient.delay) + voteclient.delay--; + + if (pickedvote != -1) + { + timer = 0; + voteclient.rsynctime++; + + if (voteendtic == -1) + { + UINT8 tempvotes[MAXPLAYERS]; + UINT8 numvotes = 0; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (votes[i] == -1) + continue; + tempvotes[numvotes] = i; + numvotes++; + } + + voteclient.rtics--; + + if (voteclient.rtics <= 0) + { + voteclient.roffset++; + voteclient.rtics = min(TICRATE/2, (voteclient.roffset/2)+5); + S_StartSound(NULL, sfx_kc39); + } + + if (voteclient.rendoff == 0 || voteclient.roffset < voteclient.rendoff) + voteclient.ranim = tempvotes[((pickedvote + voteclient.roffset) % numvotes)]; + + if (voteclient.roffset >= 24) + { + if (voteclient.rendoff == 0) + { + if (tempvotes[((pickedvote + voteclient.roffset + 4) % numvotes)] == pickedvote + && voteclient.rsynctime % (29*TICRATE/20) == 0) // Song is 1.45 seconds long (sorry @ whoever wants to replace it in a music wad :V) + { + voteclient.rendoff = voteclient.roffset+4; + S_ChangeMusicInternal("voteeb", false); + } + } + else if (voteclient.roffset >= voteclient.rendoff) + { + voteendtic = votetic + (3*TICRATE); + S_StartSound(NULL, sfx_kc48); + } + } + } + else + voteclient.ranim = pickedvote; + } + else + { + if (votetic < 3*(NEWTICRATE/7)) // give it some time before letting you control it :V + return; + + if ((!playeringame[consoleplayer] || players[consoleplayer].spectator) && votes[consoleplayer] != -1) + D_ModifyClientVote(-1); + else if (pickedvote == -1 && votes[consoleplayer] == -1 && !voteclient.delay) + { + if (InputDown(gc_aimforward, 1) || JoyAxis(AXISMOVE, 1) < 0) + { + voteclient.selection--; + pressed = true; + } + if ((InputDown(gc_aimbackward, 1) || JoyAxis(AXISMOVE, 1) > 0) && !pressed) + { + voteclient.selection++; + pressed = true; + } + if (voteclient.selection < 0) + voteclient.selection = 3; + if (voteclient.selection > 3) + voteclient.selection = 0; + if (InputDown(gc_accelerate, 1) && !pressed) + { + D_ModifyClientVote(voteclient.selection); + pressed = true; + } + } + + if (pressed) + { + S_StartSound(NULL, sfx_s3k5b); + voteclient.delay = NEWTICRATE/7; + } + + if (server) + { + UINT8 numplayers = 0, numvotes = 0; + + if (splitscreen) + { + numplayers = 1; + if (votes[0] != -1) + numvotes = 1; + } + else + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + numplayers++; + if (votes[i] != -1) + numvotes++; + } + } + + if (numvotes >= numplayers) + timer = 0; + + if (timer == 0 && voteendtic == -1) + D_PickVote(); + } + } +} + +// +// Y_StartVote +// +// MK online style voting screen, appears after intermission +// +void Y_StartVote(void) +{ + INT32 i = 0; + + votetic = -1; + +#ifdef PARANOIA + if (voteendtic != -1) + I_Error("voteendtic is dirty"); +#endif + + widebgpatch = W_CachePatchName("INTERSCW", PU_STATIC); + bgpatch = W_CachePatchName("INTERSCR", PU_STATIC); + cursor = W_CachePatchName("M_CURSOR", PU_STATIC); + randomlvl = W_CachePatchName("RANDOMLV", PU_STATIC); + + timer = cv_votetime.value*TICRATE; + pickedvote = -1; + + voteclient.selection = 0; + voteclient.delay = 0; + voteclient.ranim = 0; + voteclient.rtics = 1; + voteclient.roffset = 0; + voteclient.rsynctime = 0; + voteclient.rendoff = 0; + + for (i = 0; i < MAXPLAYERS; i++) + votes[i] = -1; + + for (i = 0; i < 4; i++) + { + lumpnum_t lumpnum; + + // set up the str + if (mapheaderinfo[votelevels[i]]->zonttl) + { + if (mapheaderinfo[votelevels[i]]->actnum) + snprintf(levelinfo[i].str, + sizeof levelinfo[i].str, + "%.32s %.32s %d", + mapheaderinfo[votelevels[i]]->lvlttl, mapheaderinfo[votelevels[i]]->zonttl, mapheaderinfo[votelevels[i]]->actnum); + else + snprintf(levelinfo[i].str, + sizeof levelinfo[i].str, + "%.32s %.32s", + mapheaderinfo[votelevels[i]]->lvlttl, mapheaderinfo[votelevels[i]]->zonttl); + } + else + { + if (mapheaderinfo[votelevels[i]]->actnum) + snprintf(levelinfo[i].str, + sizeof levelinfo[i].str, + "%.32s %d", + mapheaderinfo[votelevels[i]]->lvlttl, mapheaderinfo[votelevels[i]]->actnum); + else + snprintf(levelinfo[i].str, + sizeof levelinfo[i].str, + "%.32s", + mapheaderinfo[votelevels[i]]->lvlttl); + } + + levelinfo[i].str[sizeof levelinfo[i].str - 1] = '\0'; + + lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(votelevels[i]+1))); + if (lumpnum != LUMPERROR) + levelinfo[i].pic = W_CachePatchName(va("%sP", G_BuildMapName(votelevels[i]+1)), PU_STATIC); + else + levelinfo[i].pic = W_CachePatchName("BLANKLVL", PU_STATIC); + } +} + +// +// Y_EndVote +// +void Y_EndVote(void) +{ + Y_UnloadVoteData(); + voteendtic = -1; +} + +// +// Y_UnloadVoteData +// +static void Y_UnloadVoteData(void) +{ + if (rendermode != render_soft) + return; + + UNLOAD(widebgpatch); + UNLOAD(bgpatch); + UNLOAD(cursor); + UNLOAD(randomlvl); + + UNLOAD(levelinfo[3].pic); + UNLOAD(levelinfo[2].pic); + UNLOAD(levelinfo[1].pic); + UNLOAD(levelinfo[0].pic); +} + +// +// Y_SetupVoteFinish +// +void Y_SetupVoteFinish(INT8 pick, INT8 level) +{ + pickedvote = pick; + nextmap = votelevels[level]; + timer = 0; + S_ChangeMusicInternal("voteea", true); +} \ No newline at end of file diff --git a/src/y_inter.h b/src/y_inter.h index 358debf7..6ba83583 100644 --- a/src/y_inter.h +++ b/src/y_inter.h @@ -17,6 +17,12 @@ void Y_StartIntermission(void); void Y_EndIntermission(void); void Y_EndGame(void); +void Y_VoteDrawer(void); +void Y_VoteTicker(void); +void Y_StartVote(void); +void Y_EndVote(void); +void Y_SetupVoteFinish(INT8 pick, INT8 level); + typedef enum { int_none,