[MIRROR] 3(or 4) new traitor objectives [MDB IGNORE] (#13972)
* 3(or 4) new traitor objectives (#67287) This PR introduces 3 new kind of traitor objectives: Eyesnatcher objective, you'll need to remove your victim's eyes by any means. You will be given a device that you can use to pull out someone's eyes after 3 5 second delays. This would be quite loud and painful to the victim, so make sure you got them restrained or knocked out. "Public" Sleeper protocol - this is a high progression(30+ minutes) objective. It's basically Sleeper Protocol, but availible for everyone instead of only med personnel. Locate Structural Weakpoint - a high progression(45+ minutes) objectives that can be only completed once. You will recieve a device that you'll need to use in 2 different (somewhat secure) areas in order to triangulate the structural weakpoint of the station, you'll need to stay in the area for 30 seconds and station AI will be alerted about your attempt. After triangulating it, you will be given a location-locked explosive which will also create from 5 to 8 minor explosions in a 17-25 tile area. Kidnap personnel - Basically old contractor kidnap. Down someone, get them to the extraction location, call in a pod, stuff them into it and send it back. Additional TCs if you bring them alive. * 3(or 4) new traitor objectives Co-authored-by: SmArtKar <44720187+SmArtKar@users.noreply.github.com>
@@ -167,7 +167,7 @@
|
||||
/obj/machinery/door/airlock/wood{
|
||||
name = "Kitchen"
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"aG" = (
|
||||
/obj/structure/railing/corner,
|
||||
@@ -584,7 +584,7 @@
|
||||
/obj/item/kirbyplants{
|
||||
icon_state = "plant-10"
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"bO" = (
|
||||
/obj/effect/landmark/holding_facility,
|
||||
@@ -1103,7 +1103,7 @@
|
||||
/obj/structure/mineral_door/paperframe{
|
||||
name = "Dining Hall"
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"dk" = (
|
||||
/obj/effect/turf_decal/siding/wood{
|
||||
@@ -1115,7 +1115,7 @@
|
||||
/obj/structure/mineral_door/paperframe{
|
||||
name = "Botanical Garden"
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"dl" = (
|
||||
/obj/effect/turf_decal/siding/wood,
|
||||
@@ -1125,7 +1125,7 @@
|
||||
/obj/structure/mineral_door/paperframe{
|
||||
name = "Guest Suite *B"
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"dm" = (
|
||||
/obj/machinery/seed_extractor,
|
||||
@@ -1162,7 +1162,7 @@
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"dt" = (
|
||||
/obj/structure/closet/crate/freezer/blood,
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"du" = (
|
||||
/obj/effect/turf_decal/tile/bar,
|
||||
@@ -1456,7 +1456,7 @@
|
||||
/obj/structure/mineral_door/paperframe{
|
||||
name = "Guest Suite *A"
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"el" = (
|
||||
/obj/structure/filingcabinet/medical,
|
||||
@@ -1711,7 +1711,7 @@
|
||||
/obj/machinery/door/airlock/wood{
|
||||
name = "Washroom"
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"eU" = (
|
||||
/obj/machinery/hydroponics/constructable,
|
||||
@@ -2121,7 +2121,7 @@
|
||||
"ge" = (
|
||||
/obj/machinery/defibrillator_mount/directional/south,
|
||||
/obj/machinery/stasis,
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"gf" = (
|
||||
/obj/structure/flora/tree/pine/style_random,
|
||||
@@ -2403,7 +2403,7 @@
|
||||
name = "Momo";
|
||||
radio_key = null
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"gX" = (
|
||||
/obj/structure/table/reinforced,
|
||||
@@ -2418,7 +2418,7 @@
|
||||
/turf/open/floor/grass,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"hb" = (
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"hc" = (
|
||||
/obj/structure/table/reinforced,
|
||||
@@ -2617,7 +2617,7 @@
|
||||
/obj/structure/mineral_door/paperframe{
|
||||
name = "East Wing"
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"hz" = (
|
||||
/obj/effect/turf_decal/tile/neutral/fourcorners,
|
||||
@@ -2720,7 +2720,7 @@
|
||||
},
|
||||
/obj/machinery/washing_machine,
|
||||
/obj/machinery/light/directional/east,
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"hL" = (
|
||||
/obj/effect/turf_decal/tile/yellow/half,
|
||||
@@ -6305,7 +6305,7 @@
|
||||
dir = 1
|
||||
},
|
||||
/obj/effect/turf_decal/siding/wood,
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"sq" = (
|
||||
/obj/machinery/computer/shuttle/white_ship{
|
||||
@@ -6398,7 +6398,7 @@
|
||||
"sF" = (
|
||||
/obj/effect/turf_decal/tile/dark/fourcorners,
|
||||
/obj/effect/turf_decal/siding/wood,
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"sG" = (
|
||||
/obj/docking_port/stationary{
|
||||
@@ -6942,7 +6942,7 @@
|
||||
pixel_y = 3
|
||||
},
|
||||
/obj/item/clothing/suit/hawaiian,
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"ur" = (
|
||||
/obj/machinery/pdapainter,
|
||||
@@ -9104,7 +9104,7 @@
|
||||
/obj/effect/turf_decal/siding/wood{
|
||||
dir = 8
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"AH" = (
|
||||
/turf/open/floor/iron,
|
||||
@@ -10054,7 +10054,7 @@
|
||||
name = "Bar"
|
||||
},
|
||||
/obj/machinery/duct,
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"Do" = (
|
||||
/obj/structure/railing/corner{
|
||||
@@ -10129,7 +10129,7 @@
|
||||
/obj/structure/mineral_door/paperframe{
|
||||
name = "Dojo"
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"Dx" = (
|
||||
/obj/structure/chair/bronze{
|
||||
@@ -10990,7 +10990,7 @@
|
||||
/obj/structure/mineral_door/paperframe{
|
||||
name = "Blue"
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"FV" = (
|
||||
/obj/structure/chair/pew/left{
|
||||
@@ -13115,7 +13115,7 @@
|
||||
/obj/structure/mineral_door/paperframe{
|
||||
name = "Performance Room"
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"MI" = (
|
||||
/obj/machinery/light/directional/north,
|
||||
@@ -13740,7 +13740,7 @@
|
||||
/obj/structure/mineral_door/paperframe{
|
||||
name = "Shrine"
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"Ov" = (
|
||||
/obj/structure/table/reinforced/rglass,
|
||||
@@ -14018,7 +14018,7 @@
|
||||
/obj/structure/sign/painting/library{
|
||||
pixel_y = 32
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"Pg" = (
|
||||
/obj/machinery/computer/auxiliary_base/directional/north,
|
||||
@@ -14431,7 +14431,7 @@
|
||||
/obj/structure/mineral_door/paperframe{
|
||||
name = "Electrical Room"
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"Qn" = (
|
||||
/obj/effect/turf_decal/siding/purple{
|
||||
@@ -14476,7 +14476,7 @@
|
||||
/obj/effect/turf_decal/siding/wood{
|
||||
dir = 1
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"Qv" = (
|
||||
/obj/effect/turf_decal/tile/red{
|
||||
@@ -14608,7 +14608,7 @@
|
||||
/obj/machinery/door/airlock/wood{
|
||||
name = "Toilet"
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"QR" = (
|
||||
/obj/structure/chair/office/light{
|
||||
@@ -14787,7 +14787,7 @@
|
||||
"Rk" = (
|
||||
/obj/machinery/defibrillator_mount/directional/north,
|
||||
/obj/machinery/stasis,
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"Rl" = (
|
||||
/obj/structure/chair/comfy/brown{
|
||||
@@ -14803,7 +14803,7 @@
|
||||
/obj/structure/table/reinforced/plastitaniumglass,
|
||||
/obj/item/storage/basket,
|
||||
/obj/machinery/light/directional/west,
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"Rn" = (
|
||||
/obj/structure/chair/sofa/bench/right{
|
||||
@@ -14923,7 +14923,7 @@
|
||||
/obj/structure/sign/painting/library{
|
||||
pixel_y = 32
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"RE" = (
|
||||
/obj/effect/turf_decal/siding/wood,
|
||||
@@ -14933,7 +14933,7 @@
|
||||
/obj/structure/mineral_door/paperframe{
|
||||
name = "Guest Suite *D"
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"RF" = (
|
||||
/obj/structure/table/reinforced,
|
||||
@@ -15051,7 +15051,7 @@
|
||||
/obj/structure/mineral_door/paperframe{
|
||||
name = "Red"
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"RZ" = (
|
||||
/obj/effect/turf_decal/trimline/red,
|
||||
@@ -15064,7 +15064,7 @@
|
||||
name = "Hattori";
|
||||
radio_key = null
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"Sb" = (
|
||||
/obj/structure/table/wood,
|
||||
@@ -15237,7 +15237,7 @@
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"SF" = (
|
||||
/obj/effect/turf_decal/siding/wood,
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"SG" = (
|
||||
/obj/machinery/light/directional/south,
|
||||
@@ -15306,7 +15306,7 @@
|
||||
/obj/structure/mineral_door/paperframe{
|
||||
name = "Guest Suite *C"
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"SQ" = (
|
||||
/obj/structure/filingcabinet/medical,
|
||||
@@ -15932,7 +15932,7 @@
|
||||
/obj/structure/mineral_door/paperframe{
|
||||
name = "Spectator's Lounge"
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"Uq" = (
|
||||
/obj/structure/flora/grass/both/style_random,
|
||||
@@ -16285,7 +16285,7 @@
|
||||
icon_state = "plant-10"
|
||||
},
|
||||
/obj/machinery/light/small/directional/south,
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"Vk" = (
|
||||
/obj/effect/turf_decal/tile/brown,
|
||||
@@ -16415,7 +16415,7 @@
|
||||
/obj/effect/turf_decal/siding/wood{
|
||||
dir = 1
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"VH" = (
|
||||
/obj/effect/turf_decal/tile/bar,
|
||||
@@ -16497,7 +16497,7 @@
|
||||
dir = 1
|
||||
},
|
||||
/obj/effect/turf_decal/siding/wood,
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"VT" = (
|
||||
/obj/structure/rack,
|
||||
@@ -16786,7 +16786,7 @@
|
||||
/obj/effect/turf_decal/tile/dark/fourcorners,
|
||||
/obj/effect/turf_decal/siding/wood,
|
||||
/obj/machinery/washing_machine,
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"WA" = (
|
||||
/obj/item/kirbyplants{
|
||||
@@ -17186,7 +17186,7 @@
|
||||
/area/centcom/tdome/administration)
|
||||
"Xx" = (
|
||||
/obj/machinery/vending/dinnerware,
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"Xy" = (
|
||||
/obj/machinery/door/airlock/external/ruin{
|
||||
@@ -17292,7 +17292,7 @@
|
||||
/obj/item/organ/heart/cybernetic/tier2,
|
||||
/obj/item/organ/heart/cybernetic/tier2,
|
||||
/obj/item/defibrillator,
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"XJ" = (
|
||||
/obj/structure/flora/bush/lavendergrass/style_random,
|
||||
@@ -17935,7 +17935,7 @@
|
||||
/obj/structure/window/paperframe{
|
||||
can_atmos_pass = 0
|
||||
},
|
||||
/turf/open/floor/sepia,
|
||||
/turf/open/floor/iron/sepia,
|
||||
/area/centcom/central_command_areas/holding)
|
||||
"ZC" = (
|
||||
/obj/effect/turf_decal/stripes/line{
|
||||
|
||||
@@ -249,3 +249,10 @@ GLOBAL_LIST_INIT(ai_employers, list(
|
||||
#define OBJECTIVE_STATE_FAILED 4
|
||||
/// If the objective is no longer valid
|
||||
#define OBJECTIVE_STATE_INVALID 5
|
||||
|
||||
/// Weights for traitor objective categories
|
||||
#define OBJECTIVE_WEIGHT_TINY 5
|
||||
#define OBJECTIVE_WEIGHT_SMALL 7
|
||||
#define OBJECTIVE_WEIGHT_DEFAULT 10
|
||||
#define OBJECTIVE_WEIGHT_BIG 15
|
||||
#define OBJECTIVE_WEIGHT_HUGE 20
|
||||
|
||||
@@ -535,6 +535,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
|
||||
#define TRAIT_ALLOW_HERETIC_CASTING "allow_heretic_casting"
|
||||
/// Designates a heart as a living heart for a heretic.
|
||||
#define TRAIT_LIVING_HEART "living_heart"
|
||||
/// Prevents the same person from being chosen multiple times for kidnapping objective
|
||||
#define TRAIT_HAS_BEEN_KIDNAPPED "has_been_kidnapped"
|
||||
|
||||
//quirk traits
|
||||
#define TRAIT_ALCOHOL_TOLERANCE "alcohol_tolerance"
|
||||
|
||||
@@ -37,6 +37,8 @@ SUBSYSTEM_DEF(traitor)
|
||||
var/generate_objectives = TRUE
|
||||
/// Objectives that have been completed by type. Used for limiting objectives.
|
||||
var/list/taken_objectives_by_type = list()
|
||||
/// Contains 3 areas: 2 areas to scan in order to triangulate the third one which is the structural weakpoint itself
|
||||
var/list/station_weakpoints = list()
|
||||
|
||||
/datum/controller/subsystem/traitor/Initialize(start_timeofday)
|
||||
. = ..()
|
||||
@@ -51,6 +53,31 @@ SUBSYSTEM_DEF(traitor)
|
||||
log_world("[configuration_path] has an invalid type ([typepath]) that doesn't exist in the codebase! Please correct or remove [typepath]")
|
||||
configuration_data[actual_typepath] = data[typepath]
|
||||
|
||||
/// List of high-security areas that we pick required ones from
|
||||
var/list/allowed_areas = typecacheof(list(/area/station/command,
|
||||
/area/station/cargo/qm,
|
||||
/area/station/comms,
|
||||
/area/station/engineering,
|
||||
/area/station/science,
|
||||
/area/station/security,
|
||||
))
|
||||
|
||||
var/list/blacklisted_areas = typecacheof(list(/area/station/engineering/hallway,
|
||||
/area/station/engineering/lobby,
|
||||
/area/station/engineering/storage,
|
||||
/area/station/science/lobby,
|
||||
/area/station/science/test_area,
|
||||
/area/station/security/prison,
|
||||
))
|
||||
|
||||
var/list/possible_areas = GLOB.the_station_areas.Copy()
|
||||
for(var/area/possible_area as anything in possible_areas)
|
||||
if(!is_type_in_typecache(possible_area, allowed_areas) || initial(possible_area.outdoors) || is_type_in_typecache(possible_area, blacklisted_areas))
|
||||
possible_areas -= possible_area
|
||||
|
||||
for(var/i in 1 to 3)
|
||||
station_weakpoints += pick_n_take(possible_areas)
|
||||
|
||||
/datum/controller/subsystem/traitor/fire(resumed)
|
||||
var/player_count = length(GLOB.alive_player_list)
|
||||
// Has a maximum of 1 minute, however the value can be lower if there are lower players than the ideal
|
||||
|
||||
@@ -65,4 +65,4 @@
|
||||
/// Assoc list of objectives by type mapped to their weight. Can also contain lists of objectives mapped to weight
|
||||
var/list/objectives = list()
|
||||
/// The weight of the category. How likely this category is to be chosen.
|
||||
var/weight = 1
|
||||
var/weight = OBJECTIVE_WEIGHT_DEFAULT
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
/datum/traitor_objective_category/eyesnatching
|
||||
name = "Eyesnatching"
|
||||
objectives = list(
|
||||
/datum/traitor_objective/eyesnatching = 1,
|
||||
/datum/traitor_objective/eyesnatching/heads = 1,
|
||||
)
|
||||
weight = OBJECTIVE_WEIGHT_TINY
|
||||
|
||||
/datum/traitor_objective/eyesnatching
|
||||
name = "Steal %TARGET%'s (%JOB TITLE%) eyes"
|
||||
description = "%TARGET% messed with the wrong people. Steal their eyes to teach them a lesson. You will be provided an experimental eyesnatcher device to aid you in your mission."
|
||||
|
||||
progression_reward = list(4 MINUTES, 8 MINUTES)
|
||||
telecrystal_reward = list(1, 2)
|
||||
|
||||
// The code below is for limiting how often you can get this objective. You will get this objective at a maximum of maximum_objectives_in_period every objective_period
|
||||
/// The objective period at which we consider if it is an 'objective'. Set to 0 to accept all objectives.
|
||||
var/objective_period = 15 MINUTES
|
||||
/// The maximum number of objectives we can get within this period.
|
||||
var/maximum_objectives_in_period = 3
|
||||
|
||||
/// Who we're stealing eyes from
|
||||
var/mob/living/victim
|
||||
/// If we're targeting heads of staff or not
|
||||
var/heads_of_staff = FALSE
|
||||
/// Have we already spawned an eyesnatcher
|
||||
var/spawned_eyesnatcher = FALSE
|
||||
|
||||
/datum/traitor_objective/eyesnatching/supported_configuration_changes()
|
||||
. = ..()
|
||||
. += NAMEOF(src, objective_period)
|
||||
. += NAMEOF(src, maximum_objectives_in_period)
|
||||
|
||||
/datum/traitor_objective/eyesnatching/New(datum/uplink_handler/handler)
|
||||
. = ..()
|
||||
AddComponent(/datum/component/traitor_objective_limit_per_time, \
|
||||
/datum/traitor_objective/eyesnatching, \
|
||||
time_period = objective_period, \
|
||||
maximum_objectives = maximum_objectives_in_period \
|
||||
)
|
||||
|
||||
/datum/traitor_objective/eyesnatching/heads
|
||||
progression_reward = list(6 MINUTES, 12 MINUTES)
|
||||
telecrystal_reward = list(2, 3)
|
||||
|
||||
heads_of_staff = TRUE
|
||||
|
||||
/datum/traitor_objective/eyesnatching/generate_objective(datum/mind/generating_for, list/possible_duplicates)
|
||||
var/list/possible_targets = list()
|
||||
var/try_target_late_joiners = FALSE
|
||||
if(generating_for.late_joiner)
|
||||
try_target_late_joiners = TRUE
|
||||
|
||||
for(var/datum/mind/possible_target as anything in get_crewmember_minds())
|
||||
if(possible_target == generating_for)
|
||||
continue
|
||||
|
||||
if(!ishuman(possible_target.current))
|
||||
continue
|
||||
|
||||
if(possible_target.current.stat == DEAD)
|
||||
continue
|
||||
|
||||
if(possible_target.has_antag_datum(/datum/antagonist/traitor))
|
||||
continue
|
||||
|
||||
if(heads_of_staff)
|
||||
if(!(possible_target.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND))
|
||||
continue
|
||||
else
|
||||
if(possible_target.assigned_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND)
|
||||
continue
|
||||
|
||||
var/mob/living/carbon/human/targets_current = possible_target.current
|
||||
if(!targets_current.getorgan(/obj/item/organ/eyes))
|
||||
continue
|
||||
|
||||
possible_targets += targets_current
|
||||
|
||||
for(var/datum/traitor_objective/eyesnatching/objective as anything in possible_duplicates)
|
||||
possible_targets -= objective.victim
|
||||
|
||||
if(try_target_late_joiners)
|
||||
var/list/all_possible_targets = possible_targets.Copy()
|
||||
for(var/datum/mind/possible_target as anything in all_possible_targets)
|
||||
if(!possible_target.late_joiner)
|
||||
possible_targets -= possible_target
|
||||
|
||||
if(!possible_targets.len)
|
||||
possible_targets = all_possible_targets
|
||||
|
||||
if(!possible_targets.len)
|
||||
return FALSE //MISSION FAILED, WE'LL GET EM NEXT TIME
|
||||
|
||||
victim = pick(possible_targets)
|
||||
var/datum/mind/victim_mind = victim.mind
|
||||
|
||||
replace_in_name("%TARGET%", victim_mind.name)
|
||||
replace_in_name("%JOB TITLE%", victim_mind.assigned_role.title)
|
||||
RegisterSignal(victim, COMSIG_CARBON_LOSE_ORGAN, .proc/check_eye_removal)
|
||||
AddComponent(/datum/component/traitor_objective_register, victim, fail_signals = COMSIG_PARENT_QDELETING)
|
||||
|
||||
/datum/traitor_objective/eyesnatching/proc/check_eye_removal(datum/source, obj/item/organ/eyes/removed)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!istype(removed))
|
||||
return
|
||||
|
||||
succeed_objective()
|
||||
|
||||
/datum/traitor_objective/eyesnatching/generate_ui_buttons(mob/user)
|
||||
var/list/buttons = list()
|
||||
if(!spawned_eyesnatcher)
|
||||
buttons += add_ui_button("", "Pressing this will materialize an eyesnatcher, which can be used on incapacitaded or restrained targets to forcefully remove their eyes.", "syringe", "eyesnatcher")
|
||||
return buttons
|
||||
|
||||
/datum/traitor_objective/eyesnatching/ui_perform_action(mob/living/user, action)
|
||||
. = ..()
|
||||
switch(action)
|
||||
if("eyesnatcher")
|
||||
if(spawned_eyesnatcher)
|
||||
return
|
||||
spawned_eyesnatcher = TRUE
|
||||
var/obj/item/eyesnatcher/eyesnatcher = new(user.drop_location())
|
||||
user.put_in_hands(eyesnatcher)
|
||||
eyesnatcher.balloon_alert(user, "the eyesnatcher materializes in your hand")
|
||||
|
||||
/obj/item/eyesnatcher
|
||||
name = "portable eyeball extractor"
|
||||
desc = "An overly complicated device that can pierce target's skull and extract their eyeballs if enough brute force is applied."
|
||||
icon = 'icons/obj/surgery.dmi'
|
||||
icon_state = "eyesnatcher"
|
||||
base_icon_state = "eyesnatcher"
|
||||
inhand_icon_state = "hypo"
|
||||
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
|
||||
throwforce = 0
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
throw_speed = 3
|
||||
throw_range = 5
|
||||
var/used = FALSE
|
||||
|
||||
/obj/item/eyesnatcher/update_icon_state()
|
||||
. = ..()
|
||||
icon_state = "[base_icon_state][used ? "-used" : ""]"
|
||||
|
||||
/obj/item/eyesnatcher/attack(mob/living/carbon/human/victim, mob/living/user, params)
|
||||
if(!istype(victim) || !victim.Adjacent(user)) //No TK use
|
||||
return ..()
|
||||
|
||||
var/obj/item/organ/eyes/eyeballies = victim.getorganslot(ORGAN_SLOT_EYES)
|
||||
var/obj/item/bodypart/head/head = victim.get_bodypart(BODY_ZONE_HEAD)
|
||||
|
||||
if(!eyeballies || victim.is_eyes_covered())
|
||||
return ..()
|
||||
|
||||
if((head && head.eyes != eyeballies) || eyeballies.zone != BODY_ZONE_HEAD)
|
||||
to_chat(user, span_warning("You don't know how to apply [src] to the abomination that [victim] is!"))
|
||||
return ..()
|
||||
|
||||
if(!head || !istype(head))
|
||||
return ..()
|
||||
|
||||
user.do_attack_animation(victim, used_item = src)
|
||||
victim.visible_message(span_warning("[user] presses [src] against [victim]'s skull!"), span_userdanger("[user] presses [src] against your skull!"))
|
||||
if(!do_after(user, 5 SECONDS, target = victim, extra_checks = CALLBACK(src, .proc/eyeballs_exist, eyeballies, head, victim)))
|
||||
return
|
||||
|
||||
to_chat(victim, span_userdanger("You feel [src] pushing at your skull!"))
|
||||
to_chat(user, span_notice("You apply more pressure to [src]."))
|
||||
if(!do_after(user, 5 SECONDS, target = victim, extra_checks = CALLBACK(src, .proc/eyeballs_exist, eyeballies, head, victim)))
|
||||
return
|
||||
|
||||
var/datum/wound/blunt/severe/severe_wound_type = /datum/wound/blunt/severe
|
||||
var/datum/wound/blunt/critical/critical_wound_type = /datum/wound/blunt/critical
|
||||
victim.apply_damage(20, BRUTE, BODY_ZONE_HEAD, wound_bonus = rand(initial(severe_wound_type.threshold_minimum), initial(critical_wound_type.threshold_minimum) + 10))
|
||||
victim.visible_message(
|
||||
span_danger("[src] pierces through [victim]'s skull, horribly mutilating their eyes!"),
|
||||
span_userdanger("Something penetrates your skull, horribly mutilating your eyes! Holy fuck!"),
|
||||
span_hear("You hear a sickening sound of metal piercing flesh!")
|
||||
)
|
||||
eyeballies.applyOrganDamage(eyeballies.maxHealth)
|
||||
victim.emote("scream")
|
||||
playsound(victim, "sound/effects/wounds/crackandbleed.ogg", 100)
|
||||
log_combat(user, victim, "pierced skull of", src)
|
||||
|
||||
if(!do_after(user, 5 SECONDS, target = victim, extra_checks = CALLBACK(src, .proc/eyeballs_exist, eyeballies, head, victim)))
|
||||
return
|
||||
|
||||
if(!HAS_TRAIT(victim, TRAIT_BLIND))
|
||||
to_chat(victim, span_userdanger("You suddenly go blind!"))
|
||||
|
||||
to_chat(user, span_notice("You successfully extract [victim]'s eyeballs using [src]."))
|
||||
victim.emote("scream")
|
||||
playsound(victim, 'sound/surgery/retractor2.ogg', 100, TRUE)
|
||||
playsound(victim, 'sound/effects/pop.ogg', 100, TRAIT_MUTE)
|
||||
eyeballies.Remove(victim)
|
||||
eyeballies.forceMove(get_turf(victim))
|
||||
used = TRUE
|
||||
desc += " It has been used up."
|
||||
update_icon()
|
||||
|
||||
/obj/item/eyesnatcher/proc/eyeballs_exist(obj/item/organ/eyes/eyeballies, obj/item/bodypart/head, mob/living/carbon/human/victim)
|
||||
if(!eyeballies || QDELETED(eyeballies))
|
||||
return FALSE
|
||||
|
||||
if(!head || QDELETED(head))
|
||||
return FALSE
|
||||
|
||||
if(!victim || QDELETED(victim))
|
||||
return FALSE
|
||||
|
||||
if(eyeballies.owner != victim)
|
||||
return FALSE
|
||||
|
||||
if(head.owner != victim || head.eyes != eyes)
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
@@ -0,0 +1,293 @@
|
||||
/datum/traitor_objective_category/kidnapping
|
||||
name = "Kidnap Personnel"
|
||||
objectives = list( //Similar weights to destroy heirloom objectives
|
||||
list(
|
||||
/datum/traitor_objective/kidnapping/common = 20,
|
||||
/datum/traitor_objective/kidnapping/less_common = 1,
|
||||
) = 4,
|
||||
/datum/traitor_objective/kidnapping/uncommon = 3,
|
||||
/datum/traitor_objective/kidnapping/rare = 2,
|
||||
/datum/traitor_objective/kidnapping/captain = 1
|
||||
)
|
||||
|
||||
/datum/traitor_objective/kidnapping
|
||||
name = "Kidnap %TARGET% the %JOB TITLE% and deliver them to %AREA%"
|
||||
description = "%TARGET% holds extremely important information regarding secret NT projects - and you'll need to kidnap and deliver them to %AREA%, where our transport pod will be waiting. \
|
||||
You'll get additional reward if %TARGET% is delivered alive."
|
||||
|
||||
abstract_type = /datum/traitor_objective/kidnapping
|
||||
|
||||
//this is a prototype so this progression is for all basic level kill objectives
|
||||
progression_reward = list(2 MINUTES, 4 MINUTES)
|
||||
telecrystal_reward = list(1, 2)
|
||||
|
||||
/// The jobs that this objective is targetting.
|
||||
var/list/target_jobs
|
||||
/// The person we need to kidnap
|
||||
var/mob/living/victim
|
||||
/// Area that the victim needs to be delivered to
|
||||
var/area/dropoff_area
|
||||
/// Have we called the pod yet?
|
||||
var/pod_called = FALSE
|
||||
/// How much TC do we get from sending the target alive
|
||||
var/alive_bonus = 0
|
||||
/// All stripped victims belongings
|
||||
var/list/victim_belogings = list()
|
||||
|
||||
/datum/traitor_objective/kidnapping/common
|
||||
progression_maximum = 30 MINUTES
|
||||
target_jobs = list(
|
||||
// Medical
|
||||
/datum/job/doctor,
|
||||
/datum/job/paramedic,
|
||||
/datum/job/psychologist,
|
||||
/datum/job/chemist,
|
||||
// Service
|
||||
/datum/job/clown,
|
||||
/datum/job/botanist,
|
||||
/datum/job/janitor,
|
||||
/datum/job/mime,
|
||||
/datum/job/lawyer,
|
||||
/datum/job/chaplain,
|
||||
/datum/job/bartender,
|
||||
/datum/job/curator,
|
||||
// Cargo
|
||||
/datum/job/cargo_technician,
|
||||
// Science
|
||||
/datum/job/geneticist,
|
||||
/datum/job/roboticist,
|
||||
// Engineering
|
||||
/datum/job/station_engineer,
|
||||
/datum/job/atmospheric_technician,
|
||||
)
|
||||
|
||||
/datum/traitor_objective/kidnapping/less_common
|
||||
progression_maximum = 15 MINUTES
|
||||
target_jobs = list(
|
||||
/datum/job/assistant
|
||||
)
|
||||
|
||||
/datum/traitor_objective/kidnapping/uncommon //Hard to fish out victims
|
||||
progression_maximum = 45 MINUTES
|
||||
target_jobs = list(
|
||||
// Medical
|
||||
/datum/job/virologist,
|
||||
// Cargo
|
||||
/datum/job/quartermaster,
|
||||
/datum/job/shaft_miner,
|
||||
// Service
|
||||
/datum/job/cook,
|
||||
// Science
|
||||
/datum/job/scientist,
|
||||
)
|
||||
|
||||
progression_reward = list(4 MINUTES, 8 MINUTES)
|
||||
telecrystal_reward = list(1, 2)
|
||||
alive_bonus = 1
|
||||
|
||||
/datum/traitor_objective/kidnapping/rare
|
||||
progression_minimum = 15 MINUTES
|
||||
progression_maximum = 60 MINUTES
|
||||
target_jobs = list(
|
||||
// Security
|
||||
/datum/job/security_officer,
|
||||
/datum/job/warden,
|
||||
/datum/job/detective,
|
||||
// Heads of staff
|
||||
/datum/job/head_of_personnel,
|
||||
/datum/job/chief_medical_officer,
|
||||
/datum/job/research_director,
|
||||
)
|
||||
|
||||
progression_reward = list(8 MINUTES, 12 MINUTES)
|
||||
telecrystal_reward = list(1, 2)
|
||||
alive_bonus = 2
|
||||
|
||||
/datum/traitor_objective/kidnapping/captain
|
||||
progression_minimum = 30 MINUTES
|
||||
target_jobs = list(
|
||||
/datum/job/head_of_security,
|
||||
/datum/job/captain
|
||||
)
|
||||
|
||||
progression_reward = list(12 MINUTES, 16 MINUTES)
|
||||
telecrystal_reward = list(2, 3)
|
||||
alive_bonus = 2
|
||||
|
||||
/datum/traitor_objective/kidnapping/generate_objective(datum/mind/generating_for, list/possible_duplicates)
|
||||
var/list/possible_targets = list()
|
||||
for(var/datum/mind/possible_target as anything in get_crewmember_minds())
|
||||
if(possible_target == generating_for)
|
||||
continue
|
||||
|
||||
if(!ishuman(possible_target.current))
|
||||
continue
|
||||
|
||||
if(possible_target.current.stat == DEAD)
|
||||
continue
|
||||
|
||||
if(HAS_TRAIT(possible_target, TRAIT_HAS_BEEN_KIDNAPPED))
|
||||
continue
|
||||
|
||||
if(possible_target.has_antag_datum(/datum/antagonist/traitor))
|
||||
continue
|
||||
|
||||
if(!(possible_target.assigned_role.type in target_jobs))
|
||||
continue
|
||||
|
||||
possible_targets += possible_target
|
||||
|
||||
for(var/datum/traitor_objective/kidnapping/objective as anything in possible_duplicates)
|
||||
possible_targets -= objective.victim.mind
|
||||
|
||||
if(!length(possible_targets))
|
||||
return FALSE
|
||||
|
||||
var/datum/mind/target_mind = pick(possible_targets)
|
||||
victim = target_mind.current
|
||||
AddComponent(/datum/component/traitor_objective_register, victim, fail_signals = COMSIG_PARENT_QDELETING)
|
||||
var/list/possible_areas = GLOB.the_station_areas.Copy()
|
||||
for(var/area/possible_area as anything in possible_areas)
|
||||
if(istype(possible_area, /area/station/hallway) || istype(possible_area, /area/station/security) || initial(possible_area.outdoors))
|
||||
possible_areas -= possible_area
|
||||
|
||||
dropoff_area = pick(possible_areas)
|
||||
replace_in_name("%TARGET%", target_mind.name)
|
||||
replace_in_name("%JOB TITLE%", target_mind.assigned_role.title)
|
||||
replace_in_name("%AREA%", initial(dropoff_area.name))
|
||||
return TRUE
|
||||
|
||||
/datum/traitor_objective/kidnapping/ungenerate_objective()
|
||||
victim = null
|
||||
dropoff_area = null
|
||||
|
||||
/datum/traitor_objective/kidnapping/generate_ui_buttons(mob/user)
|
||||
var/list/buttons = list()
|
||||
if(!pod_called)
|
||||
buttons += add_ui_button("Call Extraction Pod", "Pressing this will call down an extraction pod.", "rocket", "call_pod")
|
||||
return buttons
|
||||
|
||||
/datum/traitor_objective/kidnapping/ui_perform_action(mob/living/user, action)
|
||||
. = ..()
|
||||
switch(action)
|
||||
if("call_pod")
|
||||
if(pod_called)
|
||||
return
|
||||
var/area/user_area = get_area(user)
|
||||
var/area/victim_area = get_area(victim)
|
||||
|
||||
if(user_area.type != dropoff_area)
|
||||
to_chat(user, span_warning("You must be in [initial(dropoff_area.name)] to call the extraction pod."))
|
||||
return
|
||||
|
||||
if(victim_area.type != dropoff_area)
|
||||
to_chat(user, span_warning("[victim.real_name] must be in [initial(dropoff_area.name)] for you to call the extraction pod."))
|
||||
return
|
||||
|
||||
call_pod(user)
|
||||
|
||||
/datum/traitor_objective/kidnapping/proc/call_pod(mob/living/user)
|
||||
pod_called = TRUE
|
||||
var/obj/structure/closet/supplypod/extractionpod/new_pod = new()
|
||||
RegisterSignal(new_pod, COMSIG_ATOM_ENTERED, .proc/enter_check)
|
||||
new /obj/effect/pod_landingzone(get_turf(user), new_pod)
|
||||
|
||||
/datum/traitor_objective/kidnapping/proc/enter_check(obj/structure/closet/supplypod/extractionpod/source, entered_atom)
|
||||
if(!istype(source))
|
||||
CRASH("Kidnapping objective's enter_check called with source being not an extraction pod: [source ? source.type : "N/A"]")
|
||||
|
||||
if(!ishuman(entered_atom))
|
||||
return
|
||||
|
||||
var/mob/living/carbon/human/sent_mob = entered_atom
|
||||
|
||||
if(sent_mob.mind)
|
||||
ADD_TRAIT(sent_mob.mind, TRAIT_HAS_BEEN_KIDNAPPED, TRAIT_GENERIC)
|
||||
|
||||
for(var/obj/item/belonging in sent_mob)
|
||||
if(belonging == sent_mob.get_item_by_slot(ITEM_SLOT_ICLOTHING) || belonging == sent_mob.get_item_by_slot(ITEM_SLOT_FEET))
|
||||
continue
|
||||
|
||||
sent_mob.transferItemToLoc(belonging)
|
||||
victim_belogings.Add(belonging)
|
||||
|
||||
var/datum/bank_account/cargo_account = SSeconomy.get_dep_account(ACCOUNT_CAR)
|
||||
|
||||
if(cargo_account) //Just in case
|
||||
cargo_account.adjust_money(-min(rand(1000, 3000), cargo_account.account_balance)) //Not so much, especially for competent cargo. Plus this can't be mass-triggered like it has been done with contractors
|
||||
|
||||
priority_announce("One of your crew was captured by a rival organisation - we've needed to pay their ransom to bring them back. As is policy we've taken a portion of the station's funds to offset the overall cost.", "Nanotrasen Asset Protection", has_important_message = TRUE)
|
||||
|
||||
addtimer(CALLBACK(src, .proc/handle_victim, sent_mob), 1.5 SECONDS)
|
||||
|
||||
if(sent_mob != victim)
|
||||
fail_objective(penalty_cost = telecrystal_penalty)
|
||||
source.startExitSequence(source)
|
||||
return
|
||||
|
||||
if(sent_mob.stat != DEAD)
|
||||
telecrystal_reward += alive_bonus
|
||||
|
||||
succeed_objective()
|
||||
source.startExitSequence(source)
|
||||
|
||||
/datum/traitor_objective/kidnapping/proc/handle_victim(mob/living/carbon/human/sent_mob)
|
||||
addtimer(CALLBACK(src, .proc/return_victim, sent_mob), 3 MINUTES)
|
||||
if(sent_mob.stat == DEAD)
|
||||
return
|
||||
|
||||
sent_mob.flash_act()
|
||||
sent_mob.adjust_timed_status_effect(10 SECONDS, /datum/status_effect/confusion)
|
||||
sent_mob.adjust_timed_status_effect(10 SECONDS, /datum/status_effect/dizziness)
|
||||
sent_mob.blur_eyes(5 SECONDS)
|
||||
to_chat(sent_mob, span_hypnophrase(span_reallybig("A million voices echo in your head... <i>\"Your mind held many valuable secrets - \
|
||||
we thank you for providing them. Your value is expended, and you will be ransomed back to your station. We always get paid, \
|
||||
so it's only a matter of time before we ship you back...\"</i>")))
|
||||
|
||||
/datum/traitor_objective/kidnapping/proc/return_victim(mob/living/carbon/human/sent_mob)
|
||||
if(!sent_mob || QDELETED(sent_mob)) //suicided and qdeleted themselves
|
||||
return
|
||||
|
||||
var/list/possible_turfs = list()
|
||||
for(var/turf/open/open_turf in dropoff_area)
|
||||
if(open_turf.is_blocked_turf() || isspaceturf(open_turf))
|
||||
continue
|
||||
possible_turfs += open_turf
|
||||
|
||||
if(!LAZYLEN(possible_turfs))
|
||||
var/turf/new_turf = get_safe_random_station_turf()
|
||||
if(!new_turf) //SOMEHOW
|
||||
to_chat(sent_mob, span_hypnophrase(span_reallybig("A million voices echo in your head... <i>\"Seems where you got sent here from won't \
|
||||
be able to handle our pod... You will die here instead.\"</i></span>")))
|
||||
if (sent_mob.can_heartattack())
|
||||
sent_mob.set_heartattack(TRUE)
|
||||
return
|
||||
|
||||
possible_turfs += new_turf
|
||||
|
||||
var/obj/structure/closet/supplypod/return_pod = new()
|
||||
return_pod.bluespace = TRUE
|
||||
return_pod.explosionSize = list(0,0,0,0)
|
||||
return_pod.style = STYLE_SYNDICATE
|
||||
|
||||
do_sparks(8, FALSE, sent_mob)
|
||||
sent_mob.visible_message(span_notice("[sent_mob] vanishes!"))
|
||||
for(var/obj/item/belonging in sent_mob)
|
||||
if(belonging == sent_mob.get_item_by_slot(ITEM_SLOT_ICLOTHING) || belonging == sent_mob.get_item_by_slot(ITEM_SLOT_FEET))
|
||||
continue
|
||||
|
||||
sent_mob.transferItemToLoc(belonging)
|
||||
|
||||
for(var/obj/item/belonging in victim_belogings)
|
||||
belonging.forceMove(return_pod)
|
||||
|
||||
sent_mob.forceMove(return_pod)
|
||||
sent_mob.flash_act()
|
||||
sent_mob.adjust_timed_status_effect(10 SECONDS, /datum/status_effect/confusion)
|
||||
sent_mob.adjust_timed_status_effect(10 SECONDS, /datum/status_effect/dizziness)
|
||||
sent_mob.blur_eyes(5 SECONDS)
|
||||
|
||||
new /obj/effect/pod_landingzone(pick(possible_turfs), return_pod)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
/datum/traitor_objective_category/locate_weakpoint
|
||||
name = "Locate And Destroy Weakpoint"
|
||||
objectives = list(
|
||||
/datum/traitor_objective/locate_weakpoint = 1,
|
||||
)
|
||||
|
||||
/datum/traitor_objective/locate_weakpoint
|
||||
name = "Triangulate station's structural weakpoint and detonate an explosive charge nearby."
|
||||
description = "You will be given a handheld device that you'll need to use in %AREA1% and %AREA2% in order to triangulate the station's structural weakpoint and detonate an explosive charge there. Warning: Once you start scanning either one of the areas, station's AI will be alerted."
|
||||
|
||||
progression_minimum = 45 MINUTES
|
||||
progression_reward = list(15 MINUTES, 20 MINUTES)
|
||||
telecrystal_reward = list(3, 5)
|
||||
|
||||
var/progression_objectives_minimum = 20 MINUTES
|
||||
|
||||
/// Reference to the weakpoint locator(if we sent one)
|
||||
var/obj/item/weakpoint_locator/locator
|
||||
/// Reference to the ES8 explosive (if we sent one)
|
||||
var/obj/item/grenade/c4/es8/shatter_charge
|
||||
/// Locations we have to use the locator in
|
||||
var/list/triangulation_locations
|
||||
/// Have we located the weakpoint yet?
|
||||
var/weakpoint_found = FALSE
|
||||
|
||||
/datum/traitor_objective/locate_weakpoint/generate_objective(datum/mind/generating_for, list/possible_duplicates)
|
||||
////if(handler.get_completion_progression(/datum/traitor_objective) < progression_objectives_minimum)
|
||||
//// return FALSE
|
||||
|
||||
if(!SStraitor.station_weakpoints || !LAZYLEN(SStraitor.station_weakpoints)) //This means that the weakpoint has already been hit
|
||||
return FALSE
|
||||
|
||||
var/area/weakpoint_area1 = SStraitor.station_weakpoints[1]
|
||||
var/area/weakpoint_area2 = SStraitor.station_weakpoints[2]
|
||||
replace_in_name("%AREA1%", initial(weakpoint_area1.name))
|
||||
replace_in_name("%AREA2%", initial(weakpoint_area2.name))
|
||||
triangulation_locations = list(SStraitor.station_weakpoints[1] = TRUE, SStraitor.station_weakpoints[2] = TRUE)
|
||||
RegisterSignal(generating_for, COMSIG_GLOB_TRAITOR_OBJECTIVE_COMPLETED, .proc/on_global_obj_completed)
|
||||
return TRUE
|
||||
|
||||
/datum/traitor_objective/locate_weakpoint/proc/on_global_obj_completed(datum/source, datum/traitor_objective/objective)
|
||||
SIGNAL_HANDLER
|
||||
if(istype(objective, /datum/traitor_objective/locate_weakpoint))
|
||||
fail_objective()
|
||||
|
||||
/datum/traitor_objective/locate_weakpoint/generate_ui_buttons(mob/user)
|
||||
var/list/buttons = list()
|
||||
if(!locator)
|
||||
buttons += add_ui_button("", "Pressing this will materialize a weakpoint locator in your hand.", "globe", "locator")
|
||||
if(weakpoint_found && !shatter_charge)
|
||||
buttons += add_ui_button("", "Pressing this will materialize an ES8 explosive charge in your hand.", "bomb", "shatter_charge")
|
||||
return buttons
|
||||
|
||||
/datum/traitor_objective/locate_weakpoint/ui_perform_action(mob/living/user, action)
|
||||
. = ..()
|
||||
switch(action)
|
||||
if("locator")
|
||||
if(locator)
|
||||
return
|
||||
locator = new(user.drop_location())
|
||||
user.put_in_hands(locator)
|
||||
locator.balloon_alert(user, "the weakpoint locator materializes in your hand")
|
||||
if("shatter_charge")
|
||||
if(shatter_charge)
|
||||
return
|
||||
shatter_charge = new(user.drop_location())
|
||||
user.put_in_hands(shatter_charge)
|
||||
shatter_charge.balloon_alert(user, "the ES8 charge materializes in your hand")
|
||||
|
||||
/datum/traitor_objective/locate_weakpoint/proc/weakpoint_located()
|
||||
description = "Structural weakpoint has been located in %AREA%. Detonate an ES8 explosive charge there to create a shockwave that will severely damage the station."
|
||||
var/area/weakpoint_area = SStraitor.station_weakpoints[3]
|
||||
replace_in_name("%AREA%", initial(weakpoint_area.name))
|
||||
weakpoint_found = TRUE
|
||||
|
||||
/datum/traitor_objective/locate_weakpoint/proc/create_shockwave(center_x, center_y, center_z)
|
||||
var/severity = list(EXPLODE_LIGHT, EXPLODE_LIGHT, EXPLODE_LIGHT, EXPLODE_LIGHT, EXPLODE_HEAVY, EXPLODE_HEAVY, EXPLODE_DEVASTATE) //Can't use pick_weight because explode defines are numbers
|
||||
var/wave_amount = rand(5, 8)
|
||||
var/list/bombed_turfs = list()
|
||||
for(var/i in 1 to wave_amount)
|
||||
var/wave_angle = rand(-10, 10) + 360 / wave_amount * i
|
||||
var/wave_distance = rand(17, 25)
|
||||
var/turf/tentacle_ending = locate(clamp(center_x + round(cos(wave_angle) * wave_distance), 1, world.maxx), clamp(center_y + round(sin(wave_angle) * wave_distance), 1, world.maxy), center_z)
|
||||
if(!tentacle_ending) //WUT
|
||||
continue
|
||||
|
||||
var/turf/epicenter = locate(center_x, center_y, center_z)
|
||||
for(var/turf/line_turf in get_line(epicenter, tentacle_ending))
|
||||
for(var/turf/bomb_turf in range(1, line_turf))
|
||||
if((bomb_turf in bombed_turfs) || bomb_turf == epicenter)
|
||||
continue
|
||||
bombed_turfs += bomb_turf
|
||||
var/turf_severity = pick(severity)
|
||||
EX_ACT(line_turf, turf_severity)
|
||||
for(var/atom/victim in line_turf)
|
||||
EX_ACT(victim, turf_severity - 1)
|
||||
|
||||
explosion(tentacle_ending, devastation_range = 1, heavy_impact_range = 3, light_impact_range = 5, explosion_cause = src)
|
||||
|
||||
priority_announce(
|
||||
"Attention crew, it appears that a high-power explosive charge has been detonated in your station's weakpoint, causing severe structural damage.",
|
||||
"[command_name()] High-Priority Update"
|
||||
)
|
||||
|
||||
SStraitor.station_weakpoints = null //Gone
|
||||
succeed_objective()
|
||||
|
||||
/obj/item/weakpoint_locator
|
||||
name = "structural weakpoint locator"
|
||||
desc = "A device that can triangulate station's structural weakpoint. It has to be used in %AREA1% and %AREA2% in order to triangulate the weakpoint. Warning: station's AI will be notified as soon as the process starts!"
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "weakpoint_locator"
|
||||
inhand_icon_state = "weakpoint_locator"
|
||||
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
|
||||
throwforce = 0
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
throw_speed = 3
|
||||
throw_range = 5
|
||||
|
||||
/obj/item/weakpoint_locator/Initialize(mapload)
|
||||
. = ..()
|
||||
var/area/weakpoint_area1 = SStraitor.station_weakpoints[1]
|
||||
var/area/weakpoint_area2 = SStraitor.station_weakpoints[2]
|
||||
desc = replacetext(desc, "%AREA1%", initial(weakpoint_area1.name))
|
||||
desc = replacetext(desc, "%AREA2%", initial(weakpoint_area2.name))
|
||||
|
||||
/obj/item/weakpoint_locator/attack_self(mob/living/user, modifiers)
|
||||
. = ..()
|
||||
if(!istype(user) || loc != user || !user.mind) //No TK cheese
|
||||
return
|
||||
|
||||
if(!user.mind.has_antag_datum(/datum/antagonist/traitor))
|
||||
to_chat(user, span_warning("You have zero clue how to use [src]."))
|
||||
return
|
||||
|
||||
var/datum/traitor_objective/locate_weakpoint/objective = get_weakpoint_objective(user)
|
||||
if(!objective || objective.objective_state == OBJECTIVE_STATE_INACTIVE)
|
||||
to_chat(user, span_warning("Your time to use [src] has not come yet."))
|
||||
return
|
||||
|
||||
var/area/user_area = get_area(user)
|
||||
if(!(user_area.type in objective.triangulation_locations))
|
||||
balloon_alert(user, "invalid area!")
|
||||
playsound(user, 'sound/machines/buzz-sigh.ogg', 30, TRUE)
|
||||
return
|
||||
|
||||
if(!objective.triangulation_locations[user_area.type])
|
||||
balloon_alert(user, "already scanned here!")
|
||||
playsound(user, 'sound/machines/buzz-sigh.ogg', 30, TRUE)
|
||||
return
|
||||
|
||||
user.visible_message(span_danger("[user] presses a few buttons on [src] and it starts ominously beeping!"), span_notice("You activate [src] and start scanning the area. Do not exit [get_area_name(user, TRUE)] until the scan finishes!"))
|
||||
playsound(user, 'sound/machines/triple_beep.ogg', 30, TRUE)
|
||||
var/alertstr = span_userdanger("Network Alert: Station network probing attempt detected[user_area?" in [get_area_name(user, TRUE)]":". Unable to pinpoint location"].")
|
||||
for(var/mob/living/silicon/ai/ai_player in GLOB.player_list)
|
||||
to_chat(ai_player, alertstr)
|
||||
|
||||
if(!do_after(user, 3 SECONDS, src, IGNORE_USER_LOC_CHANGE | IGNORE_TARGET_LOC_CHANGE | IGNORE_HELD_ITEM | IGNORE_INCAPACITATED | IGNORE_SLOWDOWNS, extra_checks = CALLBACK(src, .proc/scan_checks, user, user_area, objective)))
|
||||
playsound(user, 'sound/machines/buzz-sigh.ogg', 30, TRUE)
|
||||
return
|
||||
|
||||
playsound(user, 'sound/machines/ding.ogg', 100, TRUE)
|
||||
objective.triangulation_locations[user_area.type] = FALSE
|
||||
for(var/area/scan_area in objective.triangulation_locations)
|
||||
if(objective.triangulation_locations[scan_area])
|
||||
say("Next scanning location is [initial(scan_area.name)]")
|
||||
return
|
||||
|
||||
var/area/weakpoint_location = SStraitor.station_weakpoints[3]
|
||||
to_chat(user, span_notice("Scan finished. Structural weakpoint located in [initial(weakpoint_location.name)]."))
|
||||
objective.weakpoint_located()
|
||||
|
||||
/obj/item/weakpoint_locator/proc/scan_checks(mob/living/user, area/user_area, datum/traitor_objective/locate_weakpoint/parent_objective)
|
||||
if(get_area(user) != user_area)
|
||||
return FALSE
|
||||
|
||||
if(parent_objective.objective_state == OBJECTIVE_STATE_FAILED || parent_objective.objective_state == OBJECTIVE_STATE_INVALID)
|
||||
return FALSE
|
||||
|
||||
var/atom/current_loc = loc
|
||||
while(!isturf(current_loc) && !ismob(current_loc))
|
||||
current_loc = current_loc.loc
|
||||
|
||||
if(current_loc != user)
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
/obj/item/weakpoint_locator/proc/get_weakpoint_objective(mob/living/user)
|
||||
if(!user.mind)
|
||||
return
|
||||
|
||||
for(var/datum/traitor_objective/locate_weakpoint/weakpoint_objecitve in SStraitor.taken_objectives_by_type[/datum/traitor_objective/locate_weakpoint])
|
||||
var/datum/uplink_handler/handler = weakpoint_objecitve.handler
|
||||
if(handler.owner != user.mind)
|
||||
continue
|
||||
|
||||
return weakpoint_objecitve
|
||||
|
||||
/obj/item/grenade/c4/es8
|
||||
name = "ES8 explosive charge"
|
||||
desc = "A high-power explosive charge designed to create a shockwave in a structural weakpoint of the station."
|
||||
|
||||
icon_state = "plasticx40"
|
||||
inhand_icon_state = "plasticx4"
|
||||
worn_icon_state = "x4"
|
||||
|
||||
boom_sizes = list(3, 6, 9)
|
||||
|
||||
/// Reference to user's objective
|
||||
var/datum/traitor_objective/locate_weakpoint/objective
|
||||
|
||||
/obj/item/grenade/c4/es8/Destroy()
|
||||
objective = null
|
||||
return ..()
|
||||
|
||||
/obj/item/grenade/c4/es8/afterattack(atom/movable/target, mob/user, flag)
|
||||
if(!user.mind)
|
||||
return
|
||||
|
||||
if(!user.mind.has_antag_datum(/datum/antagonist/traitor))
|
||||
to_chat(user, span_warning("You can't seem to find a way to detonate the charge."))
|
||||
return
|
||||
|
||||
for(var/datum/traitor_objective/locate_weakpoint/weakpoint_objecitve in SStraitor.taken_objectives_by_type[/datum/traitor_objective/locate_weakpoint])
|
||||
var/datum/uplink_handler/handler = weakpoint_objecitve.handler
|
||||
if(handler.owner != user.mind)
|
||||
continue
|
||||
objective = weakpoint_objecitve
|
||||
|
||||
if(!objective || objective.objective_state == OBJECTIVE_STATE_INACTIVE)
|
||||
to_chat(user, span_warning("You don't think it would be wise to use [src]."))
|
||||
return
|
||||
|
||||
var/area/target_area = get_area(target)
|
||||
if (target_area.type != SStraitor.station_weakpoints[3])
|
||||
var/area/weakpoint_area = SStraitor.station_weakpoints[3]
|
||||
to_chat(user, span_warning("[src] can only be detonated in [initial(weakpoint_area.name)]."))
|
||||
return
|
||||
|
||||
return ..()
|
||||
|
||||
/obj/item/grenade/c4/es8/detonate(mob/living/lanced_by)
|
||||
var/area/target_area = get_area(target)
|
||||
if (target_area.type != SStraitor.station_weakpoints[3])
|
||||
var/obj/item/grenade/c4/es8/new_bomb = new(target.drop_location())
|
||||
new_bomb.balloon_alert_to_viewers("invalid location!")
|
||||
target.cut_overlay(plastic_overlay, TRUE)
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
if(!objective)
|
||||
return
|
||||
|
||||
objective.create_shockwave(target.x, target.y, target.z)
|
||||
return ..()
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "Sleeper Protocol"
|
||||
objectives = list(
|
||||
/datum/traitor_objective/sleeper_protocol = 1,
|
||||
/datum/traitor_objective/sleeper_protocol/everybody = 1,
|
||||
)
|
||||
|
||||
|
||||
@@ -17,12 +18,15 @@
|
||||
JOB_MEDICAL_DOCTOR,
|
||||
JOB_PARAMEDIC,
|
||||
JOB_VIROLOGIST,
|
||||
JOB_ROBOTICIST,
|
||||
)
|
||||
|
||||
var/obj/item/disk/surgery/sleeper_protocol/disk
|
||||
|
||||
var/mob/living/current_registered_mob
|
||||
|
||||
var/inverted_limitation = FALSE
|
||||
|
||||
/datum/traitor_objective/sleeper_protocol/generate_ui_buttons(mob/user)
|
||||
var/list/buttons = list()
|
||||
if(!disk)
|
||||
@@ -46,7 +50,9 @@
|
||||
|
||||
/datum/traitor_objective/sleeper_protocol/generate_objective(datum/mind/generating_for, list/possible_duplicates)
|
||||
var/datum/job/job = generating_for.assigned_role
|
||||
if(!(job.title in limited_to))
|
||||
if(!(job.title in limited_to) && !inverted_limitation)
|
||||
return FALSE
|
||||
if((job.title in limited_to) && inverted_limitation)
|
||||
return FALSE
|
||||
AddComponent(/datum/component/traitor_objective_mind_tracker, generating_for, \
|
||||
signals = list(COMSIG_MOB_SURGERY_STEP_SUCCESS = .proc/on_surgery_success))
|
||||
@@ -87,7 +93,7 @@
|
||||
|
||||
/datum/surgery_step/brainwash/sleeper_agent
|
||||
time = 25 SECONDS
|
||||
var/list/possible_objectives = list(
|
||||
var/static/list/possible_objectives = list(
|
||||
"You love the Syndicate",
|
||||
"Do not trust Nanotrasen",
|
||||
"The Captain is a lizardperson",
|
||||
@@ -111,3 +117,11 @@
|
||||
if(!.)
|
||||
return
|
||||
target.gain_trauma(new /datum/brain_trauma/mild/phobia/conspiracies(), TRAUMA_RESILIENCE_LOBOTOMY)
|
||||
|
||||
/datum/traitor_objective/sleeper_protocol/everybody //Much harder for non-med and non-robo
|
||||
|
||||
progression_minimum = 30 MINUTES
|
||||
progression_reward = list(15 MINUTES, 20 MINUTES)
|
||||
telecrystal_reward = list(2, 3)
|
||||
|
||||
inverted_limitation = TRUE
|
||||
|
||||
@@ -78,6 +78,10 @@
|
||||
bluespace = TRUE
|
||||
explosionSize = list(0,0,1,2)
|
||||
delays = list(POD_TRANSIT = 25, POD_FALLING = 4, POD_OPENING = 30, POD_LEAVING = 30)
|
||||
reversing = TRUE
|
||||
stay_after_drop = TRUE
|
||||
leavingSound = 'sound/effects/podwoosh.ogg'
|
||||
reverse_option_list = list("Mobs"=TRUE,"Objects"=FALSE,"Anchored"=FALSE,"Underfloor"=FALSE,"Wallmounted"=FALSE,"Floors"=FALSE,"Walls"=FALSE, "Mecha"=FALSE)
|
||||
|
||||
/obj/structure/closet/supplypod/centcompod
|
||||
style = STYLE_CENTCOM
|
||||
|
||||
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
@@ -2319,7 +2319,9 @@
|
||||
#include "code\modules\antagonists\traitor\objectives\destroy_item.dm"
|
||||
#include "code\modules\antagonists\traitor\objectives\destroy_machinery.dm"
|
||||
#include "code\modules\antagonists\traitor\objectives\hack_comm_console.dm"
|
||||
#include "code\modules\antagonists\traitor\objectives\kidnapping.dm"
|
||||
#include "code\modules\antagonists\traitor\objectives\kill_pet.dm"
|
||||
#include "code\modules\antagonists\traitor\objectives\locate_weakpoint.dm"
|
||||
#include "code\modules\antagonists\traitor\objectives\sleeper_protocol.dm"
|
||||
#include "code\modules\antagonists\traitor\objectives\steal.dm"
|
||||
#include "code\modules\antagonists\traitor\objectives\final_objective\battlecruiser.dm"
|
||||
|
||||