|
|
|
|
@@ -1,3 +1,5 @@
|
|
|
|
|
#define VOTE_COOLDOWN 10
|
|
|
|
|
|
|
|
|
|
SUBSYSTEM_DEF(vote)
|
|
|
|
|
name = "Vote"
|
|
|
|
|
wait = 10
|
|
|
|
|
@@ -8,13 +10,17 @@ SUBSYSTEM_DEF(vote)
|
|
|
|
|
|
|
|
|
|
var/initiator = null
|
|
|
|
|
var/started_time = null
|
|
|
|
|
var/time_remaining = 0
|
|
|
|
|
var/end_time = 0
|
|
|
|
|
var/mode = null
|
|
|
|
|
var/vote_system = PLURALITY_VOTING
|
|
|
|
|
var/question = null
|
|
|
|
|
var/list/choices = list()
|
|
|
|
|
var/list/choice_descs = list() // optional descriptions
|
|
|
|
|
var/list/voted = list()
|
|
|
|
|
var/list/voting = list()
|
|
|
|
|
var/list/saved = list()
|
|
|
|
|
var/list/generated_actions = list()
|
|
|
|
|
var/next_pop = 0
|
|
|
|
|
|
|
|
|
|
var/obfuscated = FALSE//CIT CHANGE - adds obfuscated/admin-only votes
|
|
|
|
|
|
|
|
|
|
@@ -22,28 +28,30 @@ SUBSYSTEM_DEF(vote)
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/vote/fire() //called by master_controller
|
|
|
|
|
if(mode)
|
|
|
|
|
time_remaining = round((started_time + CONFIG_GET(number/vote_period) - world.time)/10)
|
|
|
|
|
|
|
|
|
|
if(time_remaining < 0)
|
|
|
|
|
if(end_time < world.time)
|
|
|
|
|
result()
|
|
|
|
|
SSpersistence.SaveSavedVotes()
|
|
|
|
|
for(var/client/C in voting)
|
|
|
|
|
C << browse(null, "window=vote;can_close=0")
|
|
|
|
|
reset()
|
|
|
|
|
else
|
|
|
|
|
else if(next_pop < world.time)
|
|
|
|
|
var/datum/browser/client_popup
|
|
|
|
|
for(var/client/C in voting)
|
|
|
|
|
client_popup = new(C, "vote", "Voting Panel")
|
|
|
|
|
client_popup = new(C, "vote", "Voting Panel", nwidth=600,nheight=700)
|
|
|
|
|
client_popup.set_window_options("can_close=0")
|
|
|
|
|
client_popup.set_content(interface(C))
|
|
|
|
|
client_popup.open(0)
|
|
|
|
|
next_pop = world.time+VOTE_COOLDOWN
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/vote/proc/reset()
|
|
|
|
|
initiator = null
|
|
|
|
|
time_remaining = 0
|
|
|
|
|
end_time = 0
|
|
|
|
|
mode = null
|
|
|
|
|
question = null
|
|
|
|
|
choices.Cut()
|
|
|
|
|
choice_descs.Cut()
|
|
|
|
|
voted.Cut()
|
|
|
|
|
voting.Cut()
|
|
|
|
|
obfuscated = FALSE //CIT CHANGE - obfuscated votes
|
|
|
|
|
@@ -84,17 +92,114 @@ SUBSYSTEM_DEF(vote)
|
|
|
|
|
. += option
|
|
|
|
|
return .
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/vote/proc/calculate_condorcet_votes(var/blackbox_text)
|
|
|
|
|
// https://en.wikipedia.org/wiki/Schulze_method#Implementation
|
|
|
|
|
var/list/d[][] = new/list(choices.len,choices.len) // the basic vote matrix, how many times a beats b
|
|
|
|
|
for(var/ckey in voted)
|
|
|
|
|
var/list/this_vote = voted[ckey]
|
|
|
|
|
for(var/a in 1 to choices.len)
|
|
|
|
|
for(var/b in a+1 to choices.len)
|
|
|
|
|
var/a_rank = this_vote.Find(a)
|
|
|
|
|
var/b_rank = this_vote.Find(b)
|
|
|
|
|
a_rank = a_rank ? a_rank : choices.len+1
|
|
|
|
|
b_rank = b_rank ? b_rank : choices.len+1
|
|
|
|
|
if(a_rank<b_rank)
|
|
|
|
|
d[a][b]++
|
|
|
|
|
else if(b_rank<a_rank)
|
|
|
|
|
d[b][a]++
|
|
|
|
|
//if equal, do nothing
|
|
|
|
|
var/list/p[][] = new/list(choices.len,choices.len) //matrix of shortest path from a to b
|
|
|
|
|
for(var/i in 1 to choices.len)
|
|
|
|
|
for(var/j in 1 to choices.len)
|
|
|
|
|
if(i != j)
|
|
|
|
|
var/pref_number = d[i][j]
|
|
|
|
|
var/opposite_pref = d[j][i]
|
|
|
|
|
if(pref_number>opposite_pref)
|
|
|
|
|
p[i][j] = d[i][j]
|
|
|
|
|
p[j][i] = 0
|
|
|
|
|
else
|
|
|
|
|
p[i][j] = 0
|
|
|
|
|
p[j][i] = d[i][j]
|
|
|
|
|
for(var/i in 1 to choices.len)
|
|
|
|
|
for(var/j in 1 to choices.len)
|
|
|
|
|
if(i != j)
|
|
|
|
|
for(var/k in 1 to choices.len) // YEAH O(n^3) !!
|
|
|
|
|
if(i != k && j != k)
|
|
|
|
|
p[j][k] = max(p[j][k],min(p[j][i], p[i][k]))
|
|
|
|
|
//one last pass, now that we've done the math
|
|
|
|
|
for(var/i in 1 to choices.len)
|
|
|
|
|
for(var/j in 1 to choices.len)
|
|
|
|
|
if(i != j)
|
|
|
|
|
SSblackbox.record_feedback("nested tally","voting",p[i][j],list(blackbox_text,"Shortest Paths",choices[i],choices[j]))
|
|
|
|
|
if(p[i][j] >= p[j][i])
|
|
|
|
|
choices[choices[i]]++ // higher shortest path = better candidate, so we add to choices here
|
|
|
|
|
// choices[choices[i]] is the schulze ranking, here, rather than raw vote numbers
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/vote/proc/calculate_majority_judgement_vote(var/blackbox_text)
|
|
|
|
|
// https://en.wikipedia.org/wiki/Majority_judgment
|
|
|
|
|
var/list/scores_by_choice = list()
|
|
|
|
|
for(var/choice in choices)
|
|
|
|
|
scores_by_choice[choice] = list()
|
|
|
|
|
for(var/ckey in voted)
|
|
|
|
|
var/list/this_vote = voted[ckey]
|
|
|
|
|
var/list/pretty_vote = list()
|
|
|
|
|
for(var/choice in this_vote)
|
|
|
|
|
sorted_insert(scores_by_choice[choice],this_vote[choice],/proc/cmp_numeric_asc)
|
|
|
|
|
// START BALLOT GATHERING
|
|
|
|
|
pretty_vote += choice
|
|
|
|
|
pretty_vote[choice] = GLOB.vote_score_options[this_vote[choice]]
|
|
|
|
|
SSblackbox.record_feedback("associative","voting_ballots",1,pretty_vote)
|
|
|
|
|
// END BALLOT GATHERING
|
|
|
|
|
for(var/score_name in scores_by_choice)
|
|
|
|
|
var/list/score = scores_by_choice[score_name]
|
|
|
|
|
for(var/indiv_score in score)
|
|
|
|
|
SSblackbox.record_feedback("nested tally","voting",1,list(blackbox_text,"Scores",score_name,GLOB.vote_score_options[indiv_score]))
|
|
|
|
|
if(score.len == 0)
|
|
|
|
|
scores_by_choice -= score_name
|
|
|
|
|
while(scores_by_choice.len > 1)
|
|
|
|
|
var/highest_median = 0
|
|
|
|
|
for(var/score_name in scores_by_choice) // first get highest median
|
|
|
|
|
var/list/score = scores_by_choice[score_name]
|
|
|
|
|
if(!score.len)
|
|
|
|
|
scores_by_choice -= score_name
|
|
|
|
|
continue
|
|
|
|
|
var/median = score[max(1,round(score.len/2))]
|
|
|
|
|
if(median >= highest_median)
|
|
|
|
|
highest_median = median
|
|
|
|
|
for(var/score_name in scores_by_choice) // then, remove
|
|
|
|
|
var/list/score = scores_by_choice[score_name]
|
|
|
|
|
var/median = score[max(1,round(score.len/2))]
|
|
|
|
|
if(median < highest_median)
|
|
|
|
|
scores_by_choice -= score_name
|
|
|
|
|
for(var/score_name in scores_by_choice) // after removals
|
|
|
|
|
var/list/score = scores_by_choice[score_name]
|
|
|
|
|
if(score.len == 0)
|
|
|
|
|
choices[score_name] += 100 // we're in a tie situation--just go with the first one
|
|
|
|
|
return
|
|
|
|
|
var/median_pos = max(1,round(score.len/2))
|
|
|
|
|
score.Cut(median_pos,median_pos+1)
|
|
|
|
|
choices[score_name]++
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/vote/proc/announce_result()
|
|
|
|
|
var/list/winners = get_result()
|
|
|
|
|
var/vote_title_text
|
|
|
|
|
var/text
|
|
|
|
|
if(question)
|
|
|
|
|
text += "<b>[question]</b>"
|
|
|
|
|
vote_title_text = "[question]"
|
|
|
|
|
else
|
|
|
|
|
text += "<b>[capitalize(mode)] Vote</b>"
|
|
|
|
|
vote_title_text = "[capitalize(mode)] Vote"
|
|
|
|
|
if(vote_system == RANKED_CHOICE_VOTING)
|
|
|
|
|
calculate_condorcet_votes(vote_title_text)
|
|
|
|
|
if(vote_system == SCORE_VOTING)
|
|
|
|
|
calculate_majority_judgement_vote(vote_title_text)
|
|
|
|
|
var/list/winners = get_result()
|
|
|
|
|
var/was_roundtype_vote = mode == "roundtype" || mode == "dynamic"
|
|
|
|
|
if(winners.len > 0)
|
|
|
|
|
if(question)
|
|
|
|
|
text += "<b>[question]</b>"
|
|
|
|
|
else
|
|
|
|
|
text += "<b>[capitalize(mode)] Vote</b>"
|
|
|
|
|
if(was_roundtype_vote)
|
|
|
|
|
stored_gamemode_votes = list()
|
|
|
|
|
if(!obfuscated && vote_system == RANKED_CHOICE_VOTING)
|
|
|
|
|
text += "\nIt should be noted that this is not a raw tally of votes (impossible in ranked choice) but the score determined by the schulze method of voting, so the numbers will look weird!"
|
|
|
|
|
for(var/i=1,i<=choices.len,i++)
|
|
|
|
|
var/votes = choices[choices[i]]
|
|
|
|
|
if(!votes)
|
|
|
|
|
@@ -116,17 +221,25 @@ SUBSYSTEM_DEF(vote)
|
|
|
|
|
log_vote(text)
|
|
|
|
|
remove_action_buttons()
|
|
|
|
|
to_chat(world, "\n<font color='purple'>[text]</font>")
|
|
|
|
|
switch(vote_system)
|
|
|
|
|
if(APPROVAL_VOTING,PLURALITY_VOTING)
|
|
|
|
|
for(var/i=1,i<=choices.len,i++)
|
|
|
|
|
SSblackbox.record_feedback("nested tally","voting",choices[choices[i]],list(vote_title_text,choices[i]))
|
|
|
|
|
if(RANKED_CHOICE_VOTING)
|
|
|
|
|
for(var/i=1,i<=voted.len,i++)
|
|
|
|
|
var/list/myvote = voted[voted[i]]
|
|
|
|
|
for(var/j=1,j<=myvote.len,j++)
|
|
|
|
|
SSblackbox.record_feedback("nested tally","voting",1,list(vote_title_text,"[j]\th",choices[myvote[j]]))
|
|
|
|
|
if(obfuscated) //CIT CHANGE - adds obfuscated votes. this messages admins with the vote's true results
|
|
|
|
|
var/admintext = "Obfuscated results"
|
|
|
|
|
if(vote_system == RANKED_CHOICE_VOTING)
|
|
|
|
|
admintext += "\nIt should be noted that this is not a raw tally of votes (impossible in ranked choice) but the score determined by the schulze method of voting, so the numbers will look weird!"
|
|
|
|
|
for(var/i=1,i<=choices.len,i++)
|
|
|
|
|
var/votes = choices[choices[i]]
|
|
|
|
|
admintext += "\n<b>[choices[i]]:</b> [votes]"
|
|
|
|
|
message_admins(admintext)
|
|
|
|
|
return .
|
|
|
|
|
|
|
|
|
|
#define PEACE "calm"
|
|
|
|
|
#define CHAOS "chaotic"
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/vote/proc/result()
|
|
|
|
|
. = announce_result()
|
|
|
|
|
var/restart = 0
|
|
|
|
|
@@ -152,33 +265,15 @@ SUBSYSTEM_DEF(vote)
|
|
|
|
|
if("dynamic")
|
|
|
|
|
if(SSticker.current_state > GAME_STATE_PREGAME)//Don't change the mode if the round already started.
|
|
|
|
|
return message_admins("A vote has tried to change the gamemode, but the game has already started. Aborting.")
|
|
|
|
|
GLOB.master_mode = "dynamic"
|
|
|
|
|
if("extended" in choices)
|
|
|
|
|
if(. == "extended")
|
|
|
|
|
GLOB.dynamic_forced_extended = TRUE // we still do the rest of the stuff
|
|
|
|
|
choices[PEACE] += choices["extended"]
|
|
|
|
|
var/mean = 0
|
|
|
|
|
var/voters = 0
|
|
|
|
|
for(var/client/c in GLOB.clients)
|
|
|
|
|
var/vote = c.prefs.preferred_chaos
|
|
|
|
|
if(vote)
|
|
|
|
|
voters += 1
|
|
|
|
|
switch(vote)
|
|
|
|
|
if(CHAOS_NONE)
|
|
|
|
|
mean -= 0.1
|
|
|
|
|
if(CHAOS_LOW)
|
|
|
|
|
mean -= 0.05
|
|
|
|
|
if(CHAOS_HIGH)
|
|
|
|
|
mean += 0.05
|
|
|
|
|
if(CHAOS_MAX)
|
|
|
|
|
mean += 0.1
|
|
|
|
|
mean/=voters
|
|
|
|
|
if(voted.len != 0)
|
|
|
|
|
mean += (choices[PEACE]*-1+choices[CHAOS])/voted.len
|
|
|
|
|
GLOB.dynamic_curve_centre = mean*20
|
|
|
|
|
GLOB.dynamic_curve_width = CLAMP(2-abs(mean*5),0.5,4)
|
|
|
|
|
to_chat(world,"<span class='boldannounce'>Dynamic curve centre set to [GLOB.dynamic_curve_centre] and width set to [GLOB.dynamic_curve_width].</span>")
|
|
|
|
|
log_admin("Dynamic curve centre set to [GLOB.dynamic_curve_centre] and width set to [GLOB.dynamic_curve_width]")
|
|
|
|
|
if(. == "Secret")
|
|
|
|
|
GLOB.master_mode = "secret"
|
|
|
|
|
SSticker.save_mode(.)
|
|
|
|
|
message_admins("The gamemode has been voted for, and has been changed to: [GLOB.master_mode]")
|
|
|
|
|
log_admin("Gamemode has been voted for and switched to: [GLOB.master_mode].")
|
|
|
|
|
else
|
|
|
|
|
GLOB.master_mode = "dynamic"
|
|
|
|
|
var/datum/dynamic_storyteller/S = config.pick_storyteller(.)
|
|
|
|
|
GLOB.dynamic_storyteller_type = S
|
|
|
|
|
if("map")
|
|
|
|
|
var/datum/map_config/VM = config.maplist[.]
|
|
|
|
|
message_admins("The map has been voted for and will change to: [VM.map_name]")
|
|
|
|
|
@@ -196,27 +291,58 @@ SUBSYSTEM_DEF(vote)
|
|
|
|
|
else
|
|
|
|
|
to_chat(world, "<span style='boldannounce'>Notice:Restart vote will not restart the server automatically because there are active admins on.</span>")
|
|
|
|
|
message_admins("A restart vote has passed, but there are active admins on with +server, so it has been canceled. If you wish, you may restart the server.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return .
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/vote/proc/submit_vote(vote)
|
|
|
|
|
/datum/controller/subsystem/vote/proc/submit_vote(vote, score = 0)
|
|
|
|
|
if(mode)
|
|
|
|
|
if(CONFIG_GET(flag/no_dead_vote) && usr.stat == DEAD && !usr.client.holder)
|
|
|
|
|
return 0
|
|
|
|
|
if(!(usr.ckey in voted))
|
|
|
|
|
if(vote && 1<=vote && vote<=choices.len)
|
|
|
|
|
voted += usr.ckey
|
|
|
|
|
voted[usr.ckey] = vote
|
|
|
|
|
choices[choices[vote]]++ //check this
|
|
|
|
|
return vote
|
|
|
|
|
else if(vote && 1<=vote && vote<=choices.len)
|
|
|
|
|
choices[choices[voted[usr.ckey]]]--
|
|
|
|
|
voted[usr.ckey] = vote
|
|
|
|
|
choices[choices[vote]]++
|
|
|
|
|
return vote
|
|
|
|
|
if(vote && ISINRANGE(vote, 1, choices.len))
|
|
|
|
|
switch(vote_system)
|
|
|
|
|
if(PLURALITY_VOTING)
|
|
|
|
|
if(usr.ckey in voted)
|
|
|
|
|
choices[choices[voted[usr.ckey]]]--
|
|
|
|
|
voted[usr.ckey] = vote
|
|
|
|
|
choices[choices[vote]]++
|
|
|
|
|
return vote
|
|
|
|
|
else
|
|
|
|
|
voted += usr.ckey
|
|
|
|
|
voted[usr.ckey] = vote
|
|
|
|
|
choices[choices[vote]]++ //check this
|
|
|
|
|
return vote
|
|
|
|
|
if(APPROVAL_VOTING)
|
|
|
|
|
if(usr.ckey in voted)
|
|
|
|
|
if(vote in voted[usr.ckey])
|
|
|
|
|
voted[usr.ckey] -= vote
|
|
|
|
|
choices[choices[vote]]--
|
|
|
|
|
else
|
|
|
|
|
voted[usr.ckey] += vote
|
|
|
|
|
choices[choices[vote]]++
|
|
|
|
|
else
|
|
|
|
|
voted += usr.ckey
|
|
|
|
|
voted[usr.ckey] = list(vote)
|
|
|
|
|
choices[choices[vote]]++
|
|
|
|
|
return vote
|
|
|
|
|
if(RANKED_CHOICE_VOTING)
|
|
|
|
|
if(usr.ckey in voted)
|
|
|
|
|
if(vote in voted[usr.ckey])
|
|
|
|
|
voted[usr.ckey] -= vote
|
|
|
|
|
else
|
|
|
|
|
voted += usr.ckey
|
|
|
|
|
voted[usr.ckey] = list()
|
|
|
|
|
voted[usr.ckey] += vote
|
|
|
|
|
saved -= usr.ckey
|
|
|
|
|
if(SCORE_VOTING)
|
|
|
|
|
if(!(usr.ckey in voted))
|
|
|
|
|
voted += usr.ckey
|
|
|
|
|
voted[usr.ckey] = list()
|
|
|
|
|
voted[usr.ckey][choices[vote]] = score
|
|
|
|
|
saved -= usr.ckey
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, hideresults)//CIT CHANGE - adds hideresults argument to votes to allow for obfuscated votes
|
|
|
|
|
/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, hideresults, votesystem = PLURALITY_VOTING, forced = FALSE,vote_time = -1)//CIT CHANGE - adds hideresults argument to votes to allow for obfuscated votes
|
|
|
|
|
vote_system = votesystem
|
|
|
|
|
if(!mode)
|
|
|
|
|
if(started_time)
|
|
|
|
|
var/next_allowed_time = (started_time + CONFIG_GET(number/vote_delay))
|
|
|
|
|
@@ -257,11 +383,17 @@ SUBSYSTEM_DEF(vote)
|
|
|
|
|
if("roundtype") //CIT CHANGE - adds the roundstart secret/extended vote
|
|
|
|
|
choices.Add("secret", "extended")
|
|
|
|
|
if("dynamic")
|
|
|
|
|
var/saved_threats = SSpersistence.saved_threat_levels
|
|
|
|
|
if((saved_threats[1]+saved_threats[2]+saved_threats[3])>150)
|
|
|
|
|
choices.Add("extended",PEACE,CHAOS)
|
|
|
|
|
else
|
|
|
|
|
choices.Add(PEACE,CHAOS)
|
|
|
|
|
for(var/T in config.storyteller_cache)
|
|
|
|
|
var/datum/dynamic_storyteller/S = T
|
|
|
|
|
var/recent_rounds = 0
|
|
|
|
|
for(var/i in 1 to SSpersistence.saved_storytellers.len)
|
|
|
|
|
if(SSpersistence.saved_storytellers[i] == initial(S.name))
|
|
|
|
|
recent_rounds++
|
|
|
|
|
if(recent_rounds < initial(S.weight))
|
|
|
|
|
choices.Add(initial(S.name))
|
|
|
|
|
choice_descs.Add(initial(S.desc))
|
|
|
|
|
choices.Add("Secret")
|
|
|
|
|
choice_descs.Add("Standard secret. Switches mode if it wins.")
|
|
|
|
|
if("custom")
|
|
|
|
|
question = stripped_input(usr,"What is the vote for?")
|
|
|
|
|
if(!question)
|
|
|
|
|
@@ -280,9 +412,11 @@ SUBSYSTEM_DEF(vote)
|
|
|
|
|
if(mode == "custom")
|
|
|
|
|
text += "\n[question]"
|
|
|
|
|
log_vote(text)
|
|
|
|
|
var/vp = CONFIG_GET(number/vote_period)
|
|
|
|
|
var/vp = vote_time
|
|
|
|
|
if(vp == -1)
|
|
|
|
|
vp = CONFIG_GET(number/vote_period)
|
|
|
|
|
to_chat(world, "\n<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 [DisplayTimeText(vp)] to vote.</font>")
|
|
|
|
|
time_remaining = round(vp/10)
|
|
|
|
|
end_time = started_time+vp
|
|
|
|
|
for(var/c in GLOB.clients)
|
|
|
|
|
SEND_SOUND(c, sound('sound/misc/server-ready.ogg'))
|
|
|
|
|
var/client/C = c
|
|
|
|
|
@@ -292,6 +426,11 @@ SUBSYSTEM_DEF(vote)
|
|
|
|
|
C.player_details.player_actions += V
|
|
|
|
|
V.Grant(C.mob)
|
|
|
|
|
generated_actions += V
|
|
|
|
|
if(forced)
|
|
|
|
|
var/datum/browser/popup = new(C, "vote", "Voting Panel",nwidth=600,nheight=700)
|
|
|
|
|
popup.set_window_options("can_close=0")
|
|
|
|
|
popup.set_content(SSvote.interface(C))
|
|
|
|
|
popup.open(0)
|
|
|
|
|
return 1
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
@@ -311,14 +450,71 @@ SUBSYSTEM_DEF(vote)
|
|
|
|
|
. += "<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]]
|
|
|
|
|
var/ivotedforthis = ((C.ckey in voted) && (voted[C.ckey] == i) ? TRUE : FALSE)
|
|
|
|
|
if(!votes)
|
|
|
|
|
votes = 0
|
|
|
|
|
. += "<li>[ivotedforthis ? "<b>" : ""]<a href='?src=[REF(src)];vote=[i]'>[choices[i]]</a> ([obfuscated ? (admin ? "??? ([votes])" : "???") : votes] votes)[ivotedforthis ? "</b>" : ""]</li>" // CIT CHANGE - adds obfuscated votes
|
|
|
|
|
. += "</ul><hr>"
|
|
|
|
|
switch(vote_system)
|
|
|
|
|
if(PLURALITY_VOTING)
|
|
|
|
|
. += "<h3>Vote one.</h3>"
|
|
|
|
|
if(APPROVAL_VOTING)
|
|
|
|
|
. += "<h3>Vote any number of choices.</h3>"
|
|
|
|
|
if(RANKED_CHOICE_VOTING)
|
|
|
|
|
. += "<h3>Vote by order of preference. Revoting will demote to the bottom. 1 is your favorite, and higher numbers are worse.</h3>"
|
|
|
|
|
if(SCORE_VOTING)
|
|
|
|
|
. += "<h3>Grade the candidates by how much you like them.</h3>"
|
|
|
|
|
. += "<h3>No-votes have no power--your opinion is only heard if you vote!</h3>"
|
|
|
|
|
. += "Time Left: [DisplayTimeText(end_time-world.time)]<hr><ul>"
|
|
|
|
|
switch(vote_system)
|
|
|
|
|
if(PLURALITY_VOTING, APPROVAL_VOTING)
|
|
|
|
|
for(var/i=1,i<=choices.len,i++)
|
|
|
|
|
var/votes = choices[choices[i]]
|
|
|
|
|
var/ivotedforthis = FALSE
|
|
|
|
|
switch(vote_system)
|
|
|
|
|
if(PLURALITY_VOTING)
|
|
|
|
|
ivotedforthis = ((C.ckey in voted) && (voted[C.ckey] == i))
|
|
|
|
|
if(APPROVAL_VOTING)
|
|
|
|
|
ivotedforthis = ((C.ckey in voted) && (i in voted[C.ckey]))
|
|
|
|
|
if(!votes)
|
|
|
|
|
votes = 0
|
|
|
|
|
. += "<li>[ivotedforthis ? "<b>" : ""]<a href='?src=[REF(src)];vote=[i]'>[choices[i]]</a> ([obfuscated ? (admin ? "??? ([votes])" : "???") : votes] votes)[ivotedforthis ? "</b>" : ""]</li>" // CIT CHANGE - adds obfuscated votes
|
|
|
|
|
if(choice_descs.len >= i)
|
|
|
|
|
. += "<li>[choice_descs[i]]</li>"
|
|
|
|
|
. += "</ul><hr>"
|
|
|
|
|
if(RANKED_CHOICE_VOTING)
|
|
|
|
|
var/list/myvote = voted[C.ckey]
|
|
|
|
|
for(var/i=1,i<=choices.len,i++)
|
|
|
|
|
var/vote = (myvote ? (myvote.Find(i)) : 0)
|
|
|
|
|
if(vote)
|
|
|
|
|
. += "<li><b><a href='?src=[REF(src)];vote=[i]'>[choices[i]]</a> ([vote])</b></li>"
|
|
|
|
|
else
|
|
|
|
|
. += "<li><a href='?src=[REF(src)];vote=[i]'>[choices[i]]</a></li>"
|
|
|
|
|
if(choice_descs.len >= i)
|
|
|
|
|
. += "<li>[choice_descs[i]]</li>"
|
|
|
|
|
. += "</ul><hr>"
|
|
|
|
|
if(!(C.ckey in saved))
|
|
|
|
|
. += "(<a href='?src=[REF(src)];vote=save'>Save vote</a>)"
|
|
|
|
|
else
|
|
|
|
|
. += "(Saved!)"
|
|
|
|
|
. += "(<a href='?src=[REF(src)];vote=load'>Load vote from save</a>)"
|
|
|
|
|
. += "(<a href='?src=[REF(src)];vote=reset'>Reset votes</a>)"
|
|
|
|
|
if(SCORE_VOTING)
|
|
|
|
|
var/list/myvote = voted[C.ckey]
|
|
|
|
|
for(var/i=1,i<=choices.len,i++)
|
|
|
|
|
. += "<li><b>[choices[i]]</b>"
|
|
|
|
|
for(var/r in 1 to GLOB.vote_score_options.len)
|
|
|
|
|
. += " <a href='?src=[REF(src)];vote=[i];score=[r]'>"
|
|
|
|
|
if((choices[i] in myvote) && myvote[choices[i]] == r)
|
|
|
|
|
. +="<b>([GLOB.vote_score_options[r]])</b>"
|
|
|
|
|
else
|
|
|
|
|
. +="[GLOB.vote_score_options[r]]"
|
|
|
|
|
. += "</a>"
|
|
|
|
|
. += "</li>"
|
|
|
|
|
if(choice_descs.len >= i)
|
|
|
|
|
. += "<li>[choice_descs[i]]</li>"
|
|
|
|
|
. += "</ul><hr>"
|
|
|
|
|
if(!(C.ckey in saved))
|
|
|
|
|
. += "(<a href='?src=[REF(src)];vote=save'>Save vote</a>)"
|
|
|
|
|
else
|
|
|
|
|
. += "(Saved!)"
|
|
|
|
|
. += "(<a href='?src=[REF(src)];vote=load'>Load vote from save</a>)"
|
|
|
|
|
. += "(<a href='?src=[REF(src)];vote=reset'>Reset votes</a>)"
|
|
|
|
|
if(admin)
|
|
|
|
|
. += "(<a href='?src=[REF(src)];vote=cancel'>Cancel Vote</a>) "
|
|
|
|
|
else
|
|
|
|
|
@@ -376,8 +572,31 @@ SUBSYSTEM_DEF(vote)
|
|
|
|
|
if("custom")
|
|
|
|
|
if(usr.client.holder)
|
|
|
|
|
initiate_vote("custom",usr.key)
|
|
|
|
|
if("reset")
|
|
|
|
|
if(usr.ckey in voted)
|
|
|
|
|
voted -= usr.ckey
|
|
|
|
|
if("save")
|
|
|
|
|
if(usr.ckey in voted)
|
|
|
|
|
if(!(usr.ckey in SSpersistence.saved_votes))
|
|
|
|
|
SSpersistence.saved_votes[usr.ckey] = list()
|
|
|
|
|
SSpersistence.saved_votes[usr.ckey][mode] = voted[usr.ckey]
|
|
|
|
|
saved += usr.ckey
|
|
|
|
|
if("load")
|
|
|
|
|
if(!(usr.ckey in SSpersistence.saved_votes))
|
|
|
|
|
SSpersistence.LoadSavedVote(usr.ckey)
|
|
|
|
|
if(!(usr.ckey in SSpersistence.saved_votes))
|
|
|
|
|
SSpersistence.saved_votes[usr.ckey] = list()
|
|
|
|
|
if(usr.ckey in voted)
|
|
|
|
|
SSpersistence.saved_votes[usr.ckey][mode] = voted[usr.ckey]
|
|
|
|
|
else
|
|
|
|
|
SSpersistence.saved_votes[usr.ckey][mode] = list()
|
|
|
|
|
voted[usr.ckey] = SSpersistence.saved_votes[usr.ckey][mode]
|
|
|
|
|
saved += usr.ckey
|
|
|
|
|
else
|
|
|
|
|
submit_vote(round(text2num(href_list["vote"])))
|
|
|
|
|
if(vote_system == SCORE_VOTING)
|
|
|
|
|
submit_vote(round(text2num(href_list["vote"])),round(text2num(href_list["score"])))
|
|
|
|
|
else
|
|
|
|
|
submit_vote(round(text2num(href_list["vote"])))
|
|
|
|
|
usr.vote()
|
|
|
|
|
|
|
|
|
|
/datum/controller/subsystem/vote/proc/remove_action_buttons()
|
|
|
|
|
@@ -392,7 +611,7 @@ SUBSYSTEM_DEF(vote)
|
|
|
|
|
set category = "OOC"
|
|
|
|
|
set name = "Vote"
|
|
|
|
|
|
|
|
|
|
var/datum/browser/popup = new(src, "vote", "Voting Panel")
|
|
|
|
|
var/datum/browser/popup = new(src, "vote", "Voting Panel",nwidth=600,nheight=700)
|
|
|
|
|
popup.set_window_options("can_close=0")
|
|
|
|
|
popup.set_content(SSvote.interface(client))
|
|
|
|
|
popup.open(0)
|
|
|
|
|
@@ -419,6 +638,3 @@ SUBSYSTEM_DEF(vote)
|
|
|
|
|
var/datum/player_details/P = GLOB.player_details[owner.ckey]
|
|
|
|
|
if(P)
|
|
|
|
|
P.player_actions -= src
|
|
|
|
|
|
|
|
|
|
#undef PEACE
|
|
|
|
|
#undef CHAOS
|
|
|
|
|
|