Merge pull request #801 from Citadel-Station-13/upstream-merge-26921
[MIRROR] Internal affairs upgrades [READY]
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
var/atom/movable/constant_target = null //The thing we're always focused on, if we're in the right mode
|
||||
var/target_x = 0 //The target coordinates if we're tracking those
|
||||
var/target_y = 0
|
||||
var/minimum_range = 0 //at what range the pinpointer declares you to be at your destination
|
||||
var/nuke_warning = FALSE // If we've set off a miniature alarm about an armed nuke
|
||||
var/mode = TRACK_NUKE_DISK //What are we looking for?
|
||||
|
||||
@@ -111,7 +112,7 @@
|
||||
var/mob/living/closest_operative = get_closest_atom(/mob/living/carbon/human, possible_targets, here)
|
||||
if(closest_operative)
|
||||
target = closest_operative
|
||||
if(TRACK_ATOM)
|
||||
if(TRACK_ATOM)
|
||||
if(constant_target)
|
||||
target = constant_target
|
||||
if(TRACK_COORDINATES)
|
||||
@@ -129,7 +130,7 @@
|
||||
if(here.z != there.z)
|
||||
icon_state = "pinon[nuke_warning ? "alert" : ""]null"
|
||||
return
|
||||
if(here == there)
|
||||
if(get_dist_euclidian(here,there)<=minimum_range)
|
||||
icon_state = "pinon[nuke_warning ? "alert" : ""]direct"
|
||||
else
|
||||
setDir(get_dir(here, there))
|
||||
@@ -171,3 +172,6 @@
|
||||
desc = "An integrated tracking device, jury-rigged to search for living Syndicate operatives."
|
||||
mode = TRACK_OPERATIVES
|
||||
flags = NODROP
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -67,7 +67,14 @@
|
||||
/datum/objective/proc/update_explanation_text()
|
||||
//Default does nothing, override where needed
|
||||
|
||||
/datum/objective/proc/give_special_equipment()
|
||||
/datum/objective/proc/give_special_equipment(special_equipment)
|
||||
if(owner && owner.current)
|
||||
if(ishuman(owner.current))
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
var/list/slots = list ("backpack" = slot_in_backpack)
|
||||
for(var/eq_path in special_equipment)
|
||||
var/obj/O = new eq_path
|
||||
H.equip_in_one_of_slots(O, slots)
|
||||
|
||||
/datum/objective/assassinate
|
||||
var/target_role_type=0
|
||||
@@ -94,6 +101,14 @@
|
||||
else
|
||||
explanation_text = "Free Objective"
|
||||
|
||||
/datum/objective/assassinate/internal
|
||||
var/stolen = 0 //Have we already eliminated this target?
|
||||
|
||||
/datum/objective/assassinate/internal/update_explanation_text()
|
||||
..()
|
||||
if(target && !target.current)
|
||||
explanation_text = "Assassinate [target.name], who was obliterated"
|
||||
|
||||
|
||||
/datum/objective/mutiny
|
||||
var/target_role_type=0
|
||||
@@ -468,7 +483,7 @@ GLOBAL_LIST_EMPTY(possible_items)
|
||||
steal_target = targetinfo.targetitem
|
||||
explanation_text = "Steal [targetinfo.name]."
|
||||
dangerrating = targetinfo.difficulty
|
||||
give_special_equipment()
|
||||
give_special_equipment(targetinfo.special_equipment)
|
||||
return steal_target
|
||||
else
|
||||
explanation_text = "Free objective"
|
||||
@@ -511,15 +526,6 @@ GLOBAL_LIST_EMPTY(possible_items)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/objective/steal/give_special_equipment()
|
||||
if(owner && owner.current && targetinfo)
|
||||
if(ishuman(owner.current))
|
||||
var/mob/living/carbon/human/H = owner.current
|
||||
var/list/slots = list ("backpack" = slot_in_backpack)
|
||||
for(var/eq_path in targetinfo.special_equipment)
|
||||
var/obj/O = new eq_path
|
||||
H.equip_in_one_of_slots(O, slots)
|
||||
|
||||
|
||||
GLOBAL_LIST_EMPTY(possible_items_special)
|
||||
/datum/objective/steal/special //ninjas are so special they get their own subtype good for them
|
||||
@@ -695,6 +701,9 @@ GLOBAL_LIST_EMPTY(possible_items_special)
|
||||
explanation_text = "Destroy [target.name], the experimental AI."
|
||||
else
|
||||
explanation_text = "Free Objective"
|
||||
|
||||
/datum/objective/destroy/internal
|
||||
var/stolen = FALSE //Have we already eliminated this target?
|
||||
|
||||
/datum/objective/steal_five_of_type
|
||||
explanation_text = "Steal at least five items!"
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
#define PINPOINTER_MINIMUM_RANGE 15
|
||||
#define PINPOINTER_EXTRA_RANDOM_RANGE 10
|
||||
#define PINPOINTER_PING_TIME 40
|
||||
|
||||
/datum/game_mode/traitor/internal_affairs
|
||||
name = "Internal Affairs"
|
||||
config_tag = "internal_affairs"
|
||||
employer = "Internal Affairs"
|
||||
required_players = 25
|
||||
required_enemies = 5
|
||||
required_players = 25
|
||||
required_enemies = 5
|
||||
recommended_enemies = 8
|
||||
reroll_friendly = 0
|
||||
traitor_name = "Nanotrasen Internal Affairs Agent"
|
||||
@@ -18,15 +22,189 @@
|
||||
var/list/target_list = list()
|
||||
var/list/late_joining_list = list()
|
||||
|
||||
|
||||
/datum/game_mode/traitor/internal_affairs/post_setup()
|
||||
var/i = 0
|
||||
for(var/datum/mind/traitor in traitors)
|
||||
i++
|
||||
if(i + 1 > traitors.len)
|
||||
i = 0
|
||||
target_list[traitor] = traitors[i + 1]
|
||||
target_list[traitor] = traitors[i+1]
|
||||
..()
|
||||
|
||||
|
||||
/datum/status_effect/agent_pinpointer
|
||||
id = "agent_pinpointer"
|
||||
duration = -1
|
||||
tick_interval = PINPOINTER_PING_TIME
|
||||
alert_type = /obj/screen/alert/status_effect/agent_pinpointer
|
||||
var/minimum_range = PINPOINTER_MINIMUM_RANGE
|
||||
var/mob/scan_target = null
|
||||
|
||||
/obj/screen/alert/status_effect/agent_pinpointer
|
||||
name = "Internal Affairs Integrated Pinpointer"
|
||||
desc = "Even stealthier than a normal implant."
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "pinon"
|
||||
|
||||
/datum/status_effect/agent_pinpointer/proc/point_to_target() //If we found what we're looking for, show the distance and direction
|
||||
if(!scan_target)
|
||||
linked_alert.icon_state = "pinonnull"
|
||||
return
|
||||
var/turf/here = get_turf(owner)
|
||||
var/turf/there = get_turf(scan_target)
|
||||
if(here.z != there.z)
|
||||
linked_alert.icon_state = "pinonnull"
|
||||
return
|
||||
if(get_dist_euclidian(here,there)<=minimum_range + rand(0, PINPOINTER_EXTRA_RANDOM_RANGE))
|
||||
linked_alert.icon_state = "pinondirect"
|
||||
else
|
||||
linked_alert.setDir(get_dir(here, there))
|
||||
switch(get_dist(here, there))
|
||||
if(1 to 8)
|
||||
linked_alert.icon_state = "pinonclose"
|
||||
if(9 to 16)
|
||||
linked_alert.icon_state = "pinonmedium"
|
||||
if(16 to INFINITY)
|
||||
linked_alert.icon_state = "pinonfar"
|
||||
|
||||
|
||||
/datum/status_effect/agent_pinpointer/proc/scan_for_target()
|
||||
scan_target = null
|
||||
if(owner)
|
||||
if(owner.mind)
|
||||
if(owner.mind.objectives)
|
||||
for(var/datum/objective/objective_ in owner.mind.objectives)
|
||||
if(!is_internal_objective(objective_))
|
||||
continue
|
||||
var/datum/objective/assassinate/internal/objective = objective_
|
||||
var/mob/current = objective.target.current
|
||||
if(current&¤t.stat!=DEAD)
|
||||
scan_target = current
|
||||
break
|
||||
|
||||
|
||||
/datum/status_effect/agent_pinpointer/tick()
|
||||
if(!owner)
|
||||
qdel(src)
|
||||
return
|
||||
scan_for_target()
|
||||
point_to_target()
|
||||
|
||||
/proc/give_pinpointer(datum/mind/owner)
|
||||
if(owner && owner.current)
|
||||
owner.current.apply_status_effect(/datum/status_effect/agent_pinpointer)
|
||||
|
||||
|
||||
/datum/internal_agent_state
|
||||
var/traitored = FALSE
|
||||
var/datum/mind/owner = null
|
||||
var/list/datum/mind/targets_stolen = list()
|
||||
|
||||
/proc/is_internal_objective(datum/objective/O)
|
||||
return (istype(O, /datum/objective/assassinate/internal)||istype(O, /datum/objective/destroy/internal))
|
||||
|
||||
/proc/replace_escape_objective(datum/mind/owner)
|
||||
if(!owner||!owner.objectives)
|
||||
return
|
||||
for (var/objective_ in owner.objectives)
|
||||
if(!(istype(objective_, /datum/objective/escape)||istype(objective_,/datum/objective/survive)))
|
||||
continue
|
||||
owner.objectives -= objective_
|
||||
var/datum/objective/martyr/martyr_objective = new
|
||||
martyr_objective.owner = owner
|
||||
owner.objectives += martyr_objective
|
||||
|
||||
/proc/reinstate_escape_objective(datum/mind/owner)
|
||||
if(!owner||!owner.objectives)
|
||||
return
|
||||
for (var/objective_ in owner.objectives)
|
||||
if(!istype(objective_, /datum/objective/martyr))
|
||||
continue
|
||||
owner.objectives -= objective_
|
||||
if(issilicon(owner))
|
||||
var/datum/objective/survive/survive_objective = new
|
||||
survive_objective.owner = owner
|
||||
owner.objectives += survive_objective
|
||||
else
|
||||
var/datum/objective/escape/escape_objective = new
|
||||
escape_objective.owner = owner
|
||||
owner.objectives += escape_objective
|
||||
|
||||
/datum/internal_agent_state/proc/steal_targets(datum/mind/victim)
|
||||
if(!owner.current||owner.current.stat==DEAD) //Should already be guaranteed if this is only called from steal_targets_timer_func, but better to be safe code than sorry code
|
||||
return
|
||||
var/already_traitored = traitored
|
||||
to_chat(owner.current, "<span class='userdanger'> Target eliminated: [victim.name]</span>")
|
||||
for(var/objective_ in victim.objectives)
|
||||
if(istype(objective_, /datum/objective/assassinate/internal))
|
||||
var/datum/objective/assassinate/internal/objective = objective_
|
||||
if(objective.target==owner)
|
||||
traitored = TRUE
|
||||
else if(targets_stolen.Find(objective.target) == 0)
|
||||
var/datum/objective/assassinate/internal/new_objective = new
|
||||
new_objective.owner = owner
|
||||
new_objective.target = objective.target
|
||||
new_objective.update_explanation_text()
|
||||
owner.objectives += new_objective
|
||||
targets_stolen += objective.target
|
||||
var/status_text = objective.check_completion() ? "neutralised" : "active"
|
||||
to_chat(owner.current, "<span class='userdanger'> New target added to database: [objective.target.name] ([status_text]) </span>")
|
||||
else if(istype(objective_, /datum/objective/destroy/internal))
|
||||
var/datum/objective/destroy/internal/objective = objective_
|
||||
var/datum/objective/destroy/internal/new_objective = new
|
||||
if(objective.target==owner)
|
||||
traitored = TRUE
|
||||
else if(targets_stolen.Find(objective.target) == 0)
|
||||
new_objective.owner = owner
|
||||
new_objective.target = objective.target
|
||||
new_objective.update_explanation_text()
|
||||
owner.objectives += new_objective
|
||||
targets_stolen += objective.target
|
||||
var/status_text = objective.check_completion() ? "neutralised" : "active"
|
||||
to_chat(owner.current, "<span class='userdanger'> New target added to database: [objective.target.name] ([status_text]) </span>")
|
||||
if(traitored&&!already_traitored)
|
||||
for(var/objective_ in owner.objectives)
|
||||
if(!is_internal_objective(objective_))
|
||||
continue
|
||||
var/datum/objective/assassinate/internal/objective = objective_
|
||||
if(!objective.check_completion())
|
||||
traitored = FALSE
|
||||
return
|
||||
to_chat(owner.current,"<span class='userdanger'> All the other agents are dead, and you're the last loose end. Stage a Syndicate terrorist attack to cover up for today's events. You no longer have any limits on collateral damage.</span>")
|
||||
replace_escape_objective(owner)
|
||||
|
||||
|
||||
|
||||
/datum/internal_agent_state/proc/steal_targets_timer_func()
|
||||
if(owner&&owner.current&&owner.current.stat!=DEAD)
|
||||
for(var/objective_ in owner.objectives)
|
||||
if(!is_internal_objective(objective_))
|
||||
continue
|
||||
var/datum/objective/assassinate/internal/objective = objective_
|
||||
if(!objective.target)
|
||||
continue
|
||||
if(objective.check_completion())
|
||||
if(objective.stolen)
|
||||
continue
|
||||
else
|
||||
steal_targets(objective.target)
|
||||
objective.stolen = TRUE
|
||||
else
|
||||
if(objective.stolen)
|
||||
var/fail_msg = "<span class='userdanger'>Your sensors tell you that [objective.target.current.real_name], one of the targets you were meant to have killed, pulled one over on you, and is still alive - do the job properly this time! </span>"
|
||||
if(traitored)
|
||||
fail_msg += "<span class='userdanger'> The truth could still slip out!</font><B><font size=5 color=red> Cease any terrorist actions as soon as possible, unneeded property damage or loss of employee life will lead to your contract being terminated.</span>"
|
||||
reinstate_escape_objective(owner)
|
||||
traitored = FALSE
|
||||
to_chat(owner.current, fail_msg)
|
||||
objective.stolen = FALSE
|
||||
add_steal_targets_timer(owner)
|
||||
|
||||
/datum/internal_agent_state/proc/add_steal_targets_timer()
|
||||
var/datum/callback/C = new(src, .steal_targets_timer_func)
|
||||
addtimer(C, 30)
|
||||
|
||||
/datum/game_mode/traitor/internal_affairs/forge_traitor_objectives(datum/mind/traitor)
|
||||
|
||||
if(target_list.len && target_list[traitor]) // Is a double agent
|
||||
@@ -34,13 +212,13 @@
|
||||
// Assassinate
|
||||
var/datum/mind/target_mind = target_list[traitor]
|
||||
if(issilicon(target_mind.current))
|
||||
var/datum/objective/destroy/destroy_objective = new
|
||||
var/datum/objective/destroy/internal/destroy_objective = new
|
||||
destroy_objective.owner = traitor
|
||||
destroy_objective.target = target_mind
|
||||
destroy_objective.update_explanation_text()
|
||||
traitor.objectives += destroy_objective
|
||||
else
|
||||
var/datum/objective/assassinate/kill_objective = new
|
||||
var/datum/objective/assassinate/internal/kill_objective = new
|
||||
kill_objective.owner = traitor
|
||||
kill_objective.target = target_mind
|
||||
kill_objective.update_explanation_text()
|
||||
@@ -55,6 +233,11 @@
|
||||
var/datum/objective/escape/escape_objective = new
|
||||
escape_objective.owner = traitor
|
||||
traitor.objectives += escape_objective
|
||||
var/datum/internal_agent_state/state = new
|
||||
state.owner=traitor
|
||||
state.add_steal_targets_timer()
|
||||
if(!issilicon(traitor.current))
|
||||
give_pinpointer(traitor)
|
||||
|
||||
else
|
||||
..() // Give them standard objectives.
|
||||
@@ -106,14 +289,18 @@
|
||||
|
||||
/datum/game_mode/traitor/internal_affairs/greet_traitor(datum/mind/traitor)
|
||||
var/crime = pick("distribution of contraband" , "unauthorized erotic action on duty", "embezzlement", "piloting under the influence", "dereliction of duty", "syndicate collaboration", "mutiny", "multiple homicides", "corporate espionage", "recieving bribes", "malpractice", "worship of prohbited life forms", "possession of profane texts", "murder", "arson", "insulting their manager", "grand theft", "conspiracy", "attempting to unionize", "vandalism", "gross incompetence")
|
||||
to_chat(traitor.current, "<B><font size=3 color=red>You are the [traitor_name].</font></B>")
|
||||
to_chat(traitor.current, "<B><font size=3 color=red>Your target is suspected of [crime], and you have been tasked with eliminating them by any means necessary to avoid a costly and embarrassing public trial.</font></B>")
|
||||
to_chat(traitor.current, "<span class='userdanger'>You are the [traitor_name].</span>")
|
||||
to_chat(traitor.current, "<span class='userdanger'>Your target is suspected of [crime], and you have been tasked with eliminating them by any means necessary to avoid a costly and embarrassing public trial.</span>")
|
||||
to_chat(traitor.current, "<B><font size=5 color=red>While you have a license to kill, unneeded property damage or loss of employee life will lead to your contract being terminated.</font></B>")
|
||||
to_chat(traitor.current, "<B><font size=3 color=red>For the sake of plausible deniability, you have been equipped with an array of captured Syndicate weaponry available via uplink.</font></B>")
|
||||
to_chat(traitor.current, "<B><font size=3 color=red>Finally, watch your back. Your target has friends in high places, and intel suggests someone may have taken out a contract of their own to protect them.</font></B>")
|
||||
to_chat(traitor.current, "<span class='userdanger'>For the sake of plausible deniability, you have been equipped with an array of captured Syndicate weaponry available via uplink.</span>")
|
||||
to_chat(traitor.current, "<span class='userdanger'>Finally, watch your back. Your target has friends in high places, and intel suggests someone may have taken out a contract of their own to protect them.</span>")
|
||||
traitor.announce_objectives()
|
||||
|
||||
|
||||
|
||||
/datum/game_mode/traitor/internal_affairs/give_codewords(mob/living/traitor_mob)
|
||||
return
|
||||
return
|
||||
|
||||
#undef PINPOINTER_EXTRA_RANDOM_RANGE
|
||||
#undef PINPOINTER_MINIMUM_RANGE
|
||||
#undef PINPOINTER_PING_TIME
|
||||
|
||||
Reference in New Issue
Block a user