diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm index 3e2cfc56a3a..5f2524a0b64 100644 --- a/code/controllers/subsystem/mapping.dm +++ b/code/controllers/subsystem/mapping.dm @@ -284,7 +284,7 @@ GLOBAL_LIST_EMPTY(the_station_areas) log_world("ERROR: Station areas list failed to generate!") /datum/controller/subsystem/mapping/proc/maprotate() - if(map_voted) + if(map_voted || SSmapping.next_map_config) //If voted or set by other means. return var/players = GLOB.clients.len @@ -307,9 +307,13 @@ GLOBAL_LIST_EMPTY(the_station_areas) for (var/map in mapvotes) if (!map) mapvotes.Remove(map) + continue if (!(map in global.config.maplist)) mapvotes.Remove(map) continue + if(map in SSpersistence.blocked_maps) + mapvotes.Remove(map) + continue var/datum/map_config/VM = global.config.maplist[map] if (!VM) mapvotes.Remove(map) @@ -336,6 +340,13 @@ GLOBAL_LIST_EMPTY(the_station_areas) if (. && VM.map_name != config.map_name) to_chat(world, "Map rotation has chosen [VM.map_name] for next round!") +/datum/controller/subsystem/mapping/proc/mapvote() + if(map_voted || SSmapping.next_map_config) //If voted or set by other means. + return + if(SSvote.mode) //Theres already a vote running, default to rotation. + maprotate() + SSvote.initiate_vote("map", "automatic map rotation") + /datum/controller/subsystem/mapping/proc/changemap(var/datum/map_config/VM) if(!VM.MakeNextMap()) next_map_config = load_map_config(default_to_box = TRUE) diff --git a/code/controllers/subsystem/persistence.dm b/code/controllers/subsystem/persistence.dm index 17554b8a513..5282ca9dc38 100644 --- a/code/controllers/subsystem/persistence.dm +++ b/code/controllers/subsystem/persistence.dm @@ -1,6 +1,8 @@ #define FILE_ANTAG_REP "data/AntagReputation.json" #define FILE_RECENT_MAPS "data/RecentMaps.json" +#define KEEP_ROUNDS_MAP 3 + SUBSYSTEM_DEF(persistence) name = "Persistence" init_order = INIT_ORDER_PERSISTENCE @@ -9,7 +11,8 @@ SUBSYSTEM_DEF(persistence) var/list/obj/structure/chisel_message/chisel_messages = list() var/list/saved_messages = list() var/list/saved_modes = list(1,2,3) - var/list/saved_maps = list(1,2) + var/list/saved_maps = list() + var/list/blocked_maps = list() var/list/saved_trophies = list() var/list/antag_rep = list() var/list/antag_rep_change = list() @@ -116,6 +119,18 @@ SUBSYSTEM_DEF(persistence) return saved_maps = json["data"] + //Convert the mapping data to a shared blocking list, saves us doing this in several places later. + for(var/map in config.maplist) + var/datum/map_config/VM = config.maplist[map] + var/run = 0 + if(VM.map_name == SSmapping.config.map_name) + run++ + for(var/name in SSpersistence.saved_maps) + if(VM.map_name == name) + run++ + if(run >= 2) //If run twice in the last KEEP_ROUNDS_MAP + 1 (including current) rounds, disable map for voting and rotation. + blocked_maps += VM.map_name + /datum/controller/subsystem/persistence/proc/LoadAntagReputation() var/json = file2text(FILE_ANTAG_REP) if(!json) @@ -284,7 +299,13 @@ SUBSYSTEM_DEF(persistence) WRITE_FILE(json_file, json_encode(file_data)) /datum/controller/subsystem/persistence/proc/CollectMaps() - saved_maps[2] = saved_maps[1] + if(length(saved_maps) > KEEP_ROUNDS_MAP) //Get rid of extras from old configs. + saved_maps.Cut(KEEP_ROUNDS_MAP+1) + var/mapstosave = min(length(saved_maps)+1, KEEP_ROUNDS_MAP) + if(length(saved_maps) < mapstosave) //Add extras if too short, one per round. + saved_maps += mapstosave + for(var/i = mapstosave; i > 1; i--) + saved_maps[i] = saved_maps[i-1] saved_maps[1] = SSmapping.config.map_name var/json_file = file(FILE_RECENT_MAPS) var/list/file_data = list() diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index da9bcbc84fd..d82831cac68 100755 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -43,8 +43,6 @@ SUBSYSTEM_DEF(ticker) var/queue_delay = 0 var/list/queued_players = list() //used for join queues when the server exceeds the hard population cap - var/maprotatechecked = 0 - var/news_report var/late_join_disabled @@ -156,10 +154,10 @@ SUBSYSTEM_DEF(ticker) //lobby stats for statpanels if(isnull(timeLeft)) timeLeft = max(0,start_at - world.time) - totalPlayers = LAZYLEN(GLOB.new_player_list) + totalPlayers = LAZYLEN(GLOB.new_player_list) totalPlayersReady = 0 - for(var/i in GLOB.new_player_list) - var/mob/dead/new_player/player = i + for(var/i in GLOB.new_player_list) + var/mob/dead/new_player/player = i if(player.ready == PLAYER_READY_TO_PLAY) ++totalPlayersReady @@ -192,13 +190,13 @@ SUBSYSTEM_DEF(ticker) if(GAME_STATE_PLAYING) mode.process(wait * 0.1) check_queue() - check_maprotate() if(!roundend_check_paused && mode.check_finished(force_ending) || force_ending) current_state = GAME_STATE_FINISHED toggle_ooc(TRUE) // Turn it on toggle_dooc(TRUE) declare_completion(force_ending) + check_maprotate() Master.SetRunLevel(RUNLEVEL_POSTGAME) @@ -343,8 +341,8 @@ SUBSYSTEM_DEF(ticker) explosion(epi, 0, 256, 512, 0, TRUE, TRUE, 0, TRUE) /datum/controller/subsystem/ticker/proc/create_characters() - for(var/i in GLOB.new_player_list) - var/mob/dead/new_player/player = i + for(var/i in GLOB.new_player_list) + var/mob/dead/new_player/player = i if(player.ready == PLAYER_READY_TO_PLAY && player.mind) GLOB.joined_player_list += player.ckey player.create_character(FALSE) @@ -353,8 +351,8 @@ SUBSYSTEM_DEF(ticker) CHECK_TICK /datum/controller/subsystem/ticker/proc/collect_minds() - for(var/i in GLOB.new_player_list) - var/mob/dead/new_player/P = i + for(var/i in GLOB.new_player_list) + var/mob/dead/new_player/P = i if(P.new_character && P.new_character.mind) SSticker.minds += P.new_character.mind CHECK_TICK @@ -362,8 +360,8 @@ SUBSYSTEM_DEF(ticker) /datum/controller/subsystem/ticker/proc/equip_characters() var/captainless=1 - for(var/i in GLOB.new_player_list) - var/mob/dead/new_player/N = i + for(var/i in GLOB.new_player_list) + var/mob/dead/new_player/N = i var/mob/living/carbon/human/player = N.new_character if(istype(player) && player.mind && player.mind.assigned_role) if(player.mind.assigned_role == "Captain") @@ -374,16 +372,16 @@ SUBSYSTEM_DEF(ticker) SSquirks.AssignQuirks(N.new_character, N.client, TRUE) CHECK_TICK if(captainless) - for(var/i in GLOB.new_player_list) - var/mob/dead/new_player/N = i + for(var/i in GLOB.new_player_list) + var/mob/dead/new_player/N = i if(N.new_character) to_chat(N, "Captainship not forced on anyone.") CHECK_TICK /datum/controller/subsystem/ticker/proc/transfer_characters() var/list/livings = list() - for(var/i in GLOB.new_player_list) - var/mob/dead/new_player/player = i + for(var/i in GLOB.new_player_list) + var/mob/dead/new_player/player = i var/mob/living = player.transfer_character() if(living) qdel(player) @@ -449,17 +447,9 @@ SUBSYSTEM_DEF(ticker) queue_delay = 0 /datum/controller/subsystem/ticker/proc/check_maprotate() - if (!CONFIG_GET(flag/maprotation)) + if(!CONFIG_GET(flag/maprotation)) return - if (SSshuttle.emergency && SSshuttle.emergency.mode != SHUTTLE_ESCAPE || SSshuttle.canRecall()) - return - if (maprotatechecked) - return - - maprotatechecked = 1 - - //map rotate chance defaults to 75% of the length of the round (in minutes) - if (!prob((world.time/600)*CONFIG_GET(number/maprotatechancedelta))) + if(world.time - SSticker.round_start_time < 10 MINUTES) //Not forcing map rotation for very short rounds. return INVOKE_ASYNC(SSmapping, /datum/controller/subsystem/mapping/.proc/maprotate) @@ -493,12 +483,10 @@ SUBSYSTEM_DEF(ticker) queue_delay = SSticker.queue_delay queued_players = SSticker.queued_players - maprotatechecked = SSticker.maprotatechecked round_start_time = SSticker.round_start_time queue_delay = SSticker.queue_delay queued_players = SSticker.queued_players - maprotatechecked = SSticker.maprotatechecked switch (current_state) if(GAME_STATE_SETTING_UP) @@ -569,8 +557,8 @@ SUBSYSTEM_DEF(ticker) //Everyone who wanted to be an observer gets made one now /datum/controller/subsystem/ticker/proc/create_observers() - for(var/i in GLOB.new_player_list) - var/mob/dead/new_player/player = i + for(var/i in GLOB.new_player_list) + var/mob/dead/new_player/player = i if(player.ready == PLAYER_READY_TO_OBSERVE && player.mind) //Break chain since this has a sleep input in it addtimer(CALLBACK(player, /mob/dead/new_player.proc/make_me_an_observer), 1) diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm index 732a7a6fd57..2942ef64ecd 100644 --- a/code/controllers/subsystem/vote.dm +++ b/code/controllers/subsystem/vote.dm @@ -74,14 +74,16 @@ SUBSYSTEM_DEF(vote) else if(mode == "map") for (var/non_voter_ckey in non_voters) var/client/C = non_voters[non_voter_ckey] - if(C.prefs.preferred_map && choices[C.prefs.preferred_map]) //No votes if the map isnt in the vote. - var/preferred_map = C.prefs.preferred_map - choices[preferred_map] += 1 - greatest_votes = max(greatest_votes, choices[preferred_map]) - else if(config.defaultmap && choices[config.defaultmap]) //No votes if the map isnt in the vote. - var/default_map = config.defaultmap.map_name - choices[default_map] += 1 - greatest_votes = max(greatest_votes, choices[default_map]) + if(C.prefs.preferred_map) + if(choices[C.prefs.preferred_map]) //No votes if the map isnt in the vote. + var/preferred_map = C.prefs.preferred_map + choices[preferred_map] += 1 + greatest_votes = max(greatest_votes, choices[preferred_map]) + else if(config.defaultmap) + if(choices[config.defaultmap]) //No votes if the map isnt in the vote. + var/default_map = config.defaultmap.map_name + choices[default_map] += 1 + greatest_votes = max(greatest_votes, choices[default_map]) //get all options with that many votes and return them in a list . = list() if(greatest_votes) @@ -163,6 +165,9 @@ SUBSYSTEM_DEF(vote) return FALSE /datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key) + if(!Master.current_runlevel) //Server is still intializing. + to_chat(usr, "Cannot start vote, server is not done initializing.") + return FALSE var/admin = FALSE var/ckey = ckey(initiator_key) if(GLOB.admin_datums[ckey]) @@ -192,13 +197,7 @@ SUBSYSTEM_DEF(vote) return FALSE for(var/map in config.maplist) var/datum/map_config/VM = config.maplist[map] - var/run = 0 - if(VM.map_name == SSmapping.config.map_name) - run++ - for(var/name in SSpersistence.saved_maps) - if(VM.map_name == name) - run++ - if(run >= 2 || !VM.votable) //If run twice in the last three (including current) as of time of writing, disable map for voting. + if(!VM.votable || (VM.map_name in SSpersistence.blocked_maps)) continue choices.Add(VM.map_name) if("custom") diff --git a/code/modules/admin/verbs/maprotation.dm b/code/modules/admin/verbs/maprotation.dm index afdf22da953..9e61755e4ae 100644 --- a/code/modules/admin/verbs/maprotation.dm +++ b/code/modules/admin/verbs/maprotation.dm @@ -6,7 +6,6 @@ return message_admins("[key_name_admin(usr)] is forcing a random map rotation.") log_admin("[key_name(usr)] is forcing a random map rotation.") - SSticker.maprotatechecked = 1 SSmapping.maprotate() /client/proc/adminchangemap() @@ -36,8 +35,7 @@ var/chosenmap = input("Choose a map to change to", "Change Map") as null|anything in sortList(maprotatechoices)|"Custom" if (!chosenmap) return - - SSticker.maprotatechecked = 1 + if(chosenmap == "Custom") message_admins("[key_name_admin(usr)] is changing the map to a custom map") log_admin("[key_name(usr)] is changing the map to a custom map") @@ -46,29 +44,29 @@ VM.map_name = input("Choose the name for the map", "Map Name") as null|text if(isnull(VM.map_name)) VM.map_name = "Custom" - + var/map_file = input("Pick file:", "Map File") as null|file if(isnull(map_file)) return - + if(copytext("[map_file]",-4) != ".dmm") to_chat(src, "Filename must end in '.dmm': [map_file]") return if(!fcopy(map_file, "_maps/custom/[map_file]")) return - + // This is to make sure the map works so the server does not start without a map. var/datum/parsed_map/M = new (map_file) if(!M) to_chat(src, "Map '[map_file]' failed to parse properly.") return - + if(!M.bounds) to_chat(src, "Map '[map_file]' has non-existant bounds.") qdel(M) return - + qdel(M) var/shuttles = alert("Do you want to modify the shuttles?", "Map Shuttles", "Yes", "No") diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm index 580bfaae2fa..c55c2282f0c 100644 --- a/code/modules/shuttle/emergency.dm +++ b/code/modules/shuttle/emergency.dm @@ -368,6 +368,7 @@ launch_status = ENDGAME_LAUNCHED setTimer(SSshuttle.emergencyEscapeTime * engine_coeff) priority_announce("The Emergency Shuttle has left the station. Estimate [timeLeft(600)] minutes until the shuttle docks at Central Command.", null, null, "Priority") + SSmapping.mapvote() //If no map vote has been run yet, start one. if(SHUTTLE_STRANDED) SSshuttle.checkHostileEnvironment()