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()