/* 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), use this mob proc.
mob.transfer_ckey(new_mob)
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.
*/
//ambition start
#define AMBITION_COOLDOWN_TIME (5 SECONDS)
#define OBJECTIVES_COOLDOWN_TIME (2 SECONDS)
#define ADMIN_PING_COOLDOWN_TIME (10 MINUTES)
//ambition end
/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/hide_ckey = FALSE //hide ckey from round-end report
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.
/// Our skill holder.
var/datum/skill_holder/skill_holder
//ambition start
/// Lazy list for antagonists to set goals they wish to achieve, to be shown at the round-end report.
var/list/ambitions
//ambition end
///What character we spawned in as- either at roundstart or latejoin, so we know for persistent scars if we ended as the same person or not
var/mob/original_character
/// A lazy list of statuses to add next to this mind in the traitor panel
var/list/special_statuses
/datum/mind/New(var/key)
skill_holder = new(src)
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
QDEL_NULL(skill_holder)
return ..()
/datum/mind/proc/get_language_holder()
if(!language_holder)
language_holder = new (src)
return language_holder
/datum/mind/proc/transfer_to(mob/new_character, var/force_key_move = 0)
var/old_character = current
var/signals = SEND_SIGNAL(new_character, COMSIG_MOB_PRE_PLAYER_CHANGE, new_character, old_character) | SEND_SIGNAL(src, COMSIG_PRE_MIND_TRANSFER, new_character, old_character)
if(signals & COMPONENT_STOP_MIND_TRANSFER)
return
if(current) // remove ourself from our old body's mind variable
current.mind = null
SStgui.on_transfer(current, new_character)
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(TRUE, TRUE) //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
if(new_character.client)
LAZYCLEARLIST(new_character.client.recent_examines)
new_character.client.init_verbs() // re-initialize character specific verbs
current.update_atom_languages()
//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?.prefs && L.client.prefs.auto_ooc && L.client.prefs.chat_toggles & CHAT_OOC)
L.client.prefs.chat_toggles &= ~(CHAT_OOC)
hide_ckey = current.client?.prefs?.hide_ckey
SEND_SIGNAL(src, COMSIG_MIND_TRANSFER, new_character, old_character)
SEND_SIGNAL(new_character, COMSIG_MOB_ON_NEW_MIND)
//splurt change
INVOKE_ASYNC(GLOBAL_PROC, .proc/_paci_check, new_character, old_character)
//end change
/datum/mind/proc/store_memory(new_text)
if((length_char(memory) + length_char(new_text)) <= MAX_MESSAGE_LEN)
memory += "[new_text]
"
/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
//ambition start
do_add_antag_datum(A)
//ambition end
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
//ambition start
/datum/mind/proc/do_add_antag_datum(instanced_datum)
. = LAZYLEN(antag_datums)
LAZYADD(antag_datums, instanced_datum)
if(!.)
add_verb(current, /mob/proc/edit_objectives_and_ambitions)
//ambition end
/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
//ambition start
/datum/mind/proc/do_remove_antag_datum(instanced_datum)
. = LAZYLEN(antag_datums)
LAZYREMOVE(antag_datums, instanced_datum)
if(. && !LAZYLEN(antag_datums))
ambitions = null
remove_verb(current, /mob/proc/edit_objectives_and_ambitions)
//ambition end
/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
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_slaver()
remove_wizard()
remove_cultist()
remove_rev()
SSticker.mode.update_cult_icons_removed(src)
/**
* ## give_uplink
*
* A mind proc for giving anyone an uplink.
* arguments:
* * silent: if this should send a message to the mind getting the uplink. traitors do not use this silence, but the silence var on their antag datum.
* * antag_datum: the antag datum of the uplink owner, for storing it in antag memory. optional!
*/
/datum/mind/proc/equip_traitor(silent = FALSE, datum/antagonist/antag_datum)
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
var/obj/item/uplink_loc
var/implant = FALSE
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_IMPLANT)
implant = TRUE
if(!uplink_loc) // We've looked everywhere, let's just implant you
implant = TRUE
if(implant)
var/obj/item/implant/uplink/starting/new_implant = new(traitor_mob)
new_implant.implant(traitor_mob, null, silent = TRUE)
if(!silent)
to_chat(traitor_mob, span_boldnotice("Your Syndicate Uplink has been cunningly implanted in you, for a small TC fee. Simply trigger the uplink to access it."))
return new_implant
. = uplink_loc
var/unlock_text
var/datum/component/uplink/new_uplink = uplink_loc.AddComponent(/datum/component/uplink, traitor_mob.key)
if(!new_uplink)
CRASH("Uplink creation failed.")
new_uplink.setup_unlock_code()
if(uplink_loc == R)
unlock_text = "Your Uplink is cunningly disguised as your [R.name]. Simply dial the frequency [format_frequency(new_uplink.unlock_code)] to unlock its hidden features."
else if(uplink_loc == PDA)
unlock_text = "Your Uplink is cunningly disguised as your [PDA.name]. Simply enter the code \"[new_uplink.unlock_code]\" into the ringtone select to unlock its hidden features."
else if(uplink_loc == P)
unlock_text = "Your Uplink is cunningly disguised as your [P.name]. Simply twist the top of the pen [english_list(new_uplink.unlock_code)] from its starting position to unlock its hidden features."
new_uplink.unlock_text = unlock_text
if(!silent)
to_chat(traitor_mob, span_boldnotice(unlock_text))
if(!antag_datum)
traitor_mob.mind.store_memory(new_uplink.unlock_note)
return
antag_datum.antag_memory += new_uplink.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))
if(iscultist(creator, TRUE))
SSticker.mode.add_cultist(src)
else
src.add_antag_datum(/datum/antagonist/cult/neutered/traitor)
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))
if(is_servant_of_ratvar(creator, TRUE))
add_servant_of_ratvar(current)
else
add_servant_of_ratvar(current, FALSE, FALSE, /datum/antagonist/clockcult/neutered/traitor)
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, "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.")
//ambition start
/datum/mind/proc/show_memory()
var/list/output = list("[current.real_name]'s Memories:
")
//ambition end
output += memory
var/list/all_objectives = list()
for(var/datum/antagonist/A in antag_datums)
output += A.antag_memory
all_objectives |= A.objectives
if(all_objectives.len)
output += "Objectives:"
var/obj_count = 1
for(var/datum/objective/objective in all_objectives)
output += "
Objective #[obj_count++]: [objective.explanation_text]"
var/list/datum/mind/other_owners = objective.get_owners() - src
if(other_owners.len)
output += "
"
for(var/datum/mind/M in other_owners)
output += "- Conspirator: [M.name]
"
output += "
"
//ambition port start
if(LAZYLEN(ambitions))
for(var/count in 1 to LAZYLEN(ambitions))
output += "
Ambition #[count]: [ambitions[count]]"
if(!memory && !length(all_objectives) && !LAZYLEN(ambitions))
output += ""
return output.Join()
/datum/mind/proc/show_editable_objectives_and_ambitions()
var/is_admin = check_rights(R_ADMIN, FALSE)
var/self_mind = usr == current
if(!is_admin && !self_mind)
return ""
var/list/output = list()
for(var/a in antag_datums)
var/datum/antagonist/antag_datum = a
output += "Objectives:"
if(is_admin)
output += " Add Objective"
output += ""
if(is_admin)
output += "Announce objectives
"
output += "
Requested Objective Changes:"
if(self_mind)
output += " Request objective"
output += ""
if(!LAZYLEN(antag_datum.requested_objective_changes))
output += "- NONE
"
else
for(var/uid in antag_datum.requested_objective_changes)
var/list/objectives_info = antag_datum.requested_objective_changes[uid]
var/obj_request = objectives_info["request"]
switch(obj_request)
if(REQUEST_NEW_OBJECTIVE)
var/datum/objective/type_cast_objective = objectives_info["target"]
var/objective_text = objectives_info["text"]
output += "Request #[uid]: ADD [initial(type_cast_objective.name)] - [objective_text]"
if(self_mind)
output += " Cancel Request"
if(is_admin)
output += " Accept Edit Deny"
if(REQUEST_DEL_OBJECTIVE)
var/datum/objective/objective_ref = locate(objectives_info["target"]) in antag_datum.objectives
if(QDELETED(objective_ref))
stack_trace("Objective request found with deleted reference. UID: [uid] | Antag: [antag_datum] | Mind: [src] | User: [usr]")
antag_datum.remove_objective_change(uid)
continue
output += "Request #[uid]: DEL [objective_ref.name] - [objective_ref.explanation_text] - [objectives_info["text"]]"
if(self_mind)
output += " Cancel Request"
if(is_admin)
output += " Accept Deny"
if(REQUEST_WIN_OBJECTIVE, REQUEST_LOSE_OBJECTIVE)
var/datum/objective/objective_ref = locate(objectives_info["target"]) in antag_datum.objectives
if(QDELETED(objective_ref))
stack_trace("Objective request found with deleted reference. UID: [uid] | Antag: [antag_datum] | Mind: [src] | User: [usr]")
antag_datum.remove_objective_change(uid)
continue
output += "Request #[uid]: [obj_request == REQUEST_WIN_OBJECTIVE ? "WIN" : "LOSE"] [objective_ref.name] - [objective_ref.explanation_text] - [objectives_info["text"]]"
if(self_mind)
output += " Cancel Request"
if(is_admin)
output += " Accept Deny"
else
stack_trace("Objective request found with no request index. UID: [uid] | Antag: [antag_datum] | Mind: [src] | User: [usr]")
continue
output += "
"
if(self_mind)
output += "Ping the admins
"
if(is_admin)
output += "Clear ping cooldown
"
output += "
[current.real_name]'s Ambitions:"
if(LAZYLEN(ambitions) < CONFIG_GET(number/max_ambitions))
output += " Add Ambition"
output += ""
if(!LAZYLEN(ambitions))
output += "- NONE"
else
for(var/count in 1 to LAZYLEN(ambitions))
output += "
- Ambition #[count] (Edit) (Remove):
[ambitions[count]]"
output += "
(Refresh)"
return output.Join()
/mob/proc/edit_objectives_and_ambitions()
set name = "Objectives and Ambitions"
set category = "IC"
set desc = "View and edit your character's objectives and ambitions."
mind.do_edit_objectives_ambitions()
/datum/mind/proc/do_edit_objectives_ambitions()
var/datum/browser/popup = new(usr, "objectives and ambitions", "Objectives and Ambitions")
popup.set_content(show_editable_objectives_and_ambitions())
popup.open()
GLOBAL_VAR_INIT(requested_objective_uid, 0)
GLOBAL_LIST(objective_player_choices)
/proc/populate_objective_player_choices()
GLOB.objective_player_choices = list()
var/list/allowed_types = list(
/datum/objective/custom,
/datum/objective/assassinate/once,
/datum/objective/protect,
/datum/objective/escape,
/datum/objective/survive,
/datum/objective/martyr,
/datum/objective/steal,
/datum/objective/download,
)
for(var/t in allowed_types)
var/datum/objective/type_cast = t
GLOB.objective_player_choices[initial(type_cast.name)] = t
GLOBAL_LIST(objective_choices)
/proc/populate_objective_choices()
GLOB.objective_choices = list()
var/list/allowed_types = list(
/datum/objective/custom,
/datum/objective/assassinate,
/datum/objective/assassinate/once,
/datum/objective/maroon,
/datum/objective/debrain,
/datum/objective/protect,
/datum/objective/destroy,
/datum/objective/hijack,
/datum/objective/escape,
/datum/objective/survive,
/datum/objective/martyr,
/datum/objective/steal,
/datum/objective/download,
/datum/objective/nuclear,
/datum/objective/absorb,
/datum/objective/rescue_prisoner,
/datum/objective/custom
)
for(var/t in allowed_types)
var/datum/objective/type_cast = t
GLOB.objective_choices[initial(type_cast.name)] = t
/datum/mind/proc/on_objectives_request_cd_end(datum/source)
UnregisterSignal(src, list(COMSIG_CD_STOP(COOLDOWN_OBJ_ADMIN_PING), COMSIG_CD_RESET(COOLDOWN_OBJ_ADMIN_PING)))
if(!antag_datums)
return
to_chat(current, "You are now again able to ping the admins objective changes review requests.")
for(var/a in antag_datums)
var/datum/antagonist/antag_datum = a
if(!antag_datum.requested_objective_changes)
continue
to_chat(current, "You seem to have unanswered change requests. If there are online admins another gentle reminder might be in order.")
break
//ambition port end
/datum/mind/Topic(href, href_list)
//ambition start
if (href_list["refresh_obj_amb"])
do_edit_objectives_ambitions()
return
else if (href_list["add_ambition"])
if(!check_rights(R_ADMIN, FALSE))
if(usr != current)
return
if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_AMBITION))
to_chat(usr, "You must wait [AMBITION_COOLDOWN_TIME * 0.1] seconds between changes.")
return
if(!isliving(current))
return
if(!antag_datums)
return
var/max_ambitions = CONFIG_GET(number/max_ambitions)
if(LAZYLEN(ambitions) >= max_ambitions)
to_chat(usr, "There's a limit of [max_ambitions] ambitions. Edit or remove some to accomodate for your new additions.")
do_edit_objectives_ambitions()
return
var/new_ambition = stripped_multiline_input(usr, "Write new ambition", "Ambition", "", MAX_AMBITION_LEN)
if(isnull(new_ambition))
return
if(!check_rights(R_ADMIN, FALSE))
if(usr != current)
return
if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_AMBITION))
to_chat(usr, "You must wait [AMBITION_COOLDOWN_TIME * 0.1] seconds between changes.")
return
if(!isliving(current))
to_chat(usr, "The mind holder is no longer a living creature.")
return
if(!antag_datums)
to_chat(usr, "The mind holder is no longer an antagonist.")
return
if(LAZYLEN(ambitions) >= max_ambitions)
to_chat(usr, "There's a limit of [max_ambitions] ambitions. Edit or remove some to accomodate for your new additions.")
do_edit_objectives_ambitions()
return
TIMER_COOLDOWN_START(src, COOLDOWN_AMBITION, AMBITION_COOLDOWN_TIME)
LAZYADD(ambitions, new_ambition)
if(usr == current)
log_game("[key_name(usr)] has created their ambition of index [LAZYLEN(ambitions)].\nNEW AMBITION:\n[new_ambition]")
else
log_game("[key_name(usr)] has created [key_name(current)]'s ambition of index [LAZYLEN(ambitions)].\nNEW AMBITION:\n[new_ambition]")
message_admins("[ADMIN_TPMONTY(usr)] has created [ADMIN_TPMONTY(current)]'s ambition of index [LAZYLEN(ambitions)].")
do_edit_objectives_ambitions()
return
else if (href_list["edit_ambition"])
if(!check_rights(R_ADMIN, FALSE))
if(usr != current)
return
if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_AMBITION))
to_chat(usr, "You must wait [AMBITION_COOLDOWN_TIME * 0.1] seconds between changes.")
return
if(!isliving(current))
return
if(!antag_datums)
return
var/ambition_index = text2num(href_list["edit_ambition"])
if(!isnum(ambition_index) || ambition_index < 0 || ambition_index % 1)
log_admin_private("[key_name(usr)] attempted to edit their ambitions with and invalid ambition_index ([ambition_index]) at [AREACOORD(usr.loc)].")
message_admins("[ADMIN_TPMONTY(usr)] attempted to edit their ambitions with and invalid ambition_index ([ambition_index]). Possible HREF exploit.")
return
if(ambition_index > LAZYLEN(ambitions))
return
var/old_ambition = ambitions[ambition_index]
var/new_ambition = stripped_multiline_input(usr, "Write new ambition", "Ambition", ambitions[ambition_index], MAX_AMBITION_LEN)
if(isnull(new_ambition))
return
if(!check_rights(R_ADMIN, FALSE))
if(usr != current)
return
if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_AMBITION))
to_chat(usr, "You must wait [AMBITION_COOLDOWN_TIME * 0.1] seconds between changes.")
return
if(!isliving(current))
to_chat(usr, "The mind holder is no longer a living creature.")
return
if(!antag_datums)
to_chat(usr, "The mind holder is no longer an antagonist.")
return
if(ambition_index > LAZYLEN(ambitions))
to_chat(usr, "The ambition we were editing was deleted before we finished. Aborting.")
do_edit_objectives_ambitions()
return
if(old_ambition != ambitions[ambition_index])
to_chat(usr, "The ambition has changed since we started editing it. Aborting to prevent data loss.")
do_edit_objectives_ambitions()
return
TIMER_COOLDOWN_START(src, COOLDOWN_AMBITION, AMBITION_COOLDOWN_TIME)
ambitions[ambition_index] = new_ambition
if(usr == current)
log_game("[key_name(usr)] has edited their ambition of index [ambition_index].\nOLD AMBITION:\n[old_ambition]\nNEW AMBITION:\n[new_ambition]")
else
log_game("[key_name(usr)] has edited [key_name(current)]'s ambition of index [ambition_index].\nOLD AMBITION:\n[old_ambition]\nNEW AMBITION:\n[new_ambition]")
message_admins("[ADMIN_TPMONTY(usr)] has edited [ADMIN_TPMONTY(current)]'s ambition of index [ambition_index].")
do_edit_objectives_ambitions()
return
else if (href_list["remove_ambition"])
if(!check_rights(R_ADMIN, FALSE))
if(usr != current)
return
if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_AMBITION))
to_chat(usr, "You must wait [AMBITION_COOLDOWN_TIME * 0.1] seconds between changes.")
return
if(!isliving(current))
return
if(!antag_datums)
return
var/ambition_index = text2num(href_list["remove_ambition"])
if(ambition_index > LAZYLEN(ambitions))
do_edit_objectives_ambitions()
return
if(!isnum(ambition_index) || ambition_index < 0 || ambition_index % 1)
log_admin_private("[key_name(usr)] attempted to remove an ambition with and invalid ambition_index ([ambition_index]) at [AREACOORD(usr.loc)].")
message_admins("[ADMIN_TPMONTY(usr)] attempted to remove an ambition with and invalid ambition_index ([ambition_index]). Possible HREF exploit.")
return
var/old_ambition = ambitions[ambition_index]
if(alert(usr, "Are you sure you want to delete this ambition?", "Delete ambition", "Yes", "No") != "Yes")
return
if(!check_rights(R_ADMIN, FALSE))
if(usr != current)
return
if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_AMBITION))
to_chat(usr, "You must wait [AMBITION_COOLDOWN_TIME * 0.1] seconds between changes.")
return
if(!isliving(current))
to_chat(usr, "The mind holder is no longer a living creature. The ambition we were deleting should no longer exist already.")
return
if(!antag_datums)
to_chat(usr, "The mind holder is no longer an antagonist. The ambition we were deleting should no longer exist already.")
return
if(ambition_index > LAZYLEN(ambitions))
to_chat(usr, "The ambition we were deleting was deleted before we finished. No need to continue.")
do_edit_objectives_ambitions()
return
if(old_ambition != ambitions[ambition_index])
to_chat(usr, "The ambition has changed since we started considering its deletion. Aborting to prevent conflicts.")
do_edit_objectives_ambitions()
return
TIMER_COOLDOWN_START(src, COOLDOWN_AMBITION, AMBITION_COOLDOWN_TIME)
LAZYCUT(ambitions, ambition_index, ambition_index + 1)
if(usr == current)
log_game("[key_name(usr)] has deleted their ambition of index [ambition_index].\nDELETED AMBITION:\n[old_ambition]")
else
log_game("[key_name(usr)] has deleted [key_name(current)]'s ambition of index [ambition_index].\nDELETED AMBITION:\n[old_ambition]")
message_admins("[ADMIN_TPMONTY(usr)] has deleted [ADMIN_TPMONTY(current)]'s ambition of index [ambition_index].")
do_edit_objectives_ambitions()
return
else if (href_list["req_obj_ping"])
if(usr != current)
return
if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_OBJ_ADMIN_PING))
to_chat(usr, "You must wait [S_TIMER_COOLDOWN_TIMELEFT(src, COOLDOWN_OBJ_ADMIN_PING) * 0.1] seconds before your next admin ping.")
do_edit_objectives_ambitions()
return
if(!antag_datums)
return
var/pending_request = FALSE
for(var/a in antag_datums)
var/datum/antagonist/antag_datum = a
if(antag_datum.requested_objective_changes)
pending_request = TRUE
break
if(!pending_request)
to_chat(usr, "You have no pending requests to warn the admins about. Request changes for them to review before poking them.")
do_edit_objectives_ambitions()
return
var/justification = stripped_multiline_input(usr,
"Send a message to the admins requesting a review of your objective change requests.\
There's a [ADMIN_PING_COOLDOWN_TIME * 0.1] seconds cooldown between requests, so try to think it through before sending it. Cancelling this does not trigger the cooldown.",
"Request Admin Review", max_length = MAX_MESSAGE_LEN)
if(isnull(justification))
return
if(usr != current)
return
if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_OBJ_ADMIN_PING))
to_chat(usr, "You must wait [S_TIMER_COOLDOWN_TIMELEFT(src, COOLDOWN_OBJ_ADMIN_PING) * 0.1] seconds before your next admin ping.")
do_edit_objectives_ambitions()
return
if(!antag_datums)
return
pending_request = FALSE
for(var/a in antag_datums)
var/datum/antagonist/antag_datum = a
if(antag_datum.requested_objective_changes)
pending_request = TRUE
break
if(!pending_request)
return
if(!length(GLOB.admins))
to_chat(usr, "No admins currently connected, failed to notify them. Wait for one to connect before trying to ping them again.")
do_edit_objectives_ambitions()
return
S_TIMER_COOLDOWN_START(src, COOLDOWN_OBJ_ADMIN_PING, ADMIN_PING_COOLDOWN_TIME)
RegisterSignal(src, list(COMSIG_CD_STOP(COOLDOWN_OBJ_ADMIN_PING), COMSIG_CD_RESET(COOLDOWN_OBJ_ADMIN_PING)), .proc/on_objectives_request_cd_end)
log_admin("Objectives review request - [key_name(usr)] has requested a review of their objective changes, pinging the admins.")
for(var/a in GLOB.admins)
var/client/admin_client = a
if(admin_client.prefs.toggles & SOUND_ADMINHELP)
SEND_SOUND(admin_client, sound('sound/effects/adminhelp.ogg'))
window_flash(admin_client)
message_admins("[ADMIN_TPMONTY(usr)] has requested a review of their objective changes. (RPLY)")
do_edit_objectives_ambitions()
return
else if (href_list["req_obj_add"])
if(usr != current)
return
if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_OBJECTIVES))
to_chat(usr, "You must wait [OBJECTIVES_COOLDOWN_TIME * 0.1] seconds between request changes.")
do_edit_objectives_ambitions()
return
var/datum/antagonist/target_antag = locate(href_list["target_antag"]) in antag_datums
if(QDELETED(target_antag))
to_chat(usr, "No antagonist found for this objective.")
do_edit_objectives_ambitions()
return
if(!GLOB.objective_player_choices)
populate_objective_player_choices()
var/choice = input("Select desired objective type:", "Objective type") as null|anything in GLOB.objective_player_choices
var/selected_type = GLOB.objective_player_choices[choice]
if(!selected_type)
return
var/new_objective = stripped_multiline_input(usr,\
selected_type == /datum/objective/custom\
? "Write the custom objective you'd like to request the admins to grant you. Remember they can edit or deny your request at their own discretion."\
: "Justify your request for a new objective to the admins. Add the required clarifations, if you have a specific targets in mind and the likes.",\
"New Objective", max_length = MAX_MESSAGE_LEN)
if(isnull(new_objective))
return
if(usr != current)
return
if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_OBJECTIVES))
to_chat(usr, "You must wait [OBJECTIVES_COOLDOWN_TIME] minutes between request changes.")
return
if(QDELETED(target_antag))
return
TIMER_COOLDOWN_START(src, COOLDOWN_OBJECTIVES, OBJECTIVES_COOLDOWN_TIME)
var/uid = "[GLOB.requested_objective_uid++]"
target_antag.add_objective_change(uid, list("request" = REQUEST_NEW_OBJECTIVE, "target" = selected_type, "text" = new_objective))
log_admin("Objectives request [uid] - [key_name(usr)] has requested a [choice] objective: [new_objective]")
do_edit_objectives_ambitions()
return
else if (href_list["req_obj_cancel"])
if(usr != current)
return
if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_OBJECTIVES))
to_chat(usr, "You must wait [OBJECTIVES_COOLDOWN_TIME * 0.1] seconds between request changes.")
return
var/datum/antagonist/target_antag = locate(href_list["target_antag"]) in antag_datums
if(QDELETED(target_antag))
to_chat(usr, "No antagonist found for this objective.")
do_edit_objectives_ambitions()
return
var/uid = href_list["req_obj_cancel"]
if(!LAZYACCESS(target_antag.requested_objective_changes, uid))
to_chat(usr, "No requested objective change found. Perhaps it was deleted already?")
do_edit_objectives_ambitions()
return
if(alert(usr, "Are you sure you want to delete this change request?", "Delete change request", "Yes", "No") != "Yes")
return
if(usr != current)
return
if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_OBJECTIVES))
to_chat(usr, "You must wait [OBJECTIVES_COOLDOWN_TIME * 0.1] seconds between request changes.")
return
if(QDELETED(target_antag))
do_edit_objectives_ambitions()
return
if(!LAZYACCESS(target_antag.requested_objective_changes, uid))
do_edit_objectives_ambitions()
return
TIMER_COOLDOWN_START(src, COOLDOWN_OBJECTIVES, OBJECTIVES_COOLDOWN_TIME)
log_admin("Objectives request deletion - [key_name(usr)] has deleted the objective change request of UID [uid].")
target_antag.remove_objective_change(uid)
do_edit_objectives_ambitions()
return
else if (href_list["req_obj_delete"])
if(usr != current)
return
if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_OBJECTIVES))
to_chat(usr, "You must wait [OBJECTIVES_COOLDOWN_TIME * 0.1] seconds between request changes.")
return
var/datum/antagonist/target_antag = locate(href_list["target_antag"]) in antag_datums
if(QDELETED(target_antag))
to_chat(usr, "No antagonist found for this objective.")
do_edit_objectives_ambitions()
return
var/objective_reference = href_list["req_obj_delete"]
var/datum/objective/objective_to_delete = locate(objective_reference) in target_antag.objectives
if(!istype(objective_to_delete) || QDELETED(objective_to_delete))
to_chat(usr, "No objective found. Perhaps it was already deleted?")
do_edit_objectives_ambitions()
return
var/justification = stripped_multiline_input(usr,
"Justify your request for a deleting this objective to the admins.",
"Objective Deletion", max_length = MAX_MESSAGE_LEN)
if(isnull(justification))
return
if(usr != current)
return
if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_OBJECTIVES))
to_chat(usr, "You must wait [OBJECTIVES_COOLDOWN_TIME * 0.1] seconds between request changes.")
return
if(QDELETED(objective_to_delete) || QDELETED(target_antag))
do_edit_objectives_ambitions()
return
var/matching_request = FALSE
for(var/index in target_antag.requested_objective_changes)
var/list/change_request = target_antag.requested_objective_changes[index]
if(change_request["target"] != objective_reference)
continue
matching_request = TRUE
break
if(matching_request)
if(alert(usr, "There is already a change request tied to this objective waiting to be processed. Adding this request will delete the old ones.", "Delete matching objective requests?", "Yes", "No") != "Yes")
do_edit_objectives_ambitions()
return
if(usr != current)
return
if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_OBJECTIVES))
to_chat(usr, "You must wait [OBJECTIVES_COOLDOWN_TIME * 0.1] seconds between request changes.")
return
if(QDELETED(objective_to_delete) || QDELETED(target_antag))
do_edit_objectives_ambitions()
return
for(var/index in target_antag.requested_objective_changes)
var/list/change_request = target_antag.requested_objective_changes[index]
if(change_request["target"] != objective_reference)
continue
target_antag.remove_objective_change(index)
TIMER_COOLDOWN_START(src, COOLDOWN_OBJECTIVES, OBJECTIVES_COOLDOWN_TIME)
var/uid = "[GLOB.requested_objective_uid++]"
target_antag.add_objective_change(uid, list("request" = REQUEST_DEL_OBJECTIVE, "target" = objective_reference, "text" = justification))
log_admin("Objectives request [uid] - [key_name(usr)] has requested the deletion of the following objective: [objective_to_delete.explanation_text].\nTheir justification is as follows: [justification]")
do_edit_objectives_ambitions()
return
else if (href_list["req_obj_completed"])
if(usr != current)
return
if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_OBJECTIVES))
to_chat(usr, "You must wait [OBJECTIVES_COOLDOWN_TIME * 0.1] seconds between request changes.")
return
var/datum/antagonist/target_antag = locate(href_list["target_antag"]) in antag_datums
if(QDELETED(target_antag))
to_chat(usr, "No antagonist found for this objective.")
do_edit_objectives_ambitions()
return
var/objective_reference = href_list["req_obj_completed"]
var/datum/objective/objective_to_complete = locate(objective_reference) in target_antag.objectives
if(!istype(objective_to_complete) || QDELETED(objective_to_complete))
to_chat(usr, "No objective found. Perhaps it was deleted?")
do_edit_objectives_ambitions()
return
var/justification = stripped_multiline_input(usr,
"Justify to the admins your request to mark this objective as [objective_to_complete.completed ? "incomplete" : "completed"].",
"Objective [objective_to_complete.completed ? "Incompletion" : "Completion"]", max_length = MAX_MESSAGE_LEN)
if(isnull(justification))
return
if(usr != current)
return
if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_OBJECTIVES))
to_chat(usr, "You must wait [OBJECTIVES_COOLDOWN_TIME * 0.1] seconds between request changes.")
return
if(QDELETED(objective_to_complete) || QDELETED(target_antag))
do_edit_objectives_ambitions()
return
var/matching_request = FALSE
for(var/index in target_antag.requested_objective_changes)
var/list/change_request = target_antag.requested_objective_changes[index]
if(change_request["target"] != objective_reference)
continue
matching_request = TRUE
break
if(matching_request)
if(alert(usr, "There is already a change request tied to this objective waiting to be processed. Adding this request will delete the old ones.", "Delete matching objective requests?", "Yes", "No") != "Yes")
return
if(usr != current)
return
if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_OBJECTIVES))
to_chat(usr, "You must wait [OBJECTIVES_COOLDOWN_TIME * 0.1] seconds between request changes.")
return
if(QDELETED(objective_to_complete) || QDELETED(target_antag))
do_edit_objectives_ambitions()
return
for(var/index in target_antag.requested_objective_changes)
var/list/change_request = target_antag.requested_objective_changes[index]
if(change_request["target"] != objective_reference)
continue
target_antag.remove_objective_change(index)
TIMER_COOLDOWN_START(src, COOLDOWN_OBJECTIVES, OBJECTIVES_COOLDOWN_TIME)
var/uid = "[GLOB.requested_objective_uid++]"
target_antag.add_objective_change(uid, list("request" = (objective_to_complete.completed ? REQUEST_LOSE_OBJECTIVE : REQUEST_WIN_OBJECTIVE), "target" = objective_reference, "text" = justification))
log_admin("Objectives request [uid] - [key_name(usr)] has requested the [objective_to_complete.completed ? "incompletion" : "completion"] of the following objective: [objective_to_complete.explanation_text].\nTheir justification is as follows: [justification]")
do_edit_objectives_ambitions()
return
if(!check_rights(R_ADMIN))
return
var/self_antagging = usr == current
if(href_list["edit_ambitions_panel"])
do_edit_objectives_ambitions()
return
else if(href_list["req_obj_ping_cd_clear"])
if(!TIMER_COOLDOWN_CHECK(src, COOLDOWN_OBJ_ADMIN_PING))
to_chat(usr, "Mind is not under a cooldown.")
do_edit_objectives_ambitions()
return
if(alert(usr, "Are you sure you want reset this cooldown, letting the user ping the admins again?", "Clear ping cooldown", "Yes", "No") != "Yes")
do_edit_objectives_ambitions()
return
if(!check_rights(R_ADMIN))
return
if(!TIMER_COOLDOWN_CHECK(src, COOLDOWN_OBJ_ADMIN_PING))
do_edit_objectives_ambitions()
return
S_TIMER_COOLDOWN_RESET(src, COOLDOWN_OBJ_ADMIN_PING)
do_edit_objectives_ambitions()
return
else if(href_list["refresh_antag_panel"])
traitor_panel()
return
else if (href_list["req_obj_edit"])
var/datum/antagonist/antag_datum = locate(href_list["req_obj_edit"]) in antag_datums
if(QDELETED(antag_datum))
do_edit_objectives_ambitions()
to_chat(usr, "No antag found.")
return
if(antag_datum.owner != src)
do_edit_objectives_ambitions()
to_chat(usr, "Invalid antag reference.")
return
var/uid = href_list["req_obj_id"]
var/list/requested_obj_change = LAZYACCESS(antag_datum.requested_objective_changes, uid)
if(!requested_obj_change)
do_edit_objectives_ambitions()
to_chat(usr, "Invalid requested objective reference.")
return
if(requested_obj_change["request"] != REQUEST_NEW_OBJECTIVE)
do_edit_objectives_ambitions()
to_chat(usr, "This is not an editable request. How did you even got here?")
return
switch(alert(usr, "Do you want to edit the requested objective type or text?", "Edit requested objective", "Type", "Text", "Cancel"))
if("Type")
if(!check_rights(R_ADMIN))
return
if(QDELETED(antag_datum))
to_chat(usr, "No antag found.")
do_edit_objectives_ambitions()
return
if(!LAZYACCESS(antag_datum.requested_objective_changes, uid))
to_chat(usr, "Invalid requested objective change reference.")
do_edit_objectives_ambitions()
return
var/datum/objective/type_cast = requested_obj_change["target"]
var/selected_type = input("Select new requested objective type:", "Requested Objective type", initial(type_cast.name)) as null|anything in GLOB.objective_choices
selected_type = GLOB.objective_choices[selected_type]
if(!selected_type)
return
if(!check_rights(R_ADMIN))
return
if(QDELETED(antag_datum))
to_chat(usr, "No antag found.")
do_edit_objectives_ambitions()
return
if(!LAZYACCESS(antag_datum.requested_objective_changes, uid))
to_chat(usr, "Invalid requested objective change reference.")
do_edit_objectives_ambitions()
return
log_admin("[key_name(usr)] has edited the requested objective type for [current], of UID [uid], from [requested_obj_change["target"]] to [selected_type]")
message_admins("[key_name_admin(usr)] has edited the requested objective type for [current], of UID [uid], from [requested_obj_change["target"]] to [selected_type]")
requested_obj_change["target"] = selected_type
if("Text")
if(!check_rights(R_ADMIN))
return
if(QDELETED(antag_datum))
to_chat(usr, "No antag found.")
do_edit_objectives_ambitions()
return
if(!LAZYACCESS(antag_datum.requested_objective_changes, uid))
to_chat(usr, "Invalid requested objective change reference.")
do_edit_objectives_ambitions()
return
var/new_text = stripped_multiline_input(usr, "Input new requested objective text", "Requested Objective Text", requested_obj_change["text"], MAX_MESSAGE_LEN)
if (isnull(new_text))
return
if(!check_rights(R_ADMIN))
return
if(QDELETED(antag_datum))
to_chat(usr, "No antag found.")
do_edit_objectives_ambitions()
return
if(!LAZYACCESS(antag_datum.requested_objective_changes, uid))
to_chat(usr, "Invalid requested objective change reference.")
do_edit_objectives_ambitions()
return
log_admin("[key_name(usr)] has edited the requested objective text for [current], of UID [uid], from [requested_obj_change["text"]] to [new_text]")
message_admins("[key_name_admin(usr)] has edited the requested objective text for [current], of UID [uid], from [requested_obj_change["text"]] to [new_text]")
requested_obj_change["text"] = new_text
do_edit_objectives_ambitions()
return
else if (href_list["req_obj_accept"])
var/datum/antagonist/antag_datum = locate(href_list["req_obj_accept"]) in antag_datums
if(QDELETED(antag_datum))
do_edit_objectives_ambitions()
to_chat(usr, "No antag found.")
return
if(antag_datum.owner != src)
do_edit_objectives_ambitions()
to_chat(usr, "Invalid antag reference.")
return
var/uid = href_list["req_obj_id"]
var/list/requested_obj_change = LAZYACCESS(antag_datum.requested_objective_changes, uid)
if(!requested_obj_change)
do_edit_objectives_ambitions()
to_chat(usr, "Invalid requested objective reference.")
return
var/datum/objective/request_target
var/request_type = requested_obj_change["request"]
switch(request_type)
if(REQUEST_NEW_OBJECTIVE)
request_target = requested_obj_change["target"]
if(!ispath(request_target, /datum/objective))
to_chat(usr, "Invalid requested objective target path.")
return
if(REQUEST_DEL_OBJECTIVE, REQUEST_WIN_OBJECTIVE, REQUEST_LOSE_OBJECTIVE)
request_target = locate(requested_obj_change["target"]) in antag_datum.objectives
if(QDELETED(request_target))
to_chat(usr, "Invalid requested objective target reference.")
return
else
to_chat(usr, "Invalid request type.")
return
if(alert(usr, "Are you sure you want to approve this objective change?", "Approve objective change", "Yes", "No") != "Yes")
return
if(!check_rights(R_ADMIN))
return
if(QDELETED(antag_datum))
to_chat(usr, "No antag found.")
do_edit_objectives_ambitions()
return
if(!LAZYACCESS(antag_datum.requested_objective_changes, uid))
to_chat(usr, "Invalid requested objective change reference.")
do_edit_objectives_ambitions()
return
switch(request_type) //Last checks
if(REQUEST_NEW_OBJECTIVE)
if(!ispath(request_target, /datum/objective))
stack_trace("Invalid target on objective change request: [request_target]")
do_edit_objectives_ambitions()
return
if(REQUEST_DEL_OBJECTIVE, REQUEST_WIN_OBJECTIVE, REQUEST_LOSE_OBJECTIVE)
if(QDELETED(request_target))
to_chat(usr, "Invalid requested objective target reference.")
return
else
to_chat(usr, "Invalid request type.")
return
antag_datum.remove_objective_change(uid)
switch(request_type) //All is clear, let get things done.
if(REQUEST_NEW_OBJECTIVE)
request_target = new request_target()
request_target.owner = src
if(istype(request_target, /datum/objective/custom))
request_target.explanation_text = requested_obj_change["text"]
else
request_target.admin_edit(usr)
antag_datum.objectives += request_target
message_admins("[key_name_admin(usr)] approved a requested objective from [current]: [request_target.explanation_text]")
log_admin("[key_name(usr)] approved a requested objective from [current]: [request_target.explanation_text]")
if(REQUEST_DEL_OBJECTIVE)
message_admins("[key_name_admin(usr)] approved the request to delete an objective from [current]: [request_target.explanation_text]")
log_admin("[key_name(usr)] approved the request to delete an objective from [current]: [request_target.explanation_text]")
qdel(request_target)
if(REQUEST_WIN_OBJECTIVE)
message_admins("[key_name_admin(usr)] approved the victory request for an objective from [current]: [request_target.explanation_text]")
log_admin("[key_name(usr)] approved the victory request for an objective from [current]: [request_target.explanation_text]")
request_target.completed = TRUE
if(REQUEST_LOSE_OBJECTIVE)
message_admins("[key_name_admin(usr)] approved the defeat request for an objective from [current]: [request_target.explanation_text]")
log_admin("[key_name(usr)] approved the defeat request for an objective from [current]: [request_target.explanation_text]")
request_target.completed = FALSE
to_chat(current, "Your objective change request has been approved.")
do_edit_objectives_ambitions()
return
else if (href_list["req_obj_deny"])
var/datum/antagonist/antag_datum = locate(href_list["req_obj_deny"]) in antag_datums
if(QDELETED(antag_datum))
do_edit_objectives_ambitions()
to_chat(usr, "No antag found.")
return
if(antag_datum.owner != src)
do_edit_objectives_ambitions()
to_chat(usr, "Invalid antag reference.")
return
var/uid = href_list["req_obj_id"]
var/list/requested_obj_change = LAZYACCESS(antag_datum.requested_objective_changes, uid)
if(!requested_obj_change)
do_edit_objectives_ambitions()
to_chat(usr, "Invalid requested objective change reference.")
return
var/justification = stripped_multiline_input(usr, "Justify why you are denying this objective request change.", "Deny", memory, MAX_MESSAGE_LEN)
if(isnull(justification))
return
if(!check_rights(R_ADMIN))
return
if(QDELETED(antag_datum))
to_chat(usr, "No antag found.")
do_edit_objectives_ambitions()
return
if(!LAZYACCESS(antag_datum.requested_objective_changes, uid))
to_chat(usr, "Invalid requested objective change reference.")
do_edit_objectives_ambitions()
return
var/datum/objective/type_cast = requested_obj_change["target"]
var/objective_name = initial(type_cast.name)
message_admins("[key_name_admin(usr)] denied a requested [objective_name] objective from [current]: [requested_obj_change["text"]]")
log_admin("[key_name(usr)] denied a requested [objective_name] objective from [current]: [requested_obj_change["text"]]")
to_chat(current, "Your objective request has been denied for the following reason: [justification]")
antag_datum.remove_objective_change(uid)
do_edit_objectives_ambitions()
return
else if (href_list["obj_panel_complete_toggle"])
var/datum/antagonist/antag_datum = locate(href_list["target_antag"]) in antag_datums
if(QDELETED(antag_datum))
do_edit_objectives_ambitions()
to_chat(usr, "No antag found.")
return
if(antag_datum.owner != src)
do_edit_objectives_ambitions()
to_chat(usr, "Invalid antag reference.")
return
var/datum/objective/objective_to_toggle = locate(href_list["obj_panel_complete_toggle"]) in antag_datum.objectives
if(QDELETED(objective_to_toggle))
to_chat(usr, "No objective found. Perhaps it was already deleted?")
do_edit_objectives_ambitions()
return
if(objective_to_toggle.owner != src)
do_edit_objectives_ambitions()
to_chat(usr, "Invalid objective reference.")
return
objective_to_toggle.completed = !objective_to_toggle.completed
message_admins("[key_name_admin(usr)] toggled the win state for [current]'s objective: [objective_to_toggle.explanation_text]")
log_admin("[key_name(usr)] toggled the win state for [current]'s objective: [objective_to_toggle.explanation_text]")
if(alert(usr, "Would you like to alert the player of the change?", "Deny objective", "Yes", "No") == "Yes")
to_chat(current, "[objective_to_toggle.completed ? "" : ""]Your objective status has changed!")
do_edit_objectives_ambitions()
return
else if (href_list["obj_panel_delete"])
var/datum/antagonist/antag_datum = locate(href_list["target_antag"]) in antag_datums
if(QDELETED(antag_datum))
do_edit_objectives_ambitions()
to_chat(usr, "No antag found.")
return
if(antag_datum.owner != src)
do_edit_objectives_ambitions()
to_chat(usr, "Invalid antag reference.")
return
var/datum/objective/objective_to_delete = locate(href_list["obj_panel_delete"]) in antag_datum.objectives
if(QDELETED(objective_to_delete))
to_chat(usr, "No objective found. Perhaps it was already deleted?")
do_edit_objectives_ambitions()
return
if(objective_to_delete.owner != src)
do_edit_objectives_ambitions()
to_chat(usr, "Invalid objective reference.")
return
if(alert(usr, "Are you sure you want to delete this objective?", "Delete objective", "Yes", "No") != "Yes")
return
if(!check_rights(R_ADMIN))
return
if(QDELETED(objective_to_delete))
return
message_admins("[key_name_admin(usr)] removed an objective from [current]: [objective_to_delete.explanation_text]")
log_admin("[key_name(usr)] removed an objective from [current]: [objective_to_delete.explanation_text]")
qdel(objective_to_delete)
do_edit_objectives_ambitions()
return
else if (href_list["obj_panel_edit"])
var/datum/antagonist/antag_datum = locate(href_list["target_antag"]) in antag_datums
if(QDELETED(antag_datum))
do_edit_objectives_ambitions()
to_chat(usr, "No antag found.")
return
if(antag_datum.owner != src)
do_edit_objectives_ambitions()
to_chat(usr, "Invalid antag reference.")
return
var/datum/objective/objective_to_edit = locate(href_list["obj_panel_edit"]) in antag_datum.objectives
if(QDELETED(objective_to_edit))
to_chat(usr, "No objective found. Perhaps it was already deleted?")
do_edit_objectives_ambitions()
return
if(objective_to_edit.owner != src)
do_edit_objectives_ambitions()
to_chat(usr, "Invalid objective reference.")
return
var/explanation_before = objective_to_edit.explanation_text
objective_to_edit.admin_edit(usr)
if(QDELETED(objective_to_edit))
return
message_admins("[key_name_admin(usr)] edited an objective from [current]:\
Before: [explanation_before]\
After: [objective_to_edit.explanation_text]")
log_admin("[key_name(usr)] edited an objective from [current]:\
Before: [explanation_before]\
After: [objective_to_edit.explanation_text]")
do_edit_objectives_ambitions()
return
//ambition end
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,"Invalid antagonist ref to be removed.")
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 = stripped_multiline_input(usr, "Write new memory", "Memory", memory, MAX_MESSAGE_LEN)
if (isnull(new_memo))
return
memory = new_memo
else if (href_list["obj_edit"] || href_list["obj_add"])
var/objective_pos //Edited objectives need to keep same order in antag objective list
var/def_value
var/datum/antagonist/target_antag
var/datum/objective/old_objective //The old objective we're replacing/editing
var/datum/objective/new_objective //New objective we're be adding
if(href_list["obj_edit"])
for(var/datum/antagonist/A in antag_datums)
old_objective = locate(href_list["obj_edit"]) in A.objectives
if(old_objective)
target_antag = A
objective_pos = A.objectives.Find(old_objective)
break
if(!old_objective)
to_chat(usr,"Invalid objective.")
return
else
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", "(new custom antag)") 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
//ambition start
if(!GLOB.objective_choices)
populate_objective_choices()
if(old_objective && GLOB.objective_choices[old_objective.name])
def_value = old_objective.name
var/selected_type = input("Select objective type:", "Objective type", def_value) as null|anything in GLOB.objective_choices
selected_type = GLOB.objective_choices[selected_type]
if (!selected_type)
return
if(!old_objective)
//Add new one
new_objective = new selected_type
new_objective.owner = src
new_objective.admin_edit(usr)
target_antag.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(old_objective.type == selected_type)
//Edit the old
old_objective.admin_edit(usr)
new_objective = old_objective
else
//Replace the old
new_objective = new selected_type
new_objective.owner = src
new_objective.admin_edit(usr)
target_antag.objectives -= old_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]")
//ambition start
if(href_list["ambition_panel"])
do_edit_objectives_ambitions()
return
//ambition end
else if(href_list["traitor_class"])
var/static/list/choices
if(!choices)
choices = list()
for(var/C in GLOB.traitor_classes)
var/datum/traitor_class/t = C
choices[initial(t.employer)] = C
var/datum/antagonist/traitor/T = locate(href_list["target_antag"]) in antag_datums
if(T)
var/selected_type = input("Select traitor class:", "Traitor class", T.traitor_kind.employer) as null|anything in choices
selected_type = choices[selected_type]
T.set_traitor_kind(selected_type)
else if (href_list["obj_delete"])
var/datum/objective/objective
for(var/datum/antagonist/A in antag_datums)
objective = locate(href_list["obj_delete"]) in A.objectives
if(istype(objective))
break
if(!objective)
to_chat(usr,"Invalid objective.")
return
qdel(objective) //TODO: Needs cleaning objective destroys (whatever that means)
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
for(var/datum/antagonist/A in antag_datums)
objective = locate(href_list["obj_completed"]) in A.objectives
if(istype(objective))
objective = objective
break
if(!objective)
to_chat(usr,"Invalid 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, "Equipping a syndicate failed!")
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()
//ambition start
if(href_list["ambition_panel"])
do_edit_objectives_ambitions()
return
//ambition end
//Something in here might have changed your mob
if(self_antagging && (!usr || !usr.client) && current.client)
usr = current
traitor_panel()
/datum/mind/proc/get_all_objectives()
var/list/all_objectives = list()
for(var/datum/antagonist/A in antag_datums)
all_objectives |= A.objectives
return all_objectives
/datum/mind/proc/announce_objectives()
var/obj_count = 1
to_chat(current, "Your current objectives:")
for(var/objective in get_all_objectives())
var/datum/objective/O = objective
to_chat(current, "Objective #[obj_count]: [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_Contractor_Support()
if(!(has_antag_datum(/datum/antagonist/traitor/contractor_support)))
add_antag_datum(/datum/antagonist/traitor/contractor_support)
/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, "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.")
to_chat(current, "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.")
/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)
current?.client << output(null, "statbrowser:check_spells")
/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()
/// Sets our can_hijack to the fastest speed our antag datums allow.
/datum/mind/proc/get_hijack_speed()
. = 0
for(var/datum/antagonist/A in antag_datums)
. = max(., A.hijack_speed())
/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
SEND_SIGNAL(src, COMSIG_MOB_ON_NEW_MIND)
if(!mind.name)
mind.name = real_name
mind.current = src
mind.hide_ckey = client?.prefs?.hide_ckey
/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 = ""
//ambition start
#undef AMBITION_COOLDOWN_TIME
//ambition end