Allows crew transfer votes on red alert, weighted for participation in round (#22993)

With massive thanks to @JohnWildkins for help in touching a lot of
concepts I've never worked with before, as well as helping me get back
into contributing in general.

This PR allows crew transfers to be called during red alert and adds
support for weighted voting to help favour active participants in these
particular rounds.

Observers and players still in the lobby are given only half a vote
towards transfer/continue. There is also config support to define an
active 'participation time' in minutes, which is used to stop potential
abuse cases of players respawning/joining from the lobby just before (or
even during) the transfer to gain a full vote. By default, this is set
to 15 minutes, so anyone who's been actively playing in the round for at
least that long is eligible.

This is my first time touching things like the config file or any sort
of subsystem more advanced than, like, the far more basic PRs I'm used
to, but I was encouraged to look at this as a little project. It all
appears to work well locally.

---------

Signed-off-by: Matt Atlas <mattiathebest2000@hotmail.it>
Co-authored-by: Jaraci <mirandatrasen@nt.net>
Co-authored-by: Matt Atlas <mattiathebest2000@hotmail.it>
Co-authored-by: hazelrat <83198434+hazelrat@users.noreply.github.com>
This commit is contained in:
Jaraci
2026-08-06 10:12:51 +00:00
committed by GitHub
co-authored by Jaraci Matt Atlas hazelrat
parent d47cdb8f04
commit 1a37ba3d26
7 changed files with 95 additions and 10 deletions
+4
View File
@@ -247,6 +247,7 @@ GLOBAL_LIST_EMPTY(gamemode_cache)
var/allow_extra_antags = 0
var/guests_allowed = 1
var/debugparanoid = 0
var/minimum_participation_time = 15 //Minimum time, in minutes, that a player must be active in round to count as an active participant.
var/server
var/banappeals
@@ -531,6 +532,9 @@ GENERAL_PROTECT_DATUM(/datum/configuration)
if(type == "config")
switch (name)
if ("minimum_participation_time")
GLOB.config.minimum_participation_time = text2num(value) MINUTES
if ("auto_local_admin")
GLOB.config.auto_local_admin = TRUE
+6 -7
View File
@@ -120,21 +120,20 @@ SUBSYSTEM_DEF(vote)
/datum/controller/subsystem/vote/proc/submit_single_vote(mob/voter, their_vote)
if(!current_vote)
return
if(!voter?.ckey)
return
if(FALSE && voter.stat == DEAD && !voter.client?.holder) //Used to be "CONFIG_GET(flag/no_dead_vote)"
var/vote_power = current_vote.get_voting_power(voter)
if(vote_power <= 0)
return
// If user has already voted, remove their specific vote
if(voter.ckey in current_vote.choices_by_ckey)
var/their_old_vote = current_vote.choices_by_ckey[voter.ckey]
current_vote.choices[their_old_vote]--
var/list/their_old_vote = current_vote.choices_by_ckey[voter.ckey]
current_vote.choices[their_old_vote["choice"]] -= their_old_vote["power"]
else
voted += voter.ckey
current_vote.choices_by_ckey[voter.ckey] = their_vote
current_vote.choices[their_vote]++
current_vote.choices_by_ckey[voter.ckey] = list("choice" = their_vote, "power" = vote_power)
current_vote.choices[their_vote] += vote_power
return TRUE
+2
View File
@@ -35,6 +35,7 @@
var/mob/living/current
var/mob/living/original //This is being used now, don't remove it
var/active = 0
var/time_joined
var/mob/living/admin_mob_placeholder = null
@@ -538,6 +539,7 @@
mind.signature = client.prefs.signature
if (client.prefs.signfont)
mind.signfont = client.prefs.signfont
mind.time_joined = world.time
mind.current = src
//HUMAN
+9
View File
@@ -113,6 +113,15 @@
return "[contains_vote_in_name ? "[capitalize(name)]" : "[capitalize(name)] vote"] started by [initiator || "Central Command"]."
/// Handles voting power application for each voter. Used in situations where some players should have more say in a vote than others.
/datum/vote/proc/get_voting_power(mob/voter)
SHOULD_CALL_PARENT(TRUE)
if(!voter?.ckey)
return 0
if(FALSE && voter.stat == DEAD && !voter.client?.holder) //Used to be "CONFIG_GET(flag/no_dead_vote)"
return 0
return 1
/**
* Gets the result of the vote.
*
+13 -3
View File
@@ -12,7 +12,7 @@ GLOBAL_VAR(last_transfer_vote)
. = ..()
//Transfer already in progress, noone can call another vote
if(SSatlas.current_map.shuttle_call_restart_timer || (GLOB.evacuation_controller.state != EVAC_IDLE))
to_chat(by_who, SPAN_NOTICE("Transfer or evacuation are already in progress."))
to_chat(by_who, SPAN_NOTICE("A crew transfer or evacuation is already in progress."))
return FALSE
//If in lobby, roundend, setup or whatever else
@@ -24,7 +24,7 @@ GLOBAL_VAR(last_transfer_vote)
if(forced)
return TRUE
if(GLOB.security_level >= SEC_LEVEL_RED)
if(GLOB.security_level >= SEC_LEVEL_DELTA)
to_chat(by_who, "The current alert status is too high to call for a crew transfer!")
return FALSE
@@ -36,12 +36,22 @@ GLOBAL_VAR(last_transfer_vote)
next_allowed_time = GLOB.config.transfer_timeout
if(next_allowed_time > get_round_duration()) //Sorry, not the time yet
to_chat(by_who, SPAN_NOTICE("Time left until the crew transfer can be voted: [next_allowed_time - get_round_duration()]"))
to_chat(by_who, SPAN_NOTICE("Time left until a crew transfer can be voted for: [next_allowed_time - get_round_duration()]"))
return FALSE
else
return TRUE //We did it bro, we can vote the transfer now!
// Crew transfer during red alert gives less voting power to lobby-sitters and observers.
/datum/vote/crewtransfer/get_voting_power(mob/voter)
var/vote_weight = ..()
if(vote_weight != 0 && GLOB.security_level == SEC_LEVEL_RED)
vote_weight = 0.5
if(!isobserver(voter) && !isnewplayer(voter) && !((world.time - voter.mind.time_joined) < GLOB.config.minimum_participation_time))
vote_weight = 1
return vote_weight
/datum/vote/crewtransfer/get_vote_result(list/non_voters)
SHOULD_CALL_PARENT(FALSE)
+3
View File
@@ -608,3 +608,6 @@ CACHE_ASSETS 0
## Tgui payloads larger than the 2kb limit for BYOND topic requests are split into roughly 1kb chunks and sent in sequence.
## This config option limits the maximum chunk count for which the server will accept a payload, default is 32
TGUI_MAX_CHUNK_COUNT 32
# Minimum time, in minutes, that a player must be active in round to count as an active participant. Used for determining vote power for crew transfers on red alert, for example.
MINIMUM_PARTICIPATION_TIME 15
+58
View File
@@ -0,0 +1,58 @@
################################
# Example Changelog File
#
# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
#
# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
# When it is, any changes listed below will disappear.
#
# Valid Prefixes:
# bugfix
# - (fixes bugs)
# wip
# - (work in progress)
# qol
# - (quality of life)
# soundadd
# - (adds a sound)
# sounddel
# - (removes a sound)
# rscadd
# - (adds a feature)
# rscdel
# - (removes a feature)
# imageadd
# - (adds an image or sprite)
# imagedel
# - (removes an image or sprite)
# spellcheck
# - (fixes spelling or grammar)
# experiment
# - (experimental change)
# balance
# - (balance changes)
# code_imp
# - (misc internal code change)
# refactor
# - (refactors code)
# config
# - (makes a change to the config files)
# admin
# - (makes changes to administrator tools)
# server
# - (miscellaneous changes to server)
#################################
# Your name.
author: Omicega
# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
delete-after: True
# Any changes you've made. See valid prefix list above.
# INDENT WITH TWO SPACES. NOT TABS. SPACES.
# SCREW THIS UP AND IT WON'T WORK.
# Also, this gets changed to [] after reading. Just remove the brackets when you add new shit.
# Please surround your changes in double quotes ("). It works without them, but if you use certain characters it screws up compiling. The quotes will not show up in the changelog.
changes:
- rscadd: "Allows transfer votes during red alert, with support for weighting votes by round participation."