diff --git a/_maps/map_files/Mafia/mafia_ayylmao.dmm b/_maps/map_files/Mafia/mafia_ayylmao.dmm index 0f0a9c0e433..b9d8582f3b0 100644 --- a/_maps/map_files/Mafia/mafia_ayylmao.dmm +++ b/_maps/map_files/Mafia/mafia_ayylmao.dmm @@ -5,6 +5,10 @@ "b" = ( /turf/closed/indestructible/alien, /area/centcom/mafia) +"d" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/plating/abductor, +/area/centcom/mafia) "g" = ( /obj/mafia_game_board, /obj/effect/mapping_helpers/broken_floor, @@ -32,12 +36,10 @@ /turf/open/floor/plating/abductor, /area/centcom/mafia) "m" = ( -/obj/effect/landmark/mafia, /obj/structure/bed/abductor, /turf/open/floor/plating/abductor, /area/centcom/mafia) "n" = ( -/obj/effect/landmark/mafia, /obj/structure/bed/abductor, /turf/open/floor/plating/abductor2, /area/centcom/mafia) @@ -146,6 +148,10 @@ icon_state = "alien21" }, /area/centcom/mafia) +"O" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/plating/abductor2, +/area/centcom/mafia) "P" = ( /turf/closed/indestructible/abductor, /area/centcom/mafia) @@ -394,13 +400,13 @@ W G x r -r +O x -q +d x -r +O x -q +d q x E @@ -444,13 +450,13 @@ j m q q -q +d x t t t x -r +O r r n @@ -494,13 +500,13 @@ j n r r -r +O x t t t x -q +d q q m @@ -544,13 +550,13 @@ I J x q -q +d x -r +O x -q +d x -r +O r x Y diff --git a/_maps/map_files/Mafia/mafia_ball.dmm b/_maps/map_files/Mafia/mafia_ball.dmm index 2991fb8d78f..fb4dc490d36 100644 --- a/_maps/map_files/Mafia/mafia_ball.dmm +++ b/_maps/map_files/Mafia/mafia_ball.dmm @@ -252,11 +252,11 @@ b o r p -m +q q p r -n +r p q l @@ -274,7 +274,7 @@ c d d b -n +r r b b @@ -284,7 +284,7 @@ r b b q -m +q b d d @@ -301,13 +301,13 @@ b b p r -r +n p -q +m p -r +n p -q +m q p b @@ -348,19 +348,19 @@ b c d j -m q q q +m p s t s p +n r r r -n j i c @@ -398,19 +398,19 @@ b c d j -n r r r +n p s t s p +m q q q -m j d c @@ -451,13 +451,13 @@ b b p q -q +m p -r +n p -q +m p -r +n r p b @@ -474,7 +474,7 @@ c f d b -m +q q b b @@ -484,7 +484,7 @@ q b b r -n +r b d d @@ -502,11 +502,11 @@ b l q p -n +r r p q -m +q p r o diff --git a/_maps/map_files/Mafia/mafia_gothic.dmm b/_maps/map_files/Mafia/mafia_gothic.dmm index 4dd3c89f22a..817e145d111 100644 --- a/_maps/map_files/Mafia/mafia_gothic.dmm +++ b/_maps/map_files/Mafia/mafia_gothic.dmm @@ -297,11 +297,11 @@ b J r p -m +q q p E -N +O p q l @@ -319,7 +319,7 @@ c d d b -D +E O b b @@ -329,7 +329,7 @@ r b b q -m +q b d d @@ -346,13 +346,13 @@ b b p X -r +M p -q +m p -O +N p -q +m q p b @@ -393,19 +393,19 @@ b c d j -m q q q +m p t t t p -X +n r E -N +O j i c @@ -443,19 +443,19 @@ b c d j -n +X r E -O +N p t t t p +m q q q -m j d c @@ -496,13 +496,13 @@ b b p q -q +m p -X +n p -q +m p -E +D O p b @@ -519,7 +519,7 @@ c f d b -m +q q b b @@ -529,7 +529,7 @@ q b b X -M +r b d d @@ -547,11 +547,11 @@ b l q p -n +X r p q -m +q p E P diff --git a/_maps/map_files/Mafia/mafia_lavaland.dmm b/_maps/map_files/Mafia/mafia_lavaland.dmm index a3729fe8a6e..96ddef90b5c 100644 --- a/_maps/map_files/Mafia/mafia_lavaland.dmm +++ b/_maps/map_files/Mafia/mafia_lavaland.dmm @@ -60,7 +60,6 @@ /turf/open/floor/fakebasalt, /area/centcom/mafia) "an" = ( -/obj/effect/landmark/mafia, /obj/effect/turf_decal/trimline/brown/filled/line{ dir = 9 }, @@ -126,6 +125,7 @@ /obj/effect/turf_decal/trimline/brown/filled/end{ dir = 4 }, +/obj/effect/landmark/mafia, /turf/open/floor/iron, /area/centcom/mafia) "az" = ( @@ -137,17 +137,11 @@ /obj/structure/closet/secure_closet/miner/unlocked, /turf/open/floor/iron, /area/centcom/mafia) -"aA" = ( -/obj/effect/landmark/mafia, -/obj/effect/turf_decal/trimline/brown/filled/line{ - dir = 6 - }, -/turf/open/floor/iron, -/area/centcom/mafia) "aB" = ( /obj/effect/turf_decal/trimline/brown/filled/end{ dir = 1 }, +/obj/effect/landmark/mafia, /turf/open/floor/iron, /area/centcom/mafia) "aC" = ( @@ -183,13 +177,6 @@ }, /turf/open/floor/iron, /area/centcom/mafia) -"aG" = ( -/obj/effect/landmark/mafia, -/obj/effect/turf_decal/trimline/brown/filled/line{ - dir = 10 - }, -/turf/open/floor/iron, -/area/centcom/mafia) "aH" = ( /obj/effect/turf_decal/trimline/brown/filled/line{ dir = 10 @@ -209,7 +196,6 @@ /turf/open/floor/iron, /area/centcom/mafia) "aJ" = ( -/obj/effect/landmark/mafia, /obj/effect/turf_decal/trimline/brown/filled/line{ dir = 6 }, @@ -227,13 +213,6 @@ /obj/structure/closet/secure_closet/miner/unlocked, /turf/open/floor/iron, /area/centcom/mafia) -"aL" = ( -/obj/effect/landmark/mafia, -/obj/effect/turf_decal/trimline/brown/filled/line{ - dir = 5 - }, -/turf/open/floor/iron, -/area/centcom/mafia) "aM" = ( /obj/effect/turf_decal/trimline/brown/filled/line{ dir = 10 @@ -258,13 +237,7 @@ /area/centcom/mafia) "aP" = ( /obj/effect/turf_decal/trimline/brown/filled/end, -/turf/open/floor/iron, -/area/centcom/mafia) -"aQ" = ( /obj/effect/landmark/mafia, -/obj/effect/turf_decal/trimline/brown/filled/line{ - dir = 9 - }, /turf/open/floor/iron, /area/centcom/mafia) "aR" = ( @@ -277,6 +250,7 @@ /obj/effect/turf_decal/trimline/brown/filled/end{ dir = 8 }, +/obj/effect/landmark/mafia, /turf/open/floor/iron, /area/centcom/mafia) "aT" = ( @@ -473,11 +447,11 @@ aY ar aM ap -am +aq aq ap aw -aA +aV ap aq al @@ -495,7 +469,7 @@ ac ad ad aY -aL +aF aN aY aY @@ -505,7 +479,7 @@ ax aY aY aq -am +aq aY ad ad @@ -524,11 +498,11 @@ ap aO aP ap -aq +am ap ay ap -aq +am aq ap aY @@ -569,10 +543,10 @@ aY ac ad aj -am aq aq aq +am ap aq aZ @@ -628,10 +602,10 @@ aq ab aq ap +am aq aq aq -am aj ad ac @@ -672,11 +646,11 @@ aY aY ap aq -aq +am ap aS ap -aq +am ap aB aD @@ -695,7 +669,7 @@ ac af ad aY -am +aq aq aY aY @@ -705,7 +679,7 @@ aq aY aY aE -aG +aM aY ad ad @@ -723,11 +697,11 @@ aY al aq ap -aQ +as aU ap aq -am +aq ap aF aI diff --git a/_maps/map_files/Mafia/mafia_snow.dmm b/_maps/map_files/Mafia/mafia_snow.dmm index 0a4001a6b0f..b8fbc0650b4 100644 --- a/_maps/map_files/Mafia/mafia_snow.dmm +++ b/_maps/map_files/Mafia/mafia_snow.dmm @@ -8,6 +8,10 @@ "d" = ( /turf/open/floor/plating, /area/centcom/mafia) +"e" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/iron/dark, +/area/centcom/mafia) "f" = ( /obj/effect/mapping_helpers/burnt_floor, /turf/open/floor/plating, @@ -46,10 +50,6 @@ }, /turf/open/floor/holofloor/wood, /area/centcom/mafia) -"m" = ( -/obj/effect/landmark/mafia, -/turf/open/floor/holofloor/wood, -/area/centcom/mafia) "n" = ( /obj/item/bedsheet/green, /obj/structure/bed, @@ -133,11 +133,6 @@ }, /turf/open/floor/iron, /area/centcom/mafia) -"B" = ( -/obj/effect/landmark/mafia, -/obj/structure/lattice/catwalk, -/turf/open/lava/plasma/mafia, -/area/centcom/mafia) "C" = ( /obj/mafia_game_board, /turf/open/floor/holofloor/snow, @@ -181,6 +176,15 @@ "K" = ( /turf/closed/indestructible/rock/snow, /area/centcom/mafia) +"R" = ( +/obj/effect/landmark/mafia, +/turf/open/floor/iron, +/area/centcom/mafia) +"W" = ( +/obj/structure/lattice/catwalk, +/obj/effect/landmark/mafia, +/turf/open/lava/plasma/mafia, +/area/centcom/mafia) (1,1,1) = {" a @@ -342,11 +346,11 @@ s o F p -m +F q p t -y +t p z E @@ -364,7 +368,7 @@ s d d s -m +F r s s @@ -374,7 +378,7 @@ t p p z -B +z p w w @@ -391,13 +395,13 @@ s s p r -r +R p -q +e p -t +y p -z +W z p p @@ -438,19 +442,19 @@ b s d j -m +F F q -q +e x J t t p +y t t t -y p w t @@ -488,19 +492,19 @@ b s d j -m +F F r -r +R A J t t p +y t t t -y p w w @@ -541,13 +545,13 @@ s s p q -q +e p -r +R p -t +y p -z +W z p p @@ -564,7 +568,7 @@ s f d s -m +F q s s @@ -574,7 +578,7 @@ t p p z -B +z p w w @@ -592,11 +596,11 @@ s G F p -m +F r p t -y +t p z E diff --git a/_maps/map_files/Mafia/mafia_spiderclan.dmm b/_maps/map_files/Mafia/mafia_spiderclan.dmm index 090f2d27efb..ff83f8de3ec 100644 --- a/_maps/map_files/Mafia/mafia_spiderclan.dmm +++ b/_maps/map_files/Mafia/mafia_spiderclan.dmm @@ -258,11 +258,11 @@ S b r p -m +q q p r -n +r p q o @@ -280,7 +280,7 @@ c d d S -n +r r S S @@ -290,7 +290,7 @@ r S S q -m +q S d d @@ -307,13 +307,13 @@ S S p r -r +n p -q +m p -r +n p -q +m q p S @@ -354,19 +354,19 @@ S c d j -m q q q +m p t t t p +n r r r -n j i c @@ -404,19 +404,19 @@ S c d j -n r r r +n p t t t p +m q q q -m j d c @@ -457,13 +457,13 @@ S S p q -q +m p -r +n p -q +m p -r +n r p S @@ -480,7 +480,7 @@ c f d S -m +q q S S @@ -490,7 +490,7 @@ q S S r -n +r S d d @@ -508,11 +508,11 @@ S o q p -n +r r p q -m +q p r b diff --git a/_maps/map_files/Mafia/mafia_syndie.dmm b/_maps/map_files/Mafia/mafia_syndie.dmm index e8e49e65c10..12287245828 100644 --- a/_maps/map_files/Mafia/mafia_syndie.dmm +++ b/_maps/map_files/Mafia/mafia_syndie.dmm @@ -45,10 +45,6 @@ /obj/mafia_game_board, /turf/open/floor/plating, /area/centcom/mafia) -"n" = ( -/obj/effect/landmark/mafia, -/turf/open/floor/mineral/plastitanium, -/area/centcom/mafia) "o" = ( /obj/effect/turf_decal/tile/red/fourcorners, /turf/open/floor/iron/dark, @@ -68,6 +64,7 @@ name = "tactical swivel chair" }, /obj/effect/turf_decal/tile/red/fourcorners, +/obj/effect/landmark/mafia, /turf/open/floor/iron/dark, /area/centcom/mafia) "r" = ( @@ -93,12 +90,14 @@ name = "tactical swivel chair" }, /obj/effect/turf_decal/tile/red/fourcorners, +/obj/effect/landmark/mafia, /turf/open/floor/iron/dark, /area/centcom/mafia) "y" = ( /obj/structure/chair/office{ name = "tactical swivel chair" }, +/obj/effect/landmark/mafia, /turf/open/floor/mineral/plastitanium, /area/centcom/mafia) "z" = ( @@ -106,6 +105,7 @@ name = "tactical swivel chair" }, /obj/effect/turf_decal/tile/red/fourcorners, +/obj/effect/landmark/mafia, /turf/open/floor/iron/dark, /area/centcom/mafia) "A" = ( @@ -113,6 +113,7 @@ dir = 8; name = "tactical swivel chair" }, +/obj/effect/landmark/mafia, /turf/open/floor/mineral/plastitanium, /area/centcom/mafia) "B" = ( @@ -120,6 +121,7 @@ dir = 4; name = "tactical swivel chair" }, +/obj/effect/landmark/mafia, /turf/open/floor/mineral/plastitanium, /area/centcom/mafia) "C" = ( @@ -128,6 +130,7 @@ name = "tactical swivel chair" }, /obj/effect/turf_decal/tile/red/fourcorners, +/obj/effect/landmark/mafia, /turf/open/floor/iron/dark, /area/centcom/mafia) "E" = ( @@ -135,6 +138,7 @@ dir = 1; name = "tactical swivel chair" }, +/obj/effect/landmark/mafia, /turf/open/floor/mineral/plastitanium, /area/centcom/mafia) "G" = ( @@ -146,11 +150,6 @@ /obj/effect/spawner/random/clothing/syndie, /turf/open/floor/iron/dark, /area/centcom/mafia) -"H" = ( -/obj/effect/landmark/mafia, -/obj/effect/turf_decal/tile/red/fourcorners, -/turf/open/floor/iron/dark, -/area/centcom/mafia) "Q" = ( /turf/open/floor/circuit/red, /area/centcom/mafia) @@ -318,11 +317,11 @@ w G o p -n +r r p o -H +o p r b @@ -340,7 +339,7 @@ c d d w -H +o o w w @@ -350,7 +349,7 @@ o w w r -n +r w d d @@ -414,7 +413,7 @@ w c d j -n +r r r y @@ -426,7 +425,7 @@ p q o o -H +o j i c @@ -464,7 +463,7 @@ w c d j -H +o o o z @@ -476,7 +475,7 @@ p E r r -n +r j d c @@ -540,7 +539,7 @@ c f d w -n +r r w w @@ -550,7 +549,7 @@ r w w o -H +o w d d @@ -568,11 +567,11 @@ w b r p -H +o o p r -n +r p o G diff --git a/code/datums/saymode.dm b/code/datums/saymode.dm index 78ec5e6af38..52e22aeaef8 100644 --- a/code/datums/saymode.dm +++ b/code/datums/saymode.dm @@ -96,17 +96,3 @@ AI.holopad_talk(message, language) return FALSE return TRUE - -/datum/saymode/mafia - key = "j" - mode = MODE_MAFIA - -/datum/saymode/mafia/handle_message(mob/living/user, message, datum/language/language) - var/datum/mafia_controller/MF = GLOB.mafia_game - if (!MF) - return TRUE - var/datum/mafia_role/R = MF.player_role_lookup[user] - if(!R || R.team != "mafia") - return TRUE - MF.send_message(span_changeling("[R.body.real_name]: [message]"), "mafia") - return FALSE diff --git a/code/game/area/areas/centcom.dm b/code/game/area/areas/centcom.dm index 1c40347e481..f5ac4fac78e 100644 --- a/code/game/area/areas/centcom.dm +++ b/code/game/area/areas/centcom.dm @@ -213,6 +213,18 @@ name = "Syndicate Elite Squad" icon_state = "syndie-elite" +//MAFIA +/area/centcom/mafia + name = "Mafia Minigame" + icon_state = "mafia" + static_lighting = FALSE + + base_lighting_alpha = 255 + requires_power = FALSE + has_gravity = STANDARD_GRAVITY + flags_1 = NONE + area_flags = BLOCK_SUICIDE | UNIQUE_AREA + //CAPTURE THE FLAG /area/centcom/ctf name = "Capture the Flag" diff --git a/code/modules/antagonists/obsessed/obsessed.dm b/code/modules/antagonists/obsessed/obsessed.dm index 7c921fdd228..92b3140d47b 100644 --- a/code/modules/antagonists/obsessed/obsessed.dm +++ b/code/modules/antagonists/obsessed/obsessed.dm @@ -62,6 +62,7 @@ mask = /obj/item/clothing/mask/surgical neck = /obj/item/camera suit = /obj/item/clothing/suit/apron + shoes = /obj/item/clothing/shoes/sneakers/black /datum/outfit/obsessed/post_equip(mob/living/carbon/human/H) for(var/obj/item/carried_item in H.get_equipped_items(include_pockets = TRUE, include_accessories = TRUE)) diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index 20ded165fc0..f453fba3a16 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -382,6 +382,7 @@ mask = /obj/item/clothing/mask/gas l_hand = /obj/item/melee/energy/sword r_hand = /obj/item/gun/energy/recharge/ebow + shoes = /obj/item/clothing/shoes/magboots/advance /datum/outfit/traitor/post_equip(mob/living/carbon/human/H, visualsOnly) var/obj/item/melee/energy/sword/sword = locate() in H.held_items diff --git a/code/modules/asset_cache/assets/headers.dm b/code/modules/asset_cache/assets/headers.dm index e8f34dfd3f9..62c7fc532e6 100644 --- a/code/modules/asset_cache/assets/headers.dm +++ b/code/modules/asset_cache/assets/headers.dm @@ -27,4 +27,5 @@ "smmon_6.gif" = 'icons/program_icons/smmon_6.gif', "borg_mon.gif" = 'icons/program_icons/borg_mon.gif', "robotact.gif" = 'icons/program_icons/robotact.gif', + "mafia.gif" = 'icons/program_icons/mafia.gif', ) diff --git a/code/modules/mafia/_defines.dm b/code/modules/mafia/_defines.dm index 835aff9d63d..ef24645bb05 100644 --- a/code/modules/mafia/_defines.dm +++ b/code/modules/mafia/_defines.dm @@ -100,6 +100,8 @@ /// now clearing refs to prepare for the next day. Do not do any actions here, it's just for ref clearing. #define COMSIG_MAFIA_NIGHT_END "night_end" +/// signal sent to roles when the game is confirmed starting +#define COMSIG_MAFIA_GAME_START "game_start" /// signal sent to roles when the game is confirmed ending #define COMSIG_MAFIA_GAME_END "game_end" @@ -107,6 +109,8 @@ GLOBAL_LIST_EMPTY(mafia_signup) /// list of ghosts who want to play mafia that have since disconnected. They are kept in the lobby, but not counted for starting a game. GLOBAL_LIST_EMPTY(mafia_bad_signup) +/// list of PDAs who want to play mafia, every time someone enters the list it checks to see if enough are in +GLOBAL_LIST_EMPTY(pda_mafia_signup) /// the current global mafia game running. GLOBAL_VAR(mafia_game) /// list of ghosts in mafia_signup who have voted to start early diff --git a/code/modules/mafia/abilities/abilities.dm b/code/modules/mafia/abilities/abilities.dm index 59ee7570c32..3c8e643a427 100644 --- a/code/modules/mafia/abilities/abilities.dm +++ b/code/modules/mafia/abilities/abilities.dm @@ -29,6 +29,11 @@ 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, when the target is no longer set. */ @@ -52,21 +57,28 @@ if(game.phase != valid_use_period) return FALSE if(host_role.role_flags & ROLE_ROLEBLOCKED) - to_chat(host_role.body, span_warning("You were roleblocked!")) + host_role.send_message_to_player(span_warning("You were roleblocked!")) + return FALSE + if(host_role.game_status == MAFIA_DEAD) return FALSE if(potential_target) - if((use_flags & CAN_USE_ON_DEAD) && (potential_target.game_status != MAFIA_DEAD)) + 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) - to_chat(host_role.body, span_notice("This can only be used on dead players.")) + 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) - to_chat(host_role.body, span_notice("This can only be used on others.")) + 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) - to_chat(host_role.body, span_notice("This can only be used on yourself.")) + host_role.send_message_to_player(span_notice("This can only be used on yourself.")) return FALSE return TRUE @@ -90,7 +102,7 @@ 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 - to_chat(host_role.body, span_danger("Your [name] was interrupted!")) + host_role.send_message_to_player(span_danger("Your [name] was interrupted!")) return FALSE return TRUE @@ -121,5 +133,5 @@ target_role = new_target feedback_text = replacetext(feedback_text, "%WILL_PERFORM%", "now") - to_chat(host_role.body, span_notice(feedback_text)) + host_role.send_message_to_player(span_notice(feedback_text)) return TRUE diff --git a/code/modules/mafia/abilities/investigative/investigate.dm b/code/modules/mafia/abilities/investigative/investigate.dm index 0af1ed674b8..d23b51f9813 100644 --- a/code/modules/mafia/abilities/investigative/investigate.dm +++ b/code/modules/mafia/abilities/investigative/investigate.dm @@ -20,5 +20,5 @@ if(MAFIA_TEAM_SOLO) fluff = "rogue, with their own objectives..." - to_chat(host_role.body, span_warning("Your investigations reveal that [target_role.body.real_name] is [fluff]")) + host_role.send_message_to_player(span_warning("Your investigations reveal that [target_role.body.real_name] is [fluff]")) return TRUE diff --git a/code/modules/mafia/abilities/investigative/pray.dm b/code/modules/mafia/abilities/investigative/pray.dm index b5bd6ee7e88..cbf8459f9f2 100644 --- a/code/modules/mafia/abilities/investigative/pray.dm +++ b/code/modules/mafia/abilities/investigative/pray.dm @@ -14,5 +14,5 @@ if(!.) return FALSE - to_chat(host_role.body, span_warning("You invoke spirit of [target_role.body.real_name] and learn their role was [target_role.name].")) + host_role.send_message_to_player(span_warning("You invoke spirit of [target_role.body.real_name] and learn their role was [target_role.name].")) return TRUE diff --git a/code/modules/mafia/abilities/investigative/reveal.dm b/code/modules/mafia/abilities/investigative/reveal.dm index db48b552b62..5e38d3c9faf 100644 --- a/code/modules/mafia/abilities/investigative/reveal.dm +++ b/code/modules/mafia/abilities/investigative/reveal.dm @@ -13,7 +13,7 @@ if(!.) return FALSE - to_chat(host_role.body, span_warning("You have revealed the true nature of the [target_role]!")) + host_role.send_message_to_player(span_warning("You have revealed the true nature of the [target_role]!")) target_role.reveal_role(game, verbose = TRUE) return TRUE diff --git a/code/modules/mafia/abilities/investigative/thoughtfeed.dm b/code/modules/mafia/abilities/investigative/thoughtfeed.dm index 48d465173cd..f0eb49f2b4d 100644 --- a/code/modules/mafia/abilities/investigative/thoughtfeed.dm +++ b/code/modules/mafia/abilities/investigative/thoughtfeed.dm @@ -13,7 +13,7 @@ return FALSE if((target_role.role_flags & ROLE_UNDETECTABLE)) - to_chat(host_role.body,span_warning("[target_role.body.real_name]'s memories reveal that they are the [pick(game.all_roles - target_role)].")) + host_role.send_message_to_player(span_warning("[target_role.body.real_name]'s memories reveal that they are the [pick(game.all_roles - target_role)].")) else - to_chat(host_role.body,span_warning("[target_role.body.real_name]'s memories reveal that they are the [target_role.name].")) + host_role.send_message_to_player(span_warning("[target_role.body.real_name]'s memories reveal that they are the [target_role.name].")) return TRUE diff --git a/code/modules/mafia/abilities/killing/alert.dm b/code/modules/mafia/abilities/killing/alert.dm index 7af38f9befa..74710087137 100644 --- a/code/modules/mafia/abilities/killing/alert.dm +++ b/code/modules/mafia/abilities/killing/alert.dm @@ -22,8 +22,8 @@ SIGNAL_HANDLER if(attacker == host_role) return - to_chat(host_role.body, span_userdanger("You have shot a visitor!")) - to_chat(attacker.body, span_userdanger("You have visited the warden!")) + host_role.send_message_to_player(span_userdanger("You have shot a visitor!")) + attacker.send_message_to_player(span_userdanger("You have visited the warden!")) attacker.kill(game, host_role, lynch = FALSE) return MAFIA_VISIT_INTERRUPTED diff --git a/code/modules/mafia/abilities/killing/flicker_rampage.dm b/code/modules/mafia/abilities/killing/flicker_rampage.dm index 08a18e78778..10331d47986 100644 --- a/code/modules/mafia/abilities/killing/flicker_rampage.dm +++ b/code/modules/mafia/abilities/killing/flicker_rampage.dm @@ -22,11 +22,11 @@ return FALSE if(!(target_role in darkened_players)) - to_chat(target_role.body, span_userdanger("The lights begin to flicker and dim. You're in danger.")) + target_role.send_message_to_player(span_userdanger("The lights begin to flicker and dim. You're in danger.")) darkened_players += target_role else for(var/datum/mafia_role/dead_players as anything in darkened_players) - to_chat(dead_players.body, span_userdanger("A shadowy figure appears out of the darkness!")) + dead_players.send_message_to_player(span_userdanger("A shadowy figure appears out of the darkness!")) dead_players.kill(game, host_role, FALSE) darkened_players -= dead_players return TRUE @@ -37,6 +37,6 @@ return //no chance man, that's a town lynch if(attacker in darkened_players) - to_chat(host_role.body, span_userdanger("You were attacked by someone in a flickering room. You have danced in the shadows, evading them.")) + host_role.send_message_to_player(span_userdanger("You were attacked by someone in a flickering room. You have danced in the shadows, evading them.")) return MAFIA_PREVENT_KILL diff --git a/code/modules/mafia/abilities/killing/kill.dm b/code/modules/mafia/abilities/killing/kill.dm index 2c09c7525eb..d02fd6c287d 100644 --- a/code/modules/mafia/abilities/killing/kill.dm +++ b/code/modules/mafia/abilities/killing/kill.dm @@ -18,17 +18,17 @@ return FALSE if(!target_role.kill(game, host_role, FALSE)) - to_chat(host_role.body, span_danger("Your attempt at killing [target_role.body.real_name] was prevented!")) + host_role.send_message_to_player(span_danger("Your attempt at killing [target_role.body.real_name] was prevented!")) else - to_chat(target_role.body, span_userdanger("You have been [attack_action] \a [host_role.name]!")) + target_role.send_message_to_player(span_userdanger("You have been [attack_action] \a [host_role.name]!")) if(honorable && (target_role.team != MAFIA_TEAM_TOWN)) - to_chat(host_role.body, span_userdanger("You have killed an innocent crewmember. You will die tomorrow night.")) + host_role.send_message_to_player(span_userdanger("You have killed an innocent crewmember. You will die tomorrow night.")) RegisterSignal(game, COMSIG_MAFIA_SUNDOWN, PROC_REF(internal_affairs)) return TRUE /datum/mafia_ability/attack_player/proc/internal_affairs(datum/mafia_controller/game) SIGNAL_HANDLER - to_chat(host_role.body, span_userdanger("You have been killed by Nanotrasen Internal Affairs!")) + host_role.send_message_to_player(span_userdanger("You have been killed by Nanotrasen Internal Affairs!")) host_role.reveal_role(game, verbose = TRUE) host_role.kill(game, host_role, FALSE) //you technically kill yourself but that shouldn't matter diff --git a/code/modules/mafia/abilities/protective/heal.dm b/code/modules/mafia/abilities/protective/heal.dm index 65cd26ad0a1..4c47d1c85ab 100644 --- a/code/modules/mafia/abilities/protective/heal.dm +++ b/code/modules/mafia/abilities/protective/heal.dm @@ -18,7 +18,7 @@ if(!.) return FALSE if(new_target.role_flags & ROLE_VULNERABLE) - to_chat(host_role.body, span_notice("[new_target] can't be protected.")) + host_role.send_message_to_player(span_notice("[new_target] can't be protected.")) return FALSE /datum/mafia_ability/heal/perform_action_target(datum/mafia_controller/game, datum/mafia_role/day_target) @@ -39,10 +39,10 @@ /datum/mafia_ability/heal/proc/prevent_kill(datum/source, datum/mafia_controller/game, datum/mafia_role/attacker, lynch) SIGNAL_HANDLER if(host_role == target_role) - to_chat(host_role.body, span_warning("You were attacked last night!")) + host_role.send_message_to_player(span_warning("You were attacked last night!")) return MAFIA_PREVENT_KILL - to_chat(host_role.body, span_warning("The person you protected tonight was attacked!")) - to_chat(target_role.body, span_greentext("You were attacked last night, but [saving_message]!")) + host_role.send_message_to_player(span_warning("The person you protected tonight was attacked!")) + target_role.send_message_to_player(span_greentext("You were attacked last night, but [saving_message]!")) return MAFIA_PREVENT_KILL /** @@ -60,6 +60,6 @@ return FALSE if(attacker.kill(game, host_role, FALSE)) //you attack the attacker - to_chat(attacker.body, span_userdanger("You have been ambushed by Security!")) + attacker.send_message_to_player(span_userdanger("You have been ambushed by Security!")) host_role.kill(game, attacker, FALSE) //the attacker attacks you, they were able to attack the target so they can attack you. return FALSE diff --git a/code/modules/mafia/abilities/protective/vest.dm b/code/modules/mafia/abilities/protective/vest.dm index 2c65202dd43..b65a8cb73ba 100644 --- a/code/modules/mafia/abilities/protective/vest.dm +++ b/code/modules/mafia/abilities/protective/vest.dm @@ -33,7 +33,7 @@ /datum/mafia_ability/vest/proc/self_defense(datum/source, datum/mafia_controller/game, datum/mafia_role/attacker, lynch) SIGNAL_HANDLER - to_chat(host_role.body, span_greentext("Your vest saved you!")) + host_role.send_message_to_player(span_greentext("Your vest saved you!")) return MAFIA_PREVENT_KILL /datum/mafia_ability/vest/proc/end_protection(datum/mafia_controller/game) diff --git a/code/modules/mafia/abilities/voting/changeling_kill.dm b/code/modules/mafia/abilities/voting/changeling_kill.dm index 0e851d78a95..bb1b1e76c81 100644 --- a/code/modules/mafia/abilities/voting/changeling_kill.dm +++ b/code/modules/mafia/abilities/voting/changeling_kill.dm @@ -30,12 +30,32 @@ ling_sent = TRUE if(target_role.kill(game, host_role, FALSE)) - to_chat(target_role.body, span_userdanger("You have been killed by a Changeling!")) + target_role.send_message_to_player(span_userdanger("You have been killed by a Changeling!")) game.send_message(span_danger("[host_role.body.real_name] was selected to attack [target_role.body.real_name] tonight!"), MAFIA_TEAM_MAFIA) return TRUE /datum/mafia_ability/changeling_kill/set_target(datum/mafia_controller/game, datum/mafia_role/new_target) + if(new_target.team == MAFIA_TEAM_MAFIA) + return FALSE if(!validate_action_target(game, new_target)) return FALSE using_ability = TRUE game.vote_for(host_role, new_target, "Mafia", MAFIA_TEAM_MAFIA) + +/** + * handle_message + * + * During the night, Changelings talking will instead redirect it to Changeling chat. + */ +/datum/mafia_ability/changeling_kill/handle_speech(datum/source, list/speech_args) + . = ..() + var/datum/mafia_controller/mafia_game = GLOB.mafia_game + if(!mafia_game) + return FALSE + if (mafia_game.phase != MAFIA_PHASE_NIGHT) + return FALSE + + var/phrase = html_decode(speech_args[SPEECH_MESSAGE]) + mafia_game.send_message(span_changeling("[host_role.body.real_name]: [phrase]"), MAFIA_TEAM_MAFIA) + speech_args[SPEECH_MESSAGE] = "" + return TRUE diff --git a/code/modules/mafia/controller.dm b/code/modules/mafia/controller.dm index f8d9db13106..967ae9b39a3 100644 --- a/code/modules/mafia/controller.dm +++ b/code/modules/mafia/controller.dm @@ -2,6 +2,9 @@ GLOBAL_LIST_INIT(mafia_roles_by_name, setup_mafia_roles_by_name()) GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment()) +///How many votes are needed to unlock the 'Universally Hated' achievement. +#define UNIVERSALLY_HATED_REQUIREMENT 12 + /** * The mafia controller handles the mafia minigame in progress. * It is first created when the first ghost signs up to play. @@ -11,7 +14,8 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment()) var/list/datum/mafia_role/all_roles = list() ///all living roles in the game, removed on death. var/list/datum/mafia_role/living_roles = list() - ///exists to speed up role retrieval, it's a dict. `player_role_lookup[player ckey]` will give you the role they play + ///exists to speed up role retrieval, it's a dict. + /// `player_role_lookup[player ckey/PDA]` will give you the role they play var/list/player_role_lookup = list() ///what part of the game you're playing in. day phases, night phases, judgement phases, etc. var/phase = MAFIA_PHASE_SETUP @@ -105,10 +109,10 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment()) /datum/mafia_controller/Destroy(force, ...) . = ..() - if(GLOB.mafia_game == src) - GLOB.mafia_game = null end_game() QDEL_NULL(map_deleter) + if(GLOB.mafia_game == src) + GLOB.mafia_game = null /** * Triggers at beginning of the game when there is a confirmed list of valid, ready players. @@ -122,9 +126,10 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment()) * * Puts players in each role randomly * Arguments: * * setup_list: list of all the datum setups (fancy list of roles) that would work for the game - * * ready_players: list of filtered, sane players (so not playing or disconnected) for the game to put into roles + * * ready_ghosts: list of filtered, sane players (so not playing or disconnected) for the game to put into roles + * * ready_pdas: list of PDAs wanting to play the Mafia game. */ -/datum/mafia_controller/proc/prepare_game(setup_list, ready_players) +/datum/mafia_controller/proc/prepare_game(setup_list, ready_ghosts, ready_pdas) var/static/list/possible_maps = subtypesof(/datum/map_template/mafia) var/turf/spawn_area = get_turf(locate(/obj/effect/landmark/mafia_game_area) in GLOB.landmarks_list) @@ -156,16 +161,32 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment()) var/list/spawnpoints = landmarks.Copy() for(var/datum/mafia_role/role as anything in all_roles) role.assigned_landmark = pick_n_take(spawnpoints) + var/selected_player if(!debug) - role.player_key = pick_n_take(ready_players) + if(length(ready_pdas)) + selected_player = pick(ready_pdas) + else + selected_player = pick(ready_ghosts) else - role.player_key = pop(ready_players) + if(length(ready_pdas)) + selected_player = peek(ready_pdas) + else + selected_player = peek(ready_ghosts) + if(selected_player in ready_pdas) + role.player_pda = selected_player + ready_pdas -= selected_player + else + role.player_key = selected_player + ready_ghosts -= selected_player -/datum/mafia_controller/proc/send_message(msg, team) +///Sends a global message to all players, or just 'team' if set. +/datum/mafia_controller/proc/send_message(msg, team, log_only = FALSE) for(var/datum/mafia_role/role as anything in all_roles) if(team && role.team != team) continue - to_chat(role.body, msg) + role.role_messages += msg + if(!log_only) + to_chat(role.body, msg) /** * The game by this point is now all set up, and so we can put people in their bodies and start the first phase. @@ -176,10 +197,16 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment()) */ /datum/mafia_controller/proc/start_game() create_bodies() + SEND_GLOBAL_SIGNAL(COMSIG_MAFIA_GAME_START, src) start_day(can_vote = FALSE) - send_message(span_notice("The selected map is [current_map.name]!
[current_map.description]")) + send_message(span_notice("The selected map is [current_map.name]!
[current_map.description]")) send_message("Day [turn] started! There is no voting on the first day. Say hello to everybody!") next_phase_timer = addtimer(CALLBACK(src, PROC_REF(check_trial), FALSE), (FIRST_DAY_PERIOD_LENGTH / time_speedup), TIMER_STOPPABLE) //no voting period = no votes = instant night + for(var/datum/mafia_role/roles as anything in all_roles) + var/obj/item/modular_computer/modpc = roles.player_pda + if(!modpc) + continue + modpc.update_static_data_for_all_viewers() /** * How every day starts. @@ -229,7 +256,7 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment()) var/datum/mafia_role/loser = get_vote_winner("Day")//, majority_of_town = TRUE) var/loser_votes = get_vote_count(loser, "Day") if(loser) - if(loser_votes > 12) + if(loser_votes > UNIVERSALLY_HATED_REQUIREMENT) award_role(/datum/award/achievement/mafia/universally_hated, loser) //refresh the lists judgement_abstain_votes = list() @@ -369,8 +396,11 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment()) * * role: mafia_role datum to reward. */ /datum/mafia_controller/proc/award_role(award, datum/mafia_role/rewarded) - var/client/role_client = rewarded.body.client - role_client?.give_award(award, rewarded.body) + rewarded.body?.client?.give_award(award, rewarded.body) + if(!rewarded.player_pda) + return + for(var/datum/tgui/window as anything in rewarded.player_pda.open_uis) + window.user?.client?.give_award(award, rewarded.body) /** * The end of the game is in two procs, because we want a bit of time for players to see eachothers roles. @@ -584,21 +614,28 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment()) RegisterSignal(H, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(display_votes)) var/datum/action/innate/mafia_panel/mafia_panel = new(null,src) mafia_panel.Grant(H) + var/obj/item/modular_computer/modpc = role.player_pda + role.register_body(H) + if(modpc) + player_role_lookup[modpc] = role + else + player_role_lookup[H] = role var/client/player_client = GLOB.directory[role.player_key] if(player_client) - player_client.prefs.safe_transfer_prefs_to(H, is_antag = TRUE) - role.body = H - player_role_lookup[H] = role - role.put_player_in_body(player_client) + role.put_player_in_body(player_client) role.greet() -/datum/mafia_controller/ui_static_data(mob/user) +/datum/mafia_controller/ui_static_data(atom/user) var/list/data = list() - if(user.client?.holder) + if(usr.client?.holder) data["admin_controls"] = TRUE //show admin buttons to start/setup/stop + data["is_observer"] = isobserver(user) data["all_roles"] = current_setup_text + if(phase == MAFIA_PHASE_SETUP) + return data + var/datum/mafia_role/user_role = player_role_lookup[user] if(user_role) data["roleinfo"] = list( @@ -610,7 +647,7 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment()) return data -/datum/mafia_controller/ui_data(mob/user) +/datum/mafia_controller/ui_data(atom/user) var/list/data = list() data["phase"] = phase @@ -619,18 +656,25 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment()) if(phase == MAFIA_PHASE_SETUP) data["lobbydata"] = list() - for(var/key in GLOB.mafia_signup + GLOB.mafia_bad_signup) + for(var/key in GLOB.mafia_signup + GLOB.mafia_bad_signup + GLOB.pda_mafia_signup) var/list/lobby_member = list() lobby_member["name"] = key lobby_member["status"] = (key in GLOB.mafia_bad_signup) ? "Disconnected" : "Ready" data["lobbydata"] += list(lobby_member) return data - data["timeleft"] = next_phase_timer ? timeleft(next_phase_timer) : 0 //the tgui menu counts this down. + data["timeleft"] = next_phase_timer ? timeleft(next_phase_timer) : 0 var/datum/mafia_role/user_role = player_role_lookup[user] if(user_role) data["user_notes"] = user_role.written_notes + data["messages"] = list() + var/list/ui_messages = list() + for(var/i = user_role.role_messages.len to 1 step -1) + ui_messages.Add(list(list( + "msg" = user_role.role_messages[i], + ))) + data["messages"] = ui_messages data["players"] = list() for(var/datum/mafia_role/role as anything in all_roles) @@ -659,8 +703,13 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment()) if(.) return var/datum/mafia_role/user_role = player_role_lookup[usr] + var/obj/item/modular_computer/modpc = ui.src_object + if(istype(modpc)) + user_role = player_role_lookup[modpc] + else + modpc = null //Admin actions - if(usr.client?.holder) + if(ui.user.client.holder) switch(action) if("new_game") if(phase == MAFIA_PHASE_SETUP) @@ -686,9 +735,8 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment()) player.body.forceMove(get_turf(player.assigned_landmark)) if(failed.len) to_chat(usr, "List of players who no longer had a body (if you see this, the game is runtiming anyway so just hit \"New Game\" to end it)") - for(var/i in failed) - var/datum/mafia_role/fail = i - to_chat(usr, fail.player_key) + for(var/datum/mafia_role/fail as anything in failed) + to_chat(usr, fail.player_key || fail.player_pda) if("debug_setup") var/list/debug_setup = list() var/list/rolelist_dict = list("CANCEL", "FINISH") + GLOB.mafia_roles_by_name @@ -732,38 +780,61 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment()) if(!user_role)//just the dead switch(action) if("mf_signup") - var/client/C = ui.user.client + var/client/ghost_client = ui.user.client if(!SSticker.HasRoundStarted()) to_chat(usr, span_warning("Wait for the round to start.")) return - if(GLOB.mafia_signup[C.ckey]) - GLOB.mafia_signup -= C.ckey - GLOB.mafia_early_votes -= C.ckey //Remove their early start vote as well - to_chat(usr, span_notice("You unregister from Mafia.")) - return TRUE + if(isnull(modpc)) + if(GLOB.mafia_signup[ghost_client.ckey]) + GLOB.mafia_signup -= ghost_client.ckey + GLOB.mafia_early_votes -= ghost_client.ckey //Remove their early start vote as well + to_chat(usr, span_notice("You unregister from Mafia.")) + return TRUE + else + GLOB.mafia_signup[ghost_client.ckey] = TRUE + to_chat(usr, span_notice("You sign up for Mafia.")) else - GLOB.mafia_signup[C.ckey] = TRUE - to_chat(usr, span_notice("You sign up for Mafia.")) + if(GLOB.pda_mafia_signup[modpc]) + GLOB.pda_mafia_signup -= modpc + GLOB.mafia_early_votes -= modpc //Remove their early start vote as well + to_chat(usr, span_notice("You unregister from Mafia.")) + return TRUE + else + GLOB.pda_mafia_signup[modpc] = TRUE + to_chat(usr, span_notice("You sign up for Mafia.")) if(phase == MAFIA_PHASE_SETUP) check_signups() try_autostart() return TRUE if("vote_to_start") - var/client/C = ui.user.client + var/client/ghost_client = ui.user.client if(phase != MAFIA_PHASE_SETUP) to_chat(usr, span_notice("You cannot vote to start while a game is underway!")) return - if(!GLOB.mafia_signup[C.ckey]) - to_chat(usr, span_notice("You must be signed up for this game to vote!")) - return - if(GLOB.mafia_early_votes[C.ckey]) - GLOB.mafia_early_votes -= C.ckey - to_chat(usr, span_notice("You are no longer voting to start the game early.")) + if(isnull(modpc)) + if(!GLOB.mafia_signup[ghost_client.ckey]) + to_chat(usr, span_notice("You must be signed up for this game to vote!")) + return + if(GLOB.mafia_early_votes[ghost_client.ckey]) + GLOB.mafia_early_votes -= ghost_client.ckey + to_chat(usr, span_notice("You are no longer voting to start the game early.")) + else + GLOB.mafia_early_votes[ghost_client.ckey] = ghost_client + to_chat(usr, span_notice("You vote to start the game early ([length(GLOB.mafia_early_votes)] out of [max(round(length(GLOB.mafia_signup) / 2), round(MAFIA_MIN_PLAYER_COUNT / 2))]).")) + if(check_start_votes()) //See if we have enough votes to start + forced_setup() else - GLOB.mafia_early_votes[C.ckey] = C - to_chat(usr, span_notice("You vote to start the game early ([length(GLOB.mafia_early_votes)] out of [max(round(length(GLOB.mafia_signup) / 2), round(MAFIA_MIN_PLAYER_COUNT / 2))]).")) - if(check_start_votes()) //See if we have enough votes to start - forced_setup() + if(!GLOB.pda_mafia_signup[modpc]) + to_chat(usr, span_notice("You must be signed up for this game to vote!")) + return + if(GLOB.mafia_early_votes[modpc]) + GLOB.mafia_early_votes -= modpc + to_chat(usr, span_notice("You are no longer voting to start the game early.")) + else + GLOB.mafia_early_votes[modpc] = modpc + to_chat(usr, span_notice("You vote to start the game early ([length(GLOB.mafia_early_votes)] out of [max(round(length(GLOB.mafia_signup) / 2), round(MAFIA_MIN_PLAYER_COUNT / 2))]).")) + if(check_start_votes()) //See if we have enough votes to start + forced_setup() return TRUE if(user_role && user_role.game_status == MAFIA_DEAD) @@ -774,8 +845,14 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment()) if("change_notes") if(user_role.game_status == MAFIA_DEAD) return TRUE - user_role.written_notes = params["new_notes"] - user_role.body.balloon_alert(user_role.body, "notes saved") + user_role.written_notes = sanitize_text(params["new_notes"]) + user_role.send_message_to_player("notes saved", balloon_alert = TRUE) + return TRUE + if("send_message_to_chat") + if(user_role.game_status == MAFIA_DEAD) + return TRUE + var/message_said = sanitize_text(params["message"]) + user_role.body.say(message_said, forced = "mafia chat (sent by [ui.user.client])") return TRUE if("send_notes_to_chat") if(user_role.game_status == MAFIA_DEAD || !user_role.written_notes) @@ -807,21 +884,21 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment()) if("vote_abstain") if(phase != MAFIA_PHASE_JUDGEMENT || (user_role in judgement_abstain_votes)) return - to_chat(user_role.body,"You have decided to abstain.") + user_role.send_message_to_player("You have decided to abstain.") judgement_innocent_votes -= user_role judgement_guilty_votes -= user_role judgement_abstain_votes += user_role if("vote_innocent") if(phase != MAFIA_PHASE_JUDGEMENT || (user_role in judgement_innocent_votes)) return - to_chat(user_role.body,"Your vote on [on_trial.body.real_name] submitted as INNOCENT!") + user_role.send_message_to_player("Your vote on [on_trial.body.real_name] submitted as INNOCENT!") judgement_abstain_votes -= user_role//no fakers, and... judgement_guilty_votes -= user_role//no radical centrism judgement_innocent_votes += user_role if("vote_guilty") if(phase != MAFIA_PHASE_JUDGEMENT || (user_role in judgement_guilty_votes)) return - to_chat(user_role.body,"Your vote on [on_trial.body.real_name] submitted as GUILTY!") + user_role.send_message_to_player("Your vote on [on_trial.body.real_name] submitted as GUILTY!") judgement_abstain_votes -= user_role//no fakers, and... judgement_innocent_votes -= user_role//no radical centrism judgement_guilty_votes += user_role @@ -886,12 +963,15 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment()) if(setup.len) req_players = assoc_value_sum(setup) + var/list/filtered_pdas = GLOB.pda_mafia_signup + if(!isnull(filtered_pdas)) //pdas get priority + req_players -= length(GLOB.pda_mafia_signup) var/list/filtered_keys = filter_players(req_players) - var/needed_players = length(filtered_keys) + var/needed_players = length(filtered_keys) + length(filtered_pdas) if(!setup.len) //don't actually have one yet, so generate a max player random setup. it's good to do this here instead of above so it doesn't generate one every time a game could possibly start. setup = generate_standard_setup(needed_players) - prepare_game(setup, filtered_keys) + prepare_game(setup, filtered_keys, filtered_pdas) start_game() /** @@ -902,8 +982,9 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment()) /datum/mafia_controller/proc/forced_setup() check_signups() //Refresh the signup list, so our numbers are accurate and we only take active players into consideration. + var/list/filtered_pdas = GLOB.pda_mafia_signup var/list/filtered_keys = filter_players(length(GLOB.mafia_signup)) - var/req_players = length(filtered_keys) + var/req_players = length(filtered_keys) + length(filtered_pdas) if(!req_players) //If we have nobody signed up, we give up on starting log_admin("Attempted to force a mafia game to start with nobody signed up!") @@ -911,7 +992,7 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment()) var/list/setup = generate_standard_setup(req_players) - prepare_game(setup, filtered_keys) + prepare_game(setup, filtered_keys, filtered_pdas) early_start = TRUE start_game() @@ -983,7 +1064,7 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment()) /datum/mafia_controller/proc/try_autostart() if(phase != MAFIA_PHASE_SETUP || !(GLOB.ghost_role_flags & GHOSTROLE_MINIGAME)) return - if(GLOB.mafia_signup.len >= MAFIA_MAX_PLAYER_COUNT || custom_setup)//enough people to try and make something (or debug mode) + if((GLOB.mafia_signup.len + GLOB.pda_mafia_signup.len) >= MAFIA_MAX_PLAYER_COUNT || custom_setup)//enough people to try and make something (or debug mode) basic_setup() /** @@ -1034,7 +1115,7 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment()) maptext_height = 480 maptext_width = 480 ///The client that owns the popup. - var/datum/mafia_role/mafia/owner + var/datum/mafia_role/owner /atom/movable/screen/mafia_popup/Initialize(mapload, datum/mafia_role/mafia) . = ..() @@ -1045,6 +1126,9 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment()) return ..() /atom/movable/screen/mafia_popup/proc/update_text(text) + owner.role_messages += text + if(!owner.body.client) + return maptext = MAPTEXT(" [text]") maptext_width = view_to_pixels(owner.body.client?.view_size.getView())[1] owner.body.client?.screen += src @@ -1063,3 +1147,5 @@ GLOBAL_LIST_INIT(mafia_role_by_alignment, setup_mafia_role_by_alignment()) QDEL_NULL(GLOB.mafia_game) var/datum/mafia_controller/new_controller = new() return new_controller + +#undef UNIVERSALLY_HATED_REQUIREMENT diff --git a/code/modules/mafia/map_pieces.dm b/code/modules/mafia/map_pieces.dm index 342f4a9045a..0d27d6cd1b5 100644 --- a/code/modules/mafia/map_pieces.dm +++ b/code/modules/mafia/map_pieces.dm @@ -26,17 +26,6 @@ MF = create_mafia_game() MF.ui_interact(user) -/area/centcom/mafia - name = "Mafia Minigame" - icon_state = "mafia" - static_lighting = FALSE - - base_lighting_alpha = 255 - requires_power = FALSE - has_gravity = STANDARD_GRAVITY - flags_1 = NONE - area_flags = BLOCK_SUICIDE | UNIQUE_AREA - /datum/map_template/mafia should_place_on_top = FALSE ///A brief background tidbit diff --git a/code/modules/mafia/roles/changelings/changeling.dm b/code/modules/mafia/roles/changelings/changeling.dm index 2415e39f8ee..8e650515ffb 100644 --- a/code/modules/mafia/roles/changelings/changeling.dm +++ b/code/modules/mafia/roles/changelings/changeling.dm @@ -1,6 +1,6 @@ /datum/mafia_role/mafia name = "Changeling" - desc = "You're a member of the changeling hive. Use ':j' talk prefix to talk to your fellow lings." + desc = "You're a member of the changeling hive. You may speak with your fellow Changelings at night." team = MAFIA_TEAM_MAFIA role_type = MAFIA_REGULAR role_flags = ROLE_CAN_KILL diff --git a/code/modules/mafia/roles/roles.dm b/code/modules/mafia/roles/roles.dm index e8b5f8a63ab..efe0d25c5b9 100644 --- a/code/modules/mafia/roles/roles.dm +++ b/code/modules/mafia/roles/roles.dm @@ -15,7 +15,15 @@ ///The player's written notes, that they can send to chat at any time. var/written_notes + ///The ckey of the person playing as this Mafia role, CAN BE NULL IN FAVOR OF player_pda. var/player_key + ///The PDA of the person playing as this Mafia role, CAN BE NULL IN FAVOR OF player_key. + var/obj/item/modular_computer/player_pda + + ///List of all messages this role got throughout the game. + var/list/role_messages = list() + + var/mob/living/carbon/human/body var/obj/effect/landmark/mafia/assigned_landmark @@ -46,11 +54,52 @@ role_unique_actions -= abilities /datum/mafia_role/Destroy(force, ...) + UnregisterSignal(body, COMSIG_MOB_SAY) QDEL_NULL(mafia_alert) QDEL_NULL(body) QDEL_LIST(role_unique_actions) + role_messages = null return ..() +/datum/mafia_role/proc/register_body(mob/living/carbon/human/new_body) + body = new_body + RegisterSignal(new_body, COMSIG_MOB_SAY, PROC_REF(handle_speech)) + +/** + * send_message_to_player + * + * Sends a message to a player, checking if they are playing through a PDA or not. + * Args: + * * message - The message to send to the person + * * balloon_alert - Whether it should be as a balloon alert, only if it's to a non-PDA user. + */ +/datum/mafia_role/proc/send_message_to_player(message, balloon_alert = FALSE) + if(player_pda) + role_messages += message + return + if(balloon_alert) + body.balloon_alert(body, message) + return + to_chat(body, message) + +/** + * handle_speech + * + * Handles Mafia roles talking in chat. + * First it will go through their abilities for Ability-specific speech, + * if none affects it, we will go to day chat. + */ +/datum/mafia_role/proc/handle_speech(datum/source, list/speech_args) + SIGNAL_HANDLER + for(var/datum/mafia_ability/abilities as anything in role_unique_actions) + if(abilities.handle_speech(source, speech_args)) + return + var/datum/mafia_controller/mafia_game = GLOB.mafia_game + if(!mafia_game || mafia_game.phase == MAFIA_PHASE_NIGHT) + return + var/message = "[source]: [html_decode(speech_args[SPEECH_MESSAGE])]" + mafia_game.send_message(message, log_only = TRUE) + /** * Puts the player in their body and keeps track of their previous one to put them back in later. * Adds the playing_mafia trait so people examining them will know why they're currently lacking a soul. diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index 0de25b903ab..89e6eca3293 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -500,7 +500,7 @@ if(!caller || !caller.alert_able || caller.alert_silenced || !alerttext) //Yeah, we're checking alert_able. No, you don't get to make alerts that the user can't silence. return FALSE playsound(src, sound, 50, TRUE) - loc.visible_message(span_notice("[icon2html(src)] [span_notice("The [src] displays a [caller.filedesc] notification: [alerttext]")]")) + physical.loc.visible_message(span_notice("[icon2html(physical, viewers(physical.loc))] \The [src] displays a [caller.filedesc] notification: [alerttext]")) /obj/item/modular_computer/proc/ring(ringtone) // bring bring if(HAS_TRAIT(SSstation, STATION_TRAIT_PDA_GLITCHED)) @@ -517,7 +517,6 @@ var/list/data = list() data["PC_device_theme"] = device_theme - data["PC_showbatteryicon"] = !!internal_cell if(internal_cell) switch(internal_cell.percent()) @@ -535,8 +534,8 @@ data["PC_batteryicon"] = "batt_5.gif" data["PC_batterypercent"] = "[round(internal_cell.percent())]%" else - data["PC_batteryicon"] = "batt_5.gif" - data["PC_batterypercent"] = "N/C" + data["PC_batteryicon"] = null + data["PC_batterypercent"] = null switch(get_ntnet_status()) if(NTNET_NO_SIGNAL) diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm index 6f693b5bf99..8cb741d376e 100644 --- a/code/modules/modular_computers/file_system/program.dm +++ b/code/modules/modular_computers/file_system/program.dm @@ -132,7 +132,7 @@ if(isAdminGhostAI(user)) return TRUE - if(!transfer && computer && (computer.obj_flags & EMAGGED)) //emags can bypass the execution locks but not the download ones. + if(computer && (computer.obj_flags & EMAGGED) && (available_on_syndinet || !transfer)) //emagged can run anything on syndinet, and can bypass execution locks, but not download. return TRUE // Defaults to required_access diff --git a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm index 339dd3175eb..856cc9b6b02 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm @@ -19,11 +19,11 @@ /datum/computer_file/program/ntnet_dos/process_tick(seconds_per_tick) dos_speed = 0 switch(ntnet_status) - if(1) + if(NTNET_LOW_SIGNAL) dos_speed = NTNETSPEED_LOWSIGNAL * 10 - if(2) + if(NTNET_GOOD_SIGNAL) dos_speed = NTNETSPEED_HIGHSIGNAL * 10 - if(3) + if(NTNET_ETHERNET_SIGNAL) dos_speed = NTNETSPEED_ETHERNET * 10 if(target && executed) target.dos_overload += dos_speed diff --git a/code/modules/modular_computers/file_system/programs/frontier.dm b/code/modules/modular_computers/file_system/programs/frontier.dm index b724892da7e..9c6e33bcde9 100644 --- a/code/modules/modular_computers/file_system/programs/frontier.dm +++ b/code/modules/modular_computers/file_system/programs/frontier.dm @@ -18,12 +18,9 @@ /// The file under consideration. var/datum/computer_file/data/ordnance/selected_file -/datum/computer_file/program/scipaper_program/New() +/datum/computer_file/program/scipaper_program/on_install(datum/computer_file/source, obj/item/modular_computer/computer_installing) . = ..() paper_to_be = new - -/datum/computer_file/program/scipaper_program/on_start(mob/living/user) - . = ..() if(!CONFIG_GET(flag/no_default_techweb_link) && !linked_techweb) CONNECT_TO_RND_SERVER_ROUNDSTART(linked_techweb, computer) diff --git a/code/modules/modular_computers/file_system/programs/mafia_ntos.dm b/code/modules/modular_computers/file_system/programs/mafia_ntos.dm new file mode 100644 index 00000000000..7bd5b81a240 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/mafia_ntos.dm @@ -0,0 +1,72 @@ +/datum/computer_file/program/mafia + filename = "mafia" + filedesc = "Mafia" + program_icon_state = "mafia" + extended_desc = "A program that allows you to play the infamous Mafia game, straight from your Modular PC." + requires_ntnet = FALSE + size = 6 + tgui_id = "NtosMafiaPanel" + program_icon = "user-secret" + alert_able = TRUE + +/datum/computer_file/program/mafia/on_install(datum/computer_file/source, obj/item/modular_computer/computer_installing) + . = ..() + RegisterSignal(SSdcs, COMSIG_MAFIA_GAME_START, PROC_REF(on_game_start)) + +/datum/computer_file/program/mafia/Destroy(force) + var/datum/mafia_controller/game = GLOB.mafia_game + if(!game) + return + UnregisterSignal(game, COMSIG_MAFIA_GAME_END) + var/datum/mafia_role/pda_role = game.player_role_lookup[computer] + if(!pda_role) + return + game.send_message(span_notice("[pda_role.body] has deleted the game from their PDA, and therefore has left the game.")) + pda_role.kill(game) + return ..() + +/datum/computer_file/program/mafia/ui_static_data(mob/user) + var/list/data = list() + var/datum/mafia_controller/game = GLOB.mafia_game + if(!game) + game = create_mafia_game() + data += game.ui_static_data(computer) + return data + +/datum/computer_file/program/mafia/ui_data(mob/user) + var/list/data = list() + var/datum/mafia_controller/game = GLOB.mafia_game + if(!game) + game = create_mafia_game() + data += game.ui_data(computer) + return data + +/datum/computer_file/program/mafia/ui_assets(mob/user) + var/list/data = list() + var/datum/mafia_controller/game = GLOB.mafia_game + if(!game) + game = create_mafia_game() + data += game.ui_assets(user) + return data + +/datum/computer_file/program/mafia/ui_act(mob/user, params, datum/tgui/ui, datum/ui_state/state) + var/datum/mafia_controller/game = GLOB.mafia_game + if(!game) + game = create_mafia_game() + return game.ui_act(user, params, ui, state) + +///Called when a game of Mafia starts, sets the ui header to the proper one. +/datum/computer_file/program/mafia/proc/on_game_start(datum/controller/subsystem/processing/dcs/source, datum/mafia_controller/game) + SIGNAL_HANDLER + RegisterSignal(game, COMSIG_MAFIA_GAME_END, PROC_REF(on_game_end)) + ui_header = "mafia.gif" + if(game.player_role_lookup[computer]) + alert_pending = TRUE + computer.alert_call(src, "Mafia game started!") + +///Called when a game of Mafia ends, deletes its ui header. +/datum/computer_file/program/mafia/proc/on_game_end(datum/mafia_controller/game) + SIGNAL_HANDLER + UnregisterSignal(game, COMSIG_MAFIA_GAME_END) + ui_header = null + update_static_data_for_all_viewers() diff --git a/code/modules/modular_computers/file_system/programs/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/ntdownloader.dm index efa61b2630c..c9723d905b5 100644 --- a/code/modules/modular_computers/file_system/programs/ntdownloader.dm +++ b/code/modules/modular_computers/file_system/programs/ntdownloader.dm @@ -7,19 +7,16 @@ size = 4 requires_ntnet = TRUE available_on_ntnet = FALSE - ui_header = "downloader_finished.gif" tgui_id = "NtosNetDownloader" program_icon = "download" - var/datum/computer_file/program/downloaded_file = null + var/datum/computer_file/program/downloaded_file var/hacked_download = FALSE var/download_completion = FALSE //GQ of downloaded data. var/download_netspeed = 0 var/downloaderror = "" - var/list/main_repo - var/list/antag_repo - var/list/show_categories = list( + var/static/list/show_categories = list( PROGRAM_CATEGORY_CREW, PROGRAM_CATEGORY_ENGI, PROGRAM_CATEGORY_SCI, @@ -27,10 +24,9 @@ PROGRAM_CATEGORY_MISC, ) -/datum/computer_file/program/ntnetdownload/on_start() +/datum/computer_file/program/ntnetdownload/kill_program(mob/user) . = ..() - main_repo = SSmodular_computers.available_station_software - antag_repo = SSmodular_computers.available_antag_software + ui_header = null /datum/computer_file/program/ntnetdownload/proc/begin_file_download(filename) if(downloaded_file) @@ -50,10 +46,10 @@ ui_header = "downloader_running.gif" - if(PRG in main_repo) + if(PRG in SSmodular_computers.available_station_software) generate_network_log("Began downloading file [PRG.filename].[PRG.filetype] from NTNet Software Repository.") hacked_download = FALSE - else if(PRG in antag_repo) + else if(PRG in SSmodular_computers.available_antag_software) generate_network_log("Began downloading file **ENCRYPTED**.[PRG.filetype] from unspecified server.") hacked_download = TRUE else @@ -68,7 +64,7 @@ generate_network_log("Aborted download of file [hacked_download ? "**ENCRYPTED**" : "[downloaded_file.filename].[downloaded_file.filetype]"].") downloaded_file = null download_completion = FALSE - ui_header = "downloader_finished.gif" + ui_header = null /datum/computer_file/program/ntnetdownload/proc/complete_file_download() if(!downloaded_file) @@ -90,11 +86,11 @@ download_netspeed = 0 // Speed defines are found in misc.dm switch(ntnet_status) - if(1) + if(NTNET_LOW_SIGNAL) download_netspeed = NTNETSPEED_LOWSIGNAL - if(2) + if(NTNET_GOOD_SIGNAL) download_netspeed = NTNETSPEED_HIGHSIGNAL - if(3) + if(NTNET_ETHERNET_SIGNAL) download_netspeed = NTNETSPEED_ETHERNET download_completion += download_netspeed @@ -132,7 +128,7 @@ data["disk_used"] = computer.used_capacity data["emagged"] = (computer.obj_flags & EMAGGED) - var/list/repo = antag_repo | main_repo + var/list/repo = SSmodular_computers.available_antag_software | SSmodular_computers.available_station_software var/list/program_categories = list() for(var/datum/computer_file/program/programs as anything in repo) @@ -147,7 +143,7 @@ "installed" = !!computer.find_file_by_name(programs.filename), "compatible" = check_compatibility(programs), "size" = programs.size, - "access" = (computer.obj_flags & EMAGGED) && programs.available_on_syndinet ? TRUE : programs.can_run(user, transfer = TRUE, access = access), + "access" = programs.can_run(user, transfer = TRUE, access = access), "verifiedsource" = programs.available_on_ntnet, )) diff --git a/code/modules/modular_computers/file_system/programs/techweb.dm b/code/modules/modular_computers/file_system/programs/techweb.dm index dc9538cf358..77d0a0900e4 100644 --- a/code/modules/modular_computers/file_system/programs/techweb.dm +++ b/code/modules/modular_computers/file_system/programs/techweb.dm @@ -21,7 +21,7 @@ /// Sequence var for the id cache var/id_cache_seq = 1 -/datum/computer_file/program/science/on_start(mob/living/user) +/datum/computer_file/program/science/on_install(datum/computer_file/source, obj/item/modular_computer/computer_installing) . = ..() if(!CONFIG_GET(flag/no_default_techweb_link) && !stored_research) CONNECT_TO_RND_SERVER_ROUNDSTART(stored_research, computer) diff --git a/icons/obj/machines/modular_console.dmi b/icons/obj/machines/modular_console.dmi index 86e3d713957..2677dbb7122 100644 Binary files a/icons/obj/machines/modular_console.dmi and b/icons/obj/machines/modular_console.dmi differ diff --git a/icons/obj/modular_laptop.dmi b/icons/obj/modular_laptop.dmi index 22432826e92..ee275066d16 100644 Binary files a/icons/obj/modular_laptop.dmi and b/icons/obj/modular_laptop.dmi differ diff --git a/icons/obj/modular_pda.dmi b/icons/obj/modular_pda.dmi index 18df3249e62..b43be78e717 100644 Binary files a/icons/obj/modular_pda.dmi and b/icons/obj/modular_pda.dmi differ diff --git a/icons/program_icons/mafia.gif b/icons/program_icons/mafia.gif new file mode 100644 index 00000000000..5821f55e1e4 Binary files /dev/null and b/icons/program_icons/mafia.gif differ diff --git a/tgstation.dme b/tgstation.dme index 1e73952b6fd..aba28f0fd3a 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -4975,6 +4975,7 @@ #include "code\modules\modular_computers\file_system\programs\file_browser.dm" #include "code\modules\modular_computers\file_system\programs\frontier.dm" #include "code\modules\modular_computers\file_system\programs\jobmanagement.dm" +#include "code\modules\modular_computers\file_system\programs\mafia_ntos.dm" #include "code\modules\modular_computers\file_system\programs\newscasterapp.dm" #include "code\modules\modular_computers\file_system\programs\notepad.dm" #include "code\modules\modular_computers\file_system\programs\nt_pay.dm" diff --git a/tgui/packages/tgui/interfaces/MafiaPanel.tsx b/tgui/packages/tgui/interfaces/MafiaPanel.tsx index 2820822eaf4..528f6260b8d 100644 --- a/tgui/packages/tgui/interfaces/MafiaPanel.tsx +++ b/tgui/packages/tgui/interfaces/MafiaPanel.tsx @@ -1,4 +1,5 @@ import { classes } from 'common/react'; +import { decodeHtmlEntities } from 'common/string'; import { multiline } from 'common/string'; import { useBackend, useLocalState } from '../backend'; import { Box, Button, Collapsible, Flex, NoticeBox, Section, Stack, Tabs, TextArea } from '../components'; @@ -31,39 +32,58 @@ type LobbyData = { status: string; }; +type MessageData = { + msg: string; +}; + type MafiaData = { players: PlayerInfo[]; lobbydata: LobbyData[]; + messages: MessageData[]; user_notes: string; roleinfo: RoleInfo; phase: string; turn: number; timeleft: number; + is_observer: boolean; all_roles: string[]; admin_controls: boolean; }; -export const MafiaPanel = (props, context) => { +export const MafiaPanelData = (props, context) => { const { act, data } = useBackend(context); - const { phase, roleinfo, admin_controls } = data; + const { phase, roleinfo, admin_controls, messages } = data; const [mafia_tab, setMafiaMode] = useLocalState( context, 'mafia_tab', 'Role list' ); + + if (phase === 'No Game') { + return ( + + + + + + + {!!admin_controls && } + + + + + ); + } + return ( - - + + {!!roleinfo && ( + + + + )} + - {!roleinfo && ( - - - - )} {!!roleinfo && ( <> @@ -76,65 +96,125 @@ export const MafiaPanel = (props, context) => { )} )} - {!!admin_controls && } + + {!!admin_controls && } + {phase !== 'No Game' && ( - - - <> - - - - - - - setMafiaMode('Role list')}> - Role list -