mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-22 20:48:56 +01:00
Votes now show the percentage of total votes each choice got | Adds weighted random vote type | Map votes are now weighted random (#80362)
## About The Pull Request See the title. ## Why It's Good For The Game Mothblocks wanted map votes to not be simple winner take all and wanted it to be randomly chosen based on votes.  ## Changelog 🆑 add: Map Votes are now weighted random. add: Custom Votes can now take advantage of Weighted Random winner selection del: Removed Herobrine from the game /🆑 --------- Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com>
This commit is contained in:
@@ -43,6 +43,10 @@
|
||||
|
||||
#define ROUND_UP(x) ( -round(-(x)))
|
||||
|
||||
/// Returns the number of digits in a number. Only works on whole numbers.
|
||||
/// This is marginally faster than string interpolation -> length
|
||||
#define DIGITS(x) (ROUND_UP(log(10, x)))
|
||||
|
||||
// round() acts like floor(x, 1) by default but can't handle other values
|
||||
#define FLOOR(x, y) ( round((x) / (y)) * (y) )
|
||||
|
||||
|
||||
@@ -334,3 +334,10 @@
|
||||
#define VOTE_COUNT_METHOD_SINGLE 1
|
||||
/// Approval voting. Any number of selections per person, and the selection with the most votes wins.
|
||||
#define VOTE_COUNT_METHOD_MULTI 2
|
||||
|
||||
/// The choice with the most votes wins. Ties are broken by the first choice to reach that number of votes.
|
||||
#define VOTE_WINNER_METHOD_SIMPLE "Simple"
|
||||
/// The winning choice is selected randomly based on the number of votes each choice has.
|
||||
#define VOTE_WINNER_METHOD_WEIGHTED_RANDOM "Weighted Random"
|
||||
/// There is no winner for this vote.
|
||||
#define VOTE_WINNER_METHOD_NONE "None"
|
||||
|
||||
@@ -86,11 +86,31 @@ SUBSYSTEM_DEF(vote)
|
||||
// Announce the results of the vote to the world.
|
||||
var/to_display = current_vote.get_result_text(winners, final_winner, non_voters)
|
||||
|
||||
log_vote(to_display)
|
||||
var/total_votes = 0
|
||||
var/list/vote_choice_data = list()
|
||||
for(var/choice in current_vote.choices)
|
||||
var/choice_votes = current_vote.choices[choice]
|
||||
vote_choice_data["[choice]"] = choice_votes
|
||||
|
||||
// stringify the winners to prevent potential unimplemented serialization errors.
|
||||
// Perhaps this can be removed in the future and we assert that vote choices must implement serialization.
|
||||
var/final_winner_string = final_winner && "[final_winner]"
|
||||
var/list/winners_string = list()
|
||||
for(var/winner in winners)
|
||||
winners_string += "[winner]"
|
||||
|
||||
var/list/vote_log_data = list(
|
||||
"choices" = vote_choice_data,
|
||||
"total" = total_votes,
|
||||
"winners" = winners_string,
|
||||
"final_winner" = final_winner_string,
|
||||
)
|
||||
var/log_string = replacetext(to_display, "\n", "\\n") // 'keep' the newlines, but dont actually print them as newlines
|
||||
log_vote(log_string, vote_log_data)
|
||||
to_chat(world, span_infoplain(vote_font("\n[to_display]")))
|
||||
|
||||
// Finally, doing any effects on vote completion
|
||||
if (final_winner) // if no one voted final_winner will be null
|
||||
if (final_winner) // if no one voted, or the vote cannot be won, final_winner will be null
|
||||
current_vote.finalize_vote(final_winner)
|
||||
|
||||
/**
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
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
|
||||
@@ -123,33 +125,41 @@
|
||||
*/
|
||||
/datum/vote/proc/get_vote_result(list/non_voters)
|
||||
RETURN_TYPE(/list)
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
|
||||
var/list/winners = list()
|
||||
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 we currently have no winners...
|
||||
if(!length(winners))
|
||||
// And the current option has any votes, it's the new highest.
|
||||
if(vote_count > 0)
|
||||
winners += option
|
||||
highest_vote = vote_count
|
||||
if(vote_count < highest_vote)
|
||||
continue
|
||||
|
||||
// If we're greater than, and NOT equal to, the highest vote,
|
||||
// we are the new supreme winner - clear all others
|
||||
if(vote_count > highest_vote)
|
||||
winners.Cut()
|
||||
winners += option
|
||||
highest_vote = vote_count
|
||||
current_winners = list(option)
|
||||
continue
|
||||
current_winners += option
|
||||
|
||||
// If we're equal to the highest vote, we tie for winner
|
||||
else if(vote_count == highest_vote)
|
||||
winners += option
|
||||
return length(current_winners) ? current_winners : list()
|
||||
|
||||
return winners
|
||||
/// 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.
|
||||
@@ -161,17 +171,46 @@
|
||||
* 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)
|
||||
if(length(all_winners) <= 0 || !real_winner)
|
||||
return span_bold("Vote Result: Inconclusive - No Votes!")
|
||||
|
||||
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)
|
||||
returned_text += "\n[span_bold(option)]: [choices[option]]"
|
||||
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)
|
||||
|
||||
@@ -29,6 +29,24 @@
|
||||
return forced
|
||||
|
||||
/datum/vote/custom_vote/create_vote(mob/vote_creator)
|
||||
var/custom_win_method = tgui_input_list(
|
||||
vote_creator,
|
||||
"How should the vote winner be determined?",
|
||||
"Winner Method",
|
||||
list("Simple", "Weighted Random", "No Winner"),
|
||||
default = "Simple",
|
||||
)
|
||||
switch(custom_win_method)
|
||||
if("Simple")
|
||||
winner_method = VOTE_WINNER_METHOD_SIMPLE
|
||||
if("Weighted Random")
|
||||
winner_method = VOTE_WINNER_METHOD_WEIGHTED_RANDOM
|
||||
if("No Winner")
|
||||
winner_method = VOTE_WINNER_METHOD_NONE
|
||||
else
|
||||
to_chat(vote_creator, span_boldwarning("Unknown winner method. Contact a coder."))
|
||||
return FALSE
|
||||
|
||||
override_question = tgui_input_text(vote_creator, "What is the vote for?", "Custom Vote")
|
||||
if(!override_question)
|
||||
return FALSE
|
||||
@@ -52,8 +70,4 @@
|
||||
. = ..()
|
||||
. += "\n[override_question]"
|
||||
|
||||
// There are no winners or losers for custom votes
|
||||
/datum/vote/custom_vote/get_winner_text(list/all_winners, real_winner, list/non_voters)
|
||||
return "[span_bold("Did not vote:")] [length(non_voters)]"
|
||||
|
||||
#undef MAX_CUSTOM_VOTE_OPTIONS
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "Map"
|
||||
message = "Vote for next round's map!"
|
||||
count_method = VOTE_COUNT_METHOD_MULTI
|
||||
winner_method = VOTE_WINNER_METHOD_WEIGHTED_RANDOM
|
||||
|
||||
/datum/vote/map_vote/New()
|
||||
. = ..()
|
||||
|
||||
Reference in New Issue
Block a user