diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm
index 4baa576d77..6e3d1e3e52 100644
--- a/code/controllers/configuration.dm
+++ b/code/controllers/configuration.dm
@@ -21,8 +21,8 @@
var/allow_admin_jump = 1 // allows admin jumping
var/allow_admin_spawning = 1 // allows admin item spawning
var/allow_admin_rev = 1 // allows admin revives
- var/vote_delay = 600 // minimum time between voting sessions (seconds, 10 minute default)
- var/vote_period = 60 // length of voting period (seconds, default 1 minute)
+ var/vote_delay = 6000 // minimum time between voting sessions (deciseconds, 10 minute default)
+ var/vote_period = 600 // length of voting period (deciseconds, default 1 minute)
var/vote_no_default = 0 // vote does not default to nochange/norestart (tbi)
var/vote_no_dead = 0 // dead people can't vote (tbi)
// var/enable_authentication = 0 // goon authentication
diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm
index a681dbaa83..0d5628b517 100644
--- a/code/controllers/master_controller.dm
+++ b/code/controllers/master_controller.dm
@@ -107,6 +107,8 @@ datum/controller/game_controller/proc/process()
var/start_time = world.timeofday
controller_iteration++
+ vote.process()
+
//AIR
timer = world.timeofday
last_thing_processed = air_master.type
diff --git a/code/controllers/voting.dm b/code/controllers/voting.dm
new file mode 100644
index 0000000000..cc870de1c3
--- /dev/null
+++ b/code/controllers/voting.dm
@@ -0,0 +1,233 @@
+var/datum/controller/vote/vote = new()
+
+datum/controller/vote
+ var/initiator = null
+ var/started_timeofday = null
+ var/time_remaining = 0
+ var/mode = null
+ var/question = null
+ var/list/choices = list()
+ var/list/voted = list()
+ var/list/voting = list()
+
+ New()
+ if(vote != src)
+ if(istype(vote))
+ del(vote)
+ vote = src
+
+ proc/process() //called by master_controller
+ if(mode)
+ time_remaining = started_timeofday + config.vote_period
+ if(world.timeofday < started_timeofday)
+ time_remaining -= 864000
+ time_remaining = round((time_remaining - world.timeofday)/10)
+
+ var/i=1
+ if(time_remaining < 0)
+ result()
+ while(i<=voting.len)
+ var/client/C = voting[i]
+ if(C)
+ C << browse(null,"window=vote;can_close=0")
+ i++
+ reset()
+ else
+ while(i<=voting.len)
+ var/client/C = voting[i]
+ if(C)
+ C << browse(vote.interface(C),"window=vote;can_close=0")
+ i++
+ else
+ voting.Cut(i,i+1)
+
+ proc/reset()
+ initiator = null
+ time_remaining = 0
+ mode = null
+ question = null
+ choices.Cut()
+ voted.Cut()
+ voting.Cut()
+
+ proc/get_result()
+ //get the highest number of votes
+ var/greatest_votes = 0
+ for(var/option in choices)
+ var/votes = choices[option]
+ if(votes > greatest_votes)
+ greatest_votes = votes
+ //get all options with that many votes and return them in a list
+ . = list()
+ if(greatest_votes)
+ for(var/option in choices)
+ if(choices[option] == greatest_votes)
+ . += option
+ return .
+
+ proc/announce_result()
+ var/list/winners = get_result()
+ var/text
+ if(winners.len > 0)
+ if(winners.len > 1)
+ text = "Vote Tied Between:\n"
+ for(var/option in winners)
+ text += "\t[option]\n"
+ . = pick(winners)
+ text += "Vote Result: [.]"
+ else
+ text += "Vote Result: Inconclusive - No Votes!"
+ log_vote(text)
+ world << "[text]"
+ return .
+
+ proc/result()
+ . = announce_result()
+ var/restart = 0
+ if(.)
+ switch(mode)
+ if("restart")
+ if(. == "Restart Round")
+ restart = 1
+ if("gamemode")
+ if(master_mode != .)
+ world.save_mode(.)
+ if(ticker && ticker.mode)
+ restart = 1
+ else
+ master_mode = .
+
+ if(restart)
+ world << "World restarting due to vote..."
+ feedback_set_details("end_error","restart vote")
+ if(blackbox) blackbox.save_all_data_to_sql()
+ sleep(50)
+ log_game("Rebooting due to restart vote")
+ world.Reboot()
+
+ return .
+
+ proc/submit_vote(var/vote)
+ if(mode)
+ if(config.vote_no_dead && usr.stat == DEAD && !usr.client.holder)
+ return 0
+ if(!(usr.ckey in voted))
+ if(vote && 1<=vote && vote<=choices.len)
+ voted += usr.ckey
+ choices[choices[vote]]++ //check this
+ return vote
+ return 0
+
+ proc/initiate_vote(var/vote_type, var/initiator_key)
+ if(!mode)
+ if(started_timeofday != null)
+ var/next_allowed_timeofday = (started_timeofday + config.vote_delay)
+ if(world.timeofday < started_timeofday)
+ next_allowed_timeofday -= 864000
+ if(next_allowed_timeofday > world.timeofday)
+ return 0
+
+ reset()
+ switch(vote_type)
+ if("restart") choices.Add("Restart Round","Continue Playing")
+ if("gamemode") choices.Add(config.votable_modes)
+ if("custom")
+ question = html_encode(input(usr,"What is the vote for?") as text|null)
+ if(!question) return 0
+ for(var/i=1,i<=10,i++)
+ var/option = capitalize(html_encode(input(usr,"Please enter an option or hit cancel to finish") as text|null))
+ if(!option || mode || !usr.client) break
+ choices.Add(option)
+ else return 0
+ mode = vote_type
+ initiator = initiator_key
+ started_timeofday = world.timeofday
+ var/text = "[capitalize(mode)] vote started by [initiator]."
+ log_vote(text)
+ world << "[text]\nType vote to place your votes.\nYou have [config.vote_period/10] seconds to vote."
+ time_remaining = round(config.vote_period/10)
+ return 1
+ return 0
+
+ proc/interface(var/client/C)
+ if(!C) return
+ var/admin = 0
+ if(C.holder)
+ admin = 1
+ voting |= C
+
+ . = "
Voting Panel"
+ if(mode)
+ if(question) . += "Vote: '[question]'
"
+ else . += "Vote: [capitalize(mode)]
"
+ . += "Time Left: [time_remaining] s
"
+ for(var/i=1,i<=choices.len,i++)
+ var/votes = choices[choices[i]]
+ if(!votes) votes = 0
+ . += "- [choices[i]] ([votes] votes)
"
+ . += "
"
+ if(admin)
+ . += "(Cancel Vote) "
+ else
+ . += "Start a vote:
"
+ . += "Close"
+ return .
+
+
+ Topic(href,href_list[],hsrc)
+ if(!usr || !usr.client) return //not necessary but meh...just in-case somebody does something stupid
+ switch(href_list["vote"])
+ if("close")
+ voting -= usr.ckey
+ usr << browse(null, "window=vote")
+ return
+ if("cancel")
+ if(usr.client.holder)
+ reset()
+ if("toggle_restart")
+ if(usr.client.holder)
+ config.allow_vote_restart = !config.allow_vote_restart
+ if("toggle_gamemode")
+ if(usr.client.holder)
+ config.allow_vote_mode = !config.allow_vote_mode
+ if("restart")
+ if(config.allow_vote_restart || usr.client.holder)
+ initiate_vote("restart",usr.key)
+ if("gamemode")
+ if(config.allow_vote_mode || usr.client.holder)
+ initiate_vote("gamemode",usr.key)
+ if("custom")
+ if(usr.client.holder)
+ initiate_vote("custom",usr.key)
+ else
+ submit_vote(round(text2num(href_list["vote"])))
+ usr.vote()
+
+
+/mob/verb/vote()
+ set category = "OOC"
+ set name = "Vote"
+
+ if(vote)
+ src << browse(vote.interface(client),"window=vote;can_close=0")
\ No newline at end of file
diff --git a/code/datums/vote.dm b/code/datums/vote.dm
deleted file mode 100644
index 2ed3e5f651..0000000000
--- a/code/datums/vote.dm
+++ /dev/null
@@ -1,353 +0,0 @@
-/datum/vote
- var/voting = 0 // true if currently voting
- var/nextvotetime = 0 // time at which next vote can be started
- var/votetime = 60 // time at which voting will end
- var/mode = 0 // 0 = restart vote, 1 = mode vote
- // modes which can be voted for
- var/winner = null // the vote winner
-
-/datum/vote/New()
-
- nextvotetime = world.timeofday // + 10*config.vote_delay
-
-
-/datum/vote/proc/canvote()//marker1
- var/excess = world.timeofday - vote.nextvotetime
-
- if(excess < -10000) // handle clock-wrapping problems - very long delay (>20 hrs) if wrapped
- vote.nextvotetime = world.timeofday
- return 1
- return (excess >= 0)
-
-/datum/vote/proc/nextwait()
- return timetext( round( (nextvotetime - world.timeofday)/10) )
-
-/datum/vote/proc/endwait()
- return timetext( round( (votetime - world.timeofday)/10) )
-
-/datum/vote/proc/timetext(var/interval)
- var/minutes = round(interval / 60)
- var/seconds = round(interval % 60)
-
- var/tmin = "[minutes>0?num2text(minutes)+"min":null]"
- var/tsec = "[seconds>0?num2text(seconds)+"sec":null]"
-
- if(tmin && tsec) // hack to skip inter-space if either field is blank
- return "[tmin] [tsec]"
- else
- if(!tmin && !tsec) // return '0sec' if 0 time left
- return "0sec"
- return "[tmin][tsec]"
-
-/datum/vote/proc/getvotes()
- var/list/L = list()
- for(var/mob/M in player_list)
- if(M.client && M.client.inactivity < 1200) // clients inactive for 2 minutes don't count
- L[M.client.vote] += 1
-
- return L
-
-
-/datum/vote/proc/endvote()
-
- if(!voting) // means that voting was aborted by an admin
- return
-
- world << "\red ***Voting has closed."
-
- log_vote("Voting closed, result was [winner]")
- voting = 0
- nextvotetime = world.timeofday + 10*config.vote_delay
-
- for(var/mob/M in player_list) // clear vote window from all clients
- if(M.client)
- M << browse(null, "window=vote")
- M.client.showvote = 0
-
- calcwin()
-
- if(mode)
- if(!ticker)
- if(!going)
- world << "The game will start soon."
- going = 1
- var/wintext = capitalize(winner)
- if(winner=="default")
- world << "Result is \red No change."
- return
-
- // otherwise change mode
-
-
- world << "Result is change to \red [wintext]"
- world.save_mode(winner)
-
- if(ticker && ticker.mode)
- world <<"\red World will reboot in 10 seconds"
-
- feedback_set_details("end_error","mode vote - [winner]")
-
- if(blackbox)
- blackbox.save_all_data_to_sql()
-
- sleep(100)
- log_game("Rebooting due to mode vote")
- world.Reboot()
- else
- master_mode = winner
-
- else
-
- if(winner=="default")
- world << "Result is \red No restart."
- return
-
- world << "Result is \red Restart round."
-
- world <<"\red World will reboot in 5 seconds"
-
- feedback_set_details("end_error","restart vote")
-
- if(blackbox)
- blackbox.save_all_data_to_sql()
-
- sleep(50)
- log_game("Rebooting due to restart vote")
- world.Reboot()
- return
-
-
-/datum/vote/proc/calcwin()
-
- var/list/votes = getvotes()
-
- if(vote.mode)
- var/best = -1
-
- for(var/v in votes)
- if(v=="none")
- continue
- if(best < votes[v])
- best = votes[v]
-
-
- var/list/winners = list()
-
- for(var/v in votes)
- if(votes[v] == best)
- winners += v
-
- var/ret = ""
-
-
- for(var/w in winners)
- if(lentext(ret) > 0)
- ret += "/"
- if(w=="default")
- winners = list("default")
- ret = "No change"
- break
- else
- ret += capitalize(w)
-
-
-
- if(winners.len != 1)
- ret = "Tie: " + ret
-
-
- if(winners.len == 0)
- vote.winner = "default"
- ret = "No change"
- else
- vote.winner = pick(winners)
-
- return ret
- else
-
- if(votes["default"] < votes["restart"])
-
- vote.winner = "restart"
- return "Restart"
- else
- vote.winner = "default"
- return "No restart"
-
-
-/mob/verb/vote()
- set category = "OOC"
- set name = "Vote"
- usr.client.showvote = 1
-
-
- var/text = "Voting"
-
- var/footer = "
Close"
-
-
- if(config.vote_no_dead && usr.stat == 2)
- text += "Voting while dead has been disallowed."
- text += footer
- usr << browse(text, "window=vote")
- usr.client.showvote = 0
- usr.client.vote = "none"
- return
-
- if(vote.voting)
- // vote in progress, do the current
-
- text += "Vote to [vote.mode?"change mode":"restart round"] in progress.
"
- text += "[vote.endwait()] until voting is closed.
"
-
- var/list/votes = vote.getvotes()
-
- if(vote.mode) // true if changing mode
-
- text += "Current game mode is: [master_mode].
Select the mode to change to:"
-
- for(var/md in config.votable_modes)
- var/disp = capitalize(md)
- if(md=="default")
- disp = "No change"
-
- //world << "[md]|[disp]|[src.client.vote]|[votes[md]]"
-
- if(src.client.vote == md)
- text += "- [disp]"
- else
- text += "
- [disp]"
-
- text += "[votes[md]>0?" - [votes[md]] vote\s":null]
"
-
- text += "
"
-
- text +="Current winner: [vote.calcwin()]
"
-
- text += footer
-
- usr << browse(text, "window=vote")
-
- else // voting to restart
-
- text += "Restart the world?
"
-
- var/list/VL = list("default","restart")
-
- for(var/md in VL)
- var/disp = (md=="default"? "No":"Yes")
-
- if(src.client.vote == md)
- text += "- [disp]"
- else
- text += "
- [disp]"
-
- text += "[votes[md]>0?" - [votes[md]] vote\s":null]
"
-
- text += "
"
-
- text +="Current winner: [vote.calcwin()]
"
-
- text += footer
-
- usr << browse(text, "window=vote")
-
-
- else //no vote in progress
-
- if(shuttlecoming == 1)
- usr << "\blue Cannot start Vote - Shuttle has been called."
- return
-
- if(!config.allow_vote_restart && !config.allow_vote_mode)
- text += "
Player voting is disabled.