diff --git a/code/modules/mafia/_defines.dm b/code/modules/mafia/_defines.dm index 3833d5aefe..194851beed 100644 --- a/code/modules/mafia/_defines.dm +++ b/code/modules/mafia/_defines.dm @@ -1,7 +1,30 @@ +///how many people can play mafia without issues (running out of spawns, procs not expecting more than this amount of people, etc) +#define MAFIA_MAX_PLAYER_COUNT 12 + #define MAFIA_TEAM_TOWN "town" #define MAFIA_TEAM_MAFIA "mafia" #define MAFIA_TEAM_SOLO "solo" +//types of town roles for random setup gen +/// assistants it's just assistants filling up the rest of the roles +#define TOWN_OVERFLOW "overflow" +/// roles that learn info about others in the game (chaplain, detective, psych) +#define TOWN_INVEST "invest" +/// roles that keep other roles safe (doctor, and weirdly enough lawyer counts) +#define TOWN_PROTECT "protect" +/// roles that don't fit into anything else (hop) +#define TOWN_MISC "misc" + +//other types (mafia team, neutrals) +/// normal vote kill changelings +#define MAFIA_REGULAR "regular" +/// every other changeling role that has extra abilities +#define MAFIA_SPECIAL "special" +/// role that wins solo that nobody likes +#define NEUTRAL_KILL "kill" +/// role that upsets the game aka obsessed, usually worse for town than mafia but they can vote against mafia +#define NEUTRAL_DISRUPT "disrupt" + #define MAFIA_PHASE_SETUP 1 #define MAFIA_PHASE_DAY 2 #define MAFIA_PHASE_VOTING 3 @@ -20,132 +43,23 @@ //in order of events + game end -#define COMSIG_MAFIA_SUNDOWN "sundown" //the rest of these phases are at the end of the night when shutters raise, in a different order of resolution +/// when the shutters fall, before the 45 second wait and night event resolution +#define COMSIG_MAFIA_SUNDOWN "sundown" +/// after the 45 second wait, for actions that must go first #define COMSIG_MAFIA_NIGHT_START "night_start" +/// most night actions now resolve #define COMSIG_MAFIA_NIGHT_ACTION_PHASE "night_actions" +/// now killing happens from the roles that do that. the reason this is post action phase is to ensure doctors can protect and lawyers can block #define COMSIG_MAFIA_NIGHT_KILL_PHASE "night_kill" +/// now undoing states like protection, actions that must happen last, etc. right before shutters raise and the day begins #define COMSIG_MAFIA_NIGHT_END "night_end" +/// signal sent to roles when the game is confirmed ending #define COMSIG_MAFIA_GAME_END "game_end" -//list of ghosts who want to play mafia, every time someone enters the list it checks to see if enough are in +/// list of ghosts who want to play mafia, every time someone enters the list it checks to see if enough are in GLOBAL_LIST_EMPTY(mafia_signup) -//the current global mafia game running. -GLOBAL_VAR(mafia_game) /// 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) - -GLOBAL_LIST_INIT(mafia_setups,generate_mafia_setups()) - -/proc/generate_mafia_setups() - . = list() - for(var/T in subtypesof(/datum/mafia_setup)) - var/datum/mafia_setup/N = new T - . += list(N.roles) - -/datum/mafia_setup - var/name = "Make subtypes with the list and a name, more readable than list(list(),list()) etc" - var/list/roles - -// 12 Player - -/datum/mafia_setup/twelve_basic - name = "12 Player Setup Basic" - roles = list( - /datum/mafia_role=6, - /datum/mafia_role/md=1, - /datum/mafia_role/detective=1, - /datum/mafia_role/clown=1, - /datum/mafia_role/mafia=3 - ) - -/datum/mafia_setup/twelve_md - name = "12 Player Setup MD" - roles = list( - /datum/mafia_role=6, - /datum/mafia_role/md=3, - /datum/mafia_role/mafia=3 - ) - -/datum/mafia_setup/twelve_all - name = "12 Player Setup All" - roles = list( - /datum/mafia_role=1, - /datum/mafia_role/psychologist=1, - /datum/mafia_role/md=1, - /datum/mafia_role/detective=1, - /datum/mafia_role/clown=1, - /datum/mafia_role/chaplain=1, - /datum/mafia_role/lawyer=1, - /datum/mafia_role/traitor=1, - /datum/mafia_role/mafia=3, - /datum/mafia_role/fugitive=1, - /datum/mafia_role/obsessed=1 - ) - -/datum/mafia_setup/twelve_joke - name = "12 Player Setup Funny" - roles = list( - /datum/mafia_role=5, - /datum/mafia_role/detective=2, - /datum/mafia_role/clown=2, - /datum/mafia_role/mafia=3 - ) - -/datum/mafia_setup/twelve_lockdown - name = "12 Player Setup Lockdown" - roles = list( - /datum/mafia_role=5, - /datum/mafia_role/md=1, - /datum/mafia_role/detective=1, - /datum/mafia_role/lawyer=2, - /datum/mafia_role/mafia=3 - ) - -/datum/mafia_setup/twelve_rip - name = "12 Player Setup RIP" - roles = list( - /datum/mafia_role=6, - /datum/mafia_role/md=1, - /datum/mafia_role/detective=1, - /datum/mafia_role/mafia=3, - /datum/mafia_role/traitor=1 - ) - -/datum/mafia_setup/twelve_double_treason - name = "12 Player Setup Double Treason" - roles = list( - /datum/mafia_role=8, - /datum/mafia_role/detective=1, - /datum/mafia_role/traitor=1, - /datum/mafia_role/obsessed=2 - ) - -/datum/mafia_setup/twelve_fugitives - name = "12 Player Fugitives" - roles = list( - /datum/mafia_role=6, - /datum/mafia_role/psychologist=1, - /datum/mafia_role/mafia=3, - /datum/mafia_role/fugitive=2 - ) - -/datum/mafia_setup/twelve_traitor_mafia - name = "12 Player Traitor Mafia" - roles = list( - /datum/mafia_role=3, - /datum/mafia_role/psychologist=2, - /datum/mafia_role/md=2, - /datum/mafia_role/detective=2, - /datum/mafia_role/traitor=3 - ) - -/* -/datum/mafia_setup/three_test - name = "3 Player Test" - roles = list( - /datum/mafia_role/chaplain=1, - /datum/mafia_role/psychologist=1, - /datum/mafia_role/mafia=1 - ) -*/ +/// the current global mafia game running. +GLOBAL_VAR(mafia_game) diff --git a/code/modules/mafia/controller.dm b/code/modules/mafia/controller.dm index 602d697675..7a088dba1c 100644 --- a/code/modules/mafia/controller.dm +++ b/code/modules/mafia/controller.dm @@ -1,4 +1,3 @@ -/* DO NOT MERGE, REQUIRES TGUI3 MINIMUM, though the code only supports tgui4 */ /** @@ -16,7 +15,8 @@ var/phase = MAFIA_PHASE_SETUP ///how long the game has gone on for, changes with every sunrise. day one, night one, day two, etc. var/turn = 0 - + ///for debugging and testing a full game, or adminbuse. If this is not null, it will use this as a setup. clears when game is over + var/list/custom_setup = list() ///first day has no voting, and thus is shorter var/first_day_phase_period = 20 SECONDS ///talk with others about the last night @@ -193,7 +193,7 @@ */ /datum/mafia_controller/proc/check_trial(verbose = TRUE) var/datum/mafia_role/loser = get_vote_winner("Day")//, majority_of_town = TRUE) - var/loser_votes = get_vote_count(loser,"Day") + // var/loser_votes = get_vote_count(loser,"Day") if(loser) // if(loser_votes > 12) // loser.body.client?.give_award(/datum/award/achievement/mafia/universally_hated, loser.body) @@ -267,6 +267,10 @@ * * returns TRUE if someone won the game, halting other procs from continuing in the case of a victory */ /datum/mafia_controller/proc/check_victory() + //needed for achievements + var/list/total_town = list() + var/list/total_mafia = list() + var/alive_town = 0 var/alive_mafia = 0 var/list/solos_to_ask = list() //need to ask after because first round is counting team sizes @@ -276,15 +280,19 @@ ///PHASE ONE: TALLY UP ALL NUMBERS OF PEOPLE STILL ALIVE for(var/datum/mafia_role/R in all_roles) - if(R.game_status == MAFIA_ALIVE) - switch(R.team) - if(MAFIA_TEAM_MAFIA) - alive_mafia++ - if(MAFIA_TEAM_TOWN) - alive_town++ - if(MAFIA_TEAM_SOLO) + switch(R.team) + if(MAFIA_TEAM_MAFIA) + total_mafia += R + if(R.game_status == MAFIA_ALIVE) + alive_mafia += R.vote_power + if(MAFIA_TEAM_TOWN) + total_town += R + if(R.game_status == MAFIA_ALIVE) + alive_town += R.vote_power + if(MAFIA_TEAM_SOLO) + if(R.game_status == MAFIA_ALIVE) if(R.solo_counts_as_town) - alive_town++ + alive_town += R.vote_power solos_to_ask += R ///PHASE TWO: SEND STATS TO SOLO ANTAGS, SEE IF THEY WON OR TEAMS CANNOT WIN @@ -299,6 +307,8 @@ var/solo_end = FALSE for(var/datum/mafia_role/winner in total_victors) send_message("!! [uppertext(winner.name)] VICTORY !!") + // var/client/winner_client = GLOB.directory[winner.player_key] + // winner_client?.give_award(winner.winner_award, winner.body) solo_end = TRUE if(solo_end) start_the_end() @@ -306,10 +316,16 @@ if(blocked_victory) return FALSE if(alive_mafia == 0) + // for(var/datum/mafia_role/townie in total_town) + // var/client/townie_client = GLOB.directory[townie.player_key] + // townie_client?.give_award(townie.winner_award, townie.body) start_the_end("!! TOWN VICTORY !!") return TRUE else if(alive_mafia >= alive_town) //guess could change if town nightkill is added start_the_end("!! MAFIA VICTORY !!") + // for(var/datum/mafia_role/changeling in total_mafia) + // var/client/changeling_client = GLOB.directory[changeling.player_key] + // changeling_client?.give_award(changeling.winner_award, changeling.body) return TRUE /** @@ -337,8 +353,13 @@ /datum/mafia_controller/proc/end_game() map_deleter.generate() //remove the map, it will be loaded at the start of the next one QDEL_LIST(all_roles) + current_setup_text = null + custom_setup = list() turn = 0 votes = list() + //map gen does not deal with landmarks + QDEL_LIST(landmarks) + QDEL_NULL(town_center_landmark) phase = MAFIA_PHASE_SETUP /** @@ -413,17 +434,17 @@ * Arguments: * * voter: the mafia role that is trying to vote for... * * target: the mafia role that is getting voted for - * * vt: type of vote submitted (is this the day vote? is this the mafia night vote?) + * * vote_type: type of vote submitted (is this the day vote? is this the mafia night vote?) * * teams: see mafia team defines for what to put in, makes the messages only send to a specific team (so mafia night votes only sending messages to mafia at night) */ -/datum/mafia_controller/proc/vote_for(datum/mafia_role/voter,datum/mafia_role/target,vt, teams) - if(!votes[vt]) - votes[vt] = list() - var/old_vote = votes[vt][voter] +/datum/mafia_controller/proc/vote_for(datum/mafia_role/voter,datum/mafia_role/target,vote_type, teams) + if(!votes[vote_type]) + votes[vote_type] = list() + var/old_vote = votes[vote_type][voter] if(old_vote && old_vote == target) - votes[vt] -= voter + votes[vote_type] -= voter else - votes[vt][voter] = target + votes[vote_type][voter] = target if(old_vote && old_vote == target) send_message("[voter.body.real_name] retracts their vote for [target.body.real_name]!", team = teams) else @@ -437,12 +458,12 @@ /** * Clears out the votes of a certain type (day votes, mafia kill votes) while leaving others untouched */ -/datum/mafia_controller/proc/reset_votes(vt) +/datum/mafia_controller/proc/reset_votes(vote_type) var/list/bodies_to_update = list() - for(var/vote in votes[vt]) - var/datum/mafia_role/R = votes[vt][vote] + for(var/vote in votes[vote_type]) + var/datum/mafia_role/R = votes[vote_type][vote] bodies_to_update += R.body - votes[vt] = list() + votes[vote_type] = list() for(var/mob/M in bodies_to_update) M.update_icon() @@ -450,38 +471,39 @@ * Returns how many people voted for the role, in whatever vote (day vote, night kill vote) * Arguments: * * role: the mafia role the proc tries to get the amount of votes for - * * vt: the vote type (getting how many day votes were for the role, or mafia night votes for the role) + * * vote_type: the vote type (getting how many day votes were for the role, or mafia night votes for the role) */ -/datum/mafia_controller/proc/get_vote_count(role,vt) +/datum/mafia_controller/proc/get_vote_count(role,vote_type) . = 0 - for(var/votee in votes[vt]) - if(votes[vt][votee] == role) - . += 1 + for(var/v in votes[vote_type]) + var/datum/mafia_role/votee = v + if(votes[vote_type][votee] == role) + . += votee.vote_power /** * Returns whichever role got the most votes, in whatever vote (day vote, night kill vote) * returns null if no votes * Arguments: - * * vt: the vote type (getting the role that got the most day votes, or the role that got the most mafia votes) + * * vote_type: the vote type (getting the role that got the most day votes, or the role that got the most mafia votes) */ -/datum/mafia_controller/proc/get_vote_winner(vt) +/datum/mafia_controller/proc/get_vote_winner(vote_type) var/list/tally = list() - for(var/votee in votes[vt]) - if(!tally[votes[vt][votee]]) - tally[votes[vt][votee]] = 1 + for(var/votee in votes[vote_type]) + if(!tally[votes[vote_type][votee]]) + tally[votes[vote_type][votee]] = 1 else - tally[votes[vt][votee]] += 1 + tally[votes[vote_type][votee]] += 1 sortTim(tally,/proc/cmp_numeric_dsc,associative=TRUE) return length(tally) ? tally[1] : null /** * Returns a random person who voted for whatever vote (day vote, night kill vote) * Arguments: - * * vt: vote type (getting a random day voter, or mafia night voter) + * * vote_type: vote type (getting a random day voter, or mafia night voter) */ -/datum/mafia_controller/proc/get_random_voter(vt) - if(length(votes[vt])) - return pick(votes[vt]) +/datum/mafia_controller/proc/get_random_voter(vote_type) + if(length(votes[vote_type])) + return pick(votes[vote_type]) /** * Adds mutable appearances to people who get publicly voted on (so not night votes) showing how many people are picking them @@ -604,9 +626,6 @@ basic_setup() if("nuke") end_game() - for(var/i in landmarks) - qdel(i) - qdel(town_center_landmark) qdel(src) if("next_phase") var/datum/timedevent/timer = SStimer.timer_id_dict[next_phase_timer] @@ -623,11 +642,33 @@ continue 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") + 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) - switch(action) + if("debug_setup") + var/list/debug_setup = list() + var/list/rolelist_dict = list() + var/done = FALSE + for(var/p in typesof(/datum/mafia_role)) + var/datum/mafia_role/path = p + rolelist_dict[initial(path.name) + " ([uppertext(initial(path.team))])"] = path + rolelist_dict = list("CANCEL", "FINISH") + rolelist_dict + while(!done) + to_chat(usr, "You have a total player count of [assoc_value_sum(debug_setup)] in this setup.") + var/chosen_role_name = input(usr,"Select a role!","Custom Setup Creation",rolelist_dict[1]) as null|anything in rolelist_dict + if(chosen_role_name == "CANCEL") + return + if(chosen_role_name == "FINISH") + break + var/found_path = rolelist_dict[chosen_role_name] + var/role_count = input(usr,"How many? Zero to cancel.","Custom Setup Creation",0) as null|num + if(role_count > 0) + debug_setup[found_path] = role_count + custom_setup = debug_setup + if("cancel_setup") + custom_setup = list() + switch(action) //both living and dead if("mf_lookup") var/role_lookup = params["atype"] var/datum/mafia_role/helper @@ -662,8 +703,7 @@ spectators += C.ckey if(user_role.game_status == MAFIA_DEAD) return - var/self_voting = user_role == on_trial ? TRUE : FALSE //used to block people from voting themselves innocent or guilty - //User actions + //User actions (just living) switch(action) if("mf_action") if(!user_role.actions.Find(params["atype"])) @@ -678,7 +718,7 @@ if("Vote") if(phase != MAFIA_PHASE_VOTING) return - vote_for(user_role,target,vt="Day") + vote_for(user_role,target,vote_type="Day") if("Kill Vote") if(phase != MAFIA_PHASE_NIGHT || user_role.team != MAFIA_TEAM_MAFIA) return @@ -731,31 +771,87 @@ . += L[key] /** - * Returns all setups that the amount of players signed up could support (so fill each role) - * Arguments: - * * ready_count: the amount of players signed up (not sane, so some players may have disconnected or rejoined ss13). + * Returns a semirandom setup, with... + * Town, Two invest roles, one protect role, sometimes a misc role, and the rest assistants for town. + * Mafia, 2 normal mafia and one special. + * Neutral, two disruption roles, sometimes one is a killing. + * + * See _defines.dm in the mafia folder for a rundown on what these groups of roles include. */ -/datum/mafia_controller/proc/find_best_setup(ready_count) - var/list/all_setups = GLOB.mafia_setups - var/valid_setups = list() - for(var/S in all_setups) - var/req_players = assoc_value_sum(S) - if(req_players <= ready_count) - valid_setups += list(S) - return length(valid_setups) > 0 ? pick(valid_setups) : null +/datum/mafia_controller/proc/generate_random_setup() + var/invests_left = 2 + var/protects_left = 1 + var/miscs_left = prob(35) + var/mafiareg_left = 2 + var/mafiaspe_left = 1 + var/killing_role = prob(50) + var/disruptors = killing_role ? 1 : 2 //still required to calculate overflow + var/overflow_left = MAFIA_MAX_PLAYER_COUNT - (invests_left + protects_left + miscs_left + mafiareg_left + mafiaspe_left + killing_role + disruptors) + + var/list/random_setup = list() + for(var/i in 1 to MAFIA_MAX_PLAYER_COUNT) //should match the number of roles to add + if(overflow_left) + add_setup_role(random_setup, TOWN_OVERFLOW) + overflow_left-- + else if(invests_left) + add_setup_role(random_setup, TOWN_INVEST) + invests_left-- + else if(protects_left) + add_setup_role(random_setup, TOWN_PROTECT) + protects_left-- + else if(miscs_left) + add_setup_role(random_setup, TOWN_MISC) + miscs_left-- + else if(mafiareg_left) + add_setup_role(random_setup, MAFIA_REGULAR) + mafiareg_left-- + else if(mafiaspe_left) + add_setup_role(random_setup, MAFIA_SPECIAL) + mafiaspe_left-- + else if(killing_role) + add_setup_role(random_setup, NEUTRAL_KILL) + killing_role-- + else + add_setup_role(random_setup, NEUTRAL_DISRUPT) + return random_setup + +/** + * Helper proc that adds a random role of a type to a setup. if it doesn't exist in the setup, it adds the path to the list and otherwise bumps the path in the list up one + */ +/datum/mafia_controller/proc/add_setup_role(setup_list, wanted_role_type) + var/list/role_type_paths = list() + for(var/path in typesof(/datum/mafia_role)) + var/datum/mafia_role/instance = path + if(initial(instance.role_type) == wanted_role_type) + role_type_paths += instance + + var/mafia_path = pick(role_type_paths) + var/datum/mafia_role/mafia_path_type = mafia_path + var/found_role + for(var/searched_path in setup_list) + var/datum/mafia_role/searched_path_type = searched_path + if(initial(mafia_path_type.name) == initial(searched_path_type.name)) + found_role = searched_path + break + if(found_role) + setup_list[found_role] += 1 + return + setup_list[mafia_path] = 1 /** * Called when enough players have signed up to fill a setup. DOESN'T NECESSARILY MEAN THE GAME WILL START. * + * Checks for a custom setup, if so gets the required players from that and if not it sets the player requirement to MAFIA_MAX_PLAYER_COUNT and generates one IF basic setup starts a game. * Checks if everyone signed up is an observer, and is still connected. If people aren't, they're removed from the list. * If there aren't enough players post sanity, it aborts. otherwise, it selects enough people for the game and starts preparing the game for real. */ /datum/mafia_controller/proc/basic_setup() - var/ready_count = length(GLOB.mafia_signup) - var/list/setup = find_best_setup(ready_count) - if(!setup) - return - var/req_players = assoc_value_sum(setup) //12 + var/req_players + var/list/setup = custom_setup + if(!setup.len) + req_players = MAFIA_MAX_PLAYER_COUNT + else + req_players = assoc_value_sum(setup) //final list for all the players who will be in this game var/list/filtered_keys = list() @@ -783,8 +879,10 @@ for(var/unpicked in possible_keys) var/client/unpicked_client = GLOB.directory[unpicked] to_chat(unpicked_client, "Sorry, the starting mafia game has too many players and you were not picked.") - to_chat(unpicked_client, "You're still signed up, and have another chance to join when the one starting now finishes.") + to_chat(unpicked_client, "You're still signed up, getting messages from the current round, and have another chance to join when the one starting now finishes.") + 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_random_setup() prepare_game(setup,filtered_keys) start_game() @@ -794,12 +892,9 @@ * Only checks if everyone is actually valid to start (still connected and an observer) if there are enough players (basic_setup) */ /datum/mafia_controller/proc/try_autostart() - if(phase != MAFIA_PHASE_SETUP) + if(phase != MAFIA_PHASE_SETUP) // || !(GLOB.ghost_role_flags & GHOSTROLE_MINIGAME)) return - var/min_players = INFINITY // fairly sure mmo mafia is not a thing and i'm lazy - for(var/setup in GLOB.mafia_setups) - min_players = min(min_players,assoc_value_sum(setup)) - if(GLOB.mafia_signup.len >= min_players)//enough people to try and make something + if(GLOB.mafia_signup.len >= MAFIA_MAX_PLAYER_COUNT || custom_setup.len)//enough people to try and make something (or debug mode) basic_setup() /** diff --git a/code/modules/mafia/map_pieces.dm b/code/modules/mafia/map_pieces.dm index 1bca4467a1..d1756d7f11 100644 --- a/code/modules/mafia/map_pieces.dm +++ b/code/modules/mafia/map_pieces.dm @@ -14,11 +14,14 @@ name = "Mafia Game Board" icon = 'icons/obj/mafia.dmi' icon_state = "board" + anchored = TRUE var/game_id = "mafia" + var/datum/mafia_controller/MF /obj/mafia_game_board/attack_ghost(mob/user) . = ..() - var/datum/mafia_controller/MF = GLOB.mafia_game + if(!MF) + MF = GLOB.mafia_game if(!MF) MF = create_mafia_game() MF.ui_interact(user) @@ -30,7 +33,7 @@ requires_power = FALSE has_gravity = STANDARD_GRAVITY flags_1 = NONE - //block_suicide = TRUE + // block_suicide = TRUE /datum/map_template/mafia var/description = "" diff --git a/code/modules/mafia/outfits.dm b/code/modules/mafia/outfits.dm index 5bdc66a219..bbc72bd120 100644 --- a/code/modules/mafia/outfits.dm +++ b/code/modules/mafia/outfits.dm @@ -17,7 +17,7 @@ name = "Mafia Detective" uniform = /obj/item/clothing/under/rank/security/detective - //neck = /obj/item/clothing/neck/tie/detective + // neck = /obj/item/clothing/neck/tie/detective shoes = /obj/item/clothing/shoes/sneakers/brown suit = /obj/item/clothing/suit/det_suit gloves = /obj/item/clothing/gloves/color/black @@ -42,13 +42,6 @@ uniform = /obj/item/clothing/under/rank/civilian/chaplain -/datum/outfit/mafia/clown - name = "Mafia Clown" - - uniform = /obj/item/clothing/under/rank/civilian/clown - shoes = /obj/item/clothing/shoes/clown_shoes - mask = /obj/item/clothing/mask/gas/clown_hat - /datum/outfit/mafia/lawyer name = "Mafia Lawyer" @@ -56,8 +49,14 @@ suit = /obj/item/clothing/suit/toggle/lawyer shoes = /obj/item/clothing/shoes/laceup +/datum/outfit/mafia/hop + name = "Mafia Head of Personnel" - + uniform = /obj/item/clothing/under/rank/civilian/head_of_personnel + suit = /obj/item/clothing/suit/armor/vest/alt + shoes = /obj/item/clothing/shoes/sneakers/brown + head = /obj/item/clothing/head/hopcap + glasses = /obj/item/clothing/glasses/sunglasses //mafia @@ -88,9 +87,22 @@ carried_item.add_mob_blood(H)//Oh yes, there will be blood... H.regenerate_icons() +/datum/outfit/mafia/clown + name = "Mafia Clown" + + uniform = /obj/item/clothing/under/rank/civilian/clown + shoes = /obj/item/clothing/shoes/clown_shoes + mask = /obj/item/clothing/mask/gas/clown_hat + /datum/outfit/mafia/traitor name = "Mafia Traitor" mask = /obj/item/clothing/mask/gas/syndicate uniform = /obj/item/clothing/under/syndicate/tacticool shoes = /obj/item/clothing/shoes/jackboots + +/datum/outfit/mafia/nightmare + name = "Mafia Nightmare" + + uniform = null + shoes = null diff --git a/code/modules/mafia/roles.dm b/code/modules/mafia/roles.dm index 914cbb0bd3..2461a93976 100644 --- a/code/modules/mafia/roles.dm +++ b/code/modules/mafia/roles.dm @@ -3,20 +3,26 @@ var/desc = "You are a crewmember without any special abilities." var/win_condition = "kill all mafia and solo killing roles." var/team = MAFIA_TEAM_TOWN + ///how the random setup chooses which roles get put in + var/role_type = TOWN_OVERFLOW var/player_key var/mob/living/carbon/human/body var/obj/effect/landmark/mafia/assigned_landmark + ///how many votes submitted when you vote. + var/vote_power = 1 + var/detect_immune = FALSE var/revealed = FALSE var/datum/outfit/revealed_outfit = /datum/outfit/mafia/assistant //the assistants need a special path to call out they were in fact assistant, everything else can just use job equipment //action = uses var/list/actions = list() var/list/targeted_actions = list() + //what the role gets when it wins a game + // var/winner_award = /datum/award/achievement/mafia/assistant //so mafia have to also kill them to have a majority var/solo_counts_as_town = FALSE //(don't set this for town) - var/game_status = MAFIA_ALIVE ///icon state in the mafia dmi of the hud of the role, used in the mafia ui @@ -60,7 +66,6 @@ to_chat(body,"You are not aligned to town or mafia. Accomplish your own objectives!") to_chat(body, "Be sure to read the wiki page to learn more, if you have no idea what's going on.") -//please take care with this, they can break shit with their equipment unless you specifically disallow them (aka stun at the end of the game) /datum/mafia_role/proc/reveal_role(datum/mafia_controller/game, verbose = FALSE) if(revealed) return @@ -69,9 +74,13 @@ var/list/oldoutfit = body.get_equipped_items() for(var/thing in oldoutfit) qdel(thing) + special_reveal_equip(game) body.equipOutfit(revealed_outfit) revealed = TRUE +/datum/mafia_role/proc/special_reveal_equip(datum/mafia_controller/game) + return + /datum/mafia_role/proc/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target) return @@ -114,6 +123,8 @@ name = "Detective" desc = "You can investigate a single person each night to learn their team." revealed_outfit = /datum/outfit/mafia/detective + role_type = TOWN_INVEST + // winner_award = /datum/award/achievement/mafia/detective hud_icon = "huddetective" revealed_icon = "detective" @@ -140,32 +151,109 @@ current_investigation = target /datum/mafia_role/detective/proc/investigate(datum/mafia_controller/game) - var/datum/mafia_role/R = current_investigation - if(R) - var/team_text - var/fluff - switch(R.team) - if(MAFIA_TEAM_TOWN) - team_text = "Town" - fluff = "a true member of the station." - if(MAFIA_TEAM_MAFIA) - team_text = "Mafia" - fluff = "an unfeeling, hideous changeling!" - if(MAFIA_TEAM_SOLO) - team_text = "Solo" - fluff = "a rogue, with their own objectives..." - to_chat(body,"Your investigations reveal that [R.body.real_name] is [fluff]") - add_note("N[game.turn] - [R.body.real_name] - [team_text]") + var/datum/mafia_role/target = current_investigation + if(target) + if(target.detect_immune) + to_chat(body,"Your investigations reveal that [target.body.real_name] is a true member of the station.") + add_note("N[game.turn] - [target.body.real_name] - Town") + else + var/team_text + var/fluff + switch(target.team) + if(MAFIA_TEAM_TOWN) + team_text = "Town" + fluff = "a true member of the station." + if(MAFIA_TEAM_MAFIA) + team_text = "Mafia" + fluff = "an unfeeling, hideous changeling!" + if(MAFIA_TEAM_SOLO) + team_text = "Solo" + fluff = "a rogue, with their own objectives..." + to_chat(body,"Your investigations reveal that [target.body.real_name] is [fluff]") + add_note("N[game.turn] - [target.body.real_name] - [team_text]") current_investigation = null +/datum/mafia_role/psychologist + name = "Psychologist" + desc = "You can visit someone ONCE PER GAME to reveal their true role in the morning!" + revealed_outfit = /datum/outfit/mafia/psychologist + role_type = TOWN_INVEST + // winner_award = /datum/award/achievement/mafia/psychologist + + hud_icon = "hudpsychologist" + revealed_icon = "psychologist" + + targeted_actions = list("Reveal") + var/datum/mafia_role/current_target + var/can_use = TRUE + +/datum/mafia_role/psychologist/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(game,COMSIG_MAFIA_NIGHT_END,.proc/therapy_reveal) + +/datum/mafia_role/psychologist/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + if(!. || !can_use || game.phase == MAFIA_PHASE_NIGHT || target.game_status != MAFIA_ALIVE || target.revealed || target == src) + return FALSE + +/datum/mafia_role/psychologist/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + to_chat(body,"You will reveal [target.body.real_name] tonight.") + current_target = target + +/datum/mafia_role/psychologist/proc/therapy_reveal(datum/mafia_controller/game) + if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"reveal",current_target) & MAFIA_PREVENT_ACTION || game_status != MAFIA_ALIVE) //Got lynched or roleblocked by a lawyer. + current_target = null + if(current_target) + add_note("N[game.turn] - [current_target.body.real_name] - Revealed true identity") + to_chat(body,"You have revealed the true nature of the [current_target]!") + current_target.reveal_role(game, verbose = TRUE) + current_target = null + can_use = FALSE + +/datum/mafia_role/chaplain + name = "Chaplain" + desc = "You can communicate with spirits of the dead each night to discover dead crewmember roles." + revealed_outfit = /datum/outfit/mafia/chaplain + role_type = TOWN_INVEST + hud_icon = "hudchaplain" + revealed_icon = "chaplain" + // winner_award = /datum/award/achievement/mafia/chaplain + + targeted_actions = list("Pray") + var/current_target + +/datum/mafia_role/chaplain/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(game,COMSIG_MAFIA_NIGHT_ACTION_PHASE,.proc/commune) + +/datum/mafia_role/chaplain/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + if(!.) + return + return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_DEAD && target != src && !target.revealed + +/datum/mafia_role/chaplain/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target) + to_chat(body,"You will commune with the spirit of [target.body.real_name] tonight.") + current_target = target + +/datum/mafia_role/chaplain/proc/commune(datum/mafia_controller/game) + var/datum/mafia_role/target = current_target + if(target) + to_chat(body,"You invoke spirit of [target.body.real_name] and learn their role was [target.name].") + add_note("N[game.turn] - [target.body.real_name] - [target.name]") + current_target = null + /datum/mafia_role/md name = "Medical Doctor" desc = "You can protect a single person each night from killing." revealed_outfit = /datum/outfit/mafia/md // /mafia <- outfit must be readded (just make a new mafia outfits file for all of these) + role_type = TOWN_PROTECT hud_icon = "hudmedicaldoctor" revealed_icon = "medicaldoctor" - targeted_actions = list("Protect") + // winner_award = /datum/award/achievement/mafia/md + targeted_actions = list("Protect") var/datum/mafia_role/current_protected /datum/mafia_role/md/New(datum/mafia_controller/game) @@ -177,6 +265,8 @@ . = ..() if(!.) return + if(target.name == "Head of Personnel" && target.revealed) + return FALSE return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_ALIVE && target != src /datum/mafia_role/md/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target) @@ -201,44 +291,16 @@ UnregisterSignal(current_protected,COMSIG_MAFIA_ON_KILL) current_protected = null -/datum/mafia_role/chaplain - name = "Chaplain" - desc = "You can communicate with spirits of the dead each night to discover dead crewmember roles." - revealed_outfit = /datum/outfit/mafia/chaplain - hud_icon = "hudchaplain" - revealed_icon = "chaplain" - targeted_actions = list("Pray") - var/current_target - -/datum/mafia_role/chaplain/New(datum/mafia_controller/game) - . = ..() - RegisterSignal(game,COMSIG_MAFIA_NIGHT_ACTION_PHASE,.proc/commune) - -/datum/mafia_role/chaplain/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target) - . = ..() - if(!.) - return - return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_DEAD && target != src && !target.revealed - -/datum/mafia_role/chaplain/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target) - to_chat(body,"You will commune with the spirit of [target.body.real_name] tonight.") - current_target = target - -/datum/mafia_role/chaplain/proc/commune(datum/mafia_controller/game) - var/datum/mafia_role/R = current_target - if(R) - to_chat(body,"You invoke spirit of [R.body.real_name] and learn their role was [R.name].") - add_note("N[game.turn] - [R.body.real_name] - [R.name]") - current_target = null - /datum/mafia_role/lawyer name = "Lawyer" desc = "You can choose a person during the day to provide extensive legal advice to during the night, preventing night actions." revealed_outfit = /datum/outfit/mafia/lawyer + role_type = TOWN_PROTECT hud_icon = "hudlawyer" revealed_icon = "lawyer" - targeted_actions = list("Advise") + // winner_award = /datum/award/achievement/mafia/lawyer + targeted_actions = list("Advise") var/datum/mafia_role/current_target /datum/mafia_role/lawyer/New(datum/mafia_controller/game) @@ -286,41 +348,26 @@ if(game_status == MAFIA_ALIVE) //in case we got killed while imprisoning sk - bad luck edge return MAFIA_PREVENT_ACTION -/datum/mafia_role/psychologist - name = "Psychologist" - desc = "You can visit someone ONCE PER GAME to reveal their true role in the morning!" - revealed_outfit = /datum/outfit/mafia/psychologist - - hud_icon = "hudpsychologist" - revealed_icon = "psychologist" +/datum/mafia_role/hop + name = "Head of Personnel" + desc = "You can reveal yourself once per game, tripling your vote power but becoming unable to be protected!" + revealed_outfit = /datum/outfit/mafia/hop + role_type = TOWN_MISC + hud_icon = "hudheadofpersonnel" + revealed_icon = "headofpersonnel" + // winner_award = /datum/award/achievement/mafia/hop targeted_actions = list("Reveal") - var/datum/mafia_role/current_target - var/can_use = TRUE -/datum/mafia_role/psychologist/New(datum/mafia_controller/game) +/datum/mafia_role/hop/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target) . = ..() - RegisterSignal(game,COMSIG_MAFIA_NIGHT_END,.proc/therapy_reveal) - -/datum/mafia_role/psychologist/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target) - . = ..() - if(!. || !can_use || game.phase == MAFIA_PHASE_NIGHT || target.game_status != MAFIA_ALIVE || target.revealed || target == src) + if(!. || game.phase == MAFIA_PHASE_NIGHT || game.turn == 1 || target.game_status != MAFIA_ALIVE || target != src || revealed) return FALSE -/datum/mafia_role/psychologist/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target) +/datum/mafia_role/hop/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target) . = ..() - to_chat(body,"You will reveal [target.body.real_name] tonight.") - current_target = target - -/datum/mafia_role/psychologist/proc/therapy_reveal(datum/mafia_controller/game) - if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"reveal",current_target) & MAFIA_PREVENT_ACTION || game_status != MAFIA_ALIVE) //Got lynched or roleblocked by a lawyer. - current_target = null - if(current_target) - add_note("N[game.turn] - [current_target.body.real_name] - Revealed true identity") - to_chat(body,"You have revealed the true nature of the [current_target]!") - current_target.reveal_role(game, verbose = TRUE) - current_target = null - can_use = FALSE + reveal_role(game, TRUE) + vote_power = 2 ///MAFIA ROLES/// only one until i rework this to allow more, they're the "anti-town" working to kill off townies to win @@ -328,9 +375,12 @@ name = "Changeling" desc = "You're a member of the changeling hive. Use ':j' talk prefix to talk to your fellow lings." team = MAFIA_TEAM_MAFIA - revealed_outfit = /datum/outfit/mafia/changeling + role_type = MAFIA_REGULAR hud_icon = "hudchangeling" revealed_icon = "changeling" + // winner_award = /datum/award/achievement/mafia/changeling + + revealed_outfit = /datum/outfit/mafia/changeling special_theme = "syndicate" win_condition = "become majority over the town and no solo killing role can stop them." @@ -341,6 +391,48 @@ /datum/mafia_role/mafia/proc/mafia_text(datum/mafia_controller/source) to_chat(body,"Vote for who to kill tonight. The killer will be chosen randomly from voters.") +//better detective for mafia +/datum/mafia_role/mafia/thoughtfeeder + name = "Thoughtfeeder" + desc = "You're a changeling variant that feeds on the memories of others. Use ':j' talk prefix to talk to your fellow lings, and visit people at night to learn their role." + role_type = MAFIA_SPECIAL + hud_icon = "hudthoughtfeeder" + revealed_icon = "thoughtfeeder" + // winner_award = /datum/award/achievement/mafia/thoughtfeeder + + targeted_actions = list("Learn Role") + var/datum/mafia_role/current_investigation + +/datum/mafia_role/mafia/thoughtfeeder/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(game,COMSIG_MAFIA_NIGHT_ACTION_PHASE,.proc/investigate) + +/datum/mafia_role/mafia/thoughtfeeder/validate_action_target(datum/mafia_controller/game,action,datum/mafia_role/target) + . = ..() + if(!.) + return + return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_ALIVE && target != src + +/datum/mafia_role/mafia/thoughtfeeder/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target) + to_chat(body,"You will feast on the memories of [target.body.real_name] tonight.") + current_investigation = target + +/datum/mafia_role/mafia/thoughtfeeder/proc/investigate(datum/mafia_controller/game) + var/datum/mafia_role/target = current_investigation + current_investigation = null + if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"thoughtfeed",target) & MAFIA_PREVENT_ACTION) + to_chat(body,"You were unable to investigate [target.body.real_name].") + add_note("N[game.turn] - [target.body.real_name] - Unable to investigate") + return + if(target) + if(target.detect_immune) + to_chat(body,"[target.body.real_name]'s memories reveal that they are the Assistant.") + add_note("N[game.turn] - [target.body.real_name] - Assistant") + else + to_chat(body,"[target.body.real_name]'s memories reveal that they are the [target.name].") + add_note("N[game.turn] - [target.body.real_name] - [target.name]") + + ///SOLO ROLES/// they range from anomalous factors to deranged killers that try to win alone. /datum/mafia_role/traitor @@ -348,6 +440,9 @@ desc = "You're a solo traitor. You are immune to night kills, can kill every night and you win by outnumbering everyone else." win_condition = "kill everyone." team = MAFIA_TEAM_SOLO + role_type = NEUTRAL_KILL + // winner_award = /datum/award/achievement/mafia/traitor + targeted_actions = list("Night Kill") revealed_outfit = /datum/outfit/mafia/traitor @@ -386,10 +481,85 @@ to_chat(body,"You will attempt to kill [target.body.real_name] tonight.") /datum/mafia_role/traitor/proc/try_to_kill(datum/mafia_controller/source) - if(game_status == MAFIA_ALIVE && current_victim && current_victim.game_status == MAFIA_ALIVE) - if(!current_victim.kill(source)) - to_chat(body,"Your attempt at killing [current_victim.body] was prevented!") + var/datum/mafia_role/target = current_victim current_victim = null + if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,source,"traitor kill",target) & MAFIA_PREVENT_ACTION) + return + if(game_status == MAFIA_ALIVE && target && target.game_status == MAFIA_ALIVE) + if(!target.kill(source)) + to_chat(body,"Your attempt at killing [target.body] was prevented!") + +/datum/mafia_role/nightmare + name = "Nightmare" + desc = "You're a solo monster that cannot be detected by detective roles. You can flicker lights of another room each night. You can instead decide to hunt, killing everyone in a flickering room. Kill everyone to win." + win_condition = "kill everyone." + revealed_outfit = /datum/outfit/mafia/nightmare + detect_immune = TRUE + team = MAFIA_TEAM_SOLO + role_type = NEUTRAL_KILL + special_theme = "neutral" + hud_icon = "hudnightmare" + revealed_icon = "nightmare" + // winner_award = /datum/award/achievement/mafia/nightmare + + targeted_actions = list("Flicker", "Hunt") + var/list/flickering = list() + var/datum/mafia_role/flicker_target + +/datum/mafia_role/nightmare/New(datum/mafia_controller/game) + . = ..() + RegisterSignal(game,COMSIG_MAFIA_NIGHT_KILL_PHASE,.proc/flicker_or_hunt) + +/datum/mafia_role/nightmare/check_total_victory(alive_town, alive_mafia) //nightmares just want teams dead + return alive_town + alive_mafia <= 1 + +/datum/mafia_role/nightmare/block_team_victory(alive_town, alive_mafia) //no team can win until they're dead + return TRUE //while alive, town AND mafia cannot win (though since mafia know who is who it's pretty easy to win from that point) + +/datum/mafia_role/nightmare/special_reveal_equip() + body.underwear = "Nude" + body.undershirt = "Nude" + body.socks = "Nude" + body.set_species(/datum/species/shadow) + body.update_body() + +/datum/mafia_role/nightmare/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + if(!. || game.phase != MAFIA_PHASE_NIGHT || target.game_status != MAFIA_ALIVE) + return FALSE + if(action == "Flicker") + return target != src && !(target in flickering) + return target == src + +/datum/mafia_role/nightmare/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target) + . = ..() + if(target == flicker_target) + to_chat(body,"You will do nothing tonight.") + flicker_target = null + flicker_target = target + if(action == "Flicker") + to_chat(body,"You will attempt to flicker [target.body.real_name]'s room tonight.") + else + to_chat(body,"You will hunt everyone in a flickering room down tonight.") + +/datum/mafia_role/nightmare/proc/flicker_or_hunt(datum/mafia_controller/source) + if(game_status != MAFIA_ALIVE || !flicker_target) + return + if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,source,"nightmare actions",flicker_target) & MAFIA_PREVENT_ACTION) + to_chat(flicker_target.body, "Your actions were prevented!") + return + var/datum/mafia_role/target = flicker_target + flicker_target = null + if(target != src) //flicker instead of hunt + to_chat(target.body, "The lights begin to flicker and dim. You're in danger.") + flickering += target + return + for(var/r in flickering) + var/datum/mafia_role/role = r + if(role && role.game_status == MAFIA_ALIVE) + to_chat(role.body, "A shadowy monster appears out of the darkness!") + role.kill(source) + flickering -= role //just helps read better #define FUGITIVE_NOT_PRESERVING 0//will not become night immune tonight @@ -399,15 +569,19 @@ name = "Fugitive" desc = "You're on the run. You can become immune to night kills exactly twice, and you win by surviving to the end of the game with anyone." win_condition = "survive to the end of the game, with anyone" - team = MAFIA_TEAM_SOLO - actions = list("Self Preservation") - var/charges = 2 - var/protection_status = FUGITIVE_NOT_PRESERVING solo_counts_as_town = TRUE //should not count towards mafia victory, they should have the option to work with town revealed_outfit = /datum/outfit/mafia/fugitive + team = MAFIA_TEAM_SOLO + role_type = NEUTRAL_DISRUPT special_theme = "neutral" hud_icon = "hudfugitive" revealed_icon = "fugitive" + // winner_award = /datum/award/achievement/mafia/fugitive + + actions = list("Self Preservation") + var/charges = 2 + var/protection_status = FUGITIVE_NOT_PRESERVING + /datum/mafia_role/fugitive/New(datum/mafia_controller/game) . = ..() @@ -447,6 +621,8 @@ /datum/mafia_role/fugitive/proc/survived(datum/mafia_controller/game) if(game_status == MAFIA_ALIVE) + // var/client/winner_client = GLOB.directory[player_key] + // winner_client?.give_award(winner_award, body) game.send_message("!! FUGITIVE VICTORY !!") #undef FUGITIVE_NOT_PRESERVING @@ -456,11 +632,17 @@ name = "Obsessed" desc = "You're completely lost in your own mind. You win by lynching your obsession before you get killed in this mess. Obsession assigned on the first night!" win_condition = "lynch their obsession." - team = MAFIA_TEAM_SOLO revealed_outfit = /datum/outfit/mafia/obsessed // /mafia <- outfit must be readded (just make a new mafia outfits file for all of these) + solo_counts_as_town = TRUE //after winning or whatever, can side with whoever. they've already done their objective! + team = MAFIA_TEAM_SOLO + role_type = NEUTRAL_DISRUPT special_theme = "neutral" hud_icon = "hudobsessed" revealed_icon = "obsessed" + + // winner_award = /datum/award/achievement/mafia/obsessed + + revealed_outfit = /datum/outfit/mafia/obsessed // /mafia <- outfit must be readded (just make a new mafia outfits file for all of these) solo_counts_as_town = TRUE //after winning or whatever, can side with whoever. they've already done their objective! var/datum/mafia_role/obsession var/lynched_target = FALSE @@ -490,6 +672,8 @@ return if(lynch) game.send_message("!! OBSESSED VICTORY !!") + // var/client/winner_client = GLOB.directory[player_key] + // winner_client?.give_award(winner_award, body) reveal_role(game, FALSE) else to_chat(body, "You have failed your objective to lynch [obsession.body]!") @@ -501,9 +685,11 @@ revealed_outfit = /datum/outfit/mafia/clown solo_counts_as_town = TRUE team = MAFIA_TEAM_SOLO + role_type = NEUTRAL_DISRUPT special_theme = "neutral" hud_icon = "hudclown" revealed_icon = "clown" + // winner_award = /datum/award/achievement/mafia/clown /datum/mafia_role/clown/New(datum/mafia_controller/game) . = ..() @@ -514,4 +700,6 @@ var/datum/mafia_role/victim = pick(game.judgement_guilty_votes + game.judgement_abstain_votes) game.send_message("[body.real_name] WAS A CLOWN! HONK! They take down [victim.body.real_name] with their last prank.") game.send_message("!! CLOWN VICTORY !!") + // var/client/winner_client = GLOB.directory[player_key] + // winner_client?.give_award(winner_award, body) victim.kill(game,FALSE)