mirror of
https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13.git
synced 2025-12-10 18:02:57 +00:00
832 lines
29 KiB
Plaintext
832 lines
29 KiB
Plaintext
/* Note from Carnie:
|
|
The way datum/mind stuff works has been changed a lot.
|
|
Minds now represent IC characters rather than following a client around constantly.
|
|
|
|
Guidelines for using minds properly:
|
|
|
|
- Never mind.transfer_to(ghost). The var/current and var/original of a mind must always be of type mob/living!
|
|
ghost.mind is however used as a reference to the ghost's corpse
|
|
|
|
- When creating a new mob for an existing IC character (e.g. cloning a dead guy or borging a brain of a human)
|
|
the existing mind of the old mob should be transfered to the new mob like so:
|
|
|
|
mind.transfer_to(new_mob)
|
|
|
|
- You must not assign key= or ckey= after transfer_to() since the transfer_to transfers the client for you.
|
|
By setting key or ckey explicitly after transferring the mind with transfer_to you will cause bugs like DCing
|
|
the player.
|
|
|
|
- IMPORTANT NOTE 2, if you want a player to become a ghost, use mob.ghostize() It does all the hard work for you.
|
|
|
|
- When creating a new mob which will be a new IC character (e.g. putting a shade in a construct or randomly selecting
|
|
a ghost to become a xeno during an event). Simply assign the key or ckey like you've always done.
|
|
|
|
new_mob.key = key
|
|
|
|
The Login proc will handle making a new mind for that mobtype (including setting up stuff like mind.name). Simple!
|
|
However if you want that mind to have any special properties like being a traitor etc you will have to do that
|
|
yourself.
|
|
|
|
*/
|
|
|
|
/datum/mind
|
|
var/key
|
|
var/name //replaces mob/var/original_name
|
|
var/mob/living/current
|
|
var/active = 0
|
|
|
|
var/memory
|
|
|
|
var/assigned_role
|
|
var/special_role
|
|
var/list/restricted_roles = list()
|
|
|
|
var/list/datum/objective/objectives = list()
|
|
|
|
var/list/spell_list = list() // Wizard mode & "Give Spell" badmin button.
|
|
|
|
var/linglink
|
|
var/datum/martial_art/martial_art
|
|
var/static/default_martial_art = new/datum/martial_art
|
|
var/miming = 0 // Mime's vow of silence
|
|
var/list/antag_datums
|
|
var/antag_hud_icon_state = null //this mind's ANTAG_HUD should have this icon_state
|
|
var/datum/atom_hud/antag/antag_hud = null //this mind's antag HUD
|
|
var/damnation_type = 0
|
|
var/datum/mind/soulOwner //who owns the soul. Under normal circumstances, this will point to src
|
|
var/hasSoul = TRUE // If false, renders the character unable to sell their soul.
|
|
var/isholy = FALSE //is this person a chaplain or admin role allowed to use bibles
|
|
|
|
var/mob/living/enslaved_to //If this mind's master is another mob (i.e. adamantine golems)
|
|
var/datum/language_holder/language_holder
|
|
var/unconvertable = FALSE
|
|
var/late_joiner = FALSE
|
|
|
|
var/force_escaped = FALSE // Set by Into The Sunset command of the shuttle manipulator
|
|
|
|
var/list/learned_recipes //List of learned recipe TYPES.
|
|
|
|
/datum/mind/New(var/key)
|
|
src.key = key
|
|
soulOwner = src
|
|
martial_art = default_martial_art
|
|
|
|
/datum/mind/Destroy()
|
|
SSticker.minds -= src
|
|
if(islist(antag_datums))
|
|
for(var/i in antag_datums)
|
|
var/datum/antagonist/antag_datum = i
|
|
if(antag_datum.delete_on_mind_deletion)
|
|
qdel(i)
|
|
antag_datums = null
|
|
return ..()
|
|
|
|
/datum/mind/proc/get_language_holder()
|
|
if(!language_holder)
|
|
var/datum/language_holder/L = current.get_language_holder(shadow=FALSE)
|
|
language_holder = L.copy(src)
|
|
|
|
return language_holder
|
|
|
|
/datum/mind/proc/transfer_to(mob/new_character, var/force_key_move = 0)
|
|
if(current) // remove ourself from our old body's mind variable
|
|
current.mind = null
|
|
SStgui.on_transfer(current, new_character)
|
|
|
|
if(!language_holder)
|
|
var/datum/language_holder/mob_holder = new_character.get_language_holder(shadow = FALSE)
|
|
language_holder = mob_holder.copy(src)
|
|
|
|
if(key)
|
|
if(new_character.key != key) //if we're transferring into a body with a key associated which is not ours
|
|
new_character.ghostize(1) //we'll need to ghostize so that key isn't mobless.
|
|
else
|
|
key = new_character.key
|
|
|
|
if(new_character.mind) //disassociate any mind currently in our new body's mind variable
|
|
new_character.mind.current = null
|
|
|
|
var/datum/atom_hud/antag/hud_to_transfer = antag_hud//we need this because leave_hud() will clear this list
|
|
var/mob/living/old_current = current
|
|
if(current)
|
|
current.transfer_observers_to(new_character) //transfer anyone observing the old character to the new one
|
|
current = new_character //associate ourself with our new body
|
|
new_character.mind = src //and associate our new body with ourself
|
|
for(var/a in antag_datums) //Makes sure all antag datums effects are applied in the new body
|
|
var/datum/antagonist/A = a
|
|
A.on_body_transfer(old_current, current)
|
|
if(iscarbon(new_character))
|
|
var/mob/living/carbon/C = new_character
|
|
C.last_mind = src
|
|
transfer_antag_huds(hud_to_transfer) //inherit the antag HUD
|
|
transfer_actions(new_character)
|
|
transfer_martial_arts(new_character)
|
|
if(active || force_key_move)
|
|
new_character.key = key //now transfer the key to link the client to our new body
|
|
|
|
//CIT CHANGE - makes arousal update when transfering bodies
|
|
if(isliving(new_character)) //New humans and such are by default enabled arousal. Let's always use the new mind's prefs.
|
|
var/mob/living/L = new_character
|
|
if(L.client && L.client.prefs)
|
|
L.canbearoused = L.client.prefs.arousable //Technically this should make taking over a character mean the body gain the new minds setting...
|
|
L.update_arousal_hud() //Removes the old icon
|
|
|
|
/datum/mind/proc/store_memory(new_text)
|
|
if((length(memory) + length(new_text)) <= MAX_MESSAGE_LEN)
|
|
memory += "[new_text]<BR>"
|
|
|
|
/datum/mind/proc/wipe_memory()
|
|
memory = null
|
|
|
|
// Datum antag mind procs
|
|
/datum/mind/proc/add_antag_datum(datum_type_or_instance, team)
|
|
if(!datum_type_or_instance)
|
|
return
|
|
var/datum/antagonist/A
|
|
if(!ispath(datum_type_or_instance))
|
|
A = datum_type_or_instance
|
|
if(!istype(A))
|
|
return
|
|
else
|
|
A = new datum_type_or_instance()
|
|
//Choose snowflake variation if antagonist handles it
|
|
var/datum/antagonist/S = A.specialization(src)
|
|
if(S && S != A)
|
|
qdel(A)
|
|
A = S
|
|
if(!A.can_be_owned(src))
|
|
qdel(A)
|
|
return
|
|
A.owner = src
|
|
LAZYADD(antag_datums, A)
|
|
A.create_team(team)
|
|
var/datum/team/antag_team = A.get_team()
|
|
if(antag_team)
|
|
antag_team.add_member(src)
|
|
A.on_gain()
|
|
return A
|
|
|
|
/datum/mind/proc/remove_antag_datum(datum_type)
|
|
if(!datum_type)
|
|
return
|
|
var/datum/antagonist/A = has_antag_datum(datum_type)
|
|
if(A)
|
|
A.on_removal()
|
|
return TRUE
|
|
|
|
|
|
/datum/mind/proc/remove_all_antag_datums() //For the Lazy amongst us.
|
|
for(var/a in antag_datums)
|
|
var/datum/antagonist/A = a
|
|
A.on_removal()
|
|
|
|
/datum/mind/proc/has_antag_datum(datum_type, check_subtypes = TRUE)
|
|
if(!datum_type)
|
|
return
|
|
. = FALSE
|
|
for(var/a in antag_datums)
|
|
var/datum/antagonist/A = a
|
|
if(check_subtypes && istype(A, datum_type))
|
|
return A
|
|
else if(A.type == datum_type)
|
|
return A
|
|
|
|
/*
|
|
Removes antag type's references from a mind.
|
|
objectives, uplinks, powers etc are all handled.
|
|
*/
|
|
|
|
/datum/mind/proc/remove_changeling()
|
|
var/datum/antagonist/changeling/C = has_antag_datum(/datum/antagonist/changeling)
|
|
if(C)
|
|
remove_antag_datum(/datum/antagonist/changeling)
|
|
special_role = null
|
|
|
|
/datum/mind/proc/remove_traitor()
|
|
remove_antag_datum(/datum/antagonist/traitor)
|
|
|
|
/datum/mind/proc/remove_brother()
|
|
if(src in SSticker.mode.brothers)
|
|
remove_antag_datum(/datum/antagonist/brother)
|
|
SSticker.mode.update_brother_icons_removed(src)
|
|
|
|
/datum/mind/proc/remove_nukeop()
|
|
var/datum/antagonist/nukeop/nuke = has_antag_datum(/datum/antagonist/nukeop,TRUE)
|
|
if(nuke)
|
|
remove_antag_datum(nuke.type)
|
|
special_role = null
|
|
|
|
/datum/mind/proc/remove_wizard()
|
|
remove_antag_datum(/datum/antagonist/wizard)
|
|
special_role = null
|
|
|
|
/datum/mind/proc/remove_cultist()
|
|
if(src in SSticker.mode.cult)
|
|
SSticker.mode.remove_cultist(src, 0, 0)
|
|
special_role = null
|
|
remove_antag_equip()
|
|
|
|
/datum/mind/proc/remove_rev()
|
|
var/datum/antagonist/rev/rev = has_antag_datum(/datum/antagonist/rev)
|
|
if(rev)
|
|
remove_antag_datum(rev.type)
|
|
special_role = null
|
|
|
|
|
|
/datum/mind/proc/remove_antag_equip()
|
|
var/list/Mob_Contents = current.get_contents()
|
|
for(var/obj/item/I in Mob_Contents)
|
|
var/datum/component/uplink/O = I.GetComponent(/datum/component/uplink)
|
|
//Todo make this reset signal
|
|
if(O)
|
|
O.unlock_code = null
|
|
|
|
/datum/mind/proc/remove_all_antag() //For the Lazy amongst us.
|
|
remove_changeling()
|
|
remove_traitor()
|
|
remove_nukeop()
|
|
remove_wizard()
|
|
remove_cultist()
|
|
remove_rev()
|
|
SSticker.mode.update_cult_icons_removed(src)
|
|
|
|
/datum/mind/proc/equip_traitor(employer = "The Syndicate", silent = FALSE, datum/antagonist/uplink_owner)
|
|
if(!current)
|
|
return
|
|
var/mob/living/carbon/human/traitor_mob = current
|
|
if (!istype(traitor_mob))
|
|
return
|
|
|
|
var/list/all_contents = traitor_mob.GetAllContents()
|
|
var/obj/item/pda/PDA = locate() in all_contents
|
|
var/obj/item/radio/R = locate() in all_contents
|
|
var/obj/item/pen/P
|
|
|
|
if (PDA) // Prioritize PDA pen, otherwise the pocket protector pens will be chosen, which causes numerous ahelps about missing uplink
|
|
P = locate() in PDA
|
|
if (!P) // If we couldn't find a pen in the PDA, or we didn't even have a PDA, do it the old way
|
|
P = locate() in all_contents
|
|
if(!P) // I do not have a pen.
|
|
var/obj/item/pen/inowhaveapen
|
|
if(istype(traitor_mob.back,/obj/item/storage)) //ok buddy you better have a backpack!
|
|
inowhaveapen = new /obj/item/pen(traitor_mob.back)
|
|
else
|
|
inowhaveapen = new /obj/item/pen(traitor_mob.loc)
|
|
traitor_mob.put_in_hands(inowhaveapen) // I hope you don't have arms and your traitor pen gets stolen for all this trouble you've caused.
|
|
P = inowhaveapen
|
|
|
|
var/obj/item/uplink_loc
|
|
|
|
if(traitor_mob.client && traitor_mob.client.prefs)
|
|
switch(traitor_mob.client.prefs.uplink_spawn_loc)
|
|
if(UPLINK_PDA)
|
|
uplink_loc = PDA
|
|
if(!uplink_loc)
|
|
uplink_loc = R
|
|
if(!uplink_loc)
|
|
uplink_loc = P
|
|
if(UPLINK_RADIO)
|
|
uplink_loc = R
|
|
if(!uplink_loc)
|
|
uplink_loc = PDA
|
|
if(!uplink_loc)
|
|
uplink_loc = P
|
|
if(UPLINK_PEN)
|
|
uplink_loc = P
|
|
if(!uplink_loc)
|
|
uplink_loc = PDA
|
|
if(!uplink_loc)
|
|
uplink_loc = R
|
|
|
|
if (!uplink_loc)
|
|
if(!silent)
|
|
to_chat(traitor_mob, "Unfortunately, [employer] wasn't able to get you an Uplink.")
|
|
. = 0
|
|
else
|
|
. = uplink_loc
|
|
var/datum/component/uplink/U = uplink_loc.AddComponent(/datum/component/uplink, traitor_mob.key)
|
|
if(!U)
|
|
CRASH("Uplink creation failed.")
|
|
U.setup_unlock_code()
|
|
if(!silent)
|
|
if(uplink_loc == R)
|
|
to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [R.name]. Simply dial the frequency [format_frequency(U.unlock_code)] to unlock its hidden features.")
|
|
else if(uplink_loc == PDA)
|
|
to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [PDA.name]. Simply enter the code \"[U.unlock_code]\" into the ringtone select to unlock its hidden features.")
|
|
else if(uplink_loc == P)
|
|
to_chat(traitor_mob, "[employer] has cunningly disguised a Syndicate Uplink as your [P.name]. Simply twist the top of the pen [U.unlock_code] from its starting position to unlock its hidden features.")
|
|
|
|
if(uplink_owner)
|
|
uplink_owner.antag_memory += U.unlock_note + "<br>"
|
|
else
|
|
traitor_mob.mind.store_memory(U.unlock_note)
|
|
|
|
//Link a new mobs mind to the creator of said mob. They will join any team they are currently on, and will only switch teams when their creator does.
|
|
|
|
/datum/mind/proc/enslave_mind_to_creator(mob/living/creator)
|
|
if(iscultist(creator))
|
|
SSticker.mode.add_cultist(src)
|
|
|
|
else if(is_revolutionary(creator))
|
|
var/datum/antagonist/rev/converter = creator.mind.has_antag_datum(/datum/antagonist/rev,TRUE)
|
|
converter.add_revolutionary(src,FALSE)
|
|
|
|
else if(is_servant_of_ratvar(creator))
|
|
add_servant_of_ratvar(current)
|
|
|
|
else if(is_nuclear_operative(creator))
|
|
var/datum/antagonist/nukeop/converter = creator.mind.has_antag_datum(/datum/antagonist/nukeop,TRUE)
|
|
var/datum/antagonist/nukeop/N = new()
|
|
N.send_to_spawnpoint = FALSE
|
|
N.nukeop_outfit = null
|
|
add_antag_datum(N,converter.nuke_team)
|
|
|
|
|
|
enslaved_to = creator
|
|
|
|
current.faction |= creator.faction
|
|
creator.faction |= current.faction
|
|
|
|
if(creator.mind.special_role)
|
|
message_admins("[ADMIN_LOOKUPFLW(current)] has been created by [ADMIN_LOOKUPFLW(creator)], an antagonist.")
|
|
to_chat(current, "<span class='userdanger'>Despite your creators current allegiances, your true master remains [creator.real_name]. If their loyalties change, so do yours. This will never change unless your creator's body is destroyed.</span>")
|
|
|
|
/datum/mind/proc/show_memory(mob/recipient, window=1)
|
|
if(!recipient)
|
|
recipient = current
|
|
var/output = "<B>[current.real_name]'s Memories:</B><br>"
|
|
output += memory
|
|
|
|
|
|
for(var/datum/antagonist/A in antag_datums)
|
|
output += A.antag_memory
|
|
|
|
if(objectives.len)
|
|
output += "<B>Objectives:</B>"
|
|
var/obj_count = 1
|
|
for(var/datum/objective/objective in objectives)
|
|
output += "<br><B>Objective #[obj_count++]</B>: [objective.explanation_text]"
|
|
var/list/datum/mind/other_owners = objective.get_owners() - src
|
|
if(other_owners.len)
|
|
output += "<ul>"
|
|
for(var/datum/mind/M in other_owners)
|
|
output += "<li>Conspirator: [M.name]</li>"
|
|
output += "</ul>"
|
|
|
|
if(window)
|
|
recipient << browse(output,"window=memory")
|
|
else if(objectives.len || memory)
|
|
to_chat(recipient, "<i>[output]</i>")
|
|
|
|
/datum/mind/Topic(href, href_list)
|
|
if(!check_rights(R_ADMIN))
|
|
return
|
|
|
|
var/self_antagging = usr == current
|
|
|
|
if(href_list["add_antag"])
|
|
add_antag_wrapper(text2path(href_list["add_antag"]),usr)
|
|
if(href_list["remove_antag"])
|
|
var/datum/antagonist/A = locate(href_list["remove_antag"]) in antag_datums
|
|
if(!istype(A))
|
|
to_chat(usr,"<span class='warning'>Invalid antagonist ref to be removed.</span>")
|
|
return
|
|
A.admin_remove(usr)
|
|
|
|
if (href_list["role_edit"])
|
|
var/new_role = input("Select new role", "Assigned role", assigned_role) as null|anything in get_all_jobs()
|
|
if (!new_role)
|
|
return
|
|
assigned_role = new_role
|
|
|
|
else if (href_list["memory_edit"])
|
|
var/new_memo = copytext(sanitize(input("Write new memory", "Memory", memory) as null|message),1,MAX_MESSAGE_LEN)
|
|
if (isnull(new_memo))
|
|
return
|
|
memory = new_memo
|
|
|
|
else if (href_list["obj_edit"] || href_list["obj_add"])
|
|
var/datum/objective/objective
|
|
var/objective_pos
|
|
var/def_value
|
|
|
|
var/datum/antagonist/target_antag
|
|
|
|
if (href_list["obj_edit"])
|
|
objective = locate(href_list["obj_edit"])
|
|
if (!objective)
|
|
return
|
|
|
|
for(var/datum/antagonist/A in antag_datums)
|
|
if(objective in A.objectives)
|
|
target_antag = A
|
|
objective_pos = A.objectives.Find(objective)
|
|
break
|
|
|
|
if(!target_antag) //Shouldn't happen anymore
|
|
stack_trace("objective without antagonist found")
|
|
objective_pos = objectives.Find(objective)
|
|
|
|
//Text strings are easy to manipulate. Revised for simplicity.
|
|
var/temp_obj_type = "[objective.type]"//Convert path into a text string.
|
|
def_value = copytext(temp_obj_type, 19)//Convert last part of path into an objective keyword.
|
|
if(!def_value)//If it's a custom objective, it will be an empty string.
|
|
def_value = "custom"
|
|
else
|
|
//We're adding this objective
|
|
if(href_list["target_antag"])
|
|
var/datum/antagonist/X = locate(href_list["target_antag"]) in antag_datums
|
|
if(X)
|
|
target_antag = X
|
|
if(!target_antag)
|
|
switch(antag_datums.len)
|
|
if(0)
|
|
target_antag = add_antag_datum(/datum/antagonist/custom)
|
|
if(1)
|
|
target_antag = antag_datums[1]
|
|
else
|
|
var/datum/antagonist/target = input("Which antagonist gets the objective:", "Antagonist", def_value) as null|anything in antag_datums + "(new custom antag)"
|
|
if (QDELETED(target))
|
|
return
|
|
else if(target == "(new custom antag)")
|
|
target_antag = add_antag_datum(/datum/antagonist/custom)
|
|
else
|
|
target_antag = target
|
|
|
|
var/new_obj_type = input("Select objective type:", "Objective type", def_value) as null|anything in list("assassinate", "maroon", "debrain", "protect", "destroy", "prevent", "hijack", "escape", "survive", "martyr", "steal", "download", "nuclear", "capture", "absorb", "custom")
|
|
if (!new_obj_type)
|
|
return
|
|
|
|
var/datum/objective/new_objective = null
|
|
|
|
switch (new_obj_type)
|
|
if ("assassinate","protect","debrain","maroon")
|
|
var/list/possible_targets = list("Free objective")
|
|
for(var/datum/mind/possible_target in SSticker.minds)
|
|
if ((possible_target != src) && ishuman(possible_target.current))
|
|
possible_targets += possible_target.current
|
|
|
|
var/mob/def_target = null
|
|
var/list/objective_list = typecacheof(list(/datum/objective/assassinate, /datum/objective/protect, /datum/objective/debrain, /datum/objective/maroon))
|
|
if (is_type_in_typecache(objective, objective_list) && objective.target)
|
|
def_target = objective.target.current
|
|
|
|
var/mob/new_target = input("Select target:", "Objective target", def_target) as null|anything in possible_targets
|
|
if (!new_target)
|
|
return
|
|
|
|
var/objective_path = text2path("/datum/objective/[new_obj_type]")
|
|
if (new_target == "Free objective")
|
|
new_objective = new objective_path
|
|
new_objective.owner = src
|
|
new_objective.target = null
|
|
new_objective.explanation_text = "Free objective"
|
|
else
|
|
new_objective = new objective_path
|
|
new_objective.owner = src
|
|
new_objective.target = new_target.mind
|
|
//Will display as special role if the target is set as MODE. Ninjas/commandos/nuke ops.
|
|
new_objective.update_explanation_text()
|
|
|
|
if ("destroy")
|
|
var/list/possible_targets = active_ais(1)
|
|
if(possible_targets.len)
|
|
var/mob/new_target = input("Select target:", "Objective target") as null|anything in possible_targets
|
|
new_objective = new /datum/objective/destroy
|
|
new_objective.target = new_target.mind
|
|
new_objective.owner = src
|
|
new_objective.update_explanation_text()
|
|
else
|
|
to_chat(usr, "No active AIs with minds")
|
|
|
|
if ("prevent")
|
|
new_objective = new /datum/objective/block
|
|
new_objective.owner = src
|
|
|
|
if ("hijack")
|
|
new_objective = new /datum/objective/hijack
|
|
new_objective.owner = src
|
|
|
|
if ("escape")
|
|
new_objective = new /datum/objective/escape
|
|
new_objective.owner = src
|
|
|
|
if ("survive")
|
|
new_objective = new /datum/objective/survive
|
|
new_objective.owner = src
|
|
|
|
if("martyr")
|
|
new_objective = new /datum/objective/martyr
|
|
new_objective.owner = src
|
|
|
|
if ("nuclear")
|
|
new_objective = new /datum/objective/nuclear
|
|
new_objective.owner = src
|
|
|
|
if ("steal")
|
|
if (!istype(objective, /datum/objective/steal))
|
|
new_objective = new /datum/objective/steal
|
|
new_objective.owner = src
|
|
else
|
|
new_objective = objective
|
|
var/datum/objective/steal/steal = new_objective
|
|
if (!steal.select_target())
|
|
return
|
|
|
|
if("download","capture","absorb")
|
|
var/def_num
|
|
if(objective&&objective.type==text2path("/datum/objective/[new_obj_type]"))
|
|
def_num = objective.target_amount
|
|
|
|
var/target_number = input("Input target number:", "Objective", def_num) as num | null
|
|
if (isnull(target_number))//Ordinarily, you wouldn't need isnull. In this case, the value may already exist.
|
|
return
|
|
|
|
switch(new_obj_type)
|
|
if("download")
|
|
new_objective = new /datum/objective/download
|
|
new_objective.explanation_text = "Download [target_number] research node\s."
|
|
if("capture")
|
|
new_objective = new /datum/objective/capture
|
|
new_objective.explanation_text = "Capture [target_number] lifeforms with an energy net. Live, rare specimens are worth more."
|
|
if("absorb")
|
|
new_objective = new /datum/objective/absorb
|
|
new_objective.explanation_text = "Absorb [target_number] compatible genomes."
|
|
new_objective.owner = src
|
|
new_objective.target_amount = target_number
|
|
|
|
if ("custom")
|
|
var/expl = stripped_input(usr, "Custom objective:", "Objective", objective ? objective.explanation_text : "")
|
|
if (!expl)
|
|
return
|
|
new_objective = new /datum/objective
|
|
new_objective.owner = src
|
|
new_objective.explanation_text = expl
|
|
|
|
if (!new_objective)
|
|
return
|
|
|
|
if (objective)
|
|
if(target_antag)
|
|
target_antag.objectives -= objective
|
|
objectives -= objective
|
|
target_antag.objectives.Insert(objective_pos, new_objective)
|
|
message_admins("[key_name_admin(usr)] edited [current]'s objective to [new_objective.explanation_text]")
|
|
log_admin("[key_name(usr)] edited [current]'s objective to [new_objective.explanation_text]")
|
|
else
|
|
if(target_antag)
|
|
target_antag.objectives += new_objective
|
|
objectives += new_objective
|
|
message_admins("[key_name_admin(usr)] added a new objective for [current]: [new_objective.explanation_text]")
|
|
log_admin("[key_name(usr)] added a new objective for [current]: [new_objective.explanation_text]")
|
|
|
|
else if (href_list["obj_delete"])
|
|
var/datum/objective/objective = locate(href_list["obj_delete"])
|
|
if(!istype(objective))
|
|
return
|
|
|
|
for(var/datum/antagonist/A in antag_datums)
|
|
if(objective in A.objectives)
|
|
A.objectives -= objective
|
|
break
|
|
objectives -= objective
|
|
message_admins("[key_name_admin(usr)] removed an objective for [current]: [objective.explanation_text]")
|
|
log_admin("[key_name(usr)] removed an objective for [current]: [objective.explanation_text]")
|
|
|
|
else if(href_list["obj_completed"])
|
|
var/datum/objective/objective = locate(href_list["obj_completed"])
|
|
if(!istype(objective))
|
|
return
|
|
objective.completed = !objective.completed
|
|
log_admin("[key_name(usr)] toggled the win state for [current]'s objective: [objective.explanation_text]")
|
|
|
|
else if (href_list["silicon"])
|
|
switch(href_list["silicon"])
|
|
if("unemag")
|
|
var/mob/living/silicon/robot/R = current
|
|
if (istype(R))
|
|
R.SetEmagged(0)
|
|
message_admins("[key_name_admin(usr)] has unemag'ed [R].")
|
|
log_admin("[key_name(usr)] has unemag'ed [R].")
|
|
|
|
if("unemagcyborgs")
|
|
if(isAI(current))
|
|
var/mob/living/silicon/ai/ai = current
|
|
for (var/mob/living/silicon/robot/R in ai.connected_robots)
|
|
R.SetEmagged(0)
|
|
message_admins("[key_name_admin(usr)] has unemag'ed [ai]'s Cyborgs.")
|
|
log_admin("[key_name(usr)] has unemag'ed [ai]'s Cyborgs.")
|
|
|
|
else if (href_list["common"])
|
|
switch(href_list["common"])
|
|
if("undress")
|
|
for(var/obj/item/W in current)
|
|
current.dropItemToGround(W, TRUE) //The 1 forces all items to drop, since this is an admin undress.
|
|
if("takeuplink")
|
|
take_uplink()
|
|
memory = null//Remove any memory they may have had.
|
|
log_admin("[key_name(usr)] removed [current]'s uplink.")
|
|
if("crystals")
|
|
if(check_rights(R_FUN, 0))
|
|
var/datum/component/uplink/U = find_syndicate_uplink()
|
|
if(U)
|
|
var/crystals = input("Amount of telecrystals for [key]","Syndicate uplink", U.telecrystals) as null | num
|
|
if(!isnull(crystals))
|
|
U.telecrystals = crystals
|
|
message_admins("[key_name_admin(usr)] changed [current]'s telecrystal count to [crystals].")
|
|
log_admin("[key_name(usr)] changed [current]'s telecrystal count to [crystals].")
|
|
if("uplink")
|
|
if(!equip_traitor())
|
|
to_chat(usr, "<span class='danger'>Equipping a syndicate failed!</span>")
|
|
log_admin("[key_name(usr)] tried and failed to give [current] an uplink.")
|
|
else
|
|
log_admin("[key_name(usr)] gave [current] an uplink.")
|
|
|
|
else if (href_list["obj_announce"])
|
|
announce_objectives()
|
|
|
|
//Something in here might have changed your mob
|
|
if(self_antagging && (!usr || !usr.client) && current.client)
|
|
usr = current
|
|
traitor_panel()
|
|
|
|
/datum/mind/proc/announce_objectives()
|
|
var/obj_count = 1
|
|
to_chat(current, "<span class='notice'>Your current objectives:</span>")
|
|
for(var/objective in objectives)
|
|
var/datum/objective/O = objective
|
|
to_chat(current, "<B>Objective #[obj_count]</B>: [O.explanation_text]")
|
|
obj_count++
|
|
|
|
/datum/mind/proc/find_syndicate_uplink()
|
|
var/list/L = current.GetAllContents()
|
|
for (var/i in L)
|
|
var/atom/movable/I = i
|
|
. = I.GetComponent(/datum/component/uplink)
|
|
if(.)
|
|
break
|
|
|
|
/datum/mind/proc/take_uplink()
|
|
qdel(find_syndicate_uplink())
|
|
|
|
/datum/mind/proc/make_Traitor()
|
|
if(!(has_antag_datum(/datum/antagonist/traitor)))
|
|
add_antag_datum(/datum/antagonist/traitor)
|
|
|
|
/datum/mind/proc/make_Changeling()
|
|
var/datum/antagonist/changeling/C = has_antag_datum(/datum/antagonist/changeling)
|
|
if(!C)
|
|
C = add_antag_datum(/datum/antagonist/changeling)
|
|
special_role = ROLE_CHANGELING
|
|
return C
|
|
|
|
/datum/mind/proc/make_Wizard()
|
|
if(!has_antag_datum(/datum/antagonist/wizard))
|
|
special_role = ROLE_WIZARD
|
|
assigned_role = ROLE_WIZARD
|
|
add_antag_datum(/datum/antagonist/wizard)
|
|
|
|
|
|
/datum/mind/proc/make_Cultist()
|
|
if(!has_antag_datum(/datum/antagonist/cult,TRUE))
|
|
SSticker.mode.add_cultist(src,FALSE,equip=TRUE)
|
|
special_role = ROLE_CULTIST
|
|
to_chat(current, "<font color=\"purple\"><b><i>You catch a glimpse of the Realm of Nar'Sie, The Geometer of Blood. You now see how flimsy your world is, you see that it should be open to the knowledge of Nar'Sie.</b></i></font>")
|
|
to_chat(current, "<font color=\"purple\"><b><i>Assist your new brethren in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back.</b></i></font>")
|
|
|
|
/datum/mind/proc/make_Rev()
|
|
var/datum/antagonist/rev/head/head = new()
|
|
head.give_flash = TRUE
|
|
head.give_hud = TRUE
|
|
add_antag_datum(head)
|
|
special_role = ROLE_REV_HEAD
|
|
|
|
/datum/mind/proc/AddSpell(obj/effect/proc_holder/spell/S)
|
|
spell_list += S
|
|
S.action.Grant(current)
|
|
|
|
/datum/mind/proc/owns_soul()
|
|
return soulOwner == src
|
|
|
|
//To remove a specific spell from a mind
|
|
/datum/mind/proc/RemoveSpell(obj/effect/proc_holder/spell/spell)
|
|
if(!spell)
|
|
return
|
|
for(var/X in spell_list)
|
|
var/obj/effect/proc_holder/spell/S = X
|
|
if(istype(S, spell))
|
|
spell_list -= S
|
|
qdel(S)
|
|
|
|
/datum/mind/proc/RemoveAllSpells()
|
|
for(var/obj/effect/proc_holder/S in spell_list)
|
|
RemoveSpell(S)
|
|
|
|
/datum/mind/proc/transfer_martial_arts(mob/living/new_character)
|
|
if(!ishuman(new_character))
|
|
return
|
|
if(martial_art)
|
|
if(martial_art.base) //Is the martial art temporary?
|
|
martial_art.remove(new_character)
|
|
else
|
|
martial_art.teach(new_character)
|
|
|
|
/datum/mind/proc/transfer_actions(mob/living/new_character)
|
|
if(current && current.actions)
|
|
for(var/datum/action/A in current.actions)
|
|
A.Grant(new_character)
|
|
transfer_mindbound_actions(new_character)
|
|
|
|
/datum/mind/proc/transfer_mindbound_actions(mob/living/new_character)
|
|
for(var/X in spell_list)
|
|
var/obj/effect/proc_holder/spell/S = X
|
|
S.action.Grant(new_character)
|
|
var/datum/antagonist/changeling/changeling = new_character.mind.has_antag_datum(/datum/antagonist/changeling)
|
|
if(changeling &&(ishuman(new_character) || ismonkey(new_character)))
|
|
for(var/P in changeling.purchasedpowers)
|
|
var/obj/effect/proc_holder/changeling/I = P
|
|
I.action.Grant(new_character)
|
|
|
|
/datum/mind/proc/disrupt_spells(delay, list/exceptions = New())
|
|
for(var/X in spell_list)
|
|
var/obj/effect/proc_holder/spell/S = X
|
|
for(var/type in exceptions)
|
|
if(istype(S, type))
|
|
continue
|
|
S.charge_counter = delay
|
|
S.updateButtonIcon()
|
|
INVOKE_ASYNC(S, /obj/effect/proc_holder/spell.proc/start_recharge)
|
|
|
|
/datum/mind/proc/get_ghost(even_if_they_cant_reenter)
|
|
for(var/mob/dead/observer/G in GLOB.dead_mob_list)
|
|
if(G.mind == src)
|
|
if(G.can_reenter_corpse || even_if_they_cant_reenter)
|
|
return G
|
|
break
|
|
|
|
/datum/mind/proc/grab_ghost(force)
|
|
var/mob/dead/observer/G = get_ghost(even_if_they_cant_reenter = force)
|
|
. = G
|
|
if(G)
|
|
G.reenter_corpse()
|
|
|
|
|
|
/datum/mind/proc/has_objective(objective_type)
|
|
for(var/datum/antagonist/A in antag_datums)
|
|
for(var/O in A.objectives)
|
|
if(istype(O,objective_type))
|
|
return TRUE
|
|
|
|
/mob/proc/sync_mind()
|
|
mind_initialize() //updates the mind (or creates and initializes one if one doesn't exist)
|
|
mind.active = 1 //indicates that the mind is currently synced with a client
|
|
|
|
/datum/mind/proc/has_martialart(var/string)
|
|
if(martial_art && martial_art.id == string)
|
|
return martial_art
|
|
return FALSE
|
|
|
|
/mob/dead/new_player/sync_mind()
|
|
return
|
|
|
|
/mob/dead/observer/sync_mind()
|
|
return
|
|
|
|
//Initialisation procs
|
|
/mob/proc/mind_initialize()
|
|
if(mind)
|
|
mind.key = key
|
|
|
|
else
|
|
mind = new /datum/mind(key)
|
|
SSticker.minds += mind
|
|
if(!mind.name)
|
|
mind.name = real_name
|
|
mind.current = src
|
|
|
|
/mob/living/carbon/mind_initialize()
|
|
..()
|
|
last_mind = mind
|
|
|
|
//HUMAN
|
|
/mob/living/carbon/human/mind_initialize()
|
|
..()
|
|
if(!mind.assigned_role)
|
|
mind.assigned_role = "Unassigned" //default
|
|
|
|
//AI
|
|
/mob/living/silicon/ai/mind_initialize()
|
|
..()
|
|
mind.assigned_role = "AI"
|
|
|
|
//BORG
|
|
/mob/living/silicon/robot/mind_initialize()
|
|
..()
|
|
mind.assigned_role = "Cyborg"
|
|
|
|
//PAI
|
|
/mob/living/silicon/pai/mind_initialize()
|
|
..()
|
|
mind.assigned_role = ROLE_PAI
|
|
mind.special_role = ""
|