diff --git a/code/__DEFINES/pinpointers.dm b/code/__DEFINES/pinpointers.dm new file mode 100644 index 000000000000..80403e54de7f --- /dev/null +++ b/code/__DEFINES/pinpointers.dm @@ -0,0 +1,7 @@ +//I would rather have these in pinpointer.dm, but Malf_Modules.dm is loaded before that file so they need to be here. +#define TRACK_NUKE_DISK 1 //We track the nuclear authentication disk, either to protect it or steal it +#define TRACK_MALF_AI 2 //We track the malfunctioning AI, so we can prevent it from blowing us all up +#define TRACK_INFILTRATOR 3 //We track the Syndicate infiltrator, so we can get back to ship when the nuke's armed +#define TRACK_OPERATIVES 4 //We track the closest operative, so we can regroup when we need to +#define TRACK_ATOM 5 //We track a specified atom, so admins can make us function for events +#define TRACK_COORDINATES 6 //We point towards the specified coordinates on our z-level, so we can navigate diff --git a/code/game/gamemodes/malfunction/Malf_Modules.dm b/code/game/gamemodes/malfunction/Malf_Modules.dm index bae0c8eb6481..76a6de59b026 100644 --- a/code/game/gamemodes/malfunction/Malf_Modules.dm +++ b/code/game/gamemodes/malfunction/Malf_Modules.dm @@ -40,10 +40,8 @@ doomsday_device = DOOM doomsday_device.start() verbs -= /mob/living/silicon/ai/proc/nuke_station - for(var/obj/item/weapon/pinpointer/point in pinpointer_list) - for(var/mob/living/silicon/ai/A in ai_list) - if((A.stat != DEAD) && A.nuking) - point.the_disk = A //The pinpointer now tracks the AI core + for(var/obj/item/weapon/pinpointer/P in pinpointer_list) + P.switch_mode_to(TRACK_MALF_AI) //Pinpointers start tracking the AI wherever it goes /obj/machinery/doomsday_device icon = 'icons/obj/machines/nuke_terminal.dmi' diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index 440d3ac1da61..5f0b9bec7c15 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -296,7 +296,7 @@ gloves = /obj/item/clothing/gloves/combat back = /obj/item/weapon/storage/backpack ears = /obj/item/device/radio/headset/syndicate/alt - l_pocket = /obj/item/weapon/pinpointer/nukeop + l_pocket = /obj/item/weapon/pinpointer/syndicate id = /obj/item/weapon/card/id/syndicate belt = /obj/item/weapon/gun/projectile/automatic/pistol backpack_contents = list(/obj/item/weapon/storage/box/syndie=1) diff --git a/code/game/gamemodes/nuclear/nuclearbomb.dm b/code/game/gamemodes/nuclear/nuclearbomb.dm index 14418fd66faf..f1f35ccf1d6a 100644 --- a/code/game/gamemodes/nuclear/nuclearbomb.dm +++ b/code/game/gamemodes/nuclear/nuclearbomb.dm @@ -358,6 +358,9 @@ var/bomb_set if(safety) if(timing) set_security_level(previous_level) + for(var/obj/item/weapon/pinpointer/syndicate/S in pinpointer_list) + S.switch_mode_to(initial(S.mode)) + S.nuke_warning = FALSE timing = FALSE bomb_set = TRUE detonation_timer = null @@ -374,6 +377,8 @@ var/bomb_set bomb_set = TRUE set_security_level("delta") detonation_timer = world.time + (timer_set * 10) + for(var/obj/item/weapon/pinpointer/syndicate/S in pinpointer_list) + S.switch_mode_to(TRACK_INFILTRATOR) countdown.start() else bomb_set = FALSE diff --git a/code/game/gamemodes/nuclear/pinpointer.dm b/code/game/gamemodes/nuclear/pinpointer.dm index 6878855b9336..dc35428809de 100644 --- a/code/game/gamemodes/nuclear/pinpointer.dm +++ b/code/game/gamemodes/nuclear/pinpointer.dm @@ -1,5 +1,7 @@ +//Pinpointers are used to track atoms from a distance as long as they're on the same z-level. The captain and nuke ops have ones that track the nuclear authentication disk. /obj/item/weapon/pinpointer name = "pinpointer" + desc = "A handheld tracking device that locks onto certain signals." icon = 'icons/obj/device.dmi' icon_state = "pinoff" flags = CONDUCT @@ -8,290 +10,162 @@ item_state = "electronic" throw_speed = 3 throw_range = 7 - materials = list(MAT_METAL=500) - var/obj/item/weapon/disk/nuclear/the_disk = null - var/active = 0 + materials = list(MAT_METAL = 500, MAT_GLASS = 250) + var/active = FALSE + var/atom/movable/target = null //The thing we're searching for + var/atom/movable/constant_target = null //The thing we're always focused on, if we're in the right mode + var/target_x = 0 //The target coordinates if we're tracking those + var/target_y = 0 + var/nuke_warning = FALSE // If we've set off a miniature alarm about an armed nuke + var/mode = TRACK_NUKE_DISK //What are we looking for? /obj/item/weapon/pinpointer/New() ..() pinpointer_list += src /obj/item/weapon/pinpointer/Destroy() - active = 0 - pinpointer_list -= src + STOP_PROCESSING(SSfastprocess, src) return ..() -/obj/item/weapon/pinpointer/attack_self() - if(!active) - active = 1 - workdisk() - usr << "You activate the pinpointer." +/obj/item/weapon/pinpointer/attack_self(mob/living/user) + active = !active + user.visible_message("[user] [active ? "" : "de"]activates their pinpointer.", "You [active ? "" : "de"]activate your pinpointer.") + playsound(user, 'sound/items/Screwdriver2.ogg', 50, 1) + icon_state = "pin[active ? "onnull" : "off"]" + if(active) + START_PROCESSING(SSfastprocess, src) else - active = 0 - icon_state = "pinoff" - usr << "You deactivate the pinpointer." + target = null //Restarting the pinpointer forces a target reset + STOP_PROCESSING(SSfastprocess, src) -/obj/item/weapon/pinpointer/proc/scandisk() - if(!the_disk) - the_disk = locate() - -/obj/item/weapon/pinpointer/proc/point_at(atom/target, spawnself = 1) - if(!active) - return - if(!target) - icon_state = "pinonnull" - return - - var/turf/T = get_turf(target) - var/turf/L = get_turf(src) - - if(T.z != L.z) - icon_state = "pinonnull" - else - setDir(get_dir(L, T)) - switch(get_dist(L, T)) - if(-1) - icon_state = "pinondirect" - if(1 to 8) - icon_state = "pinonclose" - if(9 to 16) - icon_state = "pinonmedium" - if(16 to INFINITY) - icon_state = "pinonfar" - if(spawnself) - spawn(5) - .() - -/obj/item/weapon/pinpointer/proc/workdisk() - scandisk() - point_at(the_disk, 0) - spawn(5) - .() +/obj/item/weapon/pinpointer/attackby(obj/item/I, mob/living/user, params) + if(mode != TRACK_ATOM) + return ..() + user.visible_message("[user] tunes [src] to [I].", "You fine-tune [src]'s tracking to track [I].") + playsound(src, 'sound/machines/click.ogg', 50, 1) + constant_target = I /obj/item/weapon/pinpointer/examine(mob/user) ..() + var/msg = "Its tracking indicator reads " + switch(mode) + if(TRACK_NUKE_DISK) + msg += "\"nuclear_disk\"." + if(TRACK_MALF_AI) + msg += "\"01000001 01001001\"." + if(TRACK_INFILTRATOR) + msg += "\"vasvygengbefuvc\"." + if(TRACK_OPERATIVES) + msg += "\"[target ? "Operative [target]" : "friends"]\"." + if(TRACK_ATOM) + msg += "\"[initial(constant_target.name)]\"." + if(TRACK_COORDINATES) + msg += "\"([target_x], [target_y])\"." + else + msg = "Its tracking indicator is blank." + user << msg for(var/obj/machinery/nuclearbomb/bomb in machines) if(bomb.timing) user << "Extreme danger. Arming signal detected. Time remaining: [bomb.get_time_left()]" - -/obj/item/weapon/pinpointer/advpinpointer - name = "advanced pinpointer" - icon = 'icons/obj/device.dmi' - desc = "A larger version of the normal pinpointer, this unit features a helpful quantum entanglement detection system to locate various objects that do not broadcast a locator signal." - var/mode = 0 // Mode 0 locates disk, mode 1 locates coordinates. - var/turf/location = null - var/obj/target = null - -/obj/item/weapon/pinpointer/advpinpointer/attack_self() +/obj/item/weapon/pinpointer/process() if(!active) - active = 1 - if(mode == 0) - workdisk() - if(mode == 1) - point_at(location) - if(mode == 2) - point_at(target) - usr << "You activate the pinpointer." - else - active = 0 - icon_state = "pinoff" - usr << "You deactivate the pinpointer." - - -/obj/item/weapon/pinpointer/advpinpointer/verb/toggle_mode() - set category = "Object" - set name = "Toggle Pinpointer Mode" - set src in view(1) - - if(usr.stat || usr.restrained() || !usr.canmove) + STOP_PROCESSING(SSfastprocess, src) return + scan_for_target() + point_to_target() + my_god_jc_a_bomb() + addtimer(src, "refresh_target", 50, TRUE) - active = 0 - icon_state = "pinoff" - target=null - location = null - - switch(alert("Please select the mode you want to put the pinpointer in.", "Pinpointer Mode Select", "Location", "Disk Recovery", "Other Signature")) - if("Location") - mode = 1 - - var/locationx = input(usr, "Please input the x coordinate to search for.", "Location?" , "") as num - if(!locationx || !(usr in view(1,src))) - return - var/locationy = input(usr, "Please input the y coordinate to search for.", "Location?" , "") as num - if(!locationy || !(usr in view(1,src))) - return - - var/turf/Z = get_turf(src) - - location = locate(locationx,locationy,Z.z) - - usr << "You set the pinpointer to locate [locationx],[locationy]" - - - return attack_self() - - if("Disk Recovery") - mode = 0 - return attack_self() - - if("Other Signature") - mode = 2 - switch(alert("Search for item signature or DNA fragment?" , "Signature Mode Select" , "" , "Item" , "DNA")) - if("Item") - var/targetitem = input("Select item to search for.", "Item Mode Select","") as null|anything in possible_items - if(!targetitem) - return - target=locate(possible_items[targetitem]) - if(!target) - usr << "Failed to locate [targetitem]!" - return - usr << "You set the pinpointer to locate [targetitem]." - if("DNA") - var/DNAstring = input("Input DNA string to search for." , "Please Enter String." , "") - if(!DNAstring) - return - for(var/mob/living/carbon/C in mob_list) - if(!C.dna) - continue - if(C.dna.unique_enzymes == DNAstring) - target = C - break - - return attack_self() - - -/////////////////////// -//nuke op pinpointers// -/////////////////////// - - -/obj/item/weapon/pinpointer/nukeop - var/mode = 0 //Mode 0 locates disk, mode 1 locates the shuttle - var/obj/docking_port/mobile/home - - -/obj/item/weapon/pinpointer/nukeop/attack_self(mob/user) - if(!active) - active = 1 - var/mode_text = "Authentication Disk Locator mode" - if(!mode) - workdisk() - else - mode_text = "Shuttle Locator mode" - worklocation() - user << "You activate the pinpointer([mode_text])." - else - active = 0 - icon_state = "pinoff" - user << "You deactivate the pinpointer." - - -/obj/item/weapon/pinpointer/nukeop/workdisk() - if(!active) return - if(mode) //Check in case the mode changes while operating - worklocation() +/obj/item/weapon/pinpointer/proc/scan_for_target() //Looks for whatever it's tracking + if(target) + if(isliving(target)) + var/mob/living/L = target + if(L.stat == DEAD) + target = null return - if(bomb_set) //If the bomb is set, lead to the shuttle - mode = 1 //Ensures worklocation() continues to work - worklocation() - playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) //Plays a beep - visible_message("Shuttle Locator mode actived.") //Lets the mob holding it know that the mode has changed - return //Get outta here - scandisk() - if(!the_disk) - icon_state = "pinonnull" - return - setDir(get_dir(src, the_disk)) - switch(get_dist(src, the_disk)) - if(0) - icon_state = "pinondirect" - if(1 to 8) - icon_state = "pinonclose" - if(9 to 16) - icon_state = "pinonmedium" - if(16 to INFINITY) - icon_state = "pinonfar" + switch(mode) + if(TRACK_NUKE_DISK) + var/obj/item/weapon/disk/nuclear/N = locate() + target = N + if(TRACK_MALF_AI) + for(var/V in ai_list) + var/mob/living/silicon/ai/A = V + if(A.nuking) + target = A + for(var/V in apcs_list) + var/obj/machinery/power/apc/A = V + if(A.malfhack && A.occupier) + target = A + if(TRACK_INFILTRATOR) + target = SSshuttle.getShuttle("syndicate") + if(TRACK_OPERATIVES) + var/list/possible_targets = list() + var/turf/here = get_turf(src) + for(var/V in ticker.mode.syndicates) + var/datum/mind/M = V + if(M.current && M.current.stat != DEAD) + possible_targets |= M.current + var/mob/living/closest_operative = get_closest_atom(/mob/living/carbon/human, possible_targets, here) + if(closest_operative) + target = closest_operative + if(TRACK_ATOM) + if(constant_target) + target = constant_target + if(TRACK_COORDINATES) + var/turf/T = get_turf(src) + target = locate(target_x, target_y, T.z) - spawn(5) .() - - -/obj/item/weapon/pinpointer/nukeop/proc/worklocation() +/obj/item/weapon/pinpointer/proc/point_to_target() //If we found what we're looking for, show the distance and direction if(!active) return - if(!mode) - workdisk() + if(!target || (mode == TRACK_ATOM && !constant_target)) + icon_state = "pinon[nuke_warning ? "alert" : ""]null" return - if(!bomb_set) - mode = 0 - workdisk() - playsound(loc, 'sound/machines/twobeep.ogg', 50, 1) - visible_message("Authentication Disk Locator mode actived.") + var/turf/here = get_turf(src) + var/turf/there = get_turf(target) + if(here.z != there.z) + icon_state = "pinon[nuke_warning ? "alert" : ""]null" return - if(!home) - home = SSshuttle.getShuttle("syndicate") - if(!home) - icon_state = "pinonnull" - return - if(loc.z != home.z) //If you are on a different z-level from the shuttle - icon_state = "pinonnull" + if(here == there) + icon_state = "pinon[nuke_warning ? "alert" : ""]direct" else - setDir(get_dir(src, home)) - switch(get_dist(src, home)) - if(0) - icon_state = "pinondirect" + setDir(get_dir(here, there)) + switch(get_dist(here, there)) if(1 to 8) - icon_state = "pinonclose" + icon_state = "pinon[nuke_warning ? "alert" : "close"]" if(9 to 16) - icon_state = "pinonmedium" + icon_state = "pinon[nuke_warning ? "alert" : "medium"]" if(16 to INFINITY) - icon_state = "pinonfar" + icon_state = "pinon[nuke_warning ? "alert" : "far"]" - spawn(5) - .() +/obj/item/weapon/pinpointer/proc/my_god_jc_a_bomb() //If we should get the hell back to the ship + for(var/obj/machinery/nuclearbomb/bomb in machines) + if(bomb.timing) + if(!nuke_warning) + nuke_warning = TRUE + playsound(src, 'sound/items/Nuke_toy_lowpower.ogg', 50, 0) + if(isliving(loc)) + var/mob/living/L = loc + L << "Your [name] vibrates and lets out a tinny alarm. Uh oh." -/obj/item/weapon/pinpointer/operative - name = "operative pinpointer" - icon = 'icons/obj/device.dmi' - desc = "A pinpointer that leads to the first Syndicate operative detected." - var/mob/living/carbon/nearest_op = null +/obj/item/weapon/pinpointer/proc/switch_mode_to(new_mode) //If we shouldn't be tracking what we are + if(isliving(loc)) + var/mob/living/L = loc + L << "Your [name] beeps as it reconfigures its tracking algorithms." + playsound(L, 'sound/machines/triple_beep.ogg', 50, 1) + mode = new_mode + target = null //Switch modes so we can find the new target -/obj/item/weapon/pinpointer/operative/attack_self() - if(!usr.mind || !(usr.mind in ticker.mode.syndicates)) - usr << "AUTHENTICATION FAILURE. ACCESS DENIED." - return 0 - if(!active) - active = 1 - workop() - usr << "You activate the pinpointer." - else - active = 0 - icon_state = "pinoff" - usr << "You deactivate the pinpointer." +/obj/item/weapon/pinpointer/proc/refresh_target() //Periodically removes the target to allow the pinpointer to update (i.e. malf AI shunts, an operative dies) + target = null -/obj/item/weapon/pinpointer/operative/proc/scan_for_ops() - if(active) - nearest_op = null //Resets nearest_op every time it scans - var/closest_distance = 1000 - for(var/mob/living/carbon/M in mob_list) - if(M.mind && (M.mind in ticker.mode.syndicates)) - if(get_dist(M, get_turf(src)) < closest_distance) //Actually points toward the nearest op, instead of a random one like it used to - nearest_op = M +/obj/item/weapon/pinpointer/syndicate //Syndicate pinpointers automatically point towards the infiltrator once the nuke is active. + name = "syndicate pinpointer" + desc = "A handheld tracking device that locks onto certain signals. It's configured to switch tracking modes once it detects the activation signal of a nuclear device." -/obj/item/weapon/pinpointer/operative/proc/workop() - if(active) - scan_for_ops() - point_at(nearest_op, 0) - spawn(5) - .() - else - return 0 - -/obj/item/weapon/pinpointer/operative/examine(mob/user) - ..() - if(active) - if(nearest_op) - user << "Nearest operative detected is [nearest_op.real_name]." - else - user << "No operatives detected within scanning range." +/obj/item/weapon/pinpointer/syndicate/cyborg //Cyborg pinpointers just look for a random operative. + name = "cyborg syndicate pinpointer" + desc = "An integrated tracking device, jury-rigged to search for living Syndicate operatives." + mode = TRACK_OPERATIVES + flags = NODROP diff --git a/code/modules/mob/living/silicon/ai/death.dm b/code/modules/mob/living/silicon/ai/death.dm index 7f5891f5b82e..8270aa969986 100644 --- a/code/modules/mob/living/silicon/ai/death.dm +++ b/code/modules/mob/living/silicon/ai/death.dm @@ -24,8 +24,9 @@ if(nuking) set_security_level("red") nuking = FALSE - for(var/obj/item/weapon/pinpointer/point in pinpointer_list) - point.the_disk = null //Point back to the disk. + for(var/obj/item/weapon/pinpointer/P in pinpointer_list) + P.switch_mode_to(TRACK_NUKE_DISK) //Party's over, back to work, everyone + P.nuke_warning = FALSE if(doomsday_device) doomsday_device.timing = FALSE diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index 26021030ff5c..3f5482fe2670 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -336,7 +336,7 @@ modules += new /obj/item/weapon/gun/projectile/revolver/grenadelauncher/cyborg(src) modules += new /obj/item/weapon/card/emag(src) modules += new /obj/item/weapon/crowbar/cyborg(src) - modules += new /obj/item/weapon/pinpointer/operative(src) + modules += new /obj/item/weapon/pinpointer/syndicate/cyborg(src) emag = null fix_modules() @@ -357,7 +357,7 @@ modules += new /obj/item/roller/robo(src) modules += new /obj/item/weapon/card/emag(src) modules += new /obj/item/weapon/crowbar/cyborg(src) - modules += new /obj/item/weapon/pinpointer/operative(src) + modules += new /obj/item/weapon/pinpointer/syndicate/cyborg(src) emag = null add_module(new /obj/item/stack/medical/gauze/cyborg()) diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 9c17ba8db9bb..782692e0ac09 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -829,9 +829,6 @@ qdel(malf) src.occupier.verbs += /mob/living/silicon/ai/proc/corereturn src.occupier.cancel_camera() - if ((seclevel2num(get_security_level()) == SEC_LEVEL_DELTA) && malf.nuking) - for(var/obj/item/weapon/pinpointer/point in pinpointer_list) - point.the_disk = src //the pinpointer will detect the shunted AI /obj/machinery/power/apc/proc/malfvacate(forced) @@ -843,11 +840,6 @@ src.occupier.parent.adjustOxyLoss(src.occupier.getOxyLoss()) src.occupier.parent.cancel_camera() qdel(src.occupier) - if (seclevel2num(get_security_level()) == SEC_LEVEL_DELTA) - for(var/obj/item/weapon/pinpointer/point in pinpointer_list) - for(var/mob/living/silicon/ai/A in ai_list) - if((A.stat != DEAD) && A.nuking) - point.the_disk = A //The pinpointer tracks the AI back into its core. else src.occupier << "Primary core damaged, unable to return core processes." @@ -855,9 +847,9 @@ src.occupier.loc = src.loc src.occupier.death() src.occupier.gib() - for(var/obj/item/weapon/pinpointer/point in pinpointer_list) - point.the_disk = null //the pinpointer will go back to pointing at the nuke disc. - + for(var/obj/item/weapon/pinpointer/P in pinpointer_list) + P.switch_mode_to(TRACK_NUKE_DISK) //Pinpointers go back to tracking the nuke disk + P.nuke_warning = FALSE /obj/machinery/power/apc/surplus() if(terminal) diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi index 64825e13edd4..617d7824d079 100644 Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ diff --git a/sound/machines/triple_beep.ogg b/sound/machines/triple_beep.ogg new file mode 100644 index 000000000000..dcd3539c811e Binary files /dev/null and b/sound/machines/triple_beep.ogg differ diff --git a/tgstation.dme b/tgstation.dme index 1befd361adac..d572a21bf0cb 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -38,6 +38,7 @@ #include "code\__DEFINES\math.dm" #include "code\__DEFINES\MC.dm" #include "code\__DEFINES\misc.dm" +#include "code\__DEFINES\pinpointers.dm" #include "code\__DEFINES\pipe_construction.dm" #include "code\__DEFINES\preferences.dm" #include "code\__DEFINES\qdel.dm"