Files
Bubberstation/code/modules/mafia/abilities/abilities.dm
John Willard c3bbf807c5 Adds Coroner to Mafia & Bunch of Mafia changes (#92158)
## About The Pull Request

Changeling chat is now at night, rather than :j saymode, and it is also
separated from normal messages as [CHANGELING CHAT]
Adds a new [DEAD CHAT], all dead players in Mafia are corpselocked and
talking will instead go to Dead chat.
The Chaplain's ability is now being able to hear Dead chat at night, and
being able to in turn speak to the Dead.
The Chaplain's old ability has been given to a new role, the Coroner.
"Pray" is now "Autopsy".
Deaths in the Mafia arena aren't broadcasted anymore, to lessen
annoyance to round observers.

Also updates role icons & some outfits, as well as some bug fixes I
encountered while messing with it on localhost. I also tried (but not
fully) to make Mafia games more modular and independent, so maybe in the
future we can have more than one Mafia game running at a time.

I am limited to 2 player testing for this, so it is very much possible
that there's some issues I haven't found from my local testing, let me
know if you find anything please.

## Why It's Good For The Game

Being dead in Mafia boots you out of the round regardless of how
invested you were, that kidna sucks so hopefully being able to still
contribute something to the game, or at least discuss it with other dead
players in your own chat, makes players feel important to the game
they're playing.

I have a previous attempt of this here -
https://github.com/tgstation/tgstation/pull/75879 - but it staled out.
This differs from that attempt, as only dead players from the Mafia game
can speak in dead chat, while the old attempt allowed anyone that was
observing a mafia sign post (so dead players from the game, but not
observing the post, weren't able to speak to Chaplains, making him very
hard to be useful especially since getting information like that across
is a little hard). Being corpselocked also prevents them from being able
to see who Changelings are by simply looking at who has maptext at
night, and keeps them more focused on the game being played.

## Changelog

🆑
add: Added a new role to Mafia; the Coroner, which takes the Chaplain's
ability to see dead people's roles.
add: Mafia Chaplains now speak with the dead at night instead, and the
dead are corpselocked to prevent cheating.
fix: Mafia's HoS doesn't kill himself when executing non-townies.
qol: You can now update your notes & send them in chat while dead, as
well as look up the descriptions of other roles.
/🆑
2025-07-30 19:59:30 +10:00

136 lines
5.1 KiB
Plaintext

/datum/mafia_ability
var/name = "Mafia Ability"
var/ability_action = "brutally murder"
///The priority level this action must be sent at. Setting this to null will prevent it from being triggered automatically.
///(COMSIG_MAFIA_NIGHT_PRE_ACTION_PHASE|COMSIG_MAFIA_NIGHT_ACTION_PHASE|COMSIG_MAFIA_NIGHT_KILL_PHASE)
var/action_priority = COMSIG_MAFIA_NIGHT_ACTION_PHASE
///When the ability can be used: (MAFIA_PHASE_DAY | MAFIA_PHASE_VOTING | MAFIA_PHASE_NIGHT)
var/valid_use_period = MAFIA_PHASE_NIGHT
///Whether this ability can be used on yourself. Selections: (CAN_USE_ON_OTHERS | CAN_USE_ON_SELF | CAN_USE_ON_DEAD)
var/use_flags = CAN_USE_ON_OTHERS
///Boolean on whether the ability was selected to be used during the proper period.
var/using_ability = FALSE
///The mafia role that holds this ability.
var/datum/mafia_role/host_role
///The mafia role this ability is targeting, if necessary.
var/datum/mafia_role/target_role
/datum/mafia_ability/New(datum/mafia_role/host_role)
. = ..()
src.host_role = host_role
if(action_priority)
RegisterSignal(host_role.mafia_game_controller, action_priority, PROC_REF(perform_action_target))
RegisterSignal(host_role.mafia_game_controller, COMSIG_MAFIA_NIGHT_END, PROC_REF(clean_action_refs))
/datum/mafia_ability/Destroy(force)
host_role = null
target_role = null
return ..()
///Handles special messagese sent by ability-specific stuff (such as changeling chat).
/datum/mafia_ability/proc/handle_speech(datum/source, list/speech_args)
SIGNAL_HANDLER
return FALSE
/**
* Called when refs need to be cleared, the last thing that is called in a night cycle.
*/
/datum/mafia_ability/proc/clean_action_refs(datum/mafia_controller/game)
SIGNAL_HANDLER
SHOULD_CALL_PARENT(TRUE)
target_role = null
using_ability = initial(using_ability)
/**
* Used to check if this ability can be used on a potential target.
* Args:
* game - The Mafia controller that holds reference to the game.
* potential_target - The player we are attempting to validate the action on.
* silent - Whether to give feedback to the player about why the action cannot be used.
*/
/datum/mafia_ability/proc/validate_action_target(datum/mafia_role/potential_target, silent = FALSE)
SHOULD_CALL_PARENT(TRUE)
if(host_role.game_status == MAFIA_DEAD)
return FALSE
if(host_role.mafia_game_controller.phase != valid_use_period)
return FALSE
if(host_role.role_flags & ROLE_ROLEBLOCKED)
host_role.send_message_to_player(span_warning("You were roleblocked!"))
return FALSE
if(potential_target)
if(use_flags & CAN_USE_ON_DEAD)
if(potential_target.game_status != MAFIA_DEAD)
if(!silent)
host_role.send_message_to_player(span_notice("This can only be used on dead players."))
return FALSE
else if(potential_target.game_status == MAFIA_DEAD)
if(!silent)
host_role.send_message_to_player(span_notice("This can only be used on living players."))
return FALSE
if(!(use_flags & CAN_USE_ON_SELF) && (potential_target == host_role))
if(!silent)
host_role.send_message_to_player(span_notice("This can only be used on others."))
return FALSE
if(!(use_flags & CAN_USE_ON_OTHERS) && (potential_target != host_role))
if(!silent)
host_role.send_message_to_player(span_notice("This can only be used on yourself."))
return FALSE
return TRUE
/**
* Called when using the ability.
* Will first check if you are using the ability, then whether you can use it.
* Finally it will check if you are interrupted, then will pass that you've performed it.
* Args:
* game - The Mafia controller that holds reference to the game.
* day_target - Set when using actions during the day, this is the person that is the target during this phase.
*/
/datum/mafia_ability/proc/perform_action_target(datum/mafia_controller/game, datum/mafia_role/day_target)
SHOULD_CALL_PARENT(TRUE)
if(!using_ability)
return FALSE
if(!validate_action_target(target_role))
return FALSE
if(target_role)
if(SEND_SIGNAL(target_role, COMSIG_MAFIA_ON_VISIT, game, host_role) & MAFIA_VISIT_INTERRUPTED) //visited a warden. something that prevents you by visiting that person
host_role.send_message_to_player(span_danger("Your [name] was interrupted!"))
return FALSE
return TRUE
/**
* ##set_target
*
* Used for Night abilities ONLY
* Sets the ability's target, which will cause the action to be performed on them at the end of the night.
* Subtypes can override this for things like self-abilities (such as shooting visitors).
*/
/datum/mafia_ability/proc/set_target(datum/mafia_role/new_target)
if(!validate_action_target(new_target))
return FALSE
var/feedback_text = "You will %WILL_PERFORM% [ability_action]%SELF%"
if(use_flags & CAN_USE_ON_SELF)
feedback_text = replacetext(feedback_text, "%SELF%", ".")
else
feedback_text = replacetext(feedback_text, "%SELF%", " [new_target.body].")
if(target_role == new_target)
using_ability = FALSE
target_role = null
feedback_text = replacetext(feedback_text, "%WILL_PERFORM%", "not")
else
using_ability = TRUE
target_role = new_target
feedback_text = replacetext(feedback_text, "%WILL_PERFORM%", "now")
host_role.send_message_to_player(span_notice(feedback_text))
return TRUE