Files
Aurora.3/code/datums/votes/_vote_datum.dm
Fluffy 2e5b9a9a42 Updated vote subsystem (#18563)
* sadf

* toworld_count

* fix voting for normal users

* jhkjhb

* gdsfgds

* sdaf
2024-03-06 07:10:41 +00:00

251 lines
7.5 KiB
Plaintext

/**
* # Vote Singleton
*
* A singleton datum that represents a type of vote for the voting subsystem.
*/
/datum/vote
/// The name of the vote.
var/name
/// If supplied, an override question will be displayed instead of the name of the vote.
var/override_question
/// The sound effect played to everyone when this vote is initiated.
var/vote_sound = 'sound/effects/vote.ogg'
/// A list of default choices we have for this vote.
var/list/default_choices
/// Does the name of this vote contain the word "vote"?
var/contains_vote_in_name = FALSE
/// What message do we want to pass to the player-side vote panel as a tooltip?
var/message = "Click to initiate a vote."
// Internal values used when tracking ongoing votes.
// Don't mess with these, change the above values / override procs for subtypes.
/// An assoc list of [all choices] to [number of votes in the current running vote].
var/list/choices = list()
/// A assoc list of [ckey] to [what they voted for in the current running vote].
var/list/choices_by_ckey = list()
/// The world time this vote was started.
var/started_time
/// The time remaining in this vote's run.
var/time_remaining
/// The counting method we use for votes.
var/count_method = VOTE_COUNT_METHOD_SINGLE
/// The method for selecting a winner.
var/winner_method = VOTE_WINNER_METHOD_SIMPLE
/**
* Used to determine if this vote is a possible
* vote type for the vote subsystem.
*
* If FALSE is returned, this vote singleton
* will not be created when the vote subsystem initializes,
* meaning no one will be able to hold this vote.
*/
/datum/vote/proc/is_accessible_vote()
return !!length(default_choices)
/**
* Resets our vote to its default state.
*/
/datum/vote/proc/reset()
SHOULD_CALL_PARENT(TRUE)
choices.Cut()
choices_by_ckey.Cut()
started_time = null
time_remaining = null
/**
* If this vote has a config associated, toggles it between enabled and disabled.
* Returns TRUE on a successful toggle, FALSE otherwise
*/
/datum/vote/proc/toggle_votable(mob/toggler)
return FALSE
/**
* If this vote has a config associated, returns its value (True or False, usually).
* If it has no config, returns -1.
*/
/datum/vote/proc/is_config_enabled()
return -1
/**
* Checks if the passed mob can initiate this vote.
*
* Return TRUE if the mob can begin the vote, allowing anyone to actually vote on it.
* Return FALSE if the mob cannot initiate the vote.
*/
/datum/vote/proc/can_be_initiated(mob/by_who, forced = FALSE)
SHOULD_CALL_PARENT(TRUE)
if(started_time)
var/next_allowed_time = (started_time + GLOB.config.vote_delay)
if(next_allowed_time > world.time && !forced)
message = "A vote was initiated recently. You must wait [DisplayTimeText(next_allowed_time - world.time)] before a new vote can be started!"
return FALSE
message = initial(message)
return TRUE
/**
* Called prior to the vote being initiated.
*
* Return FALSE to prevent the vote from being initiated.
*/
/datum/vote/proc/create_vote(mob/vote_creator)
SHOULD_CALL_PARENT(TRUE)
for(var/key in default_choices)
choices[key] = 0
return TRUE
/**
* Called when this vote is actually initiated.
*
* Return a string - the text displayed to the world when the vote is initiated.
*/
/datum/vote/proc/initiate_vote(initiator, duration)
SHOULD_CALL_PARENT(TRUE)
started_time = world.time
time_remaining = round(duration / 10)
return "[contains_vote_in_name ? "[capitalize(name)]" : "[capitalize(name)] vote"] started by [initiator || "Central Command"]."
/**
* Gets the result of the vote.
*
* non_voters - a list of all ckeys who didn't vote in the vote.
*
* Returns a list of all options that won.
* If there were no votes at all, the list will be length = 0, non-null.
* If only one option one, the list will be length = 1.
* If there was a tie, the list will be length > 1.
*/
/datum/vote/proc/get_vote_result(list/non_voters)
RETURN_TYPE(/list)
SHOULD_CALL_PARENT(TRUE)
switch(winner_method)
if(VOTE_WINNER_METHOD_NONE)
return list()
if(VOTE_WINNER_METHOD_SIMPLE)
return get_simple_winner()
if(VOTE_WINNER_METHOD_WEIGHTED_RANDOM)
return get_random_winner()
stack_trace("invalid select winner method: [winner_method]. Defaulting to simple.")
return get_simple_winner()
/// Gets the winner of the vote, selecting the choice with the most votes.
/datum/vote/proc/get_simple_winner()
var/highest_vote = 0
var/list/current_winners = list()
for(var/option in choices)
var/vote_count = choices[option]
if(vote_count < highest_vote)
continue
if(vote_count > highest_vote)
highest_vote = vote_count
current_winners = list(option)
continue
current_winners += option
return length(current_winners) ? current_winners : list()
/// Gets the winner of the vote, selecting a random choice from all choices based on their vote count.
/datum/vote/proc/get_random_winner()
var/winner = pick_weight(choices)
return winner ? list(winner) : list()
/**
* Gets the resulting text displayed when the vote is completed.
*
* all_winners - list of all options that won. Can be multiple, in the event of ties.
* real_winner - the option that actually won.
* non_voters - a list of all ckeys who didn't vote in the vote.
*
* Return a formatted string of text to be displayed to everyone.
*/
/datum/vote/proc/get_result_text(list/all_winners, real_winner, list/non_voters)
var/returned_text = ""
if(override_question)
returned_text += SPAN_BOLD(override_question)
else
returned_text += SPAN_BOLD("[capitalize(name)] Vote")
returned_text += "\nWinner Selection: "
switch(winner_method)
if(VOTE_WINNER_METHOD_NONE)
returned_text += "None"
if(VOTE_WINNER_METHOD_WEIGHTED_RANDOM)
returned_text += "Weighted Random"
else
returned_text += "Simple"
var/total_votes = 0 // for determining percentage of votes
for(var/option in choices)
total_votes += choices[option]
if(total_votes <= 0)
return SPAN_BOLD("Vote Result: Inconclusive - No Votes!")
returned_text += "\nResults:"
for(var/option in choices)
returned_text += "\n"
var/votes = choices[option]
var/percentage_text = ""
if(votes > 0)
var/actual_percentage = round((votes / total_votes) * 100, 0.1)
var/text = "[actual_percentage]"
var/spaces_needed = 5 - length(text)
for(var/_ in 1 to spaces_needed)
returned_text += " "
percentage_text += "[text]%"
else
percentage_text = " 0%"
returned_text += "[percentage_text] | [SPAN_BOLD(option)]: [choices[option]]"
if(!real_winner) // vote has no winner or cannot be won, but still had votes
return returned_text
returned_text += "\n"
returned_text += get_winner_text(all_winners, real_winner, non_voters)
return returned_text
/**
* Gets the text that displays the winning options within the result text.
*
* all_winners - list of all options that won. Can be multiple, in the event of ties.
* real_winner - the option that actually won.
* non_voters - a list of all ckeys who didn't vote in the vote.
*
* Return a formatted string of text to be displayed to everyone.
*/
/datum/vote/proc/get_winner_text(list/all_winners, real_winner, list/non_voters)
var/returned_text = ""
if(length(all_winners) > 1)
returned_text += "\n[SPAN_BOLD("Vote Tied Between:")]"
for(var/a_winner in all_winners)
returned_text += "\n\t[a_winner]"
returned_text += SPAN_BOLD("\nVote Result: [real_winner]")
return returned_text
/**
* How this vote handles a tiebreaker between multiple winners.
*/
/datum/vote/proc/tiebreaker(list/winners)
return pick(winners)
/**
* Called when a vote is actually all said and done.
* Apply actual vote effects here.
*/
/datum/vote/proc/finalize_vote(winning_option)
return