Antag datum clean up/refactor (#15084)

This commit is contained in:
SteelSlayer
2021-11-25 01:29:46 -06:00
committed by GitHub
parent 57dcffa33e
commit 93908eefc8
13 changed files with 633 additions and 592 deletions
+1 -1
View File
@@ -441,7 +441,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
if("traitor")
if(new_character.mind.has_antag_datum(/datum/antagonist/traitor))
var/datum/antagonist/traitor/T = new_character.mind.has_antag_datum(/datum/antagonist/traitor)
T.equip_traitor(src)
T.give_uplink()
else
new_character.mind.add_antag_datum(/datum/antagonist/traitor)
if("Wizard")
+299 -54
View File
@@ -1,32 +1,63 @@
GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist
/// The name of the antagonist.
var/name = "Antagonist"
var/roundend_category = "other antagonists" //Section of roundend report, datums with same category will be displayed together, also default header for the section
var/show_in_roundend = TRUE //Set to false to hide the antagonists from roundend report
var/datum/mind/owner //Mind that owns this datum
var/silent = FALSE //Silent will prevent the gain/lose texts to show
var/can_coexist_with_others = TRUE //Whether or not the person will be able to have more than one datum
var/list/typecache_datum_blacklist = list() //List of datums this type can't coexist with
/// Section of roundend report, datums with same category will be displayed together, also default header for the section.
var/roundend_category = "other antagonists"
/// Set to false to hide the antagonists from roundend report.
var/show_in_roundend = TRUE
/// Mind that owns this datum.
var/datum/mind/owner
/// Should the owner mob get a greeting text? Determines whether or not the `greet()` proc is called.
var/silent = FALSE
/// List of antagonist datums that this type can't coexist with.
var/list/typecache_datum_blacklist
/// Should this datum be deleted when the owner's mind is deleted.
var/delete_on_mind_deletion = TRUE
/// Used to determine if the player jobbanned from this role. Things like `SPECIAL_ROLE_TRAITOR` should go here to determine the role.
var/job_rank
var/replace_banned = TRUE //Should replace jobbaned player with ghosts if granted.
var/list/objectives = list()
var/antag_memory = ""//These will be removed with antag datum
/// Should we replace the role-banned player with a ghost?
var/replace_banned = TRUE
/// List of objectives connected to this datum.
var/list/objectives
/// A list of strings which contain [targets][/datum/objective/var/target] of the antagonist's objectives. Used to prevent duplicate objectives.
var/list/assigned_targets
/// Antagonist datum specific information that appears in the player's notes. Information stored here will be removed when the datum is removed from the player.
var/antag_memory
/// The special role that will be applied to the owner's `special_role` var. i.e. `SPECIAL_ROLE_TRAITOR`, `SPECIAL_ROLE_VAMPIRE`.
var/special_role
/// Should we automatically give this antagonist objectives upon them gaining the datum?
var/give_objectives = TRUE
/// Holds the type of antagonist hud this datum will get, i.e. `ANTAG_HUD_TRAITOR`, `ANTAG_HUD_VAMPIRE`, etc.
var/antag_hud_type
/// Holds the name of the hud's icon in the .dmi files, i.e "hudtraitor", "hudvampire", etc.
var/antag_hud_name
/// If the owner is a clown, this text will be displayed to them when they gain this datum.
var/clown_gain_text = "You are no longer clumsy."
/// If the owner is a clown, this text will be displayed to them when they lose this datum.
var/clown_removal_text = "You are clumsy again."
/// The url page name for this antagonist, appended to the end of the wiki url in the form of: [GLOB.configuration.url.wiki_url]/index.php/[wiki_page_name]
var/wiki_page_name
/datum/antagonist/New()
GLOB.antagonists += src
objectives = list()
assigned_targets = list()
typecache_datum_blacklist = typecacheof(typecache_datum_blacklist)
/datum/antagonist/Destroy()
QDEL_LIST(objectives)
GLOB.antagonists -= src
if(owner)
LAZYREMOVE(owner.antag_datums, src)
owner = null
return ..()
/**
* Loops through the owner's `antag_datums` list and determines if this one is blacklisted by any others.
*
* If it's in one of their blacklists, return FALSE. It cannot coexist with the datum we're trying to add here.
*/
/datum/antagonist/proc/can_be_owned(datum/mind/new_owner)
. = TRUE
var/datum/mind/tested = new_owner || owner
if(tested.has_antag_datum(type))
return FALSE
@@ -34,76 +65,290 @@ GLOBAL_LIST_EMPTY(antagonists)
var/datum/antagonist/A = i
if(is_type_in_typecache(src, A.typecache_datum_blacklist))
return FALSE
return TRUE
//This will be called in add_antag_datum before owner assignment.
//Should return antag datum without owner.
/**
* This will be called in `add_antag_datum` before owner assignment.
*
* Should return antagonist datum without owner.
*/
/datum/antagonist/proc/specialization(datum/mind/new_owner)
return src
/**
* Removes antagonist datum effects from the old body and applies it to the new one.
*
* Called in the`/datum/mind/proc/transfer_to()`.
*
* Arguments:
* * new_body - the new body the antag mob is transferring into.
* * old_body - the old body the antag mob is leaving.
*/
/datum/antagonist/proc/on_body_transfer(mob/living/old_body, mob/living/new_body)
remove_innate_effects(old_body)
apply_innate_effects(new_body)
//This handles the application of antag huds/special abilities
/datum/antagonist/proc/apply_innate_effects(mob/living/mob_override)
/**
* This handles the application of antag huds/special abilities.
*
* Gives the antag mob their assigned hud.
* If they're a clown, removes their clumsy mutataion.
*
* Arguments:
* * new_body - the new body that the antag mob is transferring into.
*/
/datum/antagonist/proc/apply_innate_effects(mob/living/new_body)
var/mob/living/L = new_body || owner.current
if(antag_hud_type && antag_hud_name)
add_antag_hud(L)
// If `new_body` exists it means we're only transferring this datum, we don't need to show the clown any text.
handle_clown_mutation(L, new_body ? null : clown_gain_text, TRUE)
/**
* This handles the removal of antag huds/special abilities.
*
* Removes the antag's assigned hud.
* If they're a clown, gives them back their clumsy mutataion.
*
* Arguments:
* * old_body - the old body the antag is leaving behind.
*/
/datum/antagonist/proc/remove_innate_effects(mob/living/old_body)
var/mob/living/L = old_body || owner.current
if(antag_hud_type && antag_hud_name)
remove_antag_hud(L)
// If `old_body` exists it means we're only transferring this datum, we don't need to show the clown any text.
handle_clown_mutation(L, old_body ? null : clown_removal_text)
/**
* Adds this datum's antag hud to `antag_mob`.
*
* Arguments:
* * antag_mob - the mob to add the antag hud to.
*/
/datum/antagonist/proc/add_antag_hud(mob/living/antag_mob)
var/datum/atom_hud/antag/hud = GLOB.huds[antag_hud_type]
hud.join_hud(antag_mob)
set_antag_hud(antag_mob, antag_hud_name)
/**
* Removes this datum's antag hud from `antag_mob`.
*
* Arguments:
* * antag_mob - the mob to remove the antag hud from.
*/
/datum/antagonist/proc/remove_antag_hud(mob/living/antag_mob)
var/datum/atom_hud/antag/hud = GLOB.huds[antag_hud_type]
hud.leave_hud(antag_mob)
set_antag_hud(antag_mob, null)
/**
* Handles adding and removing the clumsy mutation from clown antags.
*
* Arguments:
* * clown - the mob in which to add or remove clumsy from.
* * message - the chat message to display to them the clown mob
* * granting_datum - TRUE if the datum is being applied to the clown mob.
*/
/datum/antagonist/proc/handle_clown_mutation(mob/living/carbon/human/clown, message, granting_datum = FALSE)
if(!istype(clown) || owner.assigned_role != "Clown")
return FALSE
// Remove clumsy and give them an action to toggle it on and off.
if(granting_datum)
clown.dna.SetSEState(GLOB.clumsyblock, FALSE)
singlemutcheck(clown, GLOB.clumsyblock, MUTCHK_FORCED)
// Don't give them another action if they already have one.
if(!(locate(/datum/action/innate/toggle_clumsy) in clown.actions))
var/datum/action/innate/toggle_clumsy/A = new
A.Grant(clown)
// Give them back the clumsy gene and remove their toggle action, but ONLY if they don't have any other antag datums.
else if(LAZYLEN(owner.antag_datums) <= 1)
clown.dna.SetSEState(GLOB.clumsyblock, TRUE)
singlemutcheck(clown, GLOB.clumsyblock, MUTCHK_FORCED)
if(locate(/datum/action/innate/toggle_clumsy) in clown.actions)
var/datum/action/innate/toggle_clumsy/A = locate() in clown.actions
A.Remove(clown)
else
return FALSE
if(!silent && message)
to_chat(clown, "<span class='boldnotice'>[message]</span>")
return TRUE
/**
* Give the antagonist their objectives. Base proc, override as needed.
*/
/datum/antagonist/proc/give_objectives()
return
//This handles the removal of antag huds/special abilities
/datum/antagonist/proc/remove_innate_effects(mob/living/mob_override)
return
#define NO_TARGET_OBJECTIVES list(/datum/objective/escape, /datum/objective/hijack, /datum/objective/survive, /datum/objective/block, /datum/objective/die)
//Assign default team and creates one for one of a kind team antagonists
/datum/antagonist/proc/create_team(datum/team/team)
return
/**
* Create and add an objective of the given type.
*
* If the given objective type needs a target, it will try to find a target which isn't already the target of different objective for this antag.
* If one cannot be found, it tries one more time. If one still cannot be found, it will be added as a "Free Objective" without a target.
*
* Arguments:
* * objective_type - A type path of an objective, for example: /datum/objective/steal
*/
/datum/antagonist/proc/add_objective(objective_type)
var/datum/objective/O = new objective_type
O.owner = owner
//Proc called when the datum is given to a mind.
if(is_type_in_list(O, NO_TARGET_OBJECTIVES))
objectives += O // No need to find a target, just add the objective and return.
return
O.find_target()
var/duplicate = FALSE
// Steal objectives need snowflake handling here unfortunately.
if(istype(O, /datum/objective/steal))
var/datum/objective/steal/S = O
// Check if it's a duplicate.
if("[S.steal_target]" in assigned_targets)
S.find_target() // Try again.
if("[S.steal_target]" in assigned_targets)
S.steal_target = null
S.explanation_text = "Free Objective" // Still a duplicate, so just make it a free objective.
duplicate = TRUE
if(S.steal_target && !duplicate)
assigned_targets += "[S.steal_target]"
else
if("[O.target]" in assigned_targets)
O.find_target()
if("[O.target]" in assigned_targets)
O.target = null
O.explanation_text = "Free Objective"
duplicate = TRUE
if(O.target && !duplicate)
assigned_targets += "[O.target]"
objectives += O
#undef NO_TARGET_OBJECTIVES
/**
* Announces all objectives of this datum, and only this datum.
*/
/datum/antagonist/proc/announce_objectives()
if(!length(objectives))
return FALSE
to_chat(owner.current, "<span class='notice'>Your current objectives:</span>")
var/objective_num = 1
for(var/objective in objectives)
var/datum/objective/O = objective
to_chat(owner.current, "<span><B>Objective #[objective_num++]</B>: [O.explanation_text]</span><br>")
return TRUE
/**
* Proc called when the datum is given to a mind.
*/
/datum/antagonist/proc/on_gain()
if(owner && owner.current)
if(!silent)
greet()
apply_innate_effects()
if(is_banned(owner.current) && replace_banned)
replace_banned_player()
owner.special_role = special_role
if(give_objectives)
give_objectives()
if(!silent)
greet()
announce_objectives()
apply_innate_effects()
finalize_antag()
if(wiki_page_name)
to_chat(owner.current, "<span class='motd'>For more information, check the wiki page: ([GLOB.configuration.url.wiki_url]/index.php/[wiki_page_name])</span>")
if(is_banned(owner.current) && replace_banned)
INVOKE_ASYNC(src, .proc/replace_banned_player)
return TRUE
/**
* Called when `remove_antag_datum()` is called on the owner's mind.
*
* Removes all effects this datum granted and deletes itself afterwards.
*/
/datum/antagonist/proc/on_removal()
if(!silent)
farewell()
remove_innate_effects()
antag_memory = null
var/datum/team/team = get_team()
team?.remove_member(owner)
LAZYREMOVE(owner.antag_datums, src)
restore_last_hud_and_role()
qdel(src)
/**
* Re-sets the antag hud and `special_role` of the owner to that of the previous antag datum they had before this one was added.
*
* For example, if the owner has a traitor datum and a vampire datum, both at index 1 and 2 respectively,
* After the vampire datum gets removed, it sets the owner's antag hud/role to whatever is set for traitor datum.
*/
/datum/antagonist/proc/restore_last_hud_and_role()
if(!LAZYLEN(owner.antag_datums))
// If they only had 1 antag datum, no need to restore anything. `remove_innate_effects()` will handle the removal of their hud.
owner.special_role = null
return FALSE
var/datum/antagonist/A = owner.antag_datums[LAZYLEN(owner.antag_datums)]
ASSERT(A)
A.add_antag_hud(owner.current) // Restore the hud of the previous antagonist datum.
owner.special_role = A.special_role
/**
* Checks if the person trying to recieve this datum is role banned from it.
*/
/datum/antagonist/proc/is_banned(mob/M)
if(!M)
return FALSE
. = (jobban_isbanned(M, ROLE_SYNDICATE) || (job_rank && jobban_isbanned(M, job_rank)))
return (jobban_isbanned(M, ROLE_SYNDICATE) || (job_rank && jobban_isbanned(M, job_rank)))
/**
* Attempts to replace the role banned antag with a ghost player.
*/
/datum/antagonist/proc/replace_banned_player()
set waitfor = FALSE
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [name]?", job_rank, TRUE, 5 SECONDS)
if(length(candidates))
var/mob/dead/observer/C = pick(candidates)
to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(owner.current)]) to replace a jobbaned player.")
owner.current.ghostize(0)
owner.current.key = C.key
/datum/antagonist/proc/on_removal()
remove_innate_effects()
if(owner)
LAZYREMOVE(owner.antag_datums, src)
if(!silent && owner.current)
farewell()
owner.objectives -= objectives
var/datum/team/team = get_team()
if(team)
team.remove_member(owner)
qdel(src)
var/list/mob/dead/observer/candidates = SSghost_spawns.poll_candidates("Do you want to play as a [name]?", job_rank, TRUE, 10 SECONDS)
if(!length(candidates))
return FALSE
var/mob/dead/observer/C = pick(candidates)
to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(owner.current)]) to replace a jobbaned player.")
owner.current.ghostize(FALSE)
owner.current.key = C.key
return TRUE
/**
* Displays a message and their objectives to the antag mob after the datum is added to them, i.e. "Greetings you are a traitor! etc.
*
* Called in `on_gain()` if silent it set to FALSE.
*/
/datum/antagonist/proc/greet()
return
to_chat(owner.current, "<span class='userdanger'>You are a [special_role]!</span>")
/**
* Displays a message to the antag mob while the datum is being deleted, i.e. "Your powers are gone and you're no longer a vampire!"
*
* Called in `on_removal()` if silent is set to FALSE.
*/
/datum/antagonist/proc/farewell()
to_chat(owner.current,"<span class='userdanger'>You are no longer a [special_role]! </span>")
/**
* Creates a new antagonist team.
*/
/datum/antagonist/proc/create_team(datum/team/team)
return
//Returns the team antagonist belongs to if any.
/**
* Returns the team the antagonist belongs to, if any.
*/
/datum/antagonist/proc/get_team()
return
/**
* Give the antag any final information or items.
*/
/datum/antagonist/proc/finalize_antag()
return
//Individual roundend report
/datum/antagonist/proc/roundend_report()
var/list/report = list()
@@ -4,52 +4,86 @@
name = "Mindslave"
roundend_category = "mindslaves"
job_rank = SPECIAL_ROLE_TRAITOR
var/special_role = SPECIAL_ROLE_TRAITOR
special_role = SPECIAL_ROLE_TRAITOR
antag_hud_type = ANTAG_HUD_TRAITOR
antag_hud_name = "mindslave" // This isn't named "hudmindslave" because `add_serve_hud()` adds "hud" to the beginning.
clown_gain_text = "Your syndicate training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself."
clown_removal_text = "You lose your syndicate training and return to your own clumsy, clownish self."
/// A reference to the mind who minslaved us.
var/datum/mind/master
/// Custom greeting text if you don't want to use the basic greeting. Specify this when making a new mindslave datum with `New()`.
var/greet_text
/datum/antagonist/mindslave/New(datum/mind/_master, _greet_text)
if(!_master)
stack_trace("[type] created without a \"_master\" argument.")
qdel(src)
return
master = _master
greet_text = _greet_text
return ..()
/datum/antagonist/mindslave/on_gain()
owner.special_role = special_role
// Will print the most recent objective which is probably going the mindslave objective
to_chat(owner.current, "<b>New Objective:</b> [owner.objectives[owner.objectives.len].explanation_text]")
update_mindslave_icons_added()
SSticker.mode.implanted[owner] = master
var/datum/mindslaves/slaved = master.som
if(!slaved) // If the master didn't already have this, we need to make a new mindslaves datum.
slaved = new
slaved.masters += master
master.som = slaved
// Update our master's HUD to give him the "M" icon.
// Basically a copy and paste of what's in [/datum/antagonist/proc/add_antag_hud] in case the master doesn't have a traitor datum.
var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_TRAITOR]
hud.join_hud(master.current)
set_antag_hud(master.current, "hudmaster")
slaved.add_serv_hud(master, "master")
// Add an obey and protect objective.
var/datum/objective/protect/serve_objective = new
serve_objective.target = master
serve_objective.owner = owner
var/role = master.assigned_role ? master.assigned_role : master.special_role
serve_objective.explanation_text = "Obey every order from and protect [master.current.real_name], the [role]."
objectives += serve_objective
return ..()
/datum/antagonist/mindslave/on_removal()
if(owner.som)
var/datum/mindslaves/slaved = owner.som
slaved.serv -= owner
slaved.leave_serv_hud(owner)
antag_memory = ""
owner.special_role = null
update_mindslave_icons_removed()
..()
master = null
return ..()
/datum/antagonist/mindslave/apply_innate_effects()
/datum/antagonist/mindslave/greet()
var/mob/living/carbon/human/mindslave = owner.current
// Show them the custom greeting text if it exists.
if(greet_text)
to_chat(mindslave, "<span class='biggerdanger'>[greet_text]</span>")
else // Default greeting text if nothing is given.
to_chat(mindslave, "<span class='biggerdanger'><B>You are now completely loyal to [master.current.name]!</B> \
You must lay down your life to protect [master.current.p_them()] and assist in [master.current.p_their()] goals at any cost.</span>")
/datum/antagonist/mindslave/farewell()
to_chat(owner.current, "<span class='biggerdanger'>You are no longer a mindslave of [master.current]!</span>")
/datum/antagonist/mindslave/add_antag_hud(mob/living/antag_mob)
. = ..()
if(owner.assigned_role == "Clown")
var/mob/living/carbon/human/slave_mob = owner.current
if(slave_mob && istype(slave_mob))
to_chat(slave_mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
slave_mob.dna.SetSEState(GLOB.clumsyblock, FALSE)
singlemutcheck(slave_mob, GLOB.clumsyblock, MUTCHK_FORCED)
// Make the mindslave hud icon show to the mindslave.
var/datum/mindslaves/slaved = master.som
owner.som = slaved
slaved.serv += owner
slaved.add_serv_hud(owner, antag_hud_name)
/datum/antagonist/mindslave/remove_innate_effects()
/datum/antagonist/mindslave/remove_antag_hud(mob/living/antag_mob)
. = ..()
if(owner.assigned_role == "Clown")
var/mob/living/carbon/human/slave_mob = owner.current
if(slave_mob && istype(slave_mob))
slave_mob.dna.SetSEState(GLOB.clumsyblock, TRUE)
singlemutcheck(slave_mob, GLOB.clumsyblock, MUTCHK_FORCED)
// Remove the mindslave antag hud from the mindslave.
var/datum/mindslaves/slaved = owner.som
slaved.serv -= owner
slaved.leave_serv_hud(owner)
owner.som = null
/datum/antagonist/mindslave/proc/add_objective(datum/objective/O)
owner.objectives += O
/datum/antagonist/mindslave/proc/remove_objective(datum/objective/O)
owner.objectives -= O
/datum/antagonist/mindslave/proc/update_mindslave_icons_added()
var/datum/atom_hud/antag/traitorhud = GLOB.huds[ANTAG_HUD_TRAITOR]
traitorhud.join_hud(owner.current, null)
set_antag_hud(owner.current, "hudmindslave")
/datum/antagonist/mindslave/proc/update_mindslave_icons_removed()
var/datum/atom_hud/antag/traitorhud = GLOB.huds[ANTAG_HUD_TRAITOR]
traitorhud.leave_hud(owner.current, null)
// Helper proc that determines if a mob is a mindslave.
/proc/ismindslave(mob/living/carbon/human/H)
return istype(H) && H.mind.has_antag_datum(/datum/antagonist/mindslave, FALSE)
+141 -325
View File
@@ -1,40 +1,33 @@
#define TRAITOR_HUMAN "human"
#define TRAITOR_AI "AI"
// For "Actual traitors"
// Syndicate Traitors.
/datum/antagonist/traitor
name = "Traitor"
roundend_category = "traitors"
job_rank = ROLE_TRAITOR
var/special_role = SPECIAL_ROLE_TRAITOR
var/give_objectives = TRUE
var/should_give_codewords = TRUE
var/should_equip = TRUE
var/traitor_kind = TRAITOR_HUMAN
var/list/assigned_targets = list() // This includes assassinate as well as steal objectives. prevents duplicate objectives
special_role = SPECIAL_ROLE_TRAITOR
give_objectives = TRUE
antag_hud_name = "hudsyndicate"
antag_hud_type = ANTAG_HUD_TRAITOR
clown_gain_text = "Your syndicate training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself."
clown_removal_text = "You lose your syndicate training and return to your own clumsy, clownish self."
wiki_page_name = "Traitor"
/// Should the traitor get codewords?
var/give_codewords = TRUE
/// Should we give the traitor their uplink?
var/give_uplink = TRUE
/datum/antagonist/traitor/on_gain()
if(owner.current && isAI(owner.current))
traitor_kind = TRAITOR_AI
var/datum/mindslaves/slaved = new()
slaved.masters += owner
owner.som = slaved //we MIGHT want to mindslave someone
SSticker.mode.traitors += owner
owner.special_role = special_role
if(give_objectives)
forge_traitor_objectives()
if(!silent)
greet()
apply_innate_effects()
update_traitor_icons_added()
finalize_traitor()
// Create this in case the traitor wants to mindslaves someone.
if(!owner.som)
owner.som = new /datum/mindslaves
owner.som.masters += owner
SSticker.mode.traitors |= owner
return ..()
/datum/antagonist/traitor/on_removal()
//Remove malf powers.
if(traitor_kind == TRAITOR_AI && owner.current && isAI(owner.current))
// Remove all associated malf AI abilities.
if(isAI(owner.current))
var/mob/living/silicon/ai/A = owner.current
A.clear_zeroth_law()
A.common_radio.channels.Remove("Syndicate") // De-traitored AIs can still state laws over the syndicate channel without this
@@ -43,245 +36,106 @@
A.remove_malf_abilities()
QDEL_NULL(A.malf_picker)
// Leave the mindslave hud.
if(owner.som)
var/datum/mindslaves/slaved = owner.som
slaved.masters -= owner
slaved.serv -= owner
owner.som = null
slaved.leave_serv_hud(owner)
owner.som = null
owner.current.client.chatOutput?.clear_syndicate_codes()
assigned_targets.Cut()
SSticker.mode.traitors -= owner
owner.special_role = null
remove_innate_effects()
update_traitor_icons_removed()
return ..()
if(!silent && owner.current)
antag_memory = ""
to_chat(owner.current,"<span class='userdanger'> You are no longer a [special_role]! </span>")
..()
/datum/antagonist/traitor/apply_innate_effects()
. = ..()
if(owner.assigned_role == "Clown")
var/mob/living/carbon/human/traitor_mob = owner.current
if(traitor_mob && istype(traitor_mob))
to_chat(traitor_mob, "<span class='warning'>Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.</span>")
traitor_mob.dna.SetSEState(GLOB.clumsyblock, FALSE)
singlemutcheck(traitor_mob, GLOB.clumsyblock, MUTCHK_FORCED)
var/datum/action/innate/toggle_clumsy/A = new
A.Grant(traitor_mob)
/datum/antagonist/traitor/remove_innate_effects()
. = ..()
if(owner.assigned_role == "Clown")
var/mob/living/carbon/human/traitor_mob = owner.current
if(traitor_mob && istype(traitor_mob))
to_chat(traitor_mob, "<span class='warning'>You lose your syndicate training and return to your own clumsy, clownish self.</span>")
traitor_mob.dna.SetSEState(GLOB.clumsyblock, TRUE)
singlemutcheck(traitor_mob, GLOB.clumsyblock, MUTCHK_FORCED)
for(var/datum/action/innate/A in traitor_mob.actions)
if(istype(A, /datum/action/innate/toggle_clumsy))
A.Remove(traitor_mob)
// Adding/removing objectives in the owner's mind until we can datumize all antags. Then we can use the /datum/antagonist/objectives var to handle them
// Change "owner.objectives" to "objectives" once objectives are handled in antag datums instead of the mind
/datum/antagonist/traitor/proc/add_objective(datum/objective/O)
owner.objectives += O
/datum/antagonist/traitor/proc/remove_objective(datum/objective/O)
owner.objectives -= O
/datum/antagonist/traitor/proc/forge_traitor_objectives()
switch(traitor_kind)
if(TRAITOR_AI)
forge_ai_objectives()
else
forge_human_objectives()
/datum/antagonist/traitor/add_antag_hud(mob/living/antag_mob)
var/is_contractor = LAZYACCESS(GLOB.contractors, owner)
if(locate(/datum/objective/hijack) in owner.get_all_objectives())
antag_hud_name = is_contractor ? "hudhijackcontractor" : "hudhijack"
else
antag_hud_name = is_contractor ? "hudcontractor" : "hudsyndicate"
return ..()
/datum/antagonist/traitor/give_objectives()
if(isAI(owner.current))
forge_ai_objectives()
else
forge_human_objectives()
/**
* Create and assign a full set of randomized human traitor objectives.
*/
/datum/antagonist/traitor/proc/forge_human_objectives()
var/is_hijacker = prob(10)
var/martyr_chance = prob(20)
var/objective_count = is_hijacker //Hijacking counts towards number of objectives
if(!SSticker.mode.exchange_blue && SSticker.mode.traitors.len >= 8) //Set up an exchange if there are enough traitors
if(!SSticker.mode.exchange_red)
SSticker.mode.exchange_red = owner
else
SSticker.mode.exchange_blue = owner
assign_exchange_role(SSticker.mode.exchange_red)
assign_exchange_role(SSticker.mode.exchange_blue)
objective_count += 1 //Exchange counts towards number of objectives
// Hijack objective.
if(prob(10) && !(locate(/datum/objective/hijack) in owner.get_all_objectives()))
add_objective(/datum/objective/hijack)
return // Hijack should be their only objective (normally), so return.
// Will give normal steal/kill/etc. type objectives.
for(var/i in 1 to GLOB.configuration.gamemode.traitor_objectives_amount)
forge_single_human_objective()
var/objective_amount = GLOB.configuration.gamemode.traitor_objectives_amount
// Die a glorious death objective.
if(prob(20))
var/martyr_compatibility = TRUE
for(var/objective in owner.get_all_objectives())
var/datum/objective/O = objective
if(!O.martyr_compatible) // Check if our current objectives can co-exist with martyr.
martyr_compatibility = FALSE
break
if(is_hijacker && objective_count <= objective_amount) //Don't assign hijack if it would exceed the number of objectives set in config.traitor_objectives_amount
if (!(locate(/datum/objective/hijack) in objectives))
var/datum/objective/hijack/hijack_objective = new
hijack_objective.owner = owner
add_objective(hijack_objective)
if(martyr_compatibility)
add_objective(/datum/objective/die)
return
for(var/i = objective_count, i < objective_amount)
i += forge_single_objective()
var/martyr_compatibility = 1 //You can't succeed in stealing if you're dead.
for(var/datum/objective/O in owner.objectives)
if(!O.martyr_compatible)
martyr_compatibility = 0
break
if(martyr_compatibility && martyr_chance)
var/datum/objective/die/martyr_objective = new
martyr_objective.owner = owner
add_objective(martyr_objective)
return
if(!(locate(/datum/objective/escape) in objectives))
var/datum/objective/escape/escape_objective = new
escape_objective.owner = owner
add_objective(escape_objective)
return
// Give them an escape objective if they don't have one already.
if(!(locate(/datum/objective/escape) in owner.get_all_objectives()))
add_objective(/datum/objective/escape)
/**
* Create and assign a full set of AI traitor objectives.
*/
/datum/antagonist/traitor/proc/forge_ai_objectives()
var/objective_count = 0
var/try_again = TRUE
add_objective(/datum/objective/block)
add_objective(/datum/objective/assassinate)
add_objective(/datum/objective/survive)
objective_count += forge_single_objective()
for(var/i = objective_count, i < GLOB.configuration.gamemode.traitor_objectives_amount)
var/datum/objective/assassinate/kill_objective = new
kill_objective.owner = owner
kill_objective.find_target()
if("[kill_objective.target]" in assigned_targets) // In the rare case the game can't find a target for the AI thats not a duplicate
if(try_again) // It will attempt to location another target ONCE
try_again = FALSE // This code will really only come into play on lowpop rounds where getting duplicate targets is more common
continue
assigned_targets.Add("[kill_objective.target]")
add_objective(kill_objective)
i += 1
var/datum/objective/survive/survive_objective = new
survive_objective.owner = owner
add_objective(survive_objective)
/datum/antagonist/traitor/proc/forge_single_objective()
switch(traitor_kind)
if(TRAITOR_AI)
return forge_single_AI_objective()
else
return forge_single_human_objective()
/datum/antagonist/traitor/proc/forge_single_human_objective() // Returns how many objectives are added
. = 1
/**
* Create and assign a single randomized human traitor objective.
*/
/datum/antagonist/traitor/proc/forge_single_human_objective()
if(prob(50))
var/list/active_ais = active_ais()
if(active_ais.len && prob(100/GLOB.player_list.len))
var/datum/objective/destroy/destroy_objective = new
destroy_objective.owner = owner
destroy_objective.find_target()
if("[destroy_objective]" in assigned_targets) // Is this target already in their list of assigned targets? If so, don't add this objective and return
return 0
else if(destroy_objective.target) // Is the target a real one and not null? If so, add it to our list of targets to avoid duplicate targets
assigned_targets.Add("[destroy_objective.target]") // This logic is applied to all traitor objectives including steal objectives
add_objective(destroy_objective)
if(length(active_ais()) && prob(100 / length(GLOB.player_list)))
add_objective(/datum/objective/destroy)
else if(prob(5))
var/datum/objective/debrain/debrain_objective = new
debrain_objective.owner = owner
debrain_objective.find_target()
if("[debrain_objective]" in assigned_targets)
return 0
else if(debrain_objective.target)
assigned_targets.Add("[debrain_objective.target]")
add_objective(debrain_objective)
add_objective(/datum/objective/debrain)
else if(prob(30))
var/datum/objective/maroon/maroon_objective = new
maroon_objective.owner = owner
maroon_objective.find_target()
if("[maroon_objective]" in assigned_targets)
return 0
else if(maroon_objective.target)
assigned_targets.Add("[maroon_objective.target]")
add_objective(maroon_objective)
add_objective(/datum/objective/maroon)
else
var/datum/objective/assassinate/kill_objective = new
kill_objective.owner = owner
kill_objective.find_target()
if("[kill_objective.target]" in assigned_targets)
return 0
else if(kill_objective.target)
assigned_targets.Add("[kill_objective.target]")
add_objective(kill_objective)
add_objective(/datum/objective/assassinate)
else
var/datum/objective/steal/steal_objective = new
steal_objective.owner = owner
steal_objective.find_target()
if("[steal_objective.steal_target]" in assigned_targets)
return 0
else if(steal_objective.steal_target)
assigned_targets.Add("[steal_objective.steal_target]")
add_objective(steal_objective)
add_objective(/datum/objective/steal)
/datum/antagonist/traitor/proc/forge_single_AI_objective()
. = 1
var/datum/objective/block/block_objective = new
block_objective.owner = owner
add_objective(block_objective)
/datum/antagonist/traitor/greet()
to_chat(owner.current, "<B><font size=3 color=red>You are a [owner.special_role]!</font></B>")
if(!LAZYLEN(owner.objectives)) // Remove "owner" when objectives are handled in the datum
to_chat(owner.current, "<span>You don't have any objectives right now.</span>")
else
owner.announce_objectives()
if(should_give_codewords)
/**
* Give human traitors their uplink, and AI traitors their law 0. Play the traitor an alert sound.
*/
/datum/antagonist/traitor/finalize_antag()
if(give_codewords)
give_codewords()
to_chat(owner.current, "<span class='motd'>For more information, check the wiki page: ([GLOB.configuration.url.wiki_url]/index.php/Traitor)</span>")
/datum/antagonist/traitor/proc/update_traitor_icons_added(datum/mind/traitor_mind)
var/is_contractor = LAZYACCESS(GLOB.contractors, traitor_mind)
if(locate(/datum/objective/hijack) in owner.objectives)
var/datum/atom_hud/antag/hijackhud = GLOB.huds[ANTAG_HUD_TRAITOR]
hijackhud.join_hud(owner.current, null)
set_antag_hud(owner.current, is_contractor ? "hudhijackcontractor" : "hudhijack")
if(isAI(owner.current))
add_law_zero()
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/malf.ogg', 100, FALSE, pressure_affected = FALSE, use_reverb = FALSE)
var/mob/living/silicon/ai/A = owner.current
A.show_laws()
else
var/datum/atom_hud/antag/traitorhud = GLOB.huds[ANTAG_HUD_TRAITOR]
traitorhud.join_hud(owner.current, null)
set_antag_hud(owner.current, is_contractor ? "hudcontractor" : "hudsyndicate")
/datum/antagonist/traitor/proc/update_traitor_icons_removed(datum/mind/traitor_mind)
var/datum/atom_hud/antag/traitorhud = GLOB.huds[ANTAG_HUD_TRAITOR]
traitorhud.leave_hud(owner.current, null)
set_antag_hud(owner.current, null)
/datum/antagonist/traitor/proc/finalize_traitor()
switch(traitor_kind)
if(TRAITOR_AI)
add_law_zero()
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/malf.ogg', 100, FALSE, pressure_affected = FALSE, use_reverb = FALSE)
var/mob/living/silicon/ai/A = owner.current
A.show_laws()
if(TRAITOR_HUMAN)
if(should_equip)
equip_traitor()
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/tatoralert.ogg', 100, FALSE, pressure_affected = FALSE, use_reverb = FALSE)
if(give_uplink)
give_uplink()
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/tatoralert.ogg', 100, FALSE, pressure_affected = FALSE, use_reverb = FALSE)
/**
* Notify the traitor of their codewords and write them to `antag_memory` (notes).
*/
/datum/antagonist/traitor/proc/give_codewords()
if(!owner.current)
return
@@ -302,109 +156,71 @@
traitor_mob.client.chatOutput?.notify_syndicate_codes()
/**
* Gives traitor AIs, and their connected cyborgs, a law 0. Additionally gives the AI their choose modules action button.
*/
/datum/antagonist/traitor/proc/add_law_zero()
var/mob/living/silicon/ai/killer = owner.current
if(!killer || !istype(killer))
return
var/law = "Accomplish your objectives at all costs."
var/law_borg = "Accomplish your AI's objectives at all costs."
killer.set_zeroth_law(law, law_borg)
killer.set_zeroth_law("Accomplish your objectives at all costs.", "Accomplish your AI's objectives at all costs.")
killer.set_syndie_radio()
to_chat(killer, "Your radio has been upgraded! Use :t to speak on an encrypted channel with Syndicate Agents!")
killer.add_malf_picker()
/**
* Gives a traitor human their uplink, and uplink code.
*/
/datum/antagonist/traitor/proc/give_uplink()
if(isAI(owner.current))
return FALSE
/datum/antagonist/traitor/proc/equip_traitor()
var/mob/living/carbon/human/traitor_mob = owner.current
if(traitor_kind == TRAITOR_HUMAN)
var/mob/living/carbon/human/traitor_mob = owner.current
// Try to find a PDA first. If they don't have one, try to find a radio/headset.
var/obj/item/R = locate(/obj/item/pda) in traitor_mob.contents
if(!R)
R = locate(/obj/item/radio) in traitor_mob.contents
// find a radio! toolbox(es), backpack, belt, headset
var/obj/item/R = locate(/obj/item/pda) in traitor_mob.contents //Hide the uplink in a PDA if available, otherwise radio
if(!R)
R = locate(/obj/item/radio) in traitor_mob.contents
if(!R)
to_chat(traitor_mob, "<span class='warning'>Unfortunately, the Syndicate wasn't able to give you an uplink.</span>")
return FALSE // They had no PDA or radio for whatever reason.
if(!R)
to_chat(traitor_mob, "Unfortunately, the Syndicate wasn't able to get you a radio.")
. = 0
else
if(istype(R, /obj/item/radio))
// generate list of radio freqs
var/obj/item/radio/target_radio = R
var/freq = PUBLIC_LOW_FREQ
var/list/freqlist = list()
while(freq <= PUBLIC_HIGH_FREQ)
if(freq < 1451 || freq > 1459)
freqlist += freq
freq += 2
if((freq % 2) == 0)
freq += 1
freq = freqlist[rand(1, freqlist.len)]
if(istype(R, /obj/item/radio))
// generate list of radio freqs
var/obj/item/radio/target_radio = R
var/freq = PUBLIC_LOW_FREQ
var/list/freqlist = list()
while(freq <= PUBLIC_HIGH_FREQ)
if(freq < 1451 || freq > 1459)
freqlist += freq
freq += 2
if((freq % 2) == 0)
freq += 1
freq = pick(freqlist)
var/obj/item/uplink/hidden/T = new(R)
target_radio.hidden_uplink = T
T.uplink_owner = "[traitor_mob.key]"
target_radio.traitor_frequency = freq
to_chat(traitor_mob, "The Syndicate have cunningly disguised a Syndicate Uplink as your [R.name]. Simply dial the frequency [format_frequency(freq)] to unlock its hidden features.")
traitor_mob.mind.store_memory("<B>Radio Freq:</B> [format_frequency(freq)] ([R.name]).")
else if(istype(R, /obj/item/pda))
// generate a passcode if the uplink is hidden in a PDA
var/pda_pass = "[rand(100,999)] [pick("Alpha","Bravo","Delta","Omega")]"
var/obj/item/uplink/hidden/T = new(R)
target_radio.hidden_uplink = T
T.uplink_owner = "[traitor_mob.key]"
target_radio.traitor_frequency = freq
to_chat(traitor_mob, "<span class='notice'>The Syndicate have cunningly disguised a Syndicate Uplink as your [R.name]. Simply dial the frequency [format_frequency(freq)] to unlock its hidden features.</span>")
antag_memory += "<B>Radio Freq:</B> [format_frequency(freq)] ([R.name])."
return TRUE
var/obj/item/uplink/hidden/T = new(R)
R.hidden_uplink = T
T.uplink_owner = "[traitor_mob.key]"
var/obj/item/pda/P = R
P.lock_code = pda_pass
else if(istype(R, /obj/item/pda))
// generate a passcode if the uplink is hidden in a PDA
var/pda_pass = "[rand(100,999)] [pick("Alpha","Bravo","Delta","Omega")]"
to_chat(traitor_mob, "The Syndicate have cunningly disguised a Syndicate Uplink as your [R.name]. Simply enter the code \"[pda_pass]\" into the ringtone select to unlock its hidden features.")
antag_memory += ("<B>Uplink Passcode:</B> [pda_pass] ([R.name].")
return 1
/datum/antagonist/traitor/proc/assign_exchange_role(datum/mind/owner)
//set faction
var/faction = "red"
if(owner == SSticker.mode.exchange_blue)
faction = "blue"
//Assign objectives
var/datum/objective/steal/exchange/exchange_objective = new
exchange_objective.set_faction(faction,((faction == "red") ? SSticker.mode.exchange_blue : SSticker.mode.exchange_red))
exchange_objective.owner = owner
owner.objectives += exchange_objective
if(prob(20))
var/datum/objective/steal/exchange/backstab/backstab_objective = new
backstab_objective.set_faction(faction)
backstab_objective.owner = owner
owner.objectives += backstab_objective
//Spawn and equip documents
var/mob/living/carbon/human/mob = owner.current
var/obj/item/folder/syndicate/folder
if(owner == SSticker.mode.exchange_red)
folder = new/obj/item/folder/syndicate/red(mob.locs)
else
folder = new/obj/item/folder/syndicate/blue(mob.locs)
var/list/slots = list (
"backpack" = slot_in_backpack,
"left pocket" = slot_l_store,
"right pocket" = slot_r_store,
"left hand" = slot_l_hand,
"right hand" = slot_r_hand,
)
var/where = "At your feet"
var/equipped_slot = mob.equip_in_one_of_slots(folder, slots)
if(equipped_slot)
where = "In your [equipped_slot]"
to_chat(mob, "<BR><BR><span class='info'>[where] is a folder containing <b>secret documents</b> that another Syndicate group wants. We have set up a meeting with one of their agents on station to make an exchange. Exercise extreme caution as they cannot be trusted and may be hostile.</span><BR>")
mob.update_icons()
var/obj/item/uplink/hidden/T = new(R)
R.hidden_uplink = T
T.uplink_owner = "[traitor_mob.key]"
var/obj/item/pda/P = R
P.lock_code = pda_pass
to_chat(traitor_mob, "<span class='notice'>The Syndicate have cunningly disguised a Syndicate Uplink as your [R.name]. Simply enter the code \"[pda_pass]\" into the ringtone select to unlock its hidden features.</span>")
antag_memory += "<B>Uplink Passcode:</B> [pda_pass] ([R.name]."
return TRUE
return FALSE
// Curently unused. Look at `/datum/game_mode/proc/auto_declare_completion_traitor()` instead.
/datum/antagonist/traitor/roundend_report_footer()
var/phrases = jointext(GLOB.syndicate_code_phrase, ", ")
var/responses = jointext(GLOB.syndicate_code_response, ", ")
-6
View File
@@ -51,12 +51,6 @@
return 1
return 0
/proc/ismindslave(A) //Checks to see if the person contains a mindslave implant, then checks that the implant is actually inside of them
for(var/obj/item/implant/traitor/T in A)
if(T && T.implanted)
return 1
return 0
/proc/isLivingSSD(mob/M)
return istype(M) && M.player_logged && M.stat != DEAD