//=============================================================================// // Map Vote Mutator v0.5 Copyright(C) 2005 Adam Mummery-Smith B.S.E. // //=============================================================================// class MapVoteGameReplicationInfo extends GameReplicationInfo; //=============================================================================// // Class: MapVoteGameReplicationInfo | Date: December 2005 // //-----------------------------------------------------------------------------// // Project: Map Vote Mutator | Author: Adam Mummery-Smith // //-----------------------------------------------------------------------------// // Description: Oddly enough, in XMP, voting is handled by the GRI. So in // // order to implement map voting, we extend this class to // // include the map voting functionality. // //-----------------------------------------------------------------------------// // Usage: N/A // //-----------------------------------------------------------------------------// // Note: // //=============================================================================// //----------------------------------------------------------------------------- // Declarations // The map being voted for var string PendingMap; var bool PendingMapDeferred; // Once a map is successfully voted as next map, it is stored here until the next rotation var string QueuedMap; // Map Vote per-player limitations var config int maxVotesPerMap, maxIdenticalVotesPerMap, initialVotePeriod, betweenVotesPeriod; // New message of the day var config string MOTD1String; var config Color MOTD1Colour; var config string MOTD2String; var config Color MOTD2Colour; var config string MOTD3String; var config Color MOTD3Colour; var config string MOTD4String; var config Color MOTD4Colour; var config string MOTD5String; var config Color MOTD5Colour; var config int MOTDDisplayTime; //----------------------------------------------------------------------------- replication { reliable if ( bNetDirty && (Role == ROLE_Authority) ) PendingMap, PendingMapDeferred, maxVotesPerMap, maxIdenticalVotesPerMap, initialVotePeriod, betweenVotesPeriod; reliable if ( bNetInitial && (Role==ROLE_Authority) ) MOTD1String, MOTD1Colour, MOTD2String, MOTD2Colour, MOTD3String, MOTD3Colour, MOTD4String, MOTD4Colour, MOTD5String, MOTD5Colour, MOTDDisplayTime; } //----------------------------------------------------------------------------- // Called by the client to start a map vote function int MapRequest(string MapName, PlayerReplicationInfo Requestor, optional bool defer) { // If the player is an admin, don't bother starting a poll, just immediately switch // or immediately queue the requested map. Over-rides all active map votes. if (Requestor.bAdmin) { if (defer) { QueuedMap = MapName; Log("MapVote: " $ Requestor.PlayerName $ " queued map " $ MapName $ " as next in rotation"); if (PendingMap != "") EndVote(); } else { Level.ServerTravel(MapName, false); if (PendingMap != "") EndVote(); } return 2; // Success } if (VoteTimeRemaining > 0) // only allow one poll at any given time. return 0; if (QueuedMap != "") // only allow votes if no map has been voted as next return 1; Log("MapVote: " $ Requestor.PlayerName $ " inititated a vote for map " $ MapName); Level.Game.Broadcast( Requestor.Owner, Requestor.PlayerName $ ColorCode(138) $ " inititated a vote for map " $ MapName ); // This is necessary to stop vote-kicks interfering with map-votes PendingKick = None; PendingMap = MapName; PendingMapDeferred = defer; PollOwner = Requestor; StartVote(); return 2; } //----------------------------------------------------------------------------- // We have to intercept this request to prevent vote-kicks that follow map votes from // making it look like we've triggered the map vote again. If the vote request succeeds, // we clear the PendingMap variable. function bool KickRequest( PlayerReplicationInfo P, PlayerReplicationInfo Requestor, optional bool bBan ) { local bool temp; temp = Super.KickRequest( P, Requestor, bBan ); if (temp) PendingMap = ""; return temp; } //----------------------------------------------------------------------------- // Ends the current vote... honest function EndVote() { Super.EndVote(); PendingKick = None; PendingMap = ""; PendingMapDeferred = false; } //----------------------------------------------------------------------------- // I know it's ugly, but I have just extended the UpdateVotes() function by overriding // it completely. Maybe in a later version I'll change it and add a call to Super.UpdateVotes() function UpdateVotes() { local int NumPlayers, NumTeamPlayers; local Controller C; local PlayerController PC; local int KickTeam; // Abort vote if time expired. if (VoteTimeRemaining <= 0) { if (PendingMap != "") Log("MapVote: Map vote timed out"); EndVote(); return; } // Existing kick/ban functionality if (PendingKick? && !PendingKick.bPendingDelete) { KickTeam = PendingKick.GetTeam(); for (C=Level.ControllerList; C?; C=C.NextController) { PC = PlayerController(C); if (PC?) { NumPlayers++; if (PC.GetTeam() == KickTeam) NumTeamPlayers++; } } // Maximize for games with few people, minimize for games with lots of people. NumVotesRequired = Max(Min((NumPlayers * KickPctTotal),(NumTeamPlayers * KickPctTeam)),2); if ((VoteTime - VoteTimeRemaining) >= MinVoteTime) { if (NumVotes >= NumVotesRequired) { if (bKickBan) Level.Game.KickBan(PendingKick); else Level.Game.Kick(PendingKick); EndVote(); } else if (-NumVotes >= NumVotesRequired) // cancel early if enough people vote no. { EndVote(); } } } else if (PendingMap?) { // New map vote functionality for (C=Level.ControllerList; C?; C=C.NextController) { PC = PlayerController(C); if (PC?) { NumPlayers++; } } NumVotesRequired = Max((NumPlayers * KickPctTotal), 1); if ((VoteTime - VoteTimeRemaining) >= MinVoteTime) { if (NumVotes >= NumVotesRequired) { if (PendingMapDeferred) { // If the vote succeeds and deferred is true, queue the map. QueuedMap = PendingMap; Log("MapVote: Map vote succeeded: " $ QueuedMap $ " is queued as the next map"); Level.Game.BroadcastLocalized(self, class'NotifyVoteSucceededMessage',,,, self); } else { // If the vote succeeds, change map immediately. Log("MapVote: Map vote succeeded: Switching to " $ QueuedMap); Level.ServerTravel(PendingMap, false); } EndVote(); } else if (-NumVotes >= NumVotesRequired) // cancel early if enough people vote no. { Log("MapVote: Map vote was not successful: ending vote"); EndVote(); } } } else // nothing left to vote on. { EndVote(); } } simulated function string GetSuccessString() { return "Vote succeeded: " $ PendingMap $ " is now queued as the next map"; } defaultproperties { maxVotesPerMap=5 maxIdenticalVotesPerMap=2 MOTD1String="This server is running the BSE Map Vote mutator ver 0.5" MOTD1Colour=(B=255,G=255,R=255) MOTD2String="www.teambse.co.uk" MOTD2Colour=(B=150,G=150,R=150) MOTD3String="" MOTD3Colour=(B=255,G=255,R=255) MOTD4String="Type MAPVOTE to start a map vote" MOTD4Colour=(G=255) MOTD5String="Use MAPVOTECFG for options" MOTD5Colour=(G=255) MOTDDisplayTime=18 initialVotePeriod=60 betweenVotesPeriod=30 }