mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-16 01:23:41 +01:00
Antag datum clean up/refactor (#15084)
This commit is contained in:
@@ -82,13 +82,13 @@
|
||||
return 0
|
||||
|
||||
//Checks for specific types in a list
|
||||
/proc/is_type_in_list(atom/A, list/L)
|
||||
if(!L || !L.len || !A)
|
||||
return 0
|
||||
/proc/is_type_in_list(datum/D, list/L)
|
||||
if(!L || !length(L) || !D)
|
||||
return FALSE
|
||||
for(var/type in L)
|
||||
if(istype(A, type))
|
||||
return 1
|
||||
return 0
|
||||
if(istype(D, type))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
//Checks for specific types in specifically structured (Assoc "type" = TRUE) lists ('typecaches')
|
||||
/proc/is_type_in_typecache(atom/A, list/L)
|
||||
|
||||
+42
-49
@@ -159,15 +159,9 @@
|
||||
/datum/mind/proc/gen_objective_text(admin = FALSE)
|
||||
. = ""
|
||||
var/obj_count = 1
|
||||
var/list/all_objectives = list()
|
||||
for(var/datum/antagonist/A in antag_datums)
|
||||
all_objectives |= A.objectives
|
||||
|
||||
if(LAZYLEN(all_objectives))
|
||||
for(var/datum/objective/objective in all_objectives)
|
||||
. += "<br><B>Objective #[obj_count++]</B>: [objective.explanation_text]"
|
||||
|
||||
for(var/datum/objective/objective in objectives)
|
||||
// If they don't have any objectives, "" will be returned.
|
||||
for(var/datum/objective/objective in get_all_objectives())
|
||||
. += "<b>Objective #[obj_count++]</b>: [objective.explanation_text]"
|
||||
if(admin)
|
||||
. += " <a href='?src=[UID()];obj_edit=\ref[objective]'>Edit</a> " // Edit
|
||||
@@ -178,6 +172,22 @@
|
||||
. += "</a>"
|
||||
. += "<br>"
|
||||
|
||||
/**
|
||||
* Gets every objective this mind owns, including all of those from any antag datums they have, and returns them as a list.
|
||||
*/
|
||||
/datum/mind/proc/get_all_objectives()
|
||||
var/list/all_objectives = list()
|
||||
|
||||
for(var/antag in antag_datums)
|
||||
var/datum/antagonist/A = antag
|
||||
all_objectives += A.objectives // Add all antag datum objectives.
|
||||
|
||||
for(var/objective in objectives)
|
||||
var/datum/objective/O = objective
|
||||
all_objectives += O // Add all mind objectives.
|
||||
|
||||
return all_objectives
|
||||
|
||||
/datum/mind/proc/_memory_edit_header(gamemode, list/alt)
|
||||
. = gamemode
|
||||
if(SSticker.mode.config_tag == gamemode || (LAZYLEN(alt) && (SSticker.mode.config_tag in alt)))
|
||||
@@ -325,7 +335,8 @@
|
||||
. = _memory_edit_header("traitor", list("traitorchan", "traitorvamp"))
|
||||
if(has_antag_datum(/datum/antagonist/traitor))
|
||||
. += "<b><font color='red'>TRAITOR</font></b>|<a href='?src=[UID()];traitor=clear'>no</a>"
|
||||
if(objectives.len==0)
|
||||
var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor)
|
||||
if(!length(T.objectives))
|
||||
. += "<br>Objectives are empty! <a href='?src=[UID()];traitor=autoobjectives'>Randomize!</a>"
|
||||
else
|
||||
. += "<a href='?src=[UID()];traitor=traitor'>traitor</a>|<b>NO</b>"
|
||||
@@ -489,7 +500,7 @@
|
||||
out += memory
|
||||
out += "<br><a href='?src=[UID()];memory_edit=1'>Edit memory</a><br>"
|
||||
out += "Objectives:<br>"
|
||||
if(objectives.len == 0)
|
||||
if(!length(get_all_objectives()))
|
||||
out += "EMPTY<br>"
|
||||
else
|
||||
out += gen_objective_text(admin = TRUE)
|
||||
@@ -701,6 +712,13 @@
|
||||
var/datum/objective/objective = locate(href_list["obj_delete"])
|
||||
if(!istype(objective))
|
||||
return
|
||||
for(var/antag in antag_datums)
|
||||
var/datum/antagonist/A = antag
|
||||
A.objectives -= objective
|
||||
A.assigned_targets -= "[objective.target]"
|
||||
if(istype(objective, /datum/objective/steal))
|
||||
var/datum/objective/steal/S = objective
|
||||
A.assigned_targets -= "[S.steal_target]"
|
||||
objectives -= objective
|
||||
|
||||
log_admin("[key_name(usr)] has removed one of [key_name(current)]'s objectives: [objective]")
|
||||
@@ -839,8 +857,6 @@
|
||||
qdel(flash)
|
||||
take_uplink()
|
||||
var/fail = 0
|
||||
var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor)
|
||||
fail |= !T.equip_traitor(src)
|
||||
fail |= !SSticker.mode.equip_revolutionary(current)
|
||||
if(fail)
|
||||
to_chat(usr, "<span class='warning'>Reequipping revolutionary goes wrong!</span>")
|
||||
@@ -1091,7 +1107,6 @@
|
||||
if(has_antag_datum(/datum/antagonist/traitor))
|
||||
to_chat(current, "<span class='warning'><FONT size = 3><B>You have been brainwashed! You are no longer a traitor!</B></FONT></span>")
|
||||
remove_antag_datum(/datum/antagonist/traitor)
|
||||
current.client.chatOutput?.clear_syndicate_codes()
|
||||
log_admin("[key_name(usr)] has de-traitored [key_name(current)]")
|
||||
message_admins("[key_name_admin(usr)] has de-traitored [key_name_admin(current)]")
|
||||
|
||||
@@ -1099,14 +1114,14 @@
|
||||
if(!(has_antag_datum(/datum/antagonist/traitor)))
|
||||
var/datum/antagonist/traitor/T = new()
|
||||
T.give_objectives = FALSE
|
||||
T.should_equip = FALSE
|
||||
T.give_uplink = FALSE
|
||||
add_antag_datum(T)
|
||||
log_admin("[key_name(usr)] has traitored [key_name(current)]")
|
||||
message_admins("[key_name_admin(usr)] has traitored [key_name_admin(current)]")
|
||||
|
||||
if("autoobjectives")
|
||||
var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor)
|
||||
T.forge_traitor_objectives(src)
|
||||
T.give_objectives()
|
||||
to_chat(usr, "<span class='notice'>The objectives for traitor [key] have been generated. You can edit them and announce manually.</span>")
|
||||
log_admin("[key_name(usr)] has automatically forged objectives for [key_name(current)]")
|
||||
message_admins("[key_name_admin(usr)] has automatically forged objectives for [key_name_admin(current)]")
|
||||
@@ -1424,8 +1439,7 @@
|
||||
if("uplink")
|
||||
if(has_antag_datum(/datum/antagonist/traitor))
|
||||
var/datum/antagonist/traitor/T = has_antag_datum(/datum/antagonist/traitor)
|
||||
T.give_codewords()
|
||||
if(!T.equip_traitor(src))
|
||||
if(!T.give_uplink())
|
||||
to_chat(usr, "<span class='warning'>Equipping a syndicate failed!</span>")
|
||||
return
|
||||
log_admin("[key_name(usr)] has given [key_name(current)] an uplink")
|
||||
@@ -1465,6 +1479,7 @@
|
||||
var/datum/team/antag_team = A.get_team()
|
||||
if(antag_team)
|
||||
antag_team.add_member(src)
|
||||
ASSERT(A.owner && A.owner.current)
|
||||
A.on_gain()
|
||||
return A
|
||||
|
||||
@@ -1473,6 +1488,8 @@
|
||||
return
|
||||
var/datum/antagonist/A = has_antag_datum(datum_type)
|
||||
if(A)
|
||||
if(!A.owner?.current)
|
||||
CRASH("attempted to remove an antag datum ([A.type]) that didn't have an owner or owner.current")
|
||||
A.on_removal()
|
||||
return TRUE
|
||||
|
||||
@@ -1480,6 +1497,9 @@
|
||||
/datum/mind/proc/remove_all_antag_datums() //For the Lazy amongst us.
|
||||
for(var/a in antag_datums)
|
||||
var/datum/antagonist/A = a
|
||||
if(!A.owner?.current)
|
||||
stack_trace("attempted to remove an antag datum ([A.type]) that didn't have an owner or owner.current")
|
||||
continue
|
||||
A.on_removal()
|
||||
|
||||
/datum/mind/proc/has_antag_datum(datum_type, check_subtypes = TRUE)
|
||||
@@ -1614,7 +1634,6 @@
|
||||
qdel(flash)
|
||||
take_uplink()
|
||||
var/fail = 0
|
||||
// fail |= !ticker.mode.equip_traitor(current, 1)
|
||||
fail |= !SSticker.mode.equip_revolutionary(current)
|
||||
|
||||
/datum/mind/proc/make_Abductor()
|
||||
@@ -1704,38 +1723,12 @@
|
||||
G.reenter_corpse()
|
||||
|
||||
|
||||
/datum/mind/proc/make_zealot(mob/living/carbon/human/missionary, convert_duration = 6000, team_color = "red")
|
||||
|
||||
/datum/mind/proc/make_zealot(mob/living/carbon/human/missionary, convert_duration = 10 MINUTES, team_color = "red")
|
||||
zealot_master = missionary
|
||||
|
||||
var/list/implanters
|
||||
if(!(missionary.mind in SSticker.mode.implanter))
|
||||
SSticker.mode.implanter[missionary.mind] = list()
|
||||
implanters = SSticker.mode.implanter[missionary.mind]
|
||||
implanters.Add(src)
|
||||
SSticker.mode.implanted.Add(src)
|
||||
SSticker.mode.implanted[src] = missionary.mind
|
||||
SSticker.mode.implanter[missionary.mind] = implanters
|
||||
SSticker.mode.traitors += src
|
||||
|
||||
|
||||
var/datum/objective/protect/zealot_objective = new
|
||||
zealot_objective.target = missionary.mind
|
||||
zealot_objective.owner = src
|
||||
zealot_objective.explanation_text = "Obey every order from and protect [missionary.real_name], the [missionary.mind.assigned_role == missionary.mind.special_role ? (missionary.mind.special_role) : (missionary.mind.assigned_role)]."
|
||||
objectives += zealot_objective
|
||||
add_antag_datum(/datum/antagonist/mindslave)
|
||||
|
||||
var/datum/antagonist/traitor/T = missionary.mind.has_antag_datum(/datum/antagonist)
|
||||
T.update_traitor_icons_added(missionary.mind)
|
||||
|
||||
to_chat(current, "<span class='warning'><B>You're now a loyal zealot of [missionary.name]!</B> You now must lay down your life to protect [missionary.p_them()] and assist in [missionary.p_their()] goals at any cost.</span>")
|
||||
|
||||
var/datum/mindslaves/slaved = missionary.mind.som
|
||||
som = slaved
|
||||
slaved.serv += current
|
||||
slaved.add_serv_hud(missionary.mind, "master") //handles master servent icons
|
||||
slaved.add_serv_hud(src, "mindslave")
|
||||
// Give the new zealot their mindslave datum with a custom greeting.
|
||||
var/greeting = "You're now a loyal zealot of [missionary.name]!</B> You now must lay down your life to protect [missionary.p_them()] and assist in [missionary.p_their()] goals at any cost."
|
||||
add_antag_datum(new /datum/antagonist/mindslave(missionary.mind, greeting))
|
||||
|
||||
var/obj/item/clothing/under/jumpsuit = null
|
||||
if(ishuman(current)) //only bother with the jumpsuit stuff if we are a human type, since we won't have the slot otherwise
|
||||
@@ -1747,7 +1740,7 @@
|
||||
|
||||
add_attack_logs(missionary, current, "Converted to a zealot for [convert_duration/600] minutes")
|
||||
addtimer(CALLBACK(src, .proc/remove_zealot, jumpsuit), convert_duration) //deconverts after the timer expires
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
/datum/mind/proc/remove_zealot(obj/item/clothing/under/jumpsuit = null)
|
||||
if(!zealot_master) //if they aren't a zealot, we can't remove their zealot status, obviously. don't bother with the rest so we don't confuse them with the messages
|
||||
|
||||
@@ -85,7 +85,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
|
||||
/datum/uplink_item/proc/spawn_item(turf/loc, obj/item/uplink/U)
|
||||
|
||||
if(hijack_only && !(usr.mind.special_role == SPECIAL_ROLE_NUKEOPS))//nukies get items that regular traitors only get with hijack. If a hijack-only item is not for nukies, then exclude it via the gamemode list.
|
||||
if(!(locate(/datum/objective/hijack) in usr.mind.objectives))
|
||||
if(!(locate(/datum/objective/hijack) in usr.mind.get_all_objectives()))
|
||||
to_chat(usr, "<span class='warning'>The Syndicate will only issue this extremely dangerous item to agents assigned the Hijack objective.</span>")
|
||||
return
|
||||
|
||||
@@ -1748,7 +1748,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
|
||||
CU.hub = new(mind, CU)
|
||||
// Update their mind stuff
|
||||
LAZYSET(GLOB.contractors, mind, CU.hub)
|
||||
AT.update_traitor_icons_added(mind)
|
||||
AT.add_antag_hud(mind.current)
|
||||
|
||||
log_game("[key_name(usr)] became a Contractor")
|
||||
return I
|
||||
|
||||
@@ -201,9 +201,11 @@ GLOBAL_LIST_INIT(possible_changeling_IDs, list("Alpha","Beta","Gamma","Delta","E
|
||||
text += "<br><b>Changeling ID:</b> [changeling.changeling.changelingID]."
|
||||
text += "<br><b>Genomes Extracted:</b> [changeling.changeling.absorbedcount]"
|
||||
|
||||
if(changeling.objectives.len)
|
||||
var/list/all_objectives = changeling.get_all_objectives()
|
||||
|
||||
if(length(all_objectives))
|
||||
var/count = 1
|
||||
for(var/datum/objective/objective in changeling.objectives)
|
||||
for(var/datum/objective/objective in all_objectives)
|
||||
if(objective.check_completion())
|
||||
text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <font color='green'><B>Success!</B></font>"
|
||||
SSblackbox.record_feedback("nested tally", "changeling_objective", 1, list("[objective.type]", "SUCCESS"))
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
/datum/game_mode
|
||||
// this includes admin-appointed traitors and multitraitors. Easy!
|
||||
/// A list of all minds which have the traitor antag datum.
|
||||
var/list/datum/mind/traitors = list()
|
||||
var/list/datum/mind/implanter = list()
|
||||
/// An associative list with mindslave minds as keys and their master's minds as values.
|
||||
var/list/datum/mind/implanted = list()
|
||||
|
||||
var/datum/mind/exchange_red
|
||||
var/datum/mind/exchange_blue
|
||||
|
||||
/datum/game_mode/traitor
|
||||
name = "traitor"
|
||||
config_tag = "traitor"
|
||||
@@ -15,11 +12,12 @@
|
||||
required_players = 0
|
||||
required_enemies = 1
|
||||
recommended_enemies = 4
|
||||
|
||||
/// A list containing references to the minds of soon-to-be traitors. This is seperate to avoid duplicate entries in the `traitors` list.
|
||||
var/list/datum/mind/pre_traitors = list()
|
||||
var/traitors_possible = 4 //hard limit on traitors if scaling is turned off
|
||||
var/const/traitor_scaling_coeff = 5.0 //how much does the amount of players get divided by to determine traitors
|
||||
var/antag_datum = /datum/antagonist/traitor //what type of antag to create
|
||||
/// Hard limit on traitors if scaling is turned off.
|
||||
var/traitors_possible = 4
|
||||
/// How much the amount of players get divided by to determine the number of traitors.
|
||||
var/const/traitor_scaling_coeff = 5
|
||||
|
||||
/datum/game_mode/traitor/announce()
|
||||
to_chat(world, "<B>The current game mode is - Traitor!</B>")
|
||||
@@ -38,8 +36,8 @@
|
||||
possible_traitors.Remove(candidate)
|
||||
|
||||
// stop setup if no possible traitors
|
||||
if(!possible_traitors.len)
|
||||
return 0
|
||||
if(!length(possible_traitors))
|
||||
return FALSE
|
||||
|
||||
var/num_traitors = 1
|
||||
|
||||
@@ -48,26 +46,23 @@
|
||||
else
|
||||
num_traitors = max(1, min(num_players(), traitors_possible))
|
||||
|
||||
for(var/j = 0, j < num_traitors, j++)
|
||||
if(!possible_traitors.len)
|
||||
for(var/i in 1 to num_traitors)
|
||||
if(!length(possible_traitors))
|
||||
break
|
||||
var/datum/mind/traitor = pick(possible_traitors)
|
||||
var/datum/mind/traitor = pick_n_take(possible_traitors)
|
||||
pre_traitors += traitor
|
||||
traitor.special_role = SPECIAL_ROLE_TRAITOR
|
||||
traitor.restricted_roles = restricted_jobs
|
||||
possible_traitors.Remove(traitor)
|
||||
|
||||
if(!pre_traitors.len)
|
||||
return 0
|
||||
return 1
|
||||
if(!length(pre_traitors))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
|
||||
/datum/game_mode/traitor/post_setup()
|
||||
for(var/datum/mind/traitor in pre_traitors)
|
||||
var/datum/antagonist/traitor/new_antag = new antag_datum()
|
||||
addtimer(CALLBACK(traitor, /datum/mind.proc/add_antag_datum, new_antag), rand(10,100))
|
||||
if(!exchange_blue)
|
||||
exchange_blue = -1 //Block latejoiners from getting exchange objectives
|
||||
for(var/t in pre_traitors)
|
||||
var/datum/mind/traitor = t
|
||||
traitor.add_antag_datum(/datum/antagonist/traitor)
|
||||
..()
|
||||
|
||||
|
||||
@@ -75,44 +70,37 @@
|
||||
..()
|
||||
return//Traitors will be checked as part of check_extra_completion. Leaving this here as a reminder.
|
||||
|
||||
/datum/game_mode/traitor/process()
|
||||
// Make sure all objectives are processed regularly, so that objectives
|
||||
// which can be checked mid-round are checked mid-round.
|
||||
for(var/datum/mind/traitor_mind in traitors)
|
||||
for(var/datum/objective/objective in traitor_mind.objectives)
|
||||
objective.check_completion()
|
||||
return 0
|
||||
|
||||
|
||||
/datum/game_mode/proc/auto_declare_completion_traitor()
|
||||
if(traitors.len)
|
||||
if(length(traitors))
|
||||
var/text = "<FONT size = 2><B>The traitors were:</B></FONT><br>"
|
||||
for(var/datum/mind/traitor in traitors)
|
||||
var/traitorwin = 1
|
||||
var/traitorwin = TRUE
|
||||
text += printplayer(traitor)
|
||||
|
||||
var/TC_uses = 0
|
||||
var/uplink_true = 0
|
||||
var/used_uplink = FALSE
|
||||
var/purchases = ""
|
||||
for(var/obj/item/uplink/H in GLOB.world_uplinks)
|
||||
if(H && H.uplink_owner && H.uplink_owner==traitor.key)
|
||||
if(H && H.uplink_owner && H.uplink_owner == traitor.key)
|
||||
TC_uses += H.used_TC
|
||||
uplink_true=1
|
||||
used_uplink = TRUE
|
||||
purchases += H.purchase_log
|
||||
|
||||
if(uplink_true) text += " (used [TC_uses] TC) [purchases]"
|
||||
if(used_uplink)
|
||||
text += " (used [TC_uses] TC) [purchases]"
|
||||
|
||||
var/all_objectives = traitor.get_all_objectives()
|
||||
|
||||
if(traitor.objectives && traitor.objectives.len)//If the traitor had no objectives, don't need to process this.
|
||||
if(length(all_objectives))//If the traitor had no objectives, don't need to process this.
|
||||
var/count = 1
|
||||
for(var/datum/objective/objective in traitor.objectives)
|
||||
for(var/datum/objective/objective in all_objectives)
|
||||
if(objective.check_completion())
|
||||
text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <font color='green'><B>Success!</B></font>"
|
||||
SSblackbox.record_feedback("nested tally", "traitor_objective", 1, list("[objective.type]", "SUCCESS"))
|
||||
else
|
||||
text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <font color='red'>Fail.</font>"
|
||||
SSblackbox.record_feedback("nested tally", "traitor_objective", 1, list("[objective.type]", "FAIL"))
|
||||
traitorwin = 0
|
||||
traitorwin = FALSE
|
||||
count++
|
||||
|
||||
var/special_role_text
|
||||
@@ -155,8 +143,7 @@
|
||||
for(var/datum/mind/mindslave in SSticker.mode.implanted)
|
||||
text += printplayer(mindslave)
|
||||
var/datum/mind/master_mind = SSticker.mode.implanted[mindslave]
|
||||
var/mob/living/carbon/human/master = master_mind.current
|
||||
text += " (slaved by: <b>[master]</b>)<br>"
|
||||
text += " (slaved by: <b>[master_mind.current]</b>)<br>"
|
||||
|
||||
var/phrases = jointext(GLOB.syndicate_code_phrase, ", ")
|
||||
var/responses = jointext(GLOB.syndicate_code_response, ", ")
|
||||
@@ -165,4 +152,4 @@
|
||||
<b>The code responses were:</b> <span class='danger'>[responses]</span><br><br>"
|
||||
|
||||
to_chat(world, text)
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
@@ -88,9 +88,11 @@
|
||||
text += "body destroyed"
|
||||
text += ")"
|
||||
|
||||
if(vampire.objectives.len)//If the traitor had no objectives, don't need to process this.
|
||||
var/list/all_objectives = vampire.get_all_objectives()
|
||||
|
||||
if(length(all_objectives))//If the traitor had no objectives, don't need to process this.
|
||||
var/count = 1
|
||||
for(var/datum/objective/objective in vampire.objectives)
|
||||
for(var/datum/objective/objective in all_objectives)
|
||||
if(objective.check_completion())
|
||||
text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <font color='green'><B>Success!</B></font>"
|
||||
SSblackbox.record_feedback("nested tally", "traitor_objective", 1, list("[objective.type]", "SUCCESS"))
|
||||
|
||||
@@ -130,10 +130,9 @@ GLOBAL_LIST_EMPTY(world_uplinks)
|
||||
var/active = 0
|
||||
|
||||
// The hidden uplink MUST be inside an obj/item's contents.
|
||||
/obj/item/uplink/hidden/New()
|
||||
spawn(2)
|
||||
if(!istype(src.loc, /obj/item))
|
||||
qdel(src)
|
||||
/obj/item/uplink/hidden/New(loc)
|
||||
if(!isitem(loc))
|
||||
qdel(src)
|
||||
..()
|
||||
|
||||
// Toggles the uplink on and off. Normally this will bypass the item's normal functions and go to the uplink menu, if activated.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "Mindslave Implant"
|
||||
desc = "Divide and Conquer"
|
||||
origin_tech = "programming=5;biotech=5;syndicate=8"
|
||||
activated = 0
|
||||
activated = FALSE
|
||||
|
||||
/obj/item/implant/traitor/get_data()
|
||||
var/dat = {"<b>Implant Specifications:</b><BR>
|
||||
@@ -16,71 +16,40 @@
|
||||
<b>Integrity:</b> Implant will last so long as the nanobots are inside the bloodstream."}
|
||||
return dat
|
||||
|
||||
/obj/item/implant/traitor/implant(mob/M, mob/user)
|
||||
if(!M.mind) // If the target is catatonic or doesn't have a mind, don't let them use it
|
||||
/obj/item/implant/traitor/implant(mob/living/carbon/human/mindslave_target, mob/living/carbon/human/user)
|
||||
// Check `activated` here so you can't just keep taking it out and putting it back into other people.
|
||||
if(!..() || activated || !istype(mindslave_target) || !istype(user)) // Both the target and the user need to be human.
|
||||
return FALSE
|
||||
|
||||
// If the target is catatonic or doesn't have a mind, return.
|
||||
if(!mindslave_target.mind)
|
||||
to_chat(user, "<span class='warning'><i>This person doesn't have a mind for you to slave!</i></span>")
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
if(!activated) //So you can't just keep taking it out and putting it back into other people.
|
||||
var/mob/living/carbon/human/mindslave_target = M
|
||||
if(ismindslave(mindslave_target))
|
||||
mindslave_target.visible_message("<span class='warning'>[mindslave_target] seems to resist the implant!</span>", "<span class='warning'>You feel a strange sensation in your head that quickly dissipates.</span>")
|
||||
qdel(src)
|
||||
return -1
|
||||
if(..())
|
||||
var/list/implanters
|
||||
if(!ishuman(mindslave_target))
|
||||
return 0
|
||||
if(!mindslave_target.mind)
|
||||
return 0
|
||||
if(mindslave_target == user)
|
||||
to_chat(user, "<span class='notice'>Making yourself loyal to yourself was a great idea! Perhaps even the best idea ever! Actually, you just feel like an idiot.</span>")
|
||||
if(isliving(user))
|
||||
var/mob/living/L = user
|
||||
L.adjustBrainLoss(20)
|
||||
removed(mindslave_target)
|
||||
qdel(src)
|
||||
return -1
|
||||
if(ismindshielded(mindslave_target))
|
||||
mindslave_target.visible_message("<span class='warning'>[mindslave_target] seems to resist the implant!</span>", "<span class='warning'>You feel a strange sensation in your head that quickly dissipates.</span>")
|
||||
removed(mindslave_target)
|
||||
qdel(src)
|
||||
return -1
|
||||
// Fails if they're already a mindslave of someone, or if they're mindshielded.
|
||||
if(ismindslave(mindslave_target) || ismindshielded(mindslave_target))
|
||||
mindslave_target.visible_message(
|
||||
"<span class='warning'>[mindslave_target] seems to resist the implant!</span>", \
|
||||
"<span class='warning'>You feel a strange sensation in your head that quickly dissipates.</span>")
|
||||
removed(mindslave_target)
|
||||
qdel(src)
|
||||
return FALSE
|
||||
|
||||
to_chat(mindslave_target, "<span class='danger'>You feel completely loyal to [user.name].</span>")
|
||||
if(!(user.mind in SSticker.mode.implanter))
|
||||
SSticker.mode.implanter[user.mind] = list()
|
||||
implanters = SSticker.mode.implanter[user.mind]
|
||||
implanters.Add(mindslave_target.mind)
|
||||
SSticker.mode.implanted.Add(mindslave_target.mind)
|
||||
SSticker.mode.implanted[mindslave_target.mind] = user.mind
|
||||
SSticker.mode.implanter[user.mind] = implanters
|
||||
// Mindslaving yourself.
|
||||
if(mindslave_target == user)
|
||||
to_chat(user, "<span class='notice'>Making yourself loyal to yourself was a great idea! Perhaps even the best idea ever! Actually, you just feel like an idiot.</span>")
|
||||
user.adjustBrainLoss(20)
|
||||
removed(mindslave_target)
|
||||
qdel(src)
|
||||
return FALSE
|
||||
|
||||
to_chat(mindslave_target, "<span class='danger'><B>You're now completely loyal to [user.name]!</B> You now must lay down your life to protect [user.p_them()] and assist in [user.p_their()] goals at any cost.</span>")
|
||||
|
||||
var/datum/objective/protect/mindslave/MS = new
|
||||
MS.owner = mindslave_target.mind
|
||||
MS.target = user.mind
|
||||
MS.explanation_text = "Obey every order from and protect [user.real_name], the [user.mind.assigned_role == user.mind.special_role ? (user.mind.special_role) : (user.mind.assigned_role)]."
|
||||
mindslave_target.mind.objectives += MS
|
||||
mindslave_target.mind.add_antag_datum(/datum/antagonist/mindslave)
|
||||
|
||||
var/datum/mindslaves/slaved = user.mind.som
|
||||
mindslave_target.mind.som = slaved
|
||||
slaved.serv += mindslave_target
|
||||
slaved.add_serv_hud(user.mind, "master") //handles master servent icons
|
||||
slaved.add_serv_hud(mindslave_target.mind, "mindslave")
|
||||
|
||||
log_admin("[key_name(user)] has mind-slaved [key_name(mindslave_target)].")
|
||||
activated = 1
|
||||
if(jobban_isbanned(M, ROLE_SYNDICATE))
|
||||
SSticker.mode.replace_jobbanned_player(M, ROLE_SYNDICATE)
|
||||
return 1
|
||||
return 0
|
||||
// Create a new mindslave datum for the target with the user as their master.
|
||||
mindslave_target.mind.add_antag_datum(new /datum/antagonist/mindslave(user.mind))
|
||||
log_admin("[key_name_admin(user)] has mind-slaved [key_name_admin(mindslave_target)].")
|
||||
return TRUE
|
||||
|
||||
/obj/item/implant/traitor/removed(mob/target)
|
||||
if(..())
|
||||
target.mind.remove_antag_datum(/datum/antagonist/mindslave)
|
||||
to_chat(target, "<span class='warning'>You are no longer a mindslave: you have complete and free control of your own faculties, once more!</span>")
|
||||
return 1
|
||||
return 0
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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, ", ")
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user