mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2025-12-26 01:52:15 +00:00
changes: Merged the voting controller into the voting subsystem. Changed the voting UI to use /datum/browser. Converted /datum/browser to use HTML5 & attempt to use edge if available. All users of the browser datum have been confirmed functional with Edge. Changed the ChemMaster to use SSassets for resource download.
469 lines
15 KiB
Plaintext
469 lines
15 KiB
Plaintext
var/datum/controller/subsystem/vote/SSvote
|
|
|
|
/datum/controller/subsystem/vote
|
|
name = "Voting"
|
|
wait = 1 SECOND
|
|
flags = SS_KEEP_TIMING | SS_FIRE_IN_LOBBY | SS_NO_TICK_CHECK
|
|
priority = SS_PRIORITY_VOTE
|
|
|
|
var/next_transfer_time
|
|
|
|
var/initiator = null
|
|
var/started_time = null
|
|
var/time_remaining = 0
|
|
var/mode = null
|
|
var/question = null
|
|
var/list/choices = list()
|
|
var/list/gamemode_names = list()
|
|
var/list/voted = list()
|
|
var/list/voting = list()
|
|
var/list/current_votes = list()
|
|
var/list/additional_text = list()
|
|
var/auto_muted = 0
|
|
var/last_transfer_vote = null
|
|
|
|
var/list/round_voters = list()
|
|
|
|
/datum/controller/subsystem/vote/New()
|
|
NEW_SS_GLOBAL(SSvote)
|
|
|
|
/datum/controller/subsystem/vote/Initialize(timeofday)
|
|
next_transfer_time = config.vote_autotransfer_initial
|
|
|
|
/datum/controller/subsystem/vote/fire(resumed = FALSE)
|
|
if (mode)
|
|
// No more change mode votes after the game has started.
|
|
if(mode == "gamemode" && ROUND_IS_STARTED)
|
|
world << "<b>Voting aborted due to game start.</b>"
|
|
src.reset()
|
|
return
|
|
|
|
// Calculate how much time is remaining by comparing current time, to time of vote start,
|
|
// plus vote duration
|
|
time_remaining = round((started_time + config.vote_period - world.time)/10)
|
|
|
|
if(time_remaining < 0)
|
|
result()
|
|
for(var/client/C in voting)
|
|
if(C)
|
|
if (C.vote_window)
|
|
C.vote_window.close()
|
|
else
|
|
log_debug("SSvote: Client [C] had no vote_window but was in voting list!")
|
|
|
|
reset()
|
|
else
|
|
for(var/client/C in voting)
|
|
if(C)
|
|
if (C.vote_window)
|
|
C.vote_window.open()
|
|
else
|
|
log_debug("SSvote: Client [C]'s vote_window was missing!")
|
|
setup_vote_window(C)
|
|
C.vote_window.open()
|
|
|
|
voting.Cut()
|
|
|
|
if (world.time >= next_transfer_time - 600)
|
|
autotransfer()
|
|
next_transfer_time += config.vote_autotransfer_interval
|
|
|
|
/datum/controller/subsystem/vote/proc/autotransfer()
|
|
initiate_vote("crew_transfer","the server", 1)
|
|
log_debug("The server has called a crew transfer vote")
|
|
|
|
/datum/controller/subsystem/vote/proc/autogamemode()
|
|
initiate_vote("gamemode","the server", 1)
|
|
log_debug("The server has called a gamemode vote")
|
|
|
|
/datum/controller/subsystem/vote/proc/reset()
|
|
initiator = null
|
|
time_remaining = 0
|
|
mode = null
|
|
question = null
|
|
choices.Cut()
|
|
voted.Cut()
|
|
voting.Cut()
|
|
current_votes.Cut()
|
|
additional_text.Cut()
|
|
|
|
/datum/controller/subsystem/vote/proc/get_result()
|
|
//get the highest number of votes
|
|
var/greatest_votes = 0
|
|
var/total_votes = 0
|
|
for(var/option in choices)
|
|
var/votes = choices[option]
|
|
total_votes += votes
|
|
if(votes > greatest_votes)
|
|
greatest_votes = votes
|
|
//default-vote for everyone who didn't vote
|
|
if(!config.vote_no_default && choices.len)
|
|
var/non_voters = (clients.len - total_votes)
|
|
if(non_voters > 0)
|
|
if(mode == "restart")
|
|
choices["Continue Playing"] += non_voters
|
|
if(choices["Continue Playing"] >= greatest_votes)
|
|
greatest_votes = choices["Continue Playing"]
|
|
else if(mode == "gamemode")
|
|
if(master_mode in choices)
|
|
choices[master_mode] += non_voters
|
|
if(choices[master_mode] >= greatest_votes)
|
|
greatest_votes = choices[master_mode]
|
|
else if(mode == "crew_transfer")
|
|
var/factor = 0.5
|
|
switch(world.time / (10 * 60)) // minutes
|
|
if(0 to 60)
|
|
factor = 0.5
|
|
if(61 to 120)
|
|
factor = 0.8
|
|
if(121 to 240)
|
|
factor = 1
|
|
if(241 to 300)
|
|
factor = 1.2
|
|
else
|
|
factor = 1.4
|
|
choices["Initiate Crew Transfer"] = round(choices["Initiate Crew Transfer"] * factor)
|
|
world << "<font color='purple'>Crew Transfer Factor: [factor]</font>"
|
|
greatest_votes = max(choices["Initiate Crew Transfer"], choices["Continue The Round"])
|
|
|
|
if(mode == "crew_transfer")
|
|
if(round(world.time / 36000)+12 <= 14)
|
|
// Credit to Scopes @ oldcode.
|
|
world << "<font color='purple'><b>Majority voting rule in effect. 2/3rds majority needed to initiate transfer.</b></font>"
|
|
choices["Initiate Crew Transfer"] = round(choices["Initiate Crew Transfer"] - round(total_votes / 3))
|
|
greatest_votes = max(choices["Initiate Crew Transfer"], choices["Continue The Round"])
|
|
|
|
//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
|
|
|
|
/datum/controller/subsystem/vote/proc/announce_result()
|
|
var/list/winners = get_result()
|
|
var/text
|
|
if(winners.len > 0)
|
|
if(winners.len > 1)
|
|
if(mode != "gamemode" || SSticker.hide_mode == 0) // Here we are making sure we don't announce potential game modes
|
|
text = "<b>Vote Tied Between:</b>\n"
|
|
for(var/option in winners)
|
|
text += "\t[option]\n"
|
|
. = pick(winners)
|
|
|
|
for(var/key in current_votes)
|
|
if(choices[current_votes[key]] == .)
|
|
round_voters += key // Keep track of who voted for the winning round.
|
|
if((mode == "gamemode" && . == "Extended") || SSticker.hide_mode == 0) // Announce Extended gamemode, but not other gamemodes
|
|
text += "<b>Vote Result: [.]</b>"
|
|
else
|
|
if(mode != "gamemode")
|
|
text += "<b>Vote Result: [.]</b>"
|
|
else
|
|
text += "<b>The vote has ended.</b>" // What will be shown if it is a gamemode vote that isn't extended
|
|
|
|
else
|
|
text += "<b>Vote Result: Inconclusive - No Votes!</b>"
|
|
if(mode == "add_antagonist")
|
|
antag_add_failed = 1
|
|
log_vote(text)
|
|
world << "<font color='purple'>[text]</font>"
|
|
|
|
/datum/controller/subsystem/vote/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(SSticker.mode)
|
|
restart = 1
|
|
else
|
|
master_mode = .
|
|
if("crew_transfer")
|
|
if(. == "Initiate Crew Transfer")
|
|
init_shift_change(null, 1)
|
|
last_transfer_vote = world.time
|
|
if("add_antagonist")
|
|
if(isnull(.) || . == "None")
|
|
antag_add_failed = 1
|
|
else
|
|
additional_antag_types |= antag_names_to_ids[.]
|
|
|
|
if(mode == "gamemode") //fire this even if the vote fails.
|
|
if(!round_progressing)
|
|
round_progressing = 1
|
|
world << "<font color='red'><b>The round will start soon.</b></font>"
|
|
|
|
if(restart)
|
|
world << "World restarting due to vote..."
|
|
feedback_set_details("end_error","restart vote")
|
|
sleep(50)
|
|
log_game("Rebooting due to restart vote")
|
|
world.Reboot()
|
|
|
|
/datum/controller/subsystem/vote/proc/submit_vote(ckey, vote)
|
|
if(mode)
|
|
if (mode == "crew_transfer")
|
|
if(config.vote_no_dead && usr && !usr.client.holder)
|
|
if (isnewplayer(usr))
|
|
usr << "<span class='warning'>You must be playing or have been playing to start a vote.</span>"
|
|
return 0
|
|
else if (isobserver(usr))
|
|
var/mob/dead/observer/O = usr
|
|
if (O.started_as_observer)
|
|
usr << "<span class='warning'>You must be playing or have been playing to start a vote.</span>"
|
|
return 0
|
|
if(vote && vote >= 1 && vote <= choices.len)
|
|
if(current_votes[ckey])
|
|
choices[choices[current_votes[ckey]]]--
|
|
voted += usr.ckey
|
|
choices[choices[vote]]++ //check this
|
|
current_votes[ckey] = vote
|
|
return vote
|
|
return 0
|
|
|
|
|
|
/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, automatic = FALSE)
|
|
if(!mode)
|
|
if(started_time != null && !(check_rights(R_ADMIN|R_MOD) || automatic))
|
|
// Transfer votes are their own little special snowflake
|
|
var/next_allowed_time = 0
|
|
if (vote_type == "crew_transfer")
|
|
if (config.vote_no_dead && !usr.client.holder)
|
|
if (isnewplayer(usr))
|
|
usr << "<span class='warning'>You must be playing or have been playing to start a vote.</span>"
|
|
return 0
|
|
else if (isobserver(usr))
|
|
var/mob/dead/observer/O = usr
|
|
if (O.started_as_observer)
|
|
usr << "<span class='warning'>You must be playing or have been playing to start a vote.</span>"
|
|
return 0
|
|
|
|
if (last_transfer_vote)
|
|
next_allowed_time = (last_transfer_vote + config.vote_delay)
|
|
else
|
|
next_allowed_time = config.transfer_timeout
|
|
else
|
|
next_allowed_time = (started_time + config.vote_delay)
|
|
|
|
if(next_allowed_time > world.time)
|
|
return 0
|
|
|
|
reset()
|
|
switch(vote_type)
|
|
if("restart")
|
|
choices.Add("Restart Round","Continue Playing")
|
|
if("gamemode")
|
|
if(SSticker.current_state >= 2)
|
|
return 0
|
|
choices.Add(config.votable_modes)
|
|
for (var/F in choices)
|
|
var/datum/game_mode/M = gamemode_cache[F]
|
|
if(!M)
|
|
continue
|
|
gamemode_names[M.config_tag] = capitalize(M.name) //It's ugly to put this here but it works
|
|
additional_text.Add("<td align = 'center'>[M.required_players]</td>")
|
|
gamemode_names["secret"] = "Secret"
|
|
if("crew_transfer")
|
|
if(check_rights(R_ADMIN|R_MOD, 0))
|
|
question = "End the shift?"
|
|
choices.Add("Initiate Crew Transfer", "Continue The Round")
|
|
else
|
|
if (get_security_level() == "red" || get_security_level() == "delta")
|
|
initiator_key << "The current alert status is too high to call for a crew transfer!"
|
|
return 0
|
|
if(SSticker.current_state <= 2)
|
|
return 0
|
|
initiator_key << "The crew transfer button has been disabled!"
|
|
question = "End the shift?"
|
|
choices.Add("Initiate Crew Transfer", "Continue The Round")
|
|
if("add_antagonist")
|
|
if(!config.allow_extra_antags || SSticker.current_state >= 2)
|
|
return 0
|
|
for(var/antag_type in all_antag_types)
|
|
var/datum/antagonist/antag = all_antag_types[antag_type]
|
|
if(!(antag.id in additional_antag_types) && antag.is_votable())
|
|
choices.Add(antag.role_text)
|
|
choices.Add("None")
|
|
if("custom")
|
|
question = sanitizeSafe(input(usr,"What is the vote for?") as text|null)
|
|
if(!question) return 0
|
|
for(var/i=1,i<=10,i++)
|
|
var/option = capitalize(sanitize(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_time = world.time
|
|
var/text = "[capitalize(mode)] vote started by [initiator]."
|
|
if(mode == "custom")
|
|
text += "\n[question]"
|
|
|
|
log_vote(text)
|
|
world << "<font color='purple'><b>[text]</b>\nType <b>vote</b> or click <a href='?src=\ref[src]'>here</a> to place your votes.\nYou have [config.vote_period/10] seconds to vote.</font>"
|
|
for(var/cc in clients)
|
|
var/client/C = cc
|
|
if(C.prefs.asfx_togs & ASFX_VOTE) //Personal mute
|
|
switch(vote_type)
|
|
if("crew_transfer")
|
|
C << sound('sound/effects/vote.ogg', repeat = 0, wait = 0, volume = 50, channel = 3)
|
|
if("gamemode")
|
|
C << sound('sound/ambience/alarm4.ogg', repeat = 0, wait = 0, volume = 50, channel = 3)
|
|
if("custom")
|
|
C << sound('sound/ambience/alarm4.ogg', repeat = 0, wait = 0, volume = 50, channel = 3)
|
|
if(mode == "gamemode" && round_progressing)
|
|
round_progressing = 0
|
|
world << "<font color='red'><b>Round start has been delayed.</b></font>"
|
|
|
|
time_remaining = round(config.vote_period/10)
|
|
return 1
|
|
return 0
|
|
|
|
/datum/controller/subsystem/vote/proc/interface(client/C)
|
|
if(!C)
|
|
return
|
|
var/is_staff = 0
|
|
if(C.holder)
|
|
if(C.holder.rights & (R_ADMIN|R_MOD))
|
|
is_staff = 1
|
|
voting |= C
|
|
|
|
. = ""
|
|
if(mode)
|
|
if(question) . += "<h2>Vote: '[question]'</h2>"
|
|
else . += "<h2>Vote: [capitalize(mode)]</h2>"
|
|
. += "Time Left: [time_remaining] s<hr>"
|
|
. += "<table width = '100%'><tr><td align = 'center'><b>Choices</b></td><td align = 'center'><b>Votes</b></td>"
|
|
if(capitalize(mode) == "Gamemode") .+= "<td align = 'center'><b>Minimum Players</b></td></tr>"
|
|
|
|
for(var/i = 1, i <= choices.len, i++)
|
|
var/votes = choices[choices[i]]
|
|
if(!votes) votes = 0
|
|
. += "<tr>"
|
|
if(mode == "gamemode")
|
|
if(current_votes[C.ckey] == i)
|
|
. += "<td><b><a href='?src=\ref[src];vote=[i]'>[gamemode_names[choices[i]]]</a></b></td><td align = 'center'>[votes]</td>"
|
|
else
|
|
. += "<td><a href='?src=\ref[src];vote=[i]'>[gamemode_names[choices[i]]]</a></td><td align = 'center'>[votes]</td>"
|
|
else
|
|
if(current_votes[C.ckey] == i)
|
|
. += "<td><b><a href='?src=\ref[src];vote=[i]'>[choices[i]]</a></b></td><td align = 'center'>[votes]</td>"
|
|
else
|
|
. += "<td><a href='?src=\ref[src];vote=[i]'>[choices[i]]</a></td><td align = 'center'>[votes]</td>"
|
|
if (additional_text.len >= i)
|
|
. += additional_text[i]
|
|
. += "</tr>"
|
|
|
|
. += "</table><hr>"
|
|
if(is_staff)
|
|
. += "(<a href='?src=\ref[src];vote=cancel'>Cancel Vote</a>) "
|
|
else
|
|
. += "<h2>Start a vote:</h2><hr><ul><li>"
|
|
//restart
|
|
if(is_staff || config.allow_vote_restart)
|
|
. += "<a href='?src=\ref[src];vote=restart'>Restart</a>"
|
|
else
|
|
. += "<font color='grey'>Restart (Disallowed)</font>"
|
|
. += "</li><li>"
|
|
if(is_staff || config.allow_vote_restart)
|
|
if (get_security_level() == "red" || get_security_level() == "delta")
|
|
. += "<a href='?src=\ref[src];vote=crew_transfer'>Crew Transfer (Disallowed, Code Red or above)</a>"
|
|
else
|
|
. += "<a href='?src=\ref[src];vote=crew_transfer'>Crew Transfer</a>"
|
|
else
|
|
. += "<font color='grey'>Crew Transfer (Disallowed)</font>"
|
|
if(is_staff)
|
|
. += "\t(<a href='?src=\ref[src];vote=toggle_restart'>[config.allow_vote_restart?"Allowed":"Disallowed"]</a>)"
|
|
. += "</li><li>"
|
|
//gamemode
|
|
if(is_staff || config.allow_vote_mode)
|
|
. += "<a href='?src=\ref[src];vote=gamemode'>GameMode</a>"
|
|
else
|
|
. += "<font color='grey'>GameMode (Disallowed)</font>"
|
|
if(is_staff)
|
|
. += "\t(<a href='?src=\ref[src];vote=toggle_gamemode'>[config.allow_vote_mode?"Allowed":"Disallowed"]</a>)"
|
|
. += "</li><li>"
|
|
//extra antagonists
|
|
if(!antag_add_failed && config.allow_extra_antags)
|
|
. += "<a href='?src=\ref[src];vote=add_antagonist'>Add Antagonist Type</a>"
|
|
else
|
|
. += "<font color='grey'>Restart (Disallowed)</font>"
|
|
. += "</li>"
|
|
//custom
|
|
if(is_staff)
|
|
. += "<li><a href='?src=\ref[src];vote=custom'>Custom</a></li>"
|
|
. += "</ul><hr>"
|
|
. += "<a href='?src=\ref[src];vote=close' style='position:absolute;right:50px'>Close</a></body></html>"
|
|
|
|
/datum/controller/subsystem/vote/Topic(href, list/href_list = 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.client
|
|
if (usr.client.vote_window)
|
|
usr.client.vote_window.close()
|
|
else
|
|
log_debug("SSvote: User [usr] had an invalid vote_window, but was the source of a Topic() call!")
|
|
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("crew_transfer")
|
|
if(config.allow_vote_restart || usr.client.holder)
|
|
initiate_vote("crew_transfer",usr.key)
|
|
if("add_antagonist")
|
|
if(config.allow_extra_antags)
|
|
initiate_vote("add_antagonist",usr.key)
|
|
if("custom")
|
|
if(usr.client.holder)
|
|
initiate_vote("custom",usr.key)
|
|
else
|
|
var/t = round(text2num(href_list["vote"]))
|
|
if(t) // It starts from 1, so there's no problem
|
|
submit_vote(usr.ckey, t)
|
|
usr.vote()
|
|
|
|
/datum/controller/subsystem/vote/proc/setup_vote_window(client/C)
|
|
if (!QDELETED(C.vote_window))
|
|
return
|
|
|
|
C.vote_window = new(C.mob, "vote", "Voting")
|
|
C.vote_window.add_head_content("<title>Voting panel</title>")
|
|
|
|
/mob/verb/vote()
|
|
set category = "OOC"
|
|
set name = "Vote"
|
|
|
|
if (!client.vote_window)
|
|
SSvote.setup_vote_window(client)
|
|
|
|
var/datum/browser/vote_window = client.vote_window
|
|
|
|
vote_window.set_user(src)
|
|
vote_window.set_content(SSvote.interface(client))
|
|
vote_window.open()
|
|
|
|
/client
|
|
var/tmp/datum/browser/vote_window
|