diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm index f6687b01e25..77b9ba8345e 100644 --- a/_maps/map_files/generic/CentCom.dmm +++ b/_maps/map_files/generic/CentCom.dmm @@ -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{ diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index 24d8417204c..9844097573b 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -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 diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 5690b8424fd..c8b68b61440 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -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" diff --git a/code/controllers/subsystem/traitor.dm b/code/controllers/subsystem/traitor.dm index db9e687e9ec..cbbba49d086 100644 --- a/code/controllers/subsystem/traitor.dm +++ b/code/controllers/subsystem/traitor.dm @@ -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 diff --git a/code/modules/antagonists/traitor/objective_category.dm b/code/modules/antagonists/traitor/objective_category.dm index e64086ad922..dcd41cd8518 100644 --- a/code/modules/antagonists/traitor/objective_category.dm +++ b/code/modules/antagonists/traitor/objective_category.dm @@ -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 diff --git a/code/modules/antagonists/traitor/objectives/eyesnatching.dm b/code/modules/antagonists/traitor/objectives/eyesnatching.dm new file mode 100644 index 00000000000..0e87513eadd --- /dev/null +++ b/code/modules/antagonists/traitor/objectives/eyesnatching.dm @@ -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 diff --git a/code/modules/antagonists/traitor/objectives/kidnapping.dm b/code/modules/antagonists/traitor/objectives/kidnapping.dm new file mode 100644 index 00000000000..b740a72d316 --- /dev/null +++ b/code/modules/antagonists/traitor/objectives/kidnapping.dm @@ -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... \"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...\""))) + +/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... \"Seems where you got sent here from won't \ + be able to handle our pod... You will die here instead.\""))) + 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) + + + diff --git a/code/modules/antagonists/traitor/objectives/locate_weakpoint.dm b/code/modules/antagonists/traitor/objectives/locate_weakpoint.dm new file mode 100644 index 00000000000..e5066b5c146 --- /dev/null +++ b/code/modules/antagonists/traitor/objectives/locate_weakpoint.dm @@ -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 ..() diff --git a/code/modules/antagonists/traitor/objectives/sleeper_protocol.dm b/code/modules/antagonists/traitor/objectives/sleeper_protocol.dm index 92f30bdaa3a..2161346f22e 100644 --- a/code/modules/antagonists/traitor/objectives/sleeper_protocol.dm +++ b/code/modules/antagonists/traitor/objectives/sleeper_protocol.dm @@ -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 diff --git a/code/modules/cargo/supplypod.dm b/code/modules/cargo/supplypod.dm index f5fbf87b976..97c1631c9e1 100644 --- a/code/modules/cargo/supplypod.dm +++ b/code/modules/cargo/supplypod.dm @@ -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 diff --git a/icons/mob/inhands/misc/devices_lefthand.dmi b/icons/mob/inhands/misc/devices_lefthand.dmi index 3c52a437283..55456a6e073 100644 Binary files a/icons/mob/inhands/misc/devices_lefthand.dmi and b/icons/mob/inhands/misc/devices_lefthand.dmi differ diff --git a/icons/mob/inhands/misc/devices_righthand.dmi b/icons/mob/inhands/misc/devices_righthand.dmi index e4c57d5ec55..a274e001eba 100644 Binary files a/icons/mob/inhands/misc/devices_righthand.dmi and b/icons/mob/inhands/misc/devices_righthand.dmi differ diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi index 96e6e0ce242..25f9fb08e47 100644 Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ diff --git a/icons/obj/surgery.dmi b/icons/obj/surgery.dmi index 24dbd2f70b7..410780b105f 100755 Binary files a/icons/obj/surgery.dmi and b/icons/obj/surgery.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 13fa2403358..e378b16fc79 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -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"