mirror of
https://github.com/vgstation-coders/vgstation13.git
synced 2025-12-10 18:32:03 +00:00
Map voting
This commit is contained in:
@@ -12,6 +12,32 @@
|
||||
|
||||
return text
|
||||
|
||||
/proc/get_maps(root="maps/voting/")
|
||||
var/list/maps = list()
|
||||
var/recursion_limit = 20 //lots of maps waiting to be played, feels like TF2
|
||||
//Get our potential maps
|
||||
//testing("starting in [root]")
|
||||
for(var/potential in flist(root))
|
||||
if(copytext(potential,-1,0 != "/")) continue // Not a directory, ignore it.
|
||||
//testing("Inside [root + potential]")
|
||||
if(!recursion_limit) break
|
||||
//our current working directory
|
||||
var/path = root + potential
|
||||
//The DMB that has the map we want.
|
||||
var/binary
|
||||
//Looking for a binary
|
||||
for(var/binaries in flist(path))
|
||||
//testing("Checking file [binaries]")
|
||||
if(copytext(binaries,-4,0) == ".dmb")
|
||||
binary = binaries
|
||||
break
|
||||
if(!binary)
|
||||
warning("Map folder [path] does not contain a valid byond binary, skipping.")
|
||||
else
|
||||
maps[potential] = path + binary
|
||||
recursion_limit--
|
||||
return maps
|
||||
|
||||
//Sends resource files to client cache
|
||||
/client/proc/getFiles()
|
||||
for(var/file in args)
|
||||
|
||||
@@ -153,6 +153,8 @@
|
||||
var/emag_recharge_rate = 0
|
||||
var/emag_recharge_ticks = 0
|
||||
|
||||
var/map_voting = 0
|
||||
|
||||
/datum/configuration/New()
|
||||
. = ..()
|
||||
var/list/L = typesof(/datum/game_mode) - /datum/game_mode
|
||||
@@ -504,6 +506,8 @@
|
||||
media_secret_key = value
|
||||
if("vgws_base_url")
|
||||
vgws_base_url = value
|
||||
if("map_voting")
|
||||
map_voting = 1
|
||||
else
|
||||
diary << "Unknown setting in configuration: '[name]'"
|
||||
|
||||
|
||||
@@ -10,304 +10,321 @@ var/global/datum/controller/vote/vote = new()
|
||||
var/list/voted = list()
|
||||
var/list/voting = list()
|
||||
var/list/current_votes = list()
|
||||
var/list/ismapvote
|
||||
var/chosen_map
|
||||
|
||||
New()
|
||||
. = ..()
|
||||
/datum/controller/vote/New()
|
||||
. = ..()
|
||||
|
||||
if (vote != src)
|
||||
if (istype(vote))
|
||||
qdel(vote)
|
||||
if (vote != src)
|
||||
if (istype(vote))
|
||||
qdel(vote)
|
||||
|
||||
vote = src
|
||||
vote = src
|
||||
|
||||
proc/process() //called by master_controller
|
||||
if(mode)
|
||||
// No more change mode votes after the game has started.
|
||||
// 3 is GAME_STATE_PLAYING, but that #define is undefined for some reason
|
||||
if(mode == "gamemode" && ticker.current_state >= 2)
|
||||
world << "<b>Voting aborted due to game start.</b>"
|
||||
src.reset()
|
||||
return
|
||||
/datum/controller/vote/proc/process() //called by master_controller
|
||||
if(mode)
|
||||
// No more change mode votes after the game has started.
|
||||
// 3 is GAME_STATE_PLAYING, but that #define is undefined for some reason
|
||||
if(mode == "gamemode" && ticker.current_state >= 2)
|
||||
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)
|
||||
C << browse(null,"window=vote;can_close=0")
|
||||
reset()
|
||||
else
|
||||
for(var/client/C in voting)
|
||||
if(C)
|
||||
C << browse(vote.interface(C),"window=vote;can_close=0")
|
||||
|
||||
voting.Cut()
|
||||
|
||||
proc/reset()
|
||||
initiator = null
|
||||
time_remaining = 0
|
||||
mode = null
|
||||
question = null
|
||||
choices.Cut()
|
||||
voted.Cut()
|
||||
voting.Cut()
|
||||
current_votes.Cut()
|
||||
|
||||
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"])
|
||||
|
||||
|
||||
//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 = "<b>Vote Tied Between:</b>\n"
|
||||
for(var/option in winners)
|
||||
text += "\t[option]\n"
|
||||
. = pick(winners)
|
||||
text += "<b>Vote Result: [.]</b>"
|
||||
else
|
||||
text += "<b>Vote Result: Inconclusive - No Votes!</b>"
|
||||
log_vote(text)
|
||||
world << "<font color='purple'>[text]</font>"
|
||||
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(!going)
|
||||
going = 1
|
||||
world << "<font color='red'><b>The round will start soon.</b></font>"
|
||||
if("crew_transfer")
|
||||
if(. == "Initiate Crew Transfer")
|
||||
init_shift_change(null, 1)
|
||||
|
||||
|
||||
if(restart)
|
||||
world << "World restarting due to vote..."
|
||||
feedback_set_details("end_error","restart vote")
|
||||
if(blackbox) blackbox.save_all_data_to_sql()
|
||||
CallHook("Reboot",list())
|
||||
sleep(50)
|
||||
log_game("Rebooting due to restart vote")
|
||||
world.Reboot()
|
||||
|
||||
return .
|
||||
|
||||
proc/submit_vote(var/ckey, var/vote)
|
||||
if(mode)
|
||||
if(config.vote_no_dead && usr.stat == DEAD && !usr.client.holder)
|
||||
return 0
|
||||
if(current_votes[ckey])
|
||||
choices[choices[current_votes[ckey]]]--
|
||||
if(vote && 1<=vote && vote<=choices.len)
|
||||
voted += usr.ckey
|
||||
choices[choices[vote]]++ //check this
|
||||
current_votes[ckey] = vote
|
||||
return vote
|
||||
return 0
|
||||
|
||||
proc/initiate_vote(var/vote_type, var/initiator_key)
|
||||
if(!mode)
|
||||
if(started_time != null && !check_rights(R_ADMIN))
|
||||
var/next_allowed_time = (started_time + config.vote_delay)
|
||||
if(next_allowed_time > world.time)
|
||||
return 0
|
||||
// Calculate how much time is remaining by comparing current time, to time of vote start,
|
||||
// plus vote duration
|
||||
time_remaining = (ismapvote && ismapvote.len) ? (round((started_time + 600 - world.time)/10)) : (round((started_time + config.vote_period - world.time)/10))
|
||||
|
||||
if(time_remaining < 0)
|
||||
result()
|
||||
for(var/client/C in voting)
|
||||
if(C)
|
||||
C << browse(null,"window=vote;can_close=0")
|
||||
reset()
|
||||
switch(vote_type)
|
||||
if("restart")
|
||||
choices.Add("Restart Round","Continue Playing")
|
||||
if("gamemode")
|
||||
if(ticker.current_state >= 2)
|
||||
return 0
|
||||
choices.Add(config.votable_modes)
|
||||
if("crew_transfer")
|
||||
if(ticker.current_state <= 2)
|
||||
return 0
|
||||
question = "End the shift?"
|
||||
choices.Add("Initiate Crew Transfer", "Continue The Round")
|
||||
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_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 vote to place your votes.\nYou have [config.vote_period/10] seconds to vote.</font>"
|
||||
switch(vote_type)
|
||||
if("crew_transfer")
|
||||
world << sound('sound/voice/Serithi/Shuttlehere.ogg')
|
||||
if("gamemode")
|
||||
world << sound('sound/voice/Serithi/pretenddemoc.ogg')
|
||||
if("custom")
|
||||
world << sound('sound/voice/Serithi/weneedvote.ogg')
|
||||
if(mode == "gamemode" && going)
|
||||
going = 0
|
||||
world << "<font color='red'><b>Round start has been delayed.</b></font>"
|
||||
|
||||
time_remaining = round(config.vote_period/10)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
proc/interface(var/client/C)
|
||||
if(!C) return
|
||||
var/admin = 0
|
||||
var/trialmin = 0
|
||||
if(C.holder)
|
||||
admin = 1
|
||||
if(C.holder.rights & R_ADMIN)
|
||||
trialmin = 1
|
||||
voting |= C
|
||||
|
||||
. = "<html><head><title>Voting Panel</title></head><body>"
|
||||
if(mode)
|
||||
if(question) . += "<h2>Vote: '[question]'</h2>"
|
||||
else . += "<h2>Vote: [capitalize(mode)]</h2>"
|
||||
. += "Time Left: [time_remaining] s<hr><ul>"
|
||||
for(var/i = 1, i <= choices.len, i++)
|
||||
var/votes = choices[choices[i]]
|
||||
if(!votes) votes = 0
|
||||
if(current_votes[C.ckey] == i)
|
||||
. += "<li><b><a href='?src=\ref[src];vote=[i]'>[choices[i]]</a> ([votes] votes)</b></li>"
|
||||
else
|
||||
. += "<li><a href='?src=\ref[src];vote=[i]'>[choices[i]]</a> ([votes] votes)</li>"
|
||||
|
||||
. += "</ul><hr>"
|
||||
if(admin)
|
||||
. += "(<a href='?src=\ref[src];vote=cancel'>Cancel Vote</a>) "
|
||||
else
|
||||
. += "<h2>Start a vote:</h2><hr><ul><li>"
|
||||
//restart
|
||||
if(trialmin || config.allow_vote_restart)
|
||||
. += "<a href='?src=\ref[src];vote=restart'>Restart</a>"
|
||||
else
|
||||
. += "<font color='grey'>Restart (Disallowed)</font>"
|
||||
. += "</li><li>"
|
||||
if(trialmin || config.allow_vote_restart)
|
||||
. += "<a href='?src=\ref[src];vote=crew_transfer'>Crew Transfer</a>"
|
||||
else
|
||||
. += "<font color='grey'>Crew Transfer (Disallowed)</font>"
|
||||
if(trialmin)
|
||||
. += "\t(<a href='?src=\ref[src];vote=toggle_restart'>[config.allow_vote_restart?"Allowed":"Disallowed"]</a>)"
|
||||
. += "</li><li>"
|
||||
//gamemode
|
||||
if(trialmin || config.allow_vote_mode)
|
||||
. += "<a href='?src=\ref[src];vote=gamemode'>GameMode</a>"
|
||||
else
|
||||
. += "<font color='grey'>GameMode (Disallowed)</font>"
|
||||
if(trialmin)
|
||||
. += "\t(<a href='?src=\ref[src];vote=toggle_gamemode'>[config.allow_vote_mode?"Allowed":"Disallowed"]</a>)"
|
||||
for(var/client/C in voting)
|
||||
if(C)
|
||||
C << browse(vote.interface(C),"window=vote;can_close=0")
|
||||
|
||||
. += "</li>"
|
||||
//custom
|
||||
if(trialmin)
|
||||
. += "<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>"
|
||||
return .
|
||||
//voting.Cut()
|
||||
|
||||
/datum/controller/vote/proc/reset()
|
||||
initiator = null
|
||||
time_remaining = 0
|
||||
mode = null
|
||||
question = null
|
||||
choices.Cut()
|
||||
voted.Cut()
|
||||
voting.Cut()
|
||||
current_votes.Cut()
|
||||
|
||||
/datum/controller/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"])
|
||||
|
||||
|
||||
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.client
|
||||
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
|
||||
//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 .
|
||||
|
||||
/datum/controller/vote/proc/announce_result()
|
||||
var/list/winners = get_result()
|
||||
var/text
|
||||
if(winners.len > 0)
|
||||
if(winners.len > 1)
|
||||
text = "<b>Vote Tied Between:</b><br>"
|
||||
for(var/option in winners)
|
||||
text += "\t[option]<br>"
|
||||
. = pick(winners)
|
||||
text += "<b>Vote Result: [.]</b>"
|
||||
else
|
||||
text += "<b>Vote Result: Inconclusive - No Votes!</b>"
|
||||
log_vote(text)
|
||||
world << "<font color='purple'>[text]</font>"
|
||||
return .
|
||||
|
||||
/datum/controller/vote/proc/result()
|
||||
. = announce_result()
|
||||
var/restart = 0
|
||||
if(.)
|
||||
switch(mode)
|
||||
if("restart")
|
||||
if(config.allow_vote_restart || usr.client.holder)
|
||||
initiate_vote("restart",usr.key)
|
||||
if(. == "Restart Round")
|
||||
restart = 1
|
||||
if("gamemode")
|
||||
if(config.allow_vote_mode || usr.client.holder)
|
||||
initiate_vote("gamemode",usr.key)
|
||||
if(master_mode != .)
|
||||
world.save_mode(.)
|
||||
if(ticker && ticker.mode)
|
||||
restart = 1
|
||||
else
|
||||
master_mode = .
|
||||
if(!going)
|
||||
going = 1
|
||||
world << "<font color='red'><b>The round will start soon.</b></font>"
|
||||
if("crew_transfer")
|
||||
if(config.allow_vote_restart || usr.client.holder)
|
||||
initiate_vote("crew_transfer",usr.key)
|
||||
if(. == "Initiate Crew Transfer")
|
||||
init_shift_change(null, 1)
|
||||
if("map")
|
||||
if(.)
|
||||
chosen_map = ismapvote[.]
|
||||
//testing("Vote picked [chosen_map]")
|
||||
|
||||
|
||||
if(restart)
|
||||
world << "World restarting due to vote..."
|
||||
feedback_set_details("end_error","restart vote")
|
||||
if(blackbox) blackbox.save_all_data_to_sql()
|
||||
CallHook("Reboot",list())
|
||||
sleep(50)
|
||||
log_game("Rebooting due to restart vote")
|
||||
world.Reboot()
|
||||
|
||||
return .
|
||||
|
||||
/datum/controller/vote/proc/submit_vote(var/ckey, var/vote)
|
||||
if(mode)
|
||||
if(config.vote_no_dead && usr.stat == DEAD && !usr.client.holder)
|
||||
return 0
|
||||
if(current_votes[ckey])
|
||||
choices[choices[current_votes[ckey]]]--
|
||||
if(vote && 1<=vote && vote<=choices.len)
|
||||
voted += usr.ckey
|
||||
choices[choices[vote]]++ //check this
|
||||
current_votes[ckey] = vote
|
||||
return vote
|
||||
return 0
|
||||
|
||||
/datum/controller/vote/proc/initiate_vote(var/vote_type, var/initiator_key)
|
||||
if(!mode)
|
||||
if(started_time != null && !check_rights(R_ADMIN))
|
||||
var/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(ticker.current_state >= 2)
|
||||
return 0
|
||||
choices.Add(config.votable_modes)
|
||||
if("crew_transfer")
|
||||
if(ticker.current_state <= 2)
|
||||
return 0
|
||||
question = "End the shift?"
|
||||
choices.Add("Initiate Crew Transfer", "Continue The Round")
|
||||
if("custom")
|
||||
if(usr.client.holder)
|
||||
initiate_vote("custom",usr.key)
|
||||
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)
|
||||
if("map")
|
||||
question = "Rock The Vote Next Map!"
|
||||
var/list/maps = get_maps()
|
||||
for(var/key in maps)
|
||||
choices.Add(key)
|
||||
if(!choices.len)
|
||||
world << "<span class='danger'>Failed to initiate map vote, no maps found.</span>"
|
||||
return 0
|
||||
ismapvote = maps
|
||||
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 += "<br>[question]"
|
||||
|
||||
log_vote(text)
|
||||
world << "<font color='purple'><b>[text]</b><br>Type vote to place your votes.<br>You have [ismapvote && ismapvote.len ? "60" : config.vote_period/10] seconds to vote.</font>"
|
||||
switch(vote_type)
|
||||
if("crew_transfer")
|
||||
world << sound('sound/voice/Serithi/Shuttlehere.ogg')
|
||||
if("gamemode")
|
||||
world << sound('sound/voice/Serithi/pretenddemoc.ogg')
|
||||
if("custom")
|
||||
world << sound('sound/voice/Serithi/weneedvote.ogg')
|
||||
if("map")
|
||||
world << sound('sound/misc/rockthevote.ogg')
|
||||
if(mode == "gamemode" && going)
|
||||
going = 0
|
||||
world << "<font color='red'><b>Round start has been delayed.</b></font>"
|
||||
|
||||
time_remaining = (ismapvote && ismapvote.len ? 60 : round(config.vote_period/10))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/controller/vote/proc/interface(var/client/C)
|
||||
if(!C) return
|
||||
var/admin = 0
|
||||
var/trialmin = 0
|
||||
if(C.holder)
|
||||
admin = 1
|
||||
if(C.holder.rights & R_ADMIN)
|
||||
trialmin = 1
|
||||
voting |= C
|
||||
|
||||
. = "<html><head><title>Voting Panel</title></head><body>"
|
||||
if(mode)
|
||||
if(question) . += "<h2>Vote: '[question]'</h2>"
|
||||
else . += "<h2>Vote: [capitalize(mode)]</h2>"
|
||||
. += "Time Left: [time_remaining] s<hr><ul>"
|
||||
for(var/i = 1, i <= choices.len, i++)
|
||||
var/votes = choices[choices[i]]
|
||||
if(!votes) votes = 0
|
||||
if(current_votes[C.ckey] == i)
|
||||
. += "<li><b><a href='?src=\ref[src];vote=[i]'>[choices[i]]</a> ([votes] votes)</b></li>"
|
||||
else
|
||||
submit_vote(usr.ckey, round(text2num(href_list["vote"])))
|
||||
usr.vote()
|
||||
. += "<li><a href='?src=\ref[src];vote=[i]'>[choices[i]]</a> ([votes] votes)</li>"
|
||||
|
||||
. += "</ul><hr>"
|
||||
if(admin)
|
||||
. += "(<a href='?src=\ref[src];vote=cancel'>Cancel Vote</a>) "
|
||||
else
|
||||
. += "<h2>Start a vote:</h2><hr><ul><li>"
|
||||
//restart
|
||||
if(trialmin || config.allow_vote_restart)
|
||||
. += "<a href='?src=\ref[src];vote=restart'>Restart</a>"
|
||||
else
|
||||
. += "<font color='grey'>Restart (Disallowed)</font>"
|
||||
. += "</li><li>"
|
||||
if(trialmin || config.allow_vote_restart)
|
||||
. += "<a href='?src=\ref[src];vote=crew_transfer'>Crew Transfer</a>"
|
||||
else
|
||||
. += "<font color='grey'>Crew Transfer (Disallowed)</font>"
|
||||
if(trialmin)
|
||||
. += "\t(<a href='?src=\ref[src];vote=toggle_restart'>[config.allow_vote_restart?"Allowed":"Disallowed"]</a>)"
|
||||
. += "</li><li>"
|
||||
//gamemode
|
||||
if(trialmin || config.allow_vote_mode)
|
||||
. += "<a href='?src=\ref[src];vote=gamemode'>GameMode</a>"
|
||||
else
|
||||
. += "<font color='grey'>GameMode (Disallowed)</font>"
|
||||
if(trialmin)
|
||||
. += "\t(<a href='?src=\ref[src];vote=toggle_gamemode'>[config.allow_vote_mode?"Allowed":"Disallowed"]</a>)"
|
||||
|
||||
. += "</li>"
|
||||
//custom
|
||||
if(trialmin)
|
||||
. += "<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>"
|
||||
return .
|
||||
|
||||
|
||||
/datum/controller/vote/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.client
|
||||
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("crew_transfer")
|
||||
if(config.allow_vote_restart || usr.client.holder)
|
||||
initiate_vote("crew_transfer",usr.key)
|
||||
if("custom")
|
||||
if(usr.client.holder)
|
||||
initiate_vote("custom",usr.key)
|
||||
else
|
||||
submit_vote(usr.ckey, round(text2num(href_list["vote"])))
|
||||
usr.vote()
|
||||
|
||||
|
||||
/mob/verb/vote()
|
||||
|
||||
@@ -63,7 +63,7 @@ var/global/datum/controller/gameticker/ticker
|
||||
vote.process()
|
||||
watchdog.check_for_update()
|
||||
if(watchdog.waiting)
|
||||
world << "\blue Server update detected, restarting momentarily."
|
||||
world << "<span class='notice'>Server update detected, restarting momentarily.</span>"
|
||||
watchdog.signal_ready()
|
||||
return
|
||||
if(going)
|
||||
@@ -209,192 +209,194 @@ var/global/datum/controller/gameticker/ticker
|
||||
var/obj/screen/cinematic = null
|
||||
|
||||
//Plus it provides an easy way to make cinematics for other events. Just use this as a template :)
|
||||
proc/station_explosion_cinematic(var/station_missed=0, var/override = null)
|
||||
if( cinematic ) return //already a cinematic in progress!
|
||||
/datum/controller/gameticker/proc/station_explosion_cinematic(var/station_missed=0, var/override = null)
|
||||
if( cinematic ) return //already a cinematic in progress!
|
||||
|
||||
//initialise our cinematic screen object
|
||||
cinematic = new(src)
|
||||
cinematic.icon = 'icons/effects/station_explosion.dmi'
|
||||
cinematic.icon_state = "station_intact"
|
||||
cinematic.layer = 20
|
||||
cinematic.mouse_opacity = 0
|
||||
cinematic.screen_loc = "1,0"
|
||||
//initialise our cinematic screen object
|
||||
cinematic = new(src)
|
||||
cinematic.icon = 'icons/effects/station_explosion.dmi'
|
||||
cinematic.icon_state = "station_intact"
|
||||
cinematic.layer = 20
|
||||
cinematic.mouse_opacity = 0
|
||||
cinematic.screen_loc = "1,0"
|
||||
|
||||
var/obj/structure/stool/bed/temp_buckle = new(src)
|
||||
//Incredibly hackish. It creates a bed within the gameticker (lol) to stop mobs running around
|
||||
if(station_missed)
|
||||
for(var/mob/living/M in living_mob_list)
|
||||
M.buckled = temp_buckle //buckles the mob so it can't do anything
|
||||
if(M.client)
|
||||
M.client.screen += cinematic //show every client the cinematic
|
||||
else //nuke kills everyone on z-level 1 to prevent "hurr-durr I survived"
|
||||
for(var/mob/living/M in living_mob_list)
|
||||
M.buckled = temp_buckle
|
||||
if(M.client)
|
||||
M.client.screen += cinematic
|
||||
var/obj/structure/stool/bed/temp_buckle = new(src)
|
||||
//Incredibly hackish. It creates a bed within the gameticker (lol) to stop mobs running around
|
||||
if(station_missed)
|
||||
for(var/mob/living/M in living_mob_list)
|
||||
M.buckled = temp_buckle //buckles the mob so it can't do anything
|
||||
if(M.client)
|
||||
M.client.screen += cinematic //show every client the cinematic
|
||||
else //nuke kills everyone on z-level 1 to prevent "hurr-durr I survived"
|
||||
for(var/mob/living/M in living_mob_list)
|
||||
M.buckled = temp_buckle
|
||||
if(M.client)
|
||||
M.client.screen += cinematic
|
||||
|
||||
switch(M.z)
|
||||
if(0) //inside a crate or something
|
||||
var/turf/T = get_turf(M)
|
||||
if(T && T.z==1) //we don't use M.death(0) because it calls a for(/mob) loop and
|
||||
M.health = 0
|
||||
M.stat = DEAD
|
||||
if(1) //on a z-level 1 turf.
|
||||
switch(M.z)
|
||||
if(0) //inside a crate or something
|
||||
var/turf/T = get_turf(M)
|
||||
if(T && T.z==1) //we don't use M.death(0) because it calls a for(/mob) loop and
|
||||
M.health = 0
|
||||
M.stat = DEAD
|
||||
if(1) //on a z-level 1 turf.
|
||||
M.health = 0
|
||||
M.stat = DEAD
|
||||
|
||||
//Now animate the cinematic
|
||||
switch(station_missed)
|
||||
if(1) //nuke was nearby but (mostly) missed
|
||||
if( mode && !override )
|
||||
override = mode.name
|
||||
switch( override )
|
||||
if("nuclear emergency") //Nuke wasn't on station when it blew up
|
||||
flick("intro_nuke",cinematic)
|
||||
sleep(35)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
flick("station_intact_fade_red",cinematic)
|
||||
cinematic.icon_state = "summary_nukefail"
|
||||
else
|
||||
flick("intro_nuke",cinematic)
|
||||
sleep(35)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
//flick("end",cinematic)
|
||||
|
||||
|
||||
if(2) //nuke was nowhere nearby //TODO: a really distant explosion animation
|
||||
sleep(50)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
|
||||
|
||||
else //station was destroyed
|
||||
if( mode && !override )
|
||||
override = mode.name
|
||||
switch( override )
|
||||
if("nuclear emergency") //Nuke Ops successfully bombed the station
|
||||
flick("intro_nuke",cinematic)
|
||||
sleep(35)
|
||||
flick("station_explode_fade_red",cinematic)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
cinematic.icon_state = "summary_nukewin"
|
||||
if("AI malfunction") //Malf (screen,explosion,summary)
|
||||
flick("intro_malf",cinematic)
|
||||
sleep(76)
|
||||
flick("station_explode_fade_red",cinematic)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
cinematic.icon_state = "summary_malf"
|
||||
if("blob") //Station nuked (nuke,explosion,summary)
|
||||
flick("intro_nuke",cinematic)
|
||||
sleep(35)
|
||||
flick("station_explode_fade_red",cinematic)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
cinematic.icon_state = "summary_selfdes"
|
||||
else //Station nuked (nuke,explosion,summary)
|
||||
flick("intro_nuke",cinematic)
|
||||
sleep(35)
|
||||
flick("station_explode_fade_red", cinematic)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
cinematic.icon_state = "summary_selfdes"
|
||||
for(var/mob/living/M in living_mob_list)
|
||||
if(M.loc.z == 1)
|
||||
M.death()//No mercy
|
||||
//If its actually the end of the round, wait for it to end.
|
||||
//Otherwise if its a verb it will continue on afterwards.
|
||||
sleep(300)
|
||||
|
||||
if(cinematic) del(cinematic) //end the cinematic
|
||||
if(temp_buckle) del(temp_buckle) //release everybody
|
||||
return
|
||||
|
||||
|
||||
proc/create_characters()
|
||||
for(var/mob/new_player/player in player_list)
|
||||
if(player.ready && player.mind)
|
||||
if(player.mind.assigned_role=="AI")
|
||||
player.close_spawn_windows()
|
||||
player.AIize()
|
||||
else if(!player.mind.assigned_role)
|
||||
continue
|
||||
//Now animate the cinematic
|
||||
switch(station_missed)
|
||||
if(1) //nuke was nearby but (mostly) missed
|
||||
if( mode && !override )
|
||||
override = mode.name
|
||||
switch( override )
|
||||
if("nuclear emergency") //Nuke wasn't on station when it blew up
|
||||
flick("intro_nuke",cinematic)
|
||||
sleep(35)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
flick("station_intact_fade_red",cinematic)
|
||||
cinematic.icon_state = "summary_nukefail"
|
||||
else
|
||||
player.FuckUpGenes(player.create_character())
|
||||
del(player)
|
||||
flick("intro_nuke",cinematic)
|
||||
sleep(35)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
//flick("end",cinematic)
|
||||
|
||||
|
||||
proc/collect_minds()
|
||||
for(var/mob/living/player in player_list)
|
||||
if(player.mind)
|
||||
ticker.minds += player.mind
|
||||
if(2) //nuke was nowhere nearby //TODO: a really distant explosion animation
|
||||
sleep(50)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
|
||||
|
||||
proc/equip_characters()
|
||||
var/captainless=1
|
||||
for(var/mob/living/carbon/human/player in player_list)
|
||||
if(player && player.mind && player.mind.assigned_role)
|
||||
if(player.mind.assigned_role == "Captain")
|
||||
captainless=0
|
||||
if(player.mind.assigned_role != "MODE")
|
||||
job_master.EquipRank(player, player.mind.assigned_role, 0)
|
||||
EquipCustomItems(player)
|
||||
if(captainless)
|
||||
for(var/mob/M in player_list)
|
||||
if(!istype(M,/mob/new_player))
|
||||
M << "Captainship not forced on anyone."
|
||||
else //station was destroyed
|
||||
if( mode && !override )
|
||||
override = mode.name
|
||||
switch( override )
|
||||
if("nuclear emergency") //Nuke Ops successfully bombed the station
|
||||
flick("intro_nuke",cinematic)
|
||||
sleep(35)
|
||||
flick("station_explode_fade_red",cinematic)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
cinematic.icon_state = "summary_nukewin"
|
||||
if("AI malfunction") //Malf (screen,explosion,summary)
|
||||
flick("intro_malf",cinematic)
|
||||
sleep(76)
|
||||
flick("station_explode_fade_red",cinematic)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
cinematic.icon_state = "summary_malf"
|
||||
if("blob") //Station nuked (nuke,explosion,summary)
|
||||
flick("intro_nuke",cinematic)
|
||||
sleep(35)
|
||||
flick("station_explode_fade_red",cinematic)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
cinematic.icon_state = "summary_selfdes"
|
||||
else //Station nuked (nuke,explosion,summary)
|
||||
flick("intro_nuke",cinematic)
|
||||
sleep(35)
|
||||
flick("station_explode_fade_red", cinematic)
|
||||
world << sound('sound/effects/explosionfar.ogg')
|
||||
cinematic.icon_state = "summary_selfdes"
|
||||
for(var/mob/living/M in living_mob_list)
|
||||
if(M.loc.z == 1)
|
||||
M.death()//No mercy
|
||||
//If its actually the end of the round, wait for it to end.
|
||||
//Otherwise if its a verb it will continue on afterwards.
|
||||
sleep(300)
|
||||
|
||||
if(cinematic) del(cinematic) //end the cinematic
|
||||
if(temp_buckle) del(temp_buckle) //release everybody
|
||||
return
|
||||
|
||||
|
||||
proc/process()
|
||||
if(current_state != GAME_STATE_PLAYING)
|
||||
return 0
|
||||
/datum/controller/gameticker/proc/create_characters()
|
||||
for(var/mob/new_player/player in player_list)
|
||||
if(player.ready && player.mind)
|
||||
if(player.mind.assigned_role=="AI")
|
||||
player.close_spawn_windows()
|
||||
player.AIize()
|
||||
else if(!player.mind.assigned_role)
|
||||
continue
|
||||
else
|
||||
player.FuckUpGenes(player.create_character())
|
||||
del(player)
|
||||
|
||||
mode.process()
|
||||
|
||||
emergency_shuttle.process()
|
||||
watchdog.check_for_update()
|
||||
/datum/controller/gameticker/proc/collect_minds()
|
||||
for(var/mob/living/player in player_list)
|
||||
if(player.mind)
|
||||
ticker.minds += player.mind
|
||||
|
||||
var/force_round_end=0
|
||||
|
||||
// If server's empty, force round end.
|
||||
if(watchdog.waiting && player_list.len == 0)
|
||||
force_round_end=1
|
||||
/datum/controller/gameticker/proc/equip_characters()
|
||||
var/captainless=1
|
||||
for(var/mob/living/carbon/human/player in player_list)
|
||||
if(player && player.mind && player.mind.assigned_role)
|
||||
if(player.mind.assigned_role == "Captain")
|
||||
captainless=0
|
||||
if(player.mind.assigned_role != "MODE")
|
||||
job_master.EquipRank(player, player.mind.assigned_role, 0)
|
||||
EquipCustomItems(player)
|
||||
if(captainless)
|
||||
for(var/mob/M in player_list)
|
||||
if(!istype(M,/mob/new_player))
|
||||
M << "Captainship not forced on anyone."
|
||||
|
||||
var/mode_finished = mode.check_finished() || (emergency_shuttle.location == 2 && emergency_shuttle.alert == 1) || force_round_end
|
||||
if(!mode.explosion_in_progress && mode_finished)
|
||||
current_state = GAME_STATE_FINISHED
|
||||
|
||||
spawn
|
||||
declare_completion()
|
||||
/datum/controller/gameticker/proc/process()
|
||||
if(current_state != GAME_STATE_PLAYING)
|
||||
return 0
|
||||
|
||||
spawn(50)
|
||||
if (mode.station_was_nuked)
|
||||
feedback_set_details("end_proper","nuke")
|
||||
if(!delay_end && !watchdog.waiting)
|
||||
world << "\blue <B>Rebooting due to destruction of station in [restart_timeout/10] seconds</B>"
|
||||
mode.process()
|
||||
|
||||
emergency_shuttle.process()
|
||||
watchdog.check_for_update()
|
||||
|
||||
var/force_round_end=0
|
||||
|
||||
// If server's empty, force round end.
|
||||
if(watchdog.waiting && player_list.len == 0)
|
||||
force_round_end=1
|
||||
|
||||
var/mode_finished = mode.check_finished() || (emergency_shuttle.location == 2 && emergency_shuttle.alert == 1) || force_round_end
|
||||
if(!mode.explosion_in_progress && mode_finished)
|
||||
current_state = GAME_STATE_FINISHED
|
||||
|
||||
spawn
|
||||
declare_completion()
|
||||
if(config.map_voting)
|
||||
vote.initiate_vote("map","The Server")
|
||||
|
||||
spawn(50)
|
||||
if (mode.station_was_nuked)
|
||||
feedback_set_details("end_proper","nuke")
|
||||
if(!delay_end && !watchdog.waiting)
|
||||
world << "<span class='notice'><B>Rebooting due to destruction of station in [restart_timeout/10] seconds</B></span>"
|
||||
else
|
||||
feedback_set_details("end_proper","proper completion")
|
||||
if(!delay_end && !watchdog.waiting)
|
||||
world << "<span class='notice'><B>Restarting in [restart_timeout/10] seconds</B></span>"
|
||||
|
||||
if(blackbox)
|
||||
blackbox.save_all_data_to_sql()
|
||||
|
||||
if (watchdog.waiting)
|
||||
world << "<span class='notice'><B>Server will shut down for an automatic update in a few seconds.</B></span>"
|
||||
watchdog.signal_ready()
|
||||
else if(!delay_end)
|
||||
sleep(restart_timeout)
|
||||
if(!delay_end)
|
||||
CallHook("Reboot",list())
|
||||
world.Reboot()
|
||||
else
|
||||
feedback_set_details("end_proper","proper completion")
|
||||
if(!delay_end && !watchdog.waiting)
|
||||
world << "\blue <B>Restarting in [restart_timeout/10] seconds</B>"
|
||||
world << "<span class='notice'><B>An admin has delayed the round end</B></span>"
|
||||
else
|
||||
world << "<span class='notice'><B>An admin has delayed the round end</B></span>"
|
||||
|
||||
if(blackbox)
|
||||
blackbox.save_all_data_to_sql()
|
||||
return 1
|
||||
|
||||
if (watchdog.waiting)
|
||||
world << "\blue <B>Server will shut down for an automatic update in a few seconds.</B>"
|
||||
watchdog.signal_ready()
|
||||
else if(!delay_end)
|
||||
sleep(restart_timeout)
|
||||
if(!delay_end)
|
||||
CallHook("Reboot",list())
|
||||
world.Reboot()
|
||||
else
|
||||
world << "\blue <B>An admin has delayed the round end</B>"
|
||||
else
|
||||
world << "\blue <B>An admin has delayed the round end</B>"
|
||||
|
||||
return 1
|
||||
|
||||
proc/getfactionbyname(var/name)
|
||||
for(var/datum/faction/F in factions)
|
||||
if(F.name == name)
|
||||
return F
|
||||
/datum/controller/gameticker/proc/getfactionbyname(var/name)
|
||||
for(var/datum/faction/F in factions)
|
||||
if(F.name == name)
|
||||
return F
|
||||
|
||||
|
||||
/datum/controller/gameticker/proc/declare_completion()
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
cache_lifespan = 0 //stops player uploaded stuff from being kept in the rsc past the current session
|
||||
|
||||
#define RECOMMENDED_VERSION 501
|
||||
|
||||
|
||||
/world/New()
|
||||
// Honk honk, fuck you science
|
||||
populate_seed_list()
|
||||
@@ -178,6 +180,26 @@
|
||||
|
||||
|
||||
/world/Reboot(reason)
|
||||
if(config.map_voting)
|
||||
//testing("we have done a map vote")
|
||||
if(fexists(vote.chosen_map))
|
||||
//testing("[vote.chosen_map] exists")
|
||||
var/start = 1
|
||||
var/pos = findtext(vote.chosen_map, "/", start)
|
||||
var/lastpos = pos
|
||||
//testing("First slash [lastpos]")
|
||||
while(pos > 0)
|
||||
lastpos = pos
|
||||
pos = findtext(vote.chosen_map, "/", start)
|
||||
start = pos + 1
|
||||
//testing("Next slash [pos]")
|
||||
var/filename = copytext(vote.chosen_map, lastpos + 1, 0)
|
||||
//testing("Found [filename]")
|
||||
|
||||
if(!fcopy(vote.chosen_map, filename))
|
||||
//testing("Fcopy failed, deleting and copying")
|
||||
fdel(filename)
|
||||
fcopy(vote.chosen_map, filename)
|
||||
spawn(0)
|
||||
world << sound(pick('sound/AI/newroundsexy.ogg','sound/misc/apcdestroyed.ogg','sound/misc/bangindonk.ogg','sound/misc/slugmissioncomplete.ogg')) // random end sounds!! - LastyBatsy
|
||||
|
||||
@@ -200,7 +222,7 @@
|
||||
if(C.is_afk(INACTIVITY_KICK))
|
||||
if(!istype(C.mob, /mob/dead))
|
||||
log_access("AFK: [key_name(C)]")
|
||||
C << "\red You have been inactive for more than 10 minutes and have been disconnected."
|
||||
C << "<span class='warning'>You have been inactive for more than 10 minutes and have been disconnected.</span>"
|
||||
del(C)
|
||||
#undef INACTIVITY_KICK
|
||||
|
||||
|
||||
11
maps/voting/Readme.txt
Normal file
11
maps/voting/Readme.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
To utilize the map voter:
|
||||
|
||||
1. Uncomment or put MAP_VOTING in config.txt
|
||||
2. Compile the DMB's for each map you want to be voted on(requires a recompile on every update)
|
||||
3. Put each map's DMB in a folder designated to the map's name in maps/voting/ eg MetaStation for Meta Station and Box for Box Station
|
||||
4. At the natural end of a round the map will be voted upon.
|
||||
|
||||
TODO:
|
||||
|
||||
Maybe add a verb for admins to force another map using file browse.
|
||||
Setup a script to easily mass compile the maps (hint hint nexy)
|
||||
BIN
sound/misc/rockthevote.ogg
Normal file
BIN
sound/misc/rockthevote.ogg
Normal file
Binary file not shown.
Reference in New Issue
Block a user