File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,230 @@
|
||||
//Clown PDA is slippery.
|
||||
/obj/item/pda/clown
|
||||
name = "clown PDA"
|
||||
default_cartridge = /obj/item/cartridge/virus/clown
|
||||
inserted_item = /obj/item/toy/crayon/rainbow
|
||||
icon_state = "pda-clown"
|
||||
desc = "A portable microcomputer by Thinktronic Systems, LTD. The surface is coated with polytetrafluoroethylene and banana drippings."
|
||||
ttone = "honk"
|
||||
var/slipvictims = list() //CIT CHANGE - makes clown PDAs track unique people slipped
|
||||
|
||||
/obj/item/pda/clown/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/slippery, 120, NO_SLIP_WHEN_WALKING|SLIP_WHEN_JOGGING, CALLBACK(src, .proc/AfterSlip))
|
||||
|
||||
/obj/item/pda/clown/proc/AfterSlip(mob/living/carbon/human/M)
|
||||
if (istype(M) && (M.real_name != owner))
|
||||
slipvictims |= M //CIT CHANGE - makes clown PDAs track unique people slipped
|
||||
var/obj/item/cartridge/virus/clown/cart = cartridge
|
||||
if(istype(cart) && cart.charges < 5)
|
||||
cart.charges++
|
||||
|
||||
// Special AI/pAI PDAs that cannot explode.
|
||||
/obj/item/pda/ai
|
||||
icon = null
|
||||
ttone = "data"
|
||||
fon = FALSE
|
||||
detonatable = FALSE
|
||||
|
||||
/obj/item/pda/ai/attack_self(mob/user)
|
||||
if ((honkamt > 0) && (prob(60)))//For clown virus.
|
||||
honkamt--
|
||||
playsound(loc, 'sound/items/bikehorn.ogg', 30, 1)
|
||||
return
|
||||
|
||||
/obj/item/pda/ai/pai
|
||||
ttone = "assist"
|
||||
|
||||
|
||||
|
||||
/obj/item/pda/medical
|
||||
name = "medical PDA"
|
||||
default_cartridge = /obj/item/cartridge/medical
|
||||
icon_state = "pda-medical"
|
||||
|
||||
/obj/item/pda/viro
|
||||
name = "virology PDA"
|
||||
default_cartridge = /obj/item/cartridge/medical
|
||||
icon_state = "pda-virology"
|
||||
|
||||
/obj/item/pda/engineering
|
||||
name = "engineering PDA"
|
||||
default_cartridge = /obj/item/cartridge/engineering
|
||||
icon_state = "pda-engineer"
|
||||
|
||||
/obj/item/pda/security
|
||||
name = "security PDA"
|
||||
default_cartridge = /obj/item/cartridge/security
|
||||
icon_state = "pda-security"
|
||||
|
||||
/obj/item/pda/detective
|
||||
name = "detective PDA"
|
||||
default_cartridge = /obj/item/cartridge/detective
|
||||
icon_state = "pda-detective"
|
||||
|
||||
/obj/item/pda/warden
|
||||
name = "warden PDA"
|
||||
default_cartridge = /obj/item/cartridge/security
|
||||
icon_state = "pda-warden"
|
||||
|
||||
/obj/item/pda/janitor
|
||||
name = "janitor PDA"
|
||||
default_cartridge = /obj/item/cartridge/janitor
|
||||
icon_state = "pda-janitor"
|
||||
ttone = "slip"
|
||||
|
||||
/obj/item/pda/toxins
|
||||
name = "scientist PDA"
|
||||
default_cartridge = /obj/item/cartridge/signal/toxins
|
||||
icon_state = "pda-science"
|
||||
ttone = "boom"
|
||||
|
||||
/obj/item/pda/mime
|
||||
name = "mime PDA"
|
||||
default_cartridge = /obj/item/cartridge/virus/mime
|
||||
inserted_item = /obj/item/toy/crayon/mime
|
||||
icon_state = "pda-mime"
|
||||
silent = TRUE
|
||||
ttone = "silence"
|
||||
|
||||
/obj/item/pda/heads
|
||||
default_cartridge = /obj/item/cartridge/head
|
||||
icon_state = "pda-hop"
|
||||
|
||||
/obj/item/pda/heads/hop
|
||||
name = "head of personnel PDA"
|
||||
default_cartridge = /obj/item/cartridge/hop
|
||||
icon_state = "pda-hop"
|
||||
|
||||
/obj/item/pda/heads/hos
|
||||
name = "head of security PDA"
|
||||
default_cartridge = /obj/item/cartridge/hos
|
||||
icon_state = "pda-hos"
|
||||
|
||||
/obj/item/pda/heads/ce
|
||||
name = "chief engineer PDA"
|
||||
default_cartridge = /obj/item/cartridge/ce
|
||||
icon_state = "pda-ce"
|
||||
|
||||
/obj/item/pda/heads/cmo
|
||||
name = "chief medical officer PDA"
|
||||
default_cartridge = /obj/item/cartridge/cmo
|
||||
icon_state = "pda-cmo"
|
||||
|
||||
/obj/item/pda/heads/rd
|
||||
name = "research director PDA"
|
||||
default_cartridge = /obj/item/cartridge/rd
|
||||
inserted_item = /obj/item/pen/fourcolor
|
||||
icon_state = "pda-rd"
|
||||
|
||||
/obj/item/pda/captain
|
||||
name = "captain PDA"
|
||||
default_cartridge = /obj/item/cartridge/captain
|
||||
inserted_item = /obj/item/pen/fountain/captain
|
||||
icon_state = "pda-captain"
|
||||
detonatable = FALSE
|
||||
|
||||
/obj/item/pda/lieutenant
|
||||
name = "lieutenant PDA"
|
||||
default_cartridge = /obj/item/cartridge/captain
|
||||
inserted_item = /obj/item/pen/fountain/captain
|
||||
icon_state = "pda-lieutenant"
|
||||
ttone = "bwoink"
|
||||
detonatable = FALSE
|
||||
hidden = TRUE
|
||||
note = "Congratulations, you have chosen the Thinktronic 5230-2 Personal Data Assistant Prestige Edition! To help with navigation, we have provided the following definitions. North: Fore. South: Aft. West: Port. East: Starboard. Quarter is either side of aft."
|
||||
|
||||
/obj/item/pda/cargo
|
||||
name = "cargo technician PDA"
|
||||
default_cartridge = /obj/item/cartridge/quartermaster
|
||||
icon_state = "pda-cargo"
|
||||
|
||||
/obj/item/pda/quartermaster
|
||||
name = "quartermaster PDA"
|
||||
default_cartridge = /obj/item/cartridge/quartermaster
|
||||
inserted_item = /obj/item/pen/fountain
|
||||
icon_state = "pda-qm"
|
||||
|
||||
/obj/item/pda/shaftminer
|
||||
name = "shaft miner PDA"
|
||||
icon_state = "pda-miner"
|
||||
|
||||
/obj/item/pda/syndicate
|
||||
default_cartridge = /obj/item/cartridge/virus/syndicate
|
||||
icon_state = "pda-syndi"
|
||||
name = "military PDA"
|
||||
owner = "John Doe"
|
||||
hidden = 1
|
||||
|
||||
/obj/item/pda/chaplain
|
||||
name = "chaplain PDA"
|
||||
icon_state = "pda-chaplain"
|
||||
ttone = "holy"
|
||||
|
||||
/obj/item/pda/lawyer
|
||||
name = "lawyer PDA"
|
||||
default_cartridge = /obj/item/cartridge/lawyer
|
||||
inserted_item = /obj/item/pen/fountain
|
||||
icon_state = "pda-lawyer"
|
||||
ttone = "objection"
|
||||
|
||||
/obj/item/pda/botanist
|
||||
name = "botanist PDA"
|
||||
//default_cartridge = /obj/item/cartridge/botanist
|
||||
icon_state = "pda-hydro"
|
||||
|
||||
/obj/item/pda/roboticist
|
||||
name = "roboticist PDA"
|
||||
icon_state = "pda-roboticist"
|
||||
default_cartridge = /obj/item/cartridge/roboticist
|
||||
|
||||
/obj/item/pda/curator
|
||||
name = "curator PDA"
|
||||
icon_state = "pda-library"
|
||||
overlays_icons = list('icons/obj/pda.dmi' = list("pda-r-library","blank","id_overlay","insert_overlay", "light_overlay", "pai_overlay"),
|
||||
'icons/obj/pda_alt.dmi' = list("pda-r","screen_default","id_overlay","insert_overlay", "light_overlay", "pai_overlay"))
|
||||
current_overlays = list("pda-r-library","blank","id_overlay","insert_overlay", "light_overlay", "pai_overlay")
|
||||
default_cartridge = /obj/item/cartridge/curator
|
||||
inserted_item = /obj/item/pen/fountain
|
||||
desc = "A portable microcomputer by Thinktronic Systems, LTD. This model is a WGW-11 series e-reader."
|
||||
note = "Congratulations, your station has chosen the Thinktronic 5290 WGW-11 Series E-reader and Personal Data Assistant! To help with navigation, we have provided the following definitions. North: Fore. South: Aft. West: Port. East: Starboard. Quarter is either side of aft."
|
||||
silent = TRUE //Quiet in the library!
|
||||
overlays_offsets = list('icons/obj/pda.dmi' = list(-3,0))
|
||||
overlays_x_offset = -3
|
||||
|
||||
/obj/item/pda/clear
|
||||
name = "clear PDA"
|
||||
icon_state = "pda-clear"
|
||||
desc = "A portable microcomputer by Thinktronic Systems, LTD. This model is a special edition with a transparent case."
|
||||
note = "Congratulations, you have chosen the Thinktronic 5230 Personal Data Assistant Deluxe Special Max Turbo Limited Edition! To help with navigation, we have provided the following definitions. North: Fore. South: Aft. West: Port. East: Starboard. Quarter is either side of aft."
|
||||
|
||||
/obj/item/pda/neko
|
||||
name = "neko PDA"
|
||||
icon_state = "pda-neko"
|
||||
overlays_icons = list('icons/obj/pda_alt.dmi' = list("pda-r", "screen_neko", "id_overlay", "insert_overlay", "light_overlay", "pai_overlay"))
|
||||
desc = "A portable microcomputer by Thinktronic Systems, LTD. This model is a special feline edition."
|
||||
note = "Congratulations, you have chosen the Thinktronic 5230 Personal Data Assistant Deluxe Special Mew Turbo Limited Edition NYA~! To help with navigation, we have provided the following definitions. North: Fore. South: Aft. West: Port. East: Starboard. Quarter is either side of aft."
|
||||
|
||||
/obj/item/pda/cook
|
||||
name = "cook PDA"
|
||||
icon_state = "pda-cook"
|
||||
|
||||
/obj/item/pda/bar
|
||||
name = "bartender PDA"
|
||||
icon_state = "pda-bartender"
|
||||
inserted_item = /obj/item/pen/fountain
|
||||
|
||||
/obj/item/pda/atmos
|
||||
name = "atmospherics PDA"
|
||||
default_cartridge = /obj/item/cartridge/atmos
|
||||
icon_state = "pda-atmos"
|
||||
|
||||
/obj/item/pda/chemist
|
||||
name = "chemist PDA"
|
||||
default_cartridge = /obj/item/cartridge/chemistry
|
||||
icon_state = "pda-chemistry"
|
||||
|
||||
/obj/item/pda/geneticist
|
||||
name = "geneticist PDA"
|
||||
default_cartridge = /obj/item/cartridge/medical
|
||||
icon_state = "pda-genetics"
|
||||
@@ -0,0 +1,775 @@
|
||||
#define CART_SECURITY (1<<0)
|
||||
#define CART_ENGINE (1<<1)
|
||||
#define CART_ATMOS (1<<2)
|
||||
#define CART_MEDICAL (1<<3)
|
||||
#define CART_MANIFEST (1<<4)
|
||||
#define CART_CLOWN (1<<5)
|
||||
#define CART_MIME (1<<6)
|
||||
#define CART_JANITOR (1<<7)
|
||||
#define CART_REAGENT_SCANNER (1<<8)
|
||||
#define CART_NEWSCASTER (1<<9)
|
||||
#define CART_REMOTE_DOOR (1<<10)
|
||||
#define CART_STATUS_DISPLAY (1<<11)
|
||||
#define CART_QUARTERMASTER (1<<12)
|
||||
#define CART_HYDROPONICS (1<<13)
|
||||
#define CART_DRONEPHONE (1<<14)
|
||||
|
||||
|
||||
/obj/item/cartridge
|
||||
name = "generic cartridge"
|
||||
desc = "A data cartridge for portable microcomputers."
|
||||
icon = 'icons/obj/pda.dmi'
|
||||
icon_state = "cart"
|
||||
item_state = "electronic"
|
||||
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
|
||||
var/obj/item/integrated_signaler/radio = null
|
||||
|
||||
var/access = 0 //Bit flags for cartridge access
|
||||
|
||||
var/remote_door_id = ""
|
||||
|
||||
var/bot_access_flags = 0 //Bit flags. Selection: SEC_BOT | MULE_BOT | FLOOR_BOT | CLEAN_BOT | MED_BOT | FIRE_BOT
|
||||
var/spam_enabled = 0 //Enables "Send to All" Option
|
||||
|
||||
var/obj/item/pda/host_pda = null
|
||||
var/menu
|
||||
var/datum/data/record/active1 = null //General
|
||||
var/datum/data/record/active2 = null //Medical
|
||||
var/datum/data/record/active3 = null //Security
|
||||
var/obj/machinery/computer/monitor/powmonitor = null // Power Monitor
|
||||
var/list/powermonitors = list()
|
||||
var/message1 // used for status_displays
|
||||
var/message2
|
||||
var/list/stored_data = list()
|
||||
var/current_channel
|
||||
|
||||
var/mob/living/simple_animal/bot/active_bot
|
||||
var/list/botlist = list()
|
||||
|
||||
/obj/item/cartridge/Initialize()
|
||||
. = ..()
|
||||
var/obj/item/pda/pda = loc
|
||||
if(istype(pda))
|
||||
host_pda = pda
|
||||
|
||||
/obj/item/cartridge/engineering
|
||||
name = "\improper Power-ON cartridge"
|
||||
icon_state = "cart-e"
|
||||
access = CART_ENGINE | CART_DRONEPHONE
|
||||
bot_access_flags = FLOOR_BOT
|
||||
|
||||
/obj/item/cartridge/atmos
|
||||
name = "\improper BreatheDeep cartridge"
|
||||
icon_state = "cart-a"
|
||||
access = CART_ATMOS | CART_DRONEPHONE
|
||||
bot_access_flags = FLOOR_BOT | FIRE_BOT
|
||||
|
||||
/obj/item/cartridge/medical
|
||||
name = "\improper Med-U cartridge"
|
||||
icon_state = "cart-m"
|
||||
access = CART_MEDICAL
|
||||
bot_access_flags = MED_BOT
|
||||
|
||||
/obj/item/cartridge/chemistry
|
||||
name = "\improper ChemWhiz cartridge"
|
||||
icon_state = "cart-chem"
|
||||
access = CART_REAGENT_SCANNER
|
||||
bot_access_flags = MED_BOT
|
||||
|
||||
/obj/item/cartridge/security
|
||||
name = "\improper R.O.B.U.S.T. cartridge"
|
||||
icon_state = "cart-s"
|
||||
access = CART_SECURITY
|
||||
bot_access_flags = SEC_BOT
|
||||
|
||||
/obj/item/cartridge/detective
|
||||
name = "\improper D.E.T.E.C.T. cartridge"
|
||||
icon_state = "cart-eye"
|
||||
access = CART_SECURITY | CART_MEDICAL | CART_MANIFEST
|
||||
bot_access_flags = SEC_BOT
|
||||
|
||||
/obj/item/cartridge/janitor
|
||||
name = "\improper CustodiPRO cartridge"
|
||||
desc = "The ultimate in clean-room design."
|
||||
icon_state = "cart-j"
|
||||
access = CART_JANITOR | CART_DRONEPHONE
|
||||
bot_access_flags = CLEAN_BOT
|
||||
|
||||
/obj/item/cartridge/lawyer
|
||||
name = "\improper P.R.O.V.E. cartridge"
|
||||
icon_state = "cart-law"
|
||||
access = CART_SECURITY
|
||||
spam_enabled = 1
|
||||
|
||||
/obj/item/cartridge/curator
|
||||
name = "\improper Lib-Tweet cartridge"
|
||||
icon_state = "cart-lib"
|
||||
access = CART_NEWSCASTER
|
||||
|
||||
/obj/item/cartridge/roboticist
|
||||
name = "\improper B.O.O.P. Remote Control cartridge"
|
||||
desc = "Packed with heavy duty triple-bot interlink!"
|
||||
icon_state = "cart-robo"
|
||||
bot_access_flags = FLOOR_BOT | CLEAN_BOT | MED_BOT | FIRE_BOT
|
||||
access = CART_DRONEPHONE
|
||||
|
||||
/obj/item/cartridge/signal
|
||||
name = "generic signaler cartridge"
|
||||
icon_state = "cart-sig"
|
||||
desc = "A data cartridge with an integrated radio signaler module."
|
||||
|
||||
/obj/item/cartridge/signal/toxins
|
||||
name = "\improper Signal Ace 2 cartridge"
|
||||
desc = "Complete with integrated radio signaler!"
|
||||
icon_state = "cart-tox"
|
||||
access = CART_REAGENT_SCANNER | CART_ATMOS
|
||||
|
||||
/obj/item/cartridge/signal/Initialize()
|
||||
. = ..()
|
||||
radio = new(src)
|
||||
|
||||
|
||||
|
||||
/obj/item/cartridge/quartermaster
|
||||
name = "space parts & space vendors cartridge"
|
||||
desc = "Perfect for the Quartermaster on the go!"
|
||||
icon_state = "cart-q"
|
||||
access = CART_QUARTERMASTER
|
||||
bot_access_flags = MULE_BOT
|
||||
|
||||
/obj/item/cartridge/head
|
||||
name = "\improper Easy-Record DELUXE cartridge"
|
||||
icon_state = "cart-h"
|
||||
access = CART_MANIFEST | CART_STATUS_DISPLAY
|
||||
|
||||
/obj/item/cartridge/hop
|
||||
name = "\improper HumanResources9001 cartridge"
|
||||
icon_state = "cart-h"
|
||||
access = CART_MANIFEST | CART_STATUS_DISPLAY | CART_JANITOR | CART_SECURITY | CART_NEWSCASTER | CART_QUARTERMASTER | CART_DRONEPHONE
|
||||
bot_access_flags = MULE_BOT | CLEAN_BOT
|
||||
|
||||
/obj/item/cartridge/hos
|
||||
name = "\improper R.O.B.U.S.T. DELUXE cartridge"
|
||||
icon_state = "cart-hos"
|
||||
access = CART_MANIFEST | CART_STATUS_DISPLAY | CART_SECURITY
|
||||
bot_access_flags = SEC_BOT
|
||||
|
||||
|
||||
/obj/item/cartridge/ce
|
||||
name = "\improper Power-On DELUXE cartridge"
|
||||
icon_state = "cart-ce"
|
||||
access = CART_MANIFEST | CART_STATUS_DISPLAY | CART_ENGINE | CART_ATMOS | CART_DRONEPHONE
|
||||
bot_access_flags = FLOOR_BOT | FIRE_BOT
|
||||
|
||||
/obj/item/cartridge/cmo
|
||||
name = "\improper Med-U DELUXE cartridge"
|
||||
icon_state = "cart-cmo"
|
||||
access = CART_MANIFEST | CART_STATUS_DISPLAY | CART_REAGENT_SCANNER | CART_MEDICAL
|
||||
bot_access_flags = MED_BOT
|
||||
|
||||
/obj/item/cartridge/rd
|
||||
name = "\improper Signal Ace DELUXE cartridge"
|
||||
icon_state = "cart-rd"
|
||||
access = CART_MANIFEST | CART_STATUS_DISPLAY | CART_REAGENT_SCANNER | CART_ATMOS | CART_DRONEPHONE
|
||||
bot_access_flags = FLOOR_BOT | CLEAN_BOT | MED_BOT | FIRE_BOT
|
||||
|
||||
/obj/item/cartridge/rd/Initialize()
|
||||
. = ..()
|
||||
radio = new(src)
|
||||
|
||||
/obj/item/cartridge/captain
|
||||
name = "\improper Value-PAK cartridge"
|
||||
desc = "Now with 350% more value!" //Give the Captain...EVERYTHING! (Except Mime, Clown, and Syndie)
|
||||
icon_state = "cart-c"
|
||||
access = ~(CART_CLOWN | CART_MIME | CART_REMOTE_DOOR)
|
||||
bot_access_flags = SEC_BOT | MULE_BOT | FLOOR_BOT | CLEAN_BOT | MED_BOT | FIRE_BOT
|
||||
spam_enabled = 1
|
||||
|
||||
/obj/item/cartridge/captain/New()
|
||||
..()
|
||||
radio = new(src)
|
||||
|
||||
/obj/item/cartridge/proc/post_status(command, data1, data2)
|
||||
|
||||
var/datum/radio_frequency/frequency = SSradio.return_frequency(FREQ_STATUS_DISPLAYS)
|
||||
|
||||
if(!frequency)
|
||||
return
|
||||
|
||||
var/datum/signal/status_signal = new(list("command" = command))
|
||||
switch(command)
|
||||
if("message")
|
||||
status_signal.data["msg1"] = data1
|
||||
status_signal.data["msg2"] = data2
|
||||
if("alert")
|
||||
status_signal.data["picture_state"] = data1
|
||||
|
||||
frequency.post_signal(src, status_signal)
|
||||
|
||||
/obj/item/cartridge/proc/generate_menu(mob/user)
|
||||
if(!host_pda)
|
||||
return
|
||||
switch(host_pda.mode)
|
||||
if(40) //signaller
|
||||
menu = "<h4>[PDAIMG(signaler)] Remote Signaling System</h4>"
|
||||
|
||||
menu += {"
|
||||
<a href='byond://?src=[REF(src)];choice=Send Signal'>Send Signal</A><BR>
|
||||
Frequency:
|
||||
<a href='byond://?src=[REF(src)];choice=Signal Frequency;sfreq=-10'>-</a>
|
||||
<a href='byond://?src=[REF(src)];choice=Signal Frequency;sfreq=-2'>-</a>
|
||||
[format_frequency(radio.frequency)]
|
||||
<a href='byond://?src=[REF(src)];choice=Signal Frequency;sfreq=2'>+</a>
|
||||
<a href='byond://?src=[REF(src)];choice=Signal Frequency;sfreq=10'>+</a><br>
|
||||
<br>
|
||||
Code:
|
||||
<a href='byond://?src=[REF(src)];choice=Signal Code;scode=-5'>-</a>
|
||||
<a href='byond://?src=[REF(src)];choice=Signal Code;scode=-1'>-</a>
|
||||
[radio.code]
|
||||
<a href='byond://?src=[REF(src)];choice=Signal Code;scode=1'>+</a>
|
||||
<a href='byond://?src=[REF(src)];choice=Signal Code;scode=5'>+</a><br>"}
|
||||
if (41) //crew manifest
|
||||
|
||||
menu = "<h4>[PDAIMG(notes)] Crew Manifest</h4>"
|
||||
menu += "Entries cannot be modified from this terminal.<br><br>"
|
||||
if(GLOB.data_core.general)
|
||||
for (var/datum/data/record/t in sortRecord(GLOB.data_core.general))
|
||||
menu += "[t.fields["name"]] - [t.fields["rank"]]<br>"
|
||||
menu += "<br>"
|
||||
|
||||
|
||||
if (42) //status displays
|
||||
menu = "<h4>[PDAIMG(status)] Station Status Display Interlink</h4>"
|
||||
|
||||
menu += "\[ <A HREF='?src=[REF(src)];choice=Status;statdisp=blank'>Clear</A> \]<BR>"
|
||||
menu += "\[ <A HREF='?src=[REF(src)];choice=Status;statdisp=shuttle'>Shuttle ETA</A> \]<BR>"
|
||||
menu += "\[ <A HREF='?src=[REF(src)];choice=Status;statdisp=message'>Message</A> \]"
|
||||
menu += "<ul><li> Line 1: <A HREF='?src=[REF(src)];choice=Status;statdisp=setmsg1'>[ message1 ? message1 : "(none)"]</A>"
|
||||
menu += "<li> Line 2: <A HREF='?src=[REF(src)];choice=Status;statdisp=setmsg2'>[ message2 ? message2 : "(none)"]</A></ul><br>"
|
||||
menu += "\[ Alert: <A HREF='?src=[REF(src)];choice=Status;statdisp=alert;alert=default'>None</A> |"
|
||||
menu += " <A HREF='?src=[REF(src)];choice=Status;statdisp=alert;alert=redalert'>Red Alert</A> |"
|
||||
menu += " <A HREF='?src=[REF(src)];choice=Status;statdisp=alert;alert=lockdown'>Lockdown</A> |"
|
||||
menu += " <A HREF='?src=[REF(src)];choice=Status;statdisp=alert;alert=biohazard'>Biohazard</A> \]<BR>"
|
||||
|
||||
if (43)
|
||||
menu = "<h4>[PDAIMG(power)] Power Monitors - Please select one</h4><BR>"
|
||||
powmonitor = null
|
||||
powermonitors = list()
|
||||
var/powercount = 0
|
||||
|
||||
|
||||
|
||||
var/turf/pda_turf = get_turf(src)
|
||||
for(var/obj/machinery/computer/monitor/pMon in GLOB.machines)
|
||||
if(pMon.stat & (NOPOWER | BROKEN)) //check to make sure the computer is functional
|
||||
continue
|
||||
if(pda_turf.z != pMon.z) //and that we're on the same zlevel as the computer (lore: limited signal strength)
|
||||
continue
|
||||
if(pMon.is_secret_monitor) //make sure it isn't a secret one (ie located on a ruin), allowing people to metagame that the location exists
|
||||
continue
|
||||
powercount++
|
||||
powermonitors += pMon
|
||||
|
||||
|
||||
if(!powercount)
|
||||
menu += "<span class='danger'>No connection<BR></span>"
|
||||
else
|
||||
|
||||
menu += "<FONT SIZE=-1>"
|
||||
var/count = 0
|
||||
for(var/obj/machinery/computer/monitor/pMon in powermonitors)
|
||||
count++
|
||||
menu += "<a href='byond://?src=[REF(src)];choice=Power Select;target=[count]'>[pMon] - [get_area_name(pMon, TRUE)] </a><BR>"
|
||||
|
||||
menu += "</FONT>"
|
||||
|
||||
if (433)
|
||||
menu = "<h4>[PDAIMG(power)] Power Monitor </h4><BR>"
|
||||
if(!powmonitor || !powmonitor.get_powernet())
|
||||
menu += "<span class='danger'>No connection<BR></span>"
|
||||
else
|
||||
var/list/L = list()
|
||||
var/datum/powernet/connected_powernet = powmonitor.get_powernet()
|
||||
for(var/obj/machinery/power/terminal/term in connected_powernet.nodes)
|
||||
if(istype(term.master, /obj/machinery/power/apc))
|
||||
var/obj/machinery/power/apc/A = term.master
|
||||
L += A
|
||||
|
||||
menu += "<PRE>Location: [get_area_name(powmonitor, TRUE)]<BR>Total power: [DisplayPower(connected_powernet.viewavail)]<BR>Total load: [DisplayPower(connected_powernet.viewload)]<BR>"
|
||||
|
||||
menu += "<FONT SIZE=-1>"
|
||||
|
||||
if(L.len > 0)
|
||||
menu += "Area Eqp./Lgt./Env. Load Cell<HR>"
|
||||
|
||||
var/list/S = list(" Off","AOff"," On", " AOn")
|
||||
var/list/chg = list("N","C","F")
|
||||
|
||||
for(var/obj/machinery/power/apc/A in L)
|
||||
menu += copytext(add_tspace(A.area.name, 30), 1, 30)
|
||||
menu += " [S[A.equipment+1]] [S[A.lighting+1]] [S[A.environ+1]] [add_lspace(DisplayPower(A.lastused_total), 6)] [A.cell ? "[add_lspace(round(A.cell.percent()), 3)]% [chg[A.charging+1]]" : " N/C"]<BR>"
|
||||
|
||||
menu += "</FONT></PRE>"
|
||||
|
||||
if (44) //medical records //This thing only displays a single screen so it's hard to really get the sub-menu stuff working.
|
||||
menu = "<h4>[PDAIMG(medical)] Medical Record List</h4>"
|
||||
if(GLOB.data_core.general)
|
||||
for(var/datum/data/record/R in sortRecord(GLOB.data_core.general))
|
||||
menu += "<a href='byond://?src=[REF(src)];choice=Medical Records;target=[R.fields["id"]]'>[R.fields["id"]]: [R.fields["name"]]<br>"
|
||||
menu += "<br>"
|
||||
if(441)
|
||||
menu = "<h4>[PDAIMG(medical)] Medical Record</h4>"
|
||||
|
||||
if(active1 in GLOB.data_core.general)
|
||||
menu += "Name: [active1.fields["name"]] ID: [active1.fields["id"]]<br>"
|
||||
menu += "Sex: [active1.fields["sex"]]<br>"
|
||||
menu += "Age: [active1.fields["age"]]<br>"
|
||||
menu += "Rank: [active1.fields["rank"]]<br>"
|
||||
menu += "Fingerprint: [active1.fields["fingerprint"]]<br>"
|
||||
menu += "Physical Status: [active1.fields["p_stat"]]<br>"
|
||||
menu += "Mental Status: [active1.fields["m_stat"]]<br>"
|
||||
else
|
||||
menu += "<b>Record Lost!</b><br>"
|
||||
|
||||
menu += "<br>"
|
||||
|
||||
menu += "<h4>[PDAIMG(medical)] Medical Data</h4>"
|
||||
if(active2 in GLOB.data_core.medical)
|
||||
menu += "Blood Type: [active2.fields["blood_type"]]<br><br>"
|
||||
|
||||
menu += "Minor Disabilities: [active2.fields["mi_dis"]]<br>"
|
||||
menu += "Details: [active2.fields["mi_dis_d"]]<br><br>"
|
||||
|
||||
menu += "Major Disabilities: [active2.fields["ma_dis"]]<br>"
|
||||
menu += "Details: [active2.fields["ma_dis_d"]]<br><br>"
|
||||
|
||||
menu += "Allergies: [active2.fields["alg"]]<br>"
|
||||
menu += "Details: [active2.fields["alg_d"]]<br><br>"
|
||||
|
||||
menu += "Current Diseases: [active2.fields["cdi"]]<br>"
|
||||
menu += "Details: [active2.fields["cdi_d"]]<br><br>"
|
||||
|
||||
menu += "Important Notes: [active2.fields["notes"]]<br>"
|
||||
else
|
||||
menu += "<b>Record Lost!</b><br>"
|
||||
|
||||
menu += "<br>"
|
||||
if (45) //security records
|
||||
menu = "<h4>[PDAIMG(cuffs)] Security Record List</h4>"
|
||||
if(GLOB.data_core.general)
|
||||
for (var/datum/data/record/R in sortRecord(GLOB.data_core.general))
|
||||
menu += "<a href='byond://?src=[REF(src)];choice=Security Records;target=[R.fields["id"]]'>[R.fields["id"]]: [R.fields["name"]]<br>"
|
||||
|
||||
menu += "<br>"
|
||||
if(451)
|
||||
menu = "<h4>[PDAIMG(cuffs)] Security Record</h4>"
|
||||
|
||||
if(active1 in GLOB.data_core.general)
|
||||
menu += "Name: [active1.fields["name"]] ID: [active1.fields["id"]]<br>"
|
||||
menu += "Sex: [active1.fields["sex"]]<br>"
|
||||
menu += "Age: [active1.fields["age"]]<br>"
|
||||
menu += "Rank: [active1.fields["rank"]]<br>"
|
||||
menu += "Fingerprint: [active1.fields["fingerprint"]]<br>"
|
||||
menu += "Physical Status: [active1.fields["p_stat"]]<br>"
|
||||
menu += "Mental Status: [active1.fields["m_stat"]]<br>"
|
||||
else
|
||||
menu += "<b>Record Lost!</b><br>"
|
||||
|
||||
menu += "<br>"
|
||||
|
||||
menu += "<h4>[PDAIMG(cuffs)] Security Data</h4>"
|
||||
if(active3 in GLOB.data_core.security)
|
||||
menu += "Criminal Status: [active3.fields["criminal"]]<br>"
|
||||
|
||||
menu += text("<BR>\nMinor Crimes:")
|
||||
|
||||
menu +={"<table style="text-align:center;" border="1" cellspacing="0" width="100%">
|
||||
<tr>
|
||||
<th>Crime</th>
|
||||
<th>Details</th>
|
||||
<th>Author</th>
|
||||
<th>Time Added</th>
|
||||
</tr>"}
|
||||
for(var/datum/data/crime/c in active3.fields["mi_crim"])
|
||||
menu += "<tr><td>[c.crimeName]</td>"
|
||||
menu += "<td>[c.crimeDetails]</td>"
|
||||
menu += "<td>[c.author]</td>"
|
||||
menu += "<td>[c.time]</td>"
|
||||
menu += "</tr>"
|
||||
menu += "</table>"
|
||||
|
||||
menu += text("<BR>\nMajor Crimes:")
|
||||
|
||||
menu +={"<table style="text-align:center;" border="1" cellspacing="0" width="100%">
|
||||
<tr>
|
||||
<th>Crime</th>
|
||||
<th>Details</th>
|
||||
<th>Author</th>
|
||||
<th>Time Added</th>
|
||||
</tr>"}
|
||||
for(var/datum/data/crime/c in active3.fields["ma_crim"])
|
||||
menu += "<tr><td>[c.crimeName]</td>"
|
||||
menu += "<td>[c.crimeDetails]</td>"
|
||||
menu += "<td>[c.author]</td>"
|
||||
menu += "<td>[c.time]</td>"
|
||||
menu += "</tr>"
|
||||
menu += "</table>"
|
||||
|
||||
menu += "<BR>\nImportant Notes:<br>"
|
||||
menu += "[active3.fields["notes"]]"
|
||||
else
|
||||
menu += "<b>Record Lost!</b><br>"
|
||||
|
||||
menu += "<br>"
|
||||
|
||||
if (47) //quartermaster order records
|
||||
menu = "<h4>[PDAIMG(crate)] Supply Record Interlink</h4>"
|
||||
|
||||
menu += "<BR><B>Supply shuttle</B><BR>"
|
||||
menu += "Location: "
|
||||
switch(SSshuttle.supply.mode)
|
||||
if(SHUTTLE_CALL)
|
||||
menu += "Moving to "
|
||||
if(!is_station_level(SSshuttle.supply.z))
|
||||
menu += "station"
|
||||
else
|
||||
menu += "CentCom"
|
||||
menu += " ([SSshuttle.supply.timeLeft(600)] Mins)"
|
||||
else
|
||||
menu += "At "
|
||||
if(!is_station_level(SSshuttle.supply.z))
|
||||
menu += "CentCom"
|
||||
else
|
||||
menu += "station"
|
||||
menu += "<BR>Current approved orders: <BR><ol>"
|
||||
for(var/S in SSshuttle.shoppinglist)
|
||||
var/datum/supply_order/SO = S
|
||||
menu += "<li>#[SO.id] - [SO.pack.name] approved by [SO.orderer] [SO.reason ? "([SO.reason])":""]</li>"
|
||||
menu += "</ol>"
|
||||
|
||||
menu += "Current requests: <BR><ol>"
|
||||
for(var/S in SSshuttle.requestlist)
|
||||
var/datum/supply_order/SO = S
|
||||
menu += "<li>#[SO.id] - [SO.pack.name] requested by [SO.orderer]</li>"
|
||||
menu += "</ol><font size=\"-3\">Upgrade NOW to Space Parts & Space Vendors PLUS for full remote order control and inventory management."
|
||||
|
||||
if (48) // quartermaster ore logs
|
||||
menu = list("<h4>[PDAIMG(crate)] Ore Silo Logs</h4>")
|
||||
if (GLOB.ore_silo_default)
|
||||
var/list/logs = GLOB.silo_access_logs[REF(GLOB.ore_silo_default)]
|
||||
var/len = LAZYLEN(logs)
|
||||
var/i = 0
|
||||
for(var/M in logs)
|
||||
if (++i > 30)
|
||||
menu += "(... older logs not shown ...)"
|
||||
break
|
||||
var/datum/ore_silo_log/entry = M
|
||||
menu += "[len - i]. [entry.formatted]<br><br>"
|
||||
if(i == 0)
|
||||
menu += "Nothing!"
|
||||
else
|
||||
menu += "<b>No ore silo detected!</b>"
|
||||
menu = jointext(menu, "")
|
||||
|
||||
if (49) //janitorial locator
|
||||
menu = "<h4>[PDAIMG(bucket)] Persistent Custodial Object Locator</h4>"
|
||||
|
||||
var/turf/cl = get_turf(src)
|
||||
if (cl)
|
||||
menu += "Current Orbital Location: <b>\[[cl.x],[cl.y]\]</b>"
|
||||
|
||||
menu += "<h4>Located Mops:</h4>"
|
||||
|
||||
var/ldat
|
||||
for (var/obj/item/mop/M in world)
|
||||
var/turf/ml = get_turf(M)
|
||||
|
||||
if(ml)
|
||||
if (ml.z != cl.z)
|
||||
continue
|
||||
var/direction = get_dir(src, M)
|
||||
ldat += "Mop - <b>\[[ml.x],[ml.y] ([uppertext(dir2text(direction))])\]</b> - [M.reagents.total_volume ? "Wet" : "Dry"]<br>"
|
||||
|
||||
if (!ldat)
|
||||
menu += "None"
|
||||
else
|
||||
menu += "[ldat]"
|
||||
|
||||
menu += "<h4>Pimpin' Ride:</h4>"
|
||||
|
||||
ldat = null
|
||||
for (var/obj/vehicle/ridden/janicart/M in world)
|
||||
var/turf/ml = get_turf(M)
|
||||
|
||||
if(ml)
|
||||
if (ml.z != cl.z)
|
||||
continue
|
||||
var/direction = get_dir(src, M)
|
||||
ldat += "Ride - <b>\[[ml.x],[ml.y] ([uppertext(dir2text(direction))])\]</b><br>"
|
||||
|
||||
if (!ldat)
|
||||
menu += "None"
|
||||
else
|
||||
menu += "[ldat]"
|
||||
|
||||
menu += "<h4>Located Janitorial Cart:</h4>"
|
||||
|
||||
ldat = null
|
||||
for (var/obj/structure/janitorialcart/B in world)
|
||||
var/turf/bl = get_turf(B)
|
||||
|
||||
if(bl)
|
||||
if (bl.z != cl.z)
|
||||
continue
|
||||
var/direction = get_dir(src, B)
|
||||
ldat += "Cart - <b>\[[bl.x],[bl.y] ([uppertext(dir2text(direction))])\]</b> - Water level: [B.reagents.total_volume]/100<br>"
|
||||
|
||||
if (!ldat)
|
||||
menu += "None"
|
||||
else
|
||||
menu += "[ldat]"
|
||||
|
||||
menu += "<h4>Located Cleanbots:</h4>"
|
||||
|
||||
ldat = null
|
||||
for (var/mob/living/simple_animal/bot/cleanbot/B in GLOB.alive_mob_list)
|
||||
var/turf/bl = get_turf(B)
|
||||
|
||||
if(bl)
|
||||
if (bl.z != cl.z)
|
||||
continue
|
||||
var/direction = get_dir(src, B)
|
||||
ldat += "Cleanbot - <b>\[[bl.x],[bl.y] ([uppertext(dir2text(direction))])\]</b> - [B.on ? "Online" : "Offline"]<br>"
|
||||
|
||||
if (!ldat)
|
||||
menu += "None"
|
||||
else
|
||||
menu += "[ldat]"
|
||||
|
||||
else
|
||||
menu += "ERROR: Unable to determine current location."
|
||||
menu += "<br><br><A href='byond://?src=[REF(src)];choice=49'>Refresh GPS Locator</a>"
|
||||
|
||||
if (53) // Newscaster
|
||||
menu = "<h4>[PDAIMG(notes)] Newscaster Access</h4>"
|
||||
menu += "<br> Current Newsfeed: <A href='byond://?src=[REF(src)];choice=Newscaster Switch Channel'>[current_channel ? current_channel : "None"]</a> <br>"
|
||||
var/datum/newscaster/feed_channel/current
|
||||
for(var/datum/newscaster/feed_channel/chan in GLOB.news_network.network_channels)
|
||||
if (chan.channel_name == current_channel)
|
||||
current = chan
|
||||
if(!current)
|
||||
menu += "<h5> ERROR : NO CHANNEL FOUND </h5>"
|
||||
return
|
||||
var/i = 1
|
||||
for(var/datum/newscaster/feed_message/msg in current.messages)
|
||||
menu +="-[msg.returnBody(-1)] <BR><FONT SIZE=1>\[Story by <FONT COLOR='maroon'>[msg.returnAuthor(-1)]</FONT>\]</FONT><BR>"
|
||||
menu +="<b><font size=1>[msg.comments.len] comment[msg.comments.len > 1 ? "s" : ""]</font></b><br>"
|
||||
if(msg.img)
|
||||
user << browse_rsc(msg.img, "tmp_photo[i].png")
|
||||
menu +="<img src='tmp_photo[i].png' width = '180'><BR>"
|
||||
i++
|
||||
for(var/datum/newscaster/feed_comment/comment in msg.comments)
|
||||
menu +="<font size=1><small>[comment.body]</font><br><font size=1><small><small><small>[comment.author] [comment.time_stamp]</small></small></small></small></font><br>"
|
||||
menu += "<br> <A href='byond://?src=[REF(src)];choice=Newscaster Message'>Post Message</a>"
|
||||
|
||||
if (54) // Beepsky, Medibot, Floorbot, and Cleanbot access
|
||||
menu = "<h4>[PDAIMG(medbot)] Bots Interlink</h4>"
|
||||
bot_control()
|
||||
if (99) //Newscaster message permission error
|
||||
menu = "<h5> ERROR : NOT AUTHORIZED [host_pda.id ? "" : "- ID SLOT EMPTY"] </h5>"
|
||||
|
||||
return menu
|
||||
|
||||
/obj/item/cartridge/Topic(href, href_list)
|
||||
..()
|
||||
|
||||
if(!usr.canUseTopic(src, !issilicon(usr)))
|
||||
usr.unset_machine()
|
||||
usr << browse(null, "window=pda")
|
||||
return
|
||||
|
||||
switch(href_list["choice"])
|
||||
if("Medical Records")
|
||||
active1 = find_record("id", href_list["target"], GLOB.data_core.general)
|
||||
if(active1)
|
||||
active2 = find_record("id", href_list["target"], GLOB.data_core.medical)
|
||||
host_pda.mode = 441
|
||||
if(!active2)
|
||||
active1 = null
|
||||
playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
|
||||
|
||||
if("Security Records")
|
||||
active1 = find_record("id", href_list["target"], GLOB.data_core.general)
|
||||
if(active1)
|
||||
active3 = find_record("id", href_list["target"], GLOB.data_core.security)
|
||||
host_pda.mode = 451
|
||||
if(!active3)
|
||||
active1 = null
|
||||
playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
|
||||
|
||||
if("Send Signal")
|
||||
INVOKE_ASYNC(radio, /obj/item/integrated_signaler.proc/send_activation)
|
||||
playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
|
||||
|
||||
if("Signal Frequency")
|
||||
var/new_frequency = sanitize_frequency(radio.frequency + text2num(href_list["sfreq"]))
|
||||
radio.set_frequency(new_frequency)
|
||||
playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
|
||||
|
||||
if("Signal Code")
|
||||
radio.code += text2num(href_list["scode"])
|
||||
radio.code = round(radio.code)
|
||||
radio.code = min(100, radio.code)
|
||||
radio.code = max(1, radio.code)
|
||||
playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
|
||||
|
||||
if("Status")
|
||||
switch(href_list["statdisp"])
|
||||
if("message")
|
||||
post_status("message", message1, message2)
|
||||
if("alert")
|
||||
post_status("alert", href_list["alert"])
|
||||
if("setmsg1")
|
||||
message1 = reject_bad_text(input("Line 1", "Enter Message Text", message1) as text|null, 40)
|
||||
updateSelfDialog()
|
||||
if("setmsg2")
|
||||
message2 = reject_bad_text(input("Line 2", "Enter Message Text", message2) as text|null, 40)
|
||||
updateSelfDialog()
|
||||
else
|
||||
post_status(href_list["statdisp"])
|
||||
playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
|
||||
|
||||
if("Power Select")
|
||||
var/pnum = text2num(href_list["target"])
|
||||
powmonitor = powermonitors[pnum]
|
||||
host_pda.mode = 433
|
||||
playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
|
||||
|
||||
if("Supply Orders")
|
||||
host_pda.mode =47
|
||||
playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
|
||||
|
||||
if("Newscaster Access")
|
||||
host_pda.mode = 53
|
||||
playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
|
||||
|
||||
if("Newscaster Message")
|
||||
var/host_pda_owner_name = host_pda.id ? "[host_pda.id.registered_name] ([host_pda.id.assignment])" : "Unknown"
|
||||
var/message = host_pda.msg_input()
|
||||
var/datum/newscaster/feed_channel/current
|
||||
for(var/datum/newscaster/feed_channel/chan in GLOB.news_network.network_channels)
|
||||
if (chan.channel_name == current_channel)
|
||||
current = chan
|
||||
if(current.locked && current.author != host_pda_owner_name)
|
||||
host_pda.mode = 99
|
||||
host_pda.Topic(null,list("choice"="Refresh"))
|
||||
return
|
||||
GLOB.news_network.SubmitArticle(message,host_pda.owner,current_channel)
|
||||
host_pda.Topic(null,list("choice"=num2text(host_pda.mode)))
|
||||
return
|
||||
playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
|
||||
|
||||
if("Newscaster Switch Channel")
|
||||
current_channel = host_pda.msg_input()
|
||||
host_pda.Topic(null,list("choice"=num2text(host_pda.mode)))
|
||||
return
|
||||
playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
|
||||
|
||||
//Bot control section! Viciously ripped from radios for being laggy and terrible.
|
||||
if(href_list["op"])
|
||||
switch(href_list["op"])
|
||||
|
||||
if("control")
|
||||
active_bot = locate(href_list["bot"])
|
||||
|
||||
if("botlist")
|
||||
active_bot = null
|
||||
|
||||
if("summon") //Args are in the correct order, they are stated here just as an easy reminder.
|
||||
active_bot.bot_control(command= "summon", user_turf= get_turf(usr), user_access= host_pda.GetAccess())
|
||||
|
||||
else //Forward all other bot commands to the bot itself!
|
||||
active_bot.bot_control(command= href_list["op"], user= usr)
|
||||
playsound(src, 'sound/machines/terminal_select.ogg', 50, 1)
|
||||
|
||||
if(href_list["mule"]) //MULEbots are special snowflakes, and need different args due to how they work.
|
||||
|
||||
active_bot.bot_control(href_list["mule"], usr, TRUE)
|
||||
|
||||
if(!host_pda)
|
||||
return
|
||||
host_pda.attack_self(usr)
|
||||
|
||||
|
||||
/obj/item/cartridge/proc/bot_control()
|
||||
|
||||
|
||||
var/mob/living/simple_animal/bot/Bot
|
||||
|
||||
if(active_bot)
|
||||
menu += "<B>[active_bot]</B><BR> Status: (<A href='byond://?src=[REF(src)];op=control;bot=[REF(active_bot)]'>[PDAIMG(refresh)]<i>refresh</i></A>)<BR>"
|
||||
menu += "Model: [active_bot.model]<BR>"
|
||||
menu += "Location: [get_area(active_bot)]<BR>"
|
||||
menu += "Mode: [active_bot.get_mode()]"
|
||||
if(active_bot.allow_pai)
|
||||
menu += "<BR>pAI: "
|
||||
if(active_bot.paicard && active_bot.paicard.pai)
|
||||
menu += "[active_bot.paicard.pai.name]"
|
||||
if(active_bot.bot_core.allowed(usr))
|
||||
menu += " (<A href='byond://?src=[REF(src)];op=ejectpai'><i>eject</i></A>)"
|
||||
else
|
||||
menu += "<i>none</i>"
|
||||
|
||||
//MULEs!
|
||||
if(active_bot.bot_type == MULE_BOT)
|
||||
var/mob/living/simple_animal/bot/mulebot/MULE = active_bot
|
||||
var/atom/Load = MULE.load
|
||||
menu += "<BR>Current Load: [ !Load ? "<i>none</i>" : "[Load.name] (<A href='byond://?src=[REF(src)];mule=unload'><i>unload</i></A>)" ]<BR>"
|
||||
menu += "Destination: [MULE.destination ? MULE.destination : "<i>None</i>"] (<A href='byond://?src=[REF(src)];mule=destination'><i>set</i></A>)<BR>"
|
||||
menu += "Set ID: [MULE.suffix] <A href='byond://?src=[REF(src)];mule=setid'><i> Modify</i></A><BR>"
|
||||
menu += "Power: [MULE.cell ? MULE.cell.percent() : 0]%<BR>"
|
||||
menu += "Home: [!MULE.home_destination ? "<i>none</i>" : MULE.home_destination ]<BR>"
|
||||
menu += "Delivery Reporting: <A href='byond://?src=[REF(src)];mule=report'>[MULE.report_delivery ? "(<B>On</B>)": "(<B>Off</B>)"]</A><BR>"
|
||||
menu += "Auto Return Home: <A href='byond://?src=[REF(src)];mule=autoret'>[MULE.auto_return ? "(<B>On</B>)": "(<B>Off</B>)"]</A><BR>"
|
||||
menu += "Auto Pickup Crate: <A href='byond://?src=[REF(src)];mule=autopick'>[MULE.auto_pickup ? "(<B>On</B>)": "(<B>Off</B>)"]</A><BR><BR>" //Hue.
|
||||
|
||||
menu += "\[<A href='byond://?src=[REF(src)];mule=stop'>Stop</A>\] "
|
||||
menu += "\[<A href='byond://?src=[REF(src)];mule=go'>Proceed</A>\] "
|
||||
menu += "\[<A href='byond://?src=[REF(src)];mule=home'>Return Home</A>\]<BR>"
|
||||
|
||||
else
|
||||
menu += "<BR>\[<A href='byond://?src=[REF(src)];op=patroloff'>Stop Patrol</A>\] " //patrolon
|
||||
menu += "\[<A href='byond://?src=[REF(src)];op=patrolon'>Start Patrol</A>\] " //patroloff
|
||||
menu += "\[<A href='byond://?src=[REF(src)];op=summon'>Summon Bot</A>\]<BR>" //summon
|
||||
menu += "Keep an ID inserted to upload access codes upon summoning."
|
||||
|
||||
menu += "<HR><A href='byond://?src=[REF(src)];op=botlist'>[PDAIMG(back)]Return to bot list</A>"
|
||||
else
|
||||
menu += "<BR><A href='byond://?src=[REF(src)];op=botlist'>[PDAIMG(refresh)]Scan for active bots</A><BR><BR>"
|
||||
var/turf/current_turf = get_turf(src)
|
||||
var/zlevel = current_turf.z
|
||||
var/botcount = 0
|
||||
for(Bot in GLOB.alive_mob_list) //Git da botz
|
||||
if(!Bot.on || Bot.z != zlevel || Bot.remote_disabled || !(bot_access_flags & Bot.bot_type)) //Only non-emagged bots on the same Z-level are detected!
|
||||
continue //Also, the PDA must have access to the bot type.
|
||||
menu += "<A href='byond://?src=[REF(src)];op=control;bot=[REF(Bot)]'><b>[Bot.name]</b> ([Bot.get_mode()])<BR>"
|
||||
botcount++
|
||||
if(!botcount) //No bots at all? Lame.
|
||||
menu += "No bots found.<BR>"
|
||||
return
|
||||
|
||||
return menu
|
||||
|
||||
//If the cartridge adds a special line to the top of the messaging app
|
||||
/obj/item/cartridge/proc/message_header()
|
||||
return ""
|
||||
|
||||
//If the cartridge adds something to each potetial messaging target
|
||||
/obj/item/cartridge/proc/message_special(obj/item/pda/target)
|
||||
return ""
|
||||
|
||||
//This is called for special abilities of cartridges
|
||||
/obj/item/cartridge/proc/special(mob/living/user, list/params)
|
||||
@@ -0,0 +1,108 @@
|
||||
/obj/item/cartridge/virus
|
||||
name = "Generic Virus PDA cart"
|
||||
var/charges = 5
|
||||
|
||||
/obj/item/cartridge/virus/proc/send_virus(obj/item/pda/target, mob/living/U)
|
||||
return
|
||||
|
||||
/obj/item/cartridge/virus/message_header()
|
||||
return "<b>[charges] viral files left.</b><HR>"
|
||||
|
||||
/obj/item/cartridge/virus/message_special(obj/item/pda/target)
|
||||
if (!istype(loc, /obj/item/pda))
|
||||
return "" //Sanity check, this shouldn't be possible.
|
||||
return " (<a href='byond://?src=[REF(loc)];choice=cart;special=virus;target=[REF(target)]'>*Send Virus*</a>)"
|
||||
|
||||
/obj/item/cartridge/virus/special(mob/living/user, list/params)
|
||||
var/obj/item/pda/P = locate(params["target"])//Leaving it alone in case it may do something useful, I guess.
|
||||
send_virus(P,user)
|
||||
|
||||
/obj/item/cartridge/virus/clown
|
||||
name = "\improper Honkworks 5.0 cartridge"
|
||||
icon_state = "cart-clown"
|
||||
desc = "A data cartridge for portable microcomputers. It smells vaguely of bananas."
|
||||
access = CART_CLOWN
|
||||
|
||||
/obj/item/cartridge/virus/clown/send_virus(obj/item/pda/target, mob/living/U)
|
||||
if(charges <= 0)
|
||||
to_chat(U, "<span class='notice'>Out of charges.</span>")
|
||||
return
|
||||
if(!isnull(target) && !target.toff)
|
||||
charges--
|
||||
to_chat(U, "<span class='notice'>Virus Sent!</span>")
|
||||
target.honkamt = (rand(15,20))
|
||||
else
|
||||
to_chat(U, "PDA not found.")
|
||||
|
||||
/obj/item/cartridge/virus/mime
|
||||
name = "\improper Gestur-O 1000 cartridge"
|
||||
icon_state = "cart-mi"
|
||||
access = CART_MIME
|
||||
|
||||
/obj/item/cartridge/virus/mime/send_virus(obj/item/pda/target, mob/living/U)
|
||||
if(charges <= 0)
|
||||
to_chat(U, "<span class='notice'>Out of charges.</span>")
|
||||
return
|
||||
if(!isnull(target) && !target.toff)
|
||||
charges--
|
||||
to_chat(U, "<span class='notice'>Virus Sent!</span>")
|
||||
target.silent = TRUE
|
||||
target.ttone = "silence"
|
||||
else
|
||||
to_chat(U, "PDA not found.")
|
||||
|
||||
/obj/item/cartridge/virus/syndicate
|
||||
name = "\improper Detomatix cartridge"
|
||||
access = CART_REMOTE_DOOR
|
||||
remote_door_id = "smindicate" //Make sure this matches the syndicate shuttle's shield/door id!! //don't ask about the name, testing.
|
||||
charges = 4
|
||||
|
||||
/obj/item/cartridge/virus/syndicate/send_virus(obj/item/pda/target, mob/living/U)
|
||||
if(charges <= 0)
|
||||
to_chat(U, "<span class='notice'>Out of charges.</span>")
|
||||
return
|
||||
if(!isnull(target) && !target.toff)
|
||||
charges--
|
||||
var/difficulty = 0
|
||||
if(target.cartridge)
|
||||
difficulty += BitCount(target.cartridge.access&(CART_MEDICAL | CART_SECURITY | CART_ENGINE | CART_CLOWN | CART_JANITOR | CART_MANIFEST))
|
||||
if(target.cartridge.access & CART_MANIFEST)
|
||||
difficulty++ //if cartridge has manifest access it has extra snowflake difficulty
|
||||
else
|
||||
difficulty += 2
|
||||
var/datum/component/uplink/hidden_uplink = target.GetComponent(/datum/component/uplink)
|
||||
if(!target.detonatable || prob(difficulty * 15) || (hidden_uplink))
|
||||
U.show_message("<span class='danger'>An error flashes on your [src].</span>", 1)
|
||||
else
|
||||
message_admins("[!is_special_character(U) ? "Non-antag " : ""][ADMIN_LOOKUPFLW(U)] triggered a PDA explosion on [target.name] at [ADMIN_VERBOSEJMP(target)].")
|
||||
var/message_log = "triggered a PDA explosion on [target.name] at [AREACOORD(target)]."
|
||||
U.log_message(message_log, LOG_ATTACK)
|
||||
U.show_message("<span class='notice'>Success!</span>", 1)
|
||||
target.explode()
|
||||
else
|
||||
to_chat(U, "PDA not found.")
|
||||
|
||||
/obj/item/cartridge/virus/frame
|
||||
name = "\improper F.R.A.M.E. cartridge"
|
||||
icon_state = "cart-f"
|
||||
var/telecrystals = 0
|
||||
|
||||
/obj/item/cartridge/virus/frame/send_virus(obj/item/pda/target, mob/living/U)
|
||||
if(charges <= 0)
|
||||
to_chat(U, "<span class='notice'>Out of charges.</span>")
|
||||
return
|
||||
if(!isnull(target) && !target.toff)
|
||||
charges--
|
||||
var/lock_code = "[rand(100,999)] [pick(GLOB.phonetic_alphabet)]"
|
||||
to_chat(U, "<span class='notice'>Virus Sent! The unlock code to the target is: [lock_code]</span>")
|
||||
var/datum/component/uplink/hidden_uplink = target.GetComponent(/datum/component/uplink)
|
||||
if(!hidden_uplink)
|
||||
hidden_uplink = target.AddComponent(/datum/component/uplink)
|
||||
hidden_uplink.unlock_code = lock_code
|
||||
else
|
||||
hidden_uplink.hidden_crystals += hidden_uplink.telecrystals //Temporarially hide the PDA's crystals, so you can't steal telecrystals.
|
||||
hidden_uplink.telecrystals = telecrystals
|
||||
telecrystals = 0
|
||||
hidden_uplink.active = TRUE
|
||||
else
|
||||
to_chat(U, "PDA not found.")
|
||||
@@ -0,0 +1,176 @@
|
||||
/obj/item/chameleon
|
||||
name = "chameleon-projector"
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "shield0"
|
||||
flags_1 = CONDUCT_1
|
||||
item_flags = NOBLUDGEON
|
||||
slot_flags = ITEM_SLOT_BELT
|
||||
item_state = "electronic"
|
||||
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
|
||||
throwforce = 5
|
||||
throw_speed = 3
|
||||
throw_range = 5
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
var/can_use = 1
|
||||
var/obj/effect/dummy/chameleon/active_dummy = null
|
||||
var/saved_appearance = null
|
||||
|
||||
/obj/item/chameleon/New()
|
||||
..()
|
||||
var/obj/item/cigbutt/butt = /obj/item/cigbutt
|
||||
saved_appearance = initial(butt.appearance)
|
||||
|
||||
/obj/item/chameleon/dropped()
|
||||
..()
|
||||
disrupt()
|
||||
|
||||
/obj/item/chameleon/equipped()
|
||||
..()
|
||||
disrupt()
|
||||
|
||||
/obj/item/chameleon/attack_self(mob/user)
|
||||
if (isturf(user.loc) || istype(user.loc, /obj/structure) || active_dummy)
|
||||
toggle(user)
|
||||
else
|
||||
to_chat(user, "<span class='warning'>You can't use [src] while inside something!</span>")
|
||||
|
||||
/obj/item/chameleon/afterattack(atom/target, mob/user , proximity)
|
||||
. = ..()
|
||||
if(!proximity)
|
||||
return
|
||||
if(!check_sprite(target))
|
||||
return
|
||||
if(active_dummy)//I now present you the blackli(f)st
|
||||
return
|
||||
if(isturf(target))
|
||||
return
|
||||
if(ismob(target))
|
||||
return
|
||||
if(istype(target, /obj/structure/falsewall))
|
||||
return
|
||||
if(iseffect(target))
|
||||
if(!(istype(target, /obj/effect/decal))) //be a footprint
|
||||
return
|
||||
playsound(get_turf(src), 'sound/weapons/flash.ogg', 100, 1, -6)
|
||||
to_chat(user, "<span class='notice'>Scanned [target].</span>")
|
||||
var/obj/temp = new/obj()
|
||||
temp.appearance = target.appearance
|
||||
temp.layer = initial(target.layer) // scanning things in your inventory
|
||||
temp.plane = initial(target.plane)
|
||||
saved_appearance = temp.appearance
|
||||
|
||||
/obj/item/chameleon/proc/check_sprite(atom/target)
|
||||
if(target.icon_state in icon_states(target.icon))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/item/chameleon/proc/toggle(mob/user)
|
||||
if(!can_use || !saved_appearance)
|
||||
return
|
||||
if(active_dummy)
|
||||
eject_all()
|
||||
playsound(get_turf(src), 'sound/effects/pop.ogg', 100, 1, -6)
|
||||
qdel(active_dummy)
|
||||
active_dummy = null
|
||||
to_chat(user, "<span class='notice'>You deactivate \the [src].</span>")
|
||||
new /obj/effect/temp_visual/emp/pulse(get_turf(src))
|
||||
else
|
||||
playsound(get_turf(src), 'sound/effects/pop.ogg', 100, 1, -6)
|
||||
var/obj/effect/dummy/chameleon/C = new/obj/effect/dummy/chameleon(user.drop_location())
|
||||
C.activate(user, saved_appearance, src)
|
||||
to_chat(user, "<span class='notice'>You activate \the [src].</span>")
|
||||
new /obj/effect/temp_visual/emp/pulse(get_turf(src))
|
||||
user.cancel_camera()
|
||||
|
||||
/obj/item/chameleon/proc/disrupt(delete_dummy = 1)
|
||||
if(active_dummy)
|
||||
for(var/mob/M in active_dummy)
|
||||
to_chat(M, "<span class='danger'>Your chameleon-projector deactivates.</span>")
|
||||
var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread
|
||||
spark_system.set_up(5, 0, src)
|
||||
spark_system.attach(src)
|
||||
spark_system.start()
|
||||
eject_all()
|
||||
if(delete_dummy)
|
||||
qdel(active_dummy)
|
||||
active_dummy = null
|
||||
can_use = 0
|
||||
spawn(50) can_use = 1
|
||||
|
||||
/obj/item/chameleon/proc/eject_all()
|
||||
for(var/atom/movable/A in active_dummy)
|
||||
A.forceMove(active_dummy.loc)
|
||||
if(ismob(A))
|
||||
var/mob/M = A
|
||||
M.reset_perspective(null)
|
||||
|
||||
/obj/effect/dummy/chameleon
|
||||
name = ""
|
||||
desc = ""
|
||||
density = FALSE
|
||||
var/can_move = 0
|
||||
var/obj/item/chameleon/master = null
|
||||
|
||||
/obj/effect/dummy/chameleon/proc/activate(mob/M, saved_appearance, obj/item/chameleon/C)
|
||||
appearance = saved_appearance
|
||||
if(istype(M.buckled, /obj/vehicle))
|
||||
var/obj/vehicle/V = M.buckled
|
||||
var/datum/component/riding/VRD = V.GetComponent(/datum/component/riding)
|
||||
if(VRD)
|
||||
VRD.force_dismount(M)
|
||||
else
|
||||
V.unbuckle_mob(M, force = TRUE)
|
||||
M.forceMove(src)
|
||||
master = C
|
||||
master.active_dummy = src
|
||||
|
||||
/obj/effect/dummy/chameleon/attackby()
|
||||
master.disrupt()
|
||||
|
||||
//ATTACK HAND IGNORING PARENT RETURN VALUE
|
||||
/obj/effect/dummy/chameleon/attack_hand()
|
||||
master.disrupt()
|
||||
|
||||
/obj/effect/dummy/chameleon/attack_animal()
|
||||
master.disrupt()
|
||||
|
||||
/obj/effect/dummy/chameleon/attack_slime()
|
||||
master.disrupt()
|
||||
|
||||
/obj/effect/dummy/chameleon/attack_alien()
|
||||
master.disrupt()
|
||||
|
||||
/obj/effect/dummy/chameleon/ex_act(S, T)
|
||||
contents_explosion(S, T)
|
||||
master.disrupt()
|
||||
|
||||
/obj/effect/dummy/chameleon/bullet_act()
|
||||
..()
|
||||
master.disrupt()
|
||||
|
||||
/obj/effect/dummy/chameleon/relaymove(mob/user, direction)
|
||||
if(isspaceturf(loc) || !direction)
|
||||
return //No magical space movement!
|
||||
|
||||
if(can_move < world.time)
|
||||
var/amount
|
||||
switch(user.bodytemperature)
|
||||
if(300 to INFINITY)
|
||||
amount = 10
|
||||
if(295 to 300)
|
||||
amount = 13
|
||||
if(280 to 295)
|
||||
amount = 16
|
||||
if(260 to 280)
|
||||
amount = 20
|
||||
else
|
||||
amount = 25
|
||||
|
||||
can_move = world.time + amount
|
||||
step(src, direction)
|
||||
return
|
||||
|
||||
/obj/effect/dummy/chameleon/Destroy()
|
||||
master.disrupt(0)
|
||||
return ..()
|
||||
@@ -0,0 +1,120 @@
|
||||
/obj/item/compressionkit
|
||||
name = "bluespace compression kit"
|
||||
desc = "An illegally modified BSRPED, capable of reducing the size of most items."
|
||||
icon = 'icons/obj/tools.dmi'
|
||||
icon_state = "compression_c"
|
||||
item_state = "RPED"
|
||||
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
var/charges = 5
|
||||
// var/damage_multiplier = 0.2 Not in use yet.
|
||||
var/mode = 0
|
||||
|
||||
/obj/item/compressionkit/examine(mob/user)
|
||||
..()
|
||||
to_chat(user, "<span class='notice'>It has [charges] charges left. Recharge with bluespace crystals.</span>")
|
||||
to_chat(user, "<span class='notice'>Use in-hand to swap toggle compress/expand mode (expand mode not yet implemented).</span>")
|
||||
|
||||
/obj/item/compressionkit/attack_self(mob/user)
|
||||
if(mode == 0)
|
||||
mode = 1
|
||||
icon_state = "compression_e"
|
||||
to_chat(user, "<span class='notice'>You switch the compressor to expand mode. This isn't implemented yet, so right now it wont do anything different!</span>")
|
||||
return
|
||||
if(mode == 1)
|
||||
mode = 0
|
||||
icon_state = "compression_c"
|
||||
to_chat(user, "<span class='notice'>You switch the compressor to compress mode. Usage will now reduce the size of objects.</span>")
|
||||
return
|
||||
else
|
||||
mode = 0
|
||||
icon_state = "compression_c"
|
||||
to_chat(user, "<span class='notice'>Some coder cocked up or an admin broke your compressor. It's been set back to compress mode..</span>")
|
||||
|
||||
/obj/item/compressionkit/proc/sparks()
|
||||
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
|
||||
s.set_up(5, 1, get_turf(src))
|
||||
s.start()
|
||||
|
||||
/obj/item/compressionkit/suicide_act(mob/living/carbon/M)
|
||||
M.visible_message("<span class='suicide'>[M] is sticking their head in [src] and turning it on! [M.p_theyre(TRUE)] going to compress their own skull!</span>")
|
||||
var/obj/item/bodypart/head = M.get_bodypart("head")
|
||||
if(!head)
|
||||
return
|
||||
var/turf/T = get_turf(M)
|
||||
var/list/organs = M.getorganszone("head") + M.getorganszone("eyes") + M.getorganszone("mouth")
|
||||
for(var/internal_organ in organs)
|
||||
var/obj/item/organ/I = internal_organ
|
||||
I.Remove(M)
|
||||
I.forceMove(T)
|
||||
head.drop_limb()
|
||||
qdel(head)
|
||||
new M.gib_type(T,1,M.get_static_viruses())
|
||||
M.add_splatter_floor(T)
|
||||
playsound(M, 'sound/weapons/flash.ogg', 50, 1)
|
||||
playsound(M, 'sound/effects/splat.ogg', 50, 1)
|
||||
|
||||
return OXYLOSS
|
||||
|
||||
/obj/item/compressionkit/afterattack(atom/target, mob/user, proximity)
|
||||
. = ..()
|
||||
if(!proximity || !target)
|
||||
return
|
||||
else
|
||||
if(charges == 0)
|
||||
playsound(get_turf(src), 'sound/machines/buzz-two.ogg', 50, 1)
|
||||
to_chat(user, "<span class='notice'>The bluespace compression kit is out of charges! Recharge it with bluespace crystals.</span>")
|
||||
return
|
||||
if(istype(target, /obj/item))
|
||||
var/obj/item/O = target
|
||||
if(O.w_class == 1)
|
||||
playsound(get_turf(src), 'sound/machines/buzz-two.ogg', 50, 1)
|
||||
to_chat(user, "<span class='notice'>[target] cannot be compressed smaller!.</span>")
|
||||
return
|
||||
if(O.GetComponent(/datum/component/storage))
|
||||
to_chat(user, "<span class='notice'>You feel like compressing an item that stores other items would be counterproductive.</span>")
|
||||
return
|
||||
if(O.w_class > 1)
|
||||
playsound(get_turf(src), 'sound/weapons/flash.ogg', 50, 1)
|
||||
user.visible_message("<span class='warning'>[user] is compressing [O] with their bluespace compression kit!</span>")
|
||||
if(do_mob(user, O, 40) && charges > 0 && O.w_class > 1)
|
||||
playsound(get_turf(src), 'sound/weapons/emitter2.ogg', 50, 1)
|
||||
sparks()
|
||||
flash_lighting_fx(3, 3, LIGHT_COLOR_CYAN)
|
||||
O.w_class -= 1
|
||||
// O.force_mult -= damage_multiplier
|
||||
charges -= 1
|
||||
to_chat(user, "<span class='notice'>You successfully compress [target]! The compressor now has [charges] charges.</span>")
|
||||
else
|
||||
to_chat(user, "<span class='notice'>Anomalous error. Summon a coder.</span>")
|
||||
|
||||
else if(ishuman(target) && user.zone_selected == BODY_ZONE_PRECISE_GROIN)
|
||||
var/mob/living/carbon/human/H = target
|
||||
var/obj/item/organ/genital/penis/P = H.getorganslot(ORGAN_SLOT_PENIS)
|
||||
if(!P)
|
||||
return
|
||||
playsound(get_turf(src), 'sound/weapons/flash.ogg', 50, 1)
|
||||
H.visible_message("<span class='warning'>[user] is preparing to shrink [H]\'s [P.name] with their bluespace compression kit!</span>")
|
||||
if(do_mob(user, H, 40) && charges > 0 && P.length > 0)
|
||||
H.visible_message("<span class='warning'>[user] has shrunk [H]\'s [P.name]!</span>")
|
||||
playsound(get_turf(src), 'sound/weapons/emitter2.ogg', 50, 1)
|
||||
sparks()
|
||||
flash_lighting_fx(3, 3, LIGHT_COLOR_CYAN)
|
||||
charges -= 1
|
||||
var/p_name = P.name
|
||||
P.modify_size(-5)
|
||||
if(QDELETED(P))
|
||||
H.visible_message("<span class='warning'>[H]\'s [p_name] vanishes!</span>")
|
||||
|
||||
|
||||
/obj/item/compressionkit/attackby(obj/item/I, mob/user, params)
|
||||
..()
|
||||
if(istype(I, /obj/item/stack/ore/bluespace_crystal))
|
||||
var/obj/item/stack/ore/bluespace_crystal/B = I
|
||||
charges += 2
|
||||
to_chat(user, "<span class='notice'>You insert [I] into [src]. It now has [charges] charges.</span>")
|
||||
if(B.amount > 1)
|
||||
B.amount -= 1
|
||||
else
|
||||
qdel(I)
|
||||
@@ -0,0 +1,529 @@
|
||||
// Dogborg Sleeper units
|
||||
|
||||
/obj/item/dogborg/sleeper
|
||||
name = "hound sleeper"
|
||||
desc = "nothing should see this."
|
||||
icon = 'icons/mob/dogborg.dmi'
|
||||
icon_state = "sleeper"
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
var/mob/living/carbon/patient
|
||||
var/inject_amount = 10
|
||||
var/min_health = -100
|
||||
var/cleaning = FALSE
|
||||
var/cleaning_cycles = 10
|
||||
var/patient_laststat = null
|
||||
var/list/injection_chems = list("antitoxin", "epinephrine", "salbutamol", "bicaridine", "kelotane")
|
||||
var/eject_port = "ingestion"
|
||||
var/escape_in_progress = FALSE
|
||||
var/message_cooldown
|
||||
var/breakout_time = 150
|
||||
var/tmp/last_hearcheck = 0
|
||||
var/tmp/list/hearing_mobs
|
||||
var/list/items_preserved = list()
|
||||
var/static/list/important_items = typecacheof(list(
|
||||
/obj/item/hand_tele,
|
||||
/obj/item/card/id,
|
||||
/obj/item/aicard,
|
||||
/obj/item/gun,
|
||||
/obj/item/pinpointer,
|
||||
/obj/item/clothing/shoes/magboots,
|
||||
/obj/item/clothing/head/helmet/space,
|
||||
/obj/item/clothing/suit/space,
|
||||
/obj/item/reagent_containers/hypospray/CMO,
|
||||
/obj/item/tank/jetpack/oxygen/captain,
|
||||
/obj/item/clothing/accessory/medal/gold/captain,
|
||||
/obj/item/clothing/suit/armor,
|
||||
/obj/item/documents,
|
||||
/obj/item/nuke_core,
|
||||
/obj/item/nuke_core_container,
|
||||
/obj/item/areaeditor/blueprints,
|
||||
/obj/item/documents/syndicate,
|
||||
/obj/item/disk/nuclear,
|
||||
/obj/item/bombcore,
|
||||
/obj/item/grenade,
|
||||
/obj/item/storage
|
||||
))
|
||||
|
||||
// Bags are prohibited from this due to the potential explotation of objects, same with brought
|
||||
|
||||
/obj/item/dogborg/sleeper/Initialize()
|
||||
. = ..()
|
||||
update_icon()
|
||||
item_flags |= NOBLUDGEON //No more attack messages
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/dogborg/sleeper/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
go_out() //just... sanity I guess, edge case shit
|
||||
return ..()
|
||||
|
||||
/obj/item/dogborg/sleeper/Exit(atom/movable/O)
|
||||
return 0
|
||||
|
||||
/obj/item/dogborg/sleeper/proc/get_host()
|
||||
if(!loc)
|
||||
return
|
||||
if(iscyborg(loc))
|
||||
return loc
|
||||
else if(iscyborg(loc.loc))
|
||||
return loc.loc //cursed cyborg code
|
||||
|
||||
/obj/item/dogborg/sleeper/afterattack(mob/living/carbon/target, mob/living/silicon/user, proximity)
|
||||
var/mob/living/silicon/robot/hound = get_host()
|
||||
if(!hound)
|
||||
return
|
||||
if(!proximity)
|
||||
return
|
||||
if(!iscarbon(target))
|
||||
return
|
||||
var/voracious = TRUE
|
||||
if(!target.client || !(target.client.prefs.cit_toggles & MEDIHOUND_SLEEPER) || !hound.client || !(hound.client.prefs.cit_toggles & MEDIHOUND_SLEEPER))
|
||||
voracious = FALSE
|
||||
if(target.buckled)
|
||||
to_chat(user, "<span class='warning'>The user is buckled and can not be put into your [src].</span>")
|
||||
return
|
||||
if(patient)
|
||||
to_chat(user, "<span class='warning'>Your [src] is already occupied.</span>")
|
||||
return
|
||||
user.visible_message("<span class='warning'>[hound.name] is carefully inserting [target.name] into their [src].</span>", "<span class='notice'>You start placing [target] into your [src]...</span>")
|
||||
if(!patient && iscarbon(target) && !target.buckled && do_after (user, 100, target = target))
|
||||
|
||||
if(!in_range(src, target)) //Proximity is probably old news by now, do a new check.
|
||||
return //If they moved away, you can't eat them.
|
||||
|
||||
if(patient)
|
||||
return //If you try to eat two people at once, you can only eat one.
|
||||
|
||||
else //If you don't have someone in you, proceed.
|
||||
if(!isjellyperson(target) && ("toxin" in injection_chems))
|
||||
injection_chems -= "toxin"
|
||||
injection_chems += "antitoxin"
|
||||
if(isjellyperson(target) && !("toxin" in injection_chems))
|
||||
injection_chems -= "antitoxin"
|
||||
injection_chems += "toxin"
|
||||
target.forceMove(src)
|
||||
target.reset_perspective(src)
|
||||
target.ExtinguishMob() //The tongue already puts out fire stacks but being put into the sleeper shouldn't allow you to keep burning.
|
||||
update_gut(hound)
|
||||
user.visible_message("<span class='warning'>[voracious ? "[hound]'s [src.name] lights up and expands as [target] slips inside into their [src.name]." : "[hound]'s sleeper indicator lights up as [target] is scooped up into [hound.p_their()] [src]."]</span>", \
|
||||
"<span class='notice'>Your [voracious ? "[src.name] lights up as [target] slips into" : "sleeper indicator light shines brightly as [target] is scooped inside"] your [src]. Life support functions engaged.</span>")
|
||||
message_admins("[key_name(hound)] has sleeper'd [key_name(patient)] as a dogborg. [ADMIN_JMP(src)]")
|
||||
playsound(hound, 'sound/effects/bin_close.ogg', 100, 1)
|
||||
|
||||
/obj/item/dogborg/sleeper/container_resist(mob/living/user)
|
||||
var/mob/living/silicon/robot/hound = get_host()
|
||||
if(!hound)
|
||||
go_out(user)
|
||||
return
|
||||
user.changeNext_move(CLICK_CD_BREAKOUT)
|
||||
user.last_special = world.time + CLICK_CD_BREAKOUT
|
||||
if(user.a_intent == INTENT_HELP)
|
||||
return
|
||||
var/voracious = TRUE
|
||||
if(!user.client || !(user.client.prefs.cit_toggles & MEDIHOUND_SLEEPER) || !hound.client || !(hound.client.prefs.cit_toggles & MEDIHOUND_SLEEPER))
|
||||
voracious = FALSE
|
||||
user.visible_message("<span class='notice'>You see [voracious ? "[user] struggling against the expanded material of [hound]'s gut!" : "and hear [user] pounding against something inside of [hound]'s [src.name]!"]</span>", \
|
||||
"<span class='notice'>[voracious ? "You start struggling inside of [src]'s tight, flexible confines," : "You start pounding against the metallic walls of [src],"] trying to trigger the release... (this will take about [DisplayTimeText(breakout_time)].)</span>", \
|
||||
"<span class='italics'>You hear a [voracious ? "couple of thumps" : "loud banging noise"] coming from within [hound].</span>")
|
||||
if(do_after(user, breakout_time, target = src))
|
||||
user.visible_message("<span class='warning'>[user] successfully broke out of [hound.name]!</span>", \
|
||||
"<span class='notice'>You successfully break out of [hound.name]!</span>")
|
||||
go_out(user, hound)
|
||||
|
||||
/obj/item/dogborg/sleeper/proc/go_out(atom/movable/target, mob/living/silicon/robot/hound)
|
||||
var/voracious = hound ? TRUE : FALSE
|
||||
var/list/targets = target && hound ? list(target) : contents
|
||||
if(hound)
|
||||
if(!hound.client || !(hound.client.prefs.cit_toggles & MEDIHOUND_SLEEPER))
|
||||
voracious = FALSE
|
||||
else
|
||||
for(var/mob/M in targets)
|
||||
if(!M.client || !(M.client.prefs.cit_toggles & MEDIHOUND_SLEEPER))
|
||||
voracious = FALSE
|
||||
if(length(targets))
|
||||
if(hound)
|
||||
hound.visible_message("<span class='warning'>[voracious ? "[hound] empties out [hound.p_their()] contents via [hound.p_their()] release port." : "[hound]'s underside slides open with an audible clunk before [hound.p_their()] [src] flips over, carelessly dumping its contents onto the ground below [hound.p_them()] before closing right back up again."]</span>", \
|
||||
"<span class='notice'>[voracious ? "You empty your contents via your release port." : "You open your sleeper hatch, quickly releasing all of the contents within before closing it again."]</span>")
|
||||
for(var/a in contents)
|
||||
var/atom/movable/AM = a
|
||||
AM.forceMove(get_turf(src))
|
||||
if(ismob(AM))
|
||||
var/mob/M = AM
|
||||
M.reset_perspective()
|
||||
playsound(loc, voracious ? 'sound/effects/splat.ogg' : 'sound/effects/bin_close.ogg', 50, 1)
|
||||
items_preserved.Cut()
|
||||
cleaning = FALSE
|
||||
if(hound)
|
||||
update_gut(hound)
|
||||
|
||||
|
||||
/obj/item/dogborg/sleeper/attack_self(mob/user)
|
||||
. = ..()
|
||||
if(. || !iscyborg(user))
|
||||
return
|
||||
ui_interact(user)
|
||||
|
||||
/obj/item/dogborg/sleeper/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
|
||||
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state)
|
||||
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "dogborg_sleeper", name, 375, 550, master_ui, state)
|
||||
ui.open()
|
||||
|
||||
/obj/item/dogborg/sleeper/ui_data()
|
||||
var/list/data = list()
|
||||
data["occupied"] = patient ? 1 : 0
|
||||
|
||||
if(cleaning && length(contents - items_preserved))
|
||||
data["items"] = "Self-cleaning mode active: [length(contents - items_preserved)] object(s) remaining."
|
||||
data["cleaning"] = cleaning
|
||||
if(injection_chems != null)
|
||||
data["chem"] = list()
|
||||
for(var/chem in injection_chems)
|
||||
var/datum/reagent/R = GLOB.chemical_reagents_list[chem]
|
||||
data["chem"] += list(list("name" = R.name, "id" = R.id))
|
||||
|
||||
data["occupant"] = list()
|
||||
var/mob/living/mob_occupant = patient
|
||||
if(mob_occupant)
|
||||
data["occupant"]["name"] = mob_occupant.name
|
||||
switch(mob_occupant.stat)
|
||||
if(CONSCIOUS)
|
||||
data["occupant"]["stat"] = "Conscious"
|
||||
data["occupant"]["statstate"] = "good"
|
||||
if(SOFT_CRIT)
|
||||
data["occupant"]["stat"] = "Conscious"
|
||||
data["occupant"]["statstate"] = "average"
|
||||
if(UNCONSCIOUS)
|
||||
data["occupant"]["stat"] = "Unconscious"
|
||||
data["occupant"]["statstate"] = "average"
|
||||
if(DEAD)
|
||||
data["occupant"]["stat"] = "Dead"
|
||||
data["occupant"]["statstate"] = "bad"
|
||||
data["occupant"]["health"] = mob_occupant.health
|
||||
data["occupant"]["maxHealth"] = mob_occupant.maxHealth
|
||||
data["occupant"]["minHealth"] = HEALTH_THRESHOLD_DEAD
|
||||
data["occupant"]["bruteLoss"] = mob_occupant.getBruteLoss()
|
||||
data["occupant"]["oxyLoss"] = mob_occupant.getOxyLoss()
|
||||
data["occupant"]["toxLoss"] = mob_occupant.getToxLoss()
|
||||
data["occupant"]["fireLoss"] = mob_occupant.getFireLoss()
|
||||
data["occupant"]["cloneLoss"] = mob_occupant.getCloneLoss()
|
||||
data["occupant"]["brainLoss"] = mob_occupant.getOrganLoss(ORGAN_SLOT_BRAIN)
|
||||
data["occupant"]["reagents"] = list()
|
||||
if(mob_occupant.reagents.reagent_list.len)
|
||||
for(var/datum/reagent/R in mob_occupant.reagents.reagent_list)
|
||||
data["occupant"]["reagents"] += list(list("name" = R.name, "volume" = R.volume))
|
||||
return data
|
||||
|
||||
/obj/item/dogborg/sleeper/ui_act(action, params)
|
||||
. = ..()
|
||||
if(. || !iscyborg(usr))
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("eject")
|
||||
go_out(null, usr)
|
||||
. = TRUE
|
||||
if("inject")
|
||||
var/chem = params["chem"]
|
||||
if(!patient)
|
||||
return
|
||||
inject_chem(chem, usr)
|
||||
. = TRUE
|
||||
if("cleaning")
|
||||
if(!contents)
|
||||
to_chat(src, "Your [src] is already cleaned.")
|
||||
return
|
||||
if(patient)
|
||||
to_chat(patient, "<span class='danger'>[usr.name]'s [src] fills with caustic enzymes around you!</span>")
|
||||
to_chat(src, "<span class='danger'>Cleaning process enabled.</span>")
|
||||
clean_cycle(usr)
|
||||
. = TRUE
|
||||
|
||||
/obj/item/dogborg/sleeper/proc/update_gut(mob/living/silicon/robot/hound)
|
||||
//Well, we HAD one, what happened to them?
|
||||
var/prociconupdate = FALSE
|
||||
var/currentenvy = hound.sleeper_nv
|
||||
hound.sleeper_nv = FALSE
|
||||
if(patient in contents)
|
||||
if(patient_laststat != patient.stat)
|
||||
if(patient.stat & DEAD)
|
||||
hound.sleeper_r = 1
|
||||
hound.sleeper_g = 0
|
||||
patient_laststat = patient.stat
|
||||
else
|
||||
hound.sleeper_r = 0
|
||||
hound.sleeper_g = 1
|
||||
patient_laststat = patient.stat
|
||||
prociconupdate = TRUE
|
||||
|
||||
if(!patient.client || !(patient.client.prefs.cit_toggles & MEDIHOUND_SLEEPER) || !hound.client || !(hound.client.prefs.cit_toggles & MEDIHOUND_SLEEPER))
|
||||
hound.sleeper_nv = TRUE
|
||||
else
|
||||
hound.sleeper_nv = FALSE
|
||||
if(hound.sleeper_nv != currentenvy)
|
||||
prociconupdate = TRUE
|
||||
|
||||
//Update icon
|
||||
if(prociconupdate)
|
||||
hound.update_icons()
|
||||
//Return original patient
|
||||
return(patient)
|
||||
//Check for a new patient
|
||||
else
|
||||
for(var/mob/living/carbon/human/C in contents)
|
||||
patient = C
|
||||
if(patient.stat & DEAD)
|
||||
hound.sleeper_r = 1
|
||||
hound.sleeper_g = 0
|
||||
patient_laststat = patient.stat
|
||||
else
|
||||
hound.sleeper_r = 0
|
||||
hound.sleeper_g = 1
|
||||
patient_laststat = patient.stat
|
||||
|
||||
if(!patient.client || !(patient.client.prefs.cit_toggles & MEDIHOUND_SLEEPER) || !hound.client || !(hound.client.prefs.cit_toggles & MEDIHOUND_SLEEPER))
|
||||
hound.sleeper_nv = TRUE
|
||||
else
|
||||
hound.sleeper_nv = FALSE
|
||||
|
||||
//Update icon and return new patient
|
||||
hound.update_icons()
|
||||
return
|
||||
|
||||
//Cleaning looks better with red on, even with nobody in it
|
||||
if(cleaning && !patient)
|
||||
hound.sleeper_r = 1
|
||||
hound.sleeper_g = 0
|
||||
//Couldn't find anyone, and not cleaning
|
||||
else if(!cleaning && !patient)
|
||||
hound.sleeper_r = 0
|
||||
hound.sleeper_g = 0
|
||||
|
||||
patient_laststat = null
|
||||
patient = null
|
||||
hound.update_icons()
|
||||
|
||||
//Gurgleborg process
|
||||
/obj/item/dogborg/sleeper/proc/clean_cycle(mob/living/silicon/robot/hound)
|
||||
//Sanity
|
||||
if(!hound)
|
||||
return
|
||||
for(var/I in items_preserved)
|
||||
if(!(I in contents))
|
||||
items_preserved -= I
|
||||
var/list/touchable_items = contents - items_preserved
|
||||
var/sound/prey_digest = sound(get_sfx("digest_prey"))
|
||||
var/sound/prey_death = sound(get_sfx("death_prey"))
|
||||
var/sound/pred_digest = sound(get_sfx("digest_pred"))
|
||||
var/sound/pred_death = sound(get_sfx("death_pred"))
|
||||
if(cleaning_cycles)
|
||||
cleaning_cycles--
|
||||
cleaning = TRUE
|
||||
for(var/mob/living/carbon/C in (touchable_items))
|
||||
if((C.status_flags & GODMODE) || !C.digestable)
|
||||
items_preserved += C
|
||||
else
|
||||
C.adjustBruteLoss(2)
|
||||
C.adjustFireLoss(3)
|
||||
if(contents)
|
||||
var/atom/target = pick(touchable_items)
|
||||
if(iscarbon(target)) //Handle the target being a mob
|
||||
var/mob/living/carbon/T = target
|
||||
if(T.stat == DEAD && T.digestable) //Mob is now dead
|
||||
message_admins("[key_name(hound)] has digested [key_name(T)] as a dogborg. ([hound ? "<a href='?_src_=holder;adminplayerobservecoodjump=1;X=[hound.x];Y=[hound.y];Z=[hound.z]'>JMP</a>" : "null"])")
|
||||
to_chat(hound,"<span class='notice'>You feel your belly slowly churn around [T], breaking them down into a soft slurry to be used as power for your systems.</span>")
|
||||
to_chat(T,"<span class='notice'>You feel [hound]'s belly slowly churn around your form, breaking you down into a soft slurry to be used as power for [hound]'s systems.</span>")
|
||||
hound.cell.give(30000) //Fueeeeellll
|
||||
if((world.time - NORMIE_HEARCHECK) > last_hearcheck)
|
||||
var/turf/source = get_turf(hound)
|
||||
LAZYCLEARLIST(hearing_mobs)
|
||||
for(var/mob/H in get_hearers_in_view(3, source))
|
||||
if(!H.client || !(H.client.prefs.cit_toggles & DIGESTION_NOISES))
|
||||
continue
|
||||
LAZYADD(hearing_mobs, H)
|
||||
last_hearcheck = world.time
|
||||
for(var/mob/H in hearing_mobs)
|
||||
if(!istype(H.loc, /obj/item/dogborg/sleeper))
|
||||
H.playsound_local(source, null, 45, falloff = 0, S = pred_death)
|
||||
else if(H in contents)
|
||||
H.playsound_local(source, null, 65, falloff = 0, S = prey_death)
|
||||
for(var/belly in T.vore_organs)
|
||||
var/obj/belly/B = belly
|
||||
for(var/atom/movable/thing in B)
|
||||
thing.forceMove(src)
|
||||
if(ismob(thing))
|
||||
to_chat(thing, "As [T] melts away around you, you find yourself in [hound]'s [name]")
|
||||
for(var/obj/item/W in T)
|
||||
if(!T.dropItemToGround(W))
|
||||
qdel(W)
|
||||
qdel(T)
|
||||
//Handle the target being anything but a mob
|
||||
else if(isobj(target))
|
||||
var/obj/T = target
|
||||
if(T.type in important_items) //If the object is in the items_preserved global list
|
||||
items_preserved += T
|
||||
//If the object is not one to preserve
|
||||
else
|
||||
qdel(T)
|
||||
update_gut()
|
||||
hound.cell.give(10)
|
||||
else
|
||||
cleaning_cycles = initial(cleaning_cycles)
|
||||
cleaning = FALSE
|
||||
to_chat(hound, "<span class='notice'>Your [src] chimes it ends its self-cleaning cycle.</span>")//Belly is entirely empty
|
||||
|
||||
if(!length(contents))
|
||||
to_chat(hound, "<span class='notice'>Your [src] is now clean. Ending self-cleaning cycle.</span>")
|
||||
cleaning = FALSE
|
||||
|
||||
//sound effects
|
||||
if(prob(50))
|
||||
if((world.time - NORMIE_HEARCHECK) > last_hearcheck)
|
||||
var/turf/source = get_turf(hound)
|
||||
LAZYCLEARLIST(hearing_mobs)
|
||||
for(var/mob/H in get_hearers_in_view(3, source))
|
||||
if(!H.client || !(H.client.prefs.cit_toggles & DIGESTION_NOISES))
|
||||
continue
|
||||
LAZYADD(hearing_mobs, H)
|
||||
last_hearcheck = world.time
|
||||
for(var/mob/H in hearing_mobs)
|
||||
if(!istype(H.loc, /obj/item/dogborg/sleeper))
|
||||
H.playsound_local(source, null, 45, falloff = 0, S = pred_digest)
|
||||
else if(H in contents)
|
||||
H.playsound_local(source, null, 65, falloff = 0, S = prey_digest)
|
||||
|
||||
update_gut(hound)
|
||||
|
||||
if(cleaning)
|
||||
addtimer(CALLBACK(src, .proc/clean_cycle, hound), 50)
|
||||
|
||||
/obj/item/dogborg/sleeper/proc/CheckAccepted(obj/item/I)
|
||||
return is_type_in_typecache(I, important_items)
|
||||
|
||||
/obj/item/dogborg/sleeper/proc/inject_chem(chem, mob/living/silicon/robot/hound)
|
||||
if(!hound)
|
||||
return
|
||||
if(hound.cell.charge <= 800) //This is so borgs don't kill themselves with it. Remember, 750 charge used every injection.
|
||||
to_chat(hound, "<span class='notice'>You don't have enough power to synthesize fluids.</span>")
|
||||
return
|
||||
if(patient.reagents.get_reagent_amount(chem) + 10 >= 20) //Preventing people from accidentally killing themselves by trying to inject too many chemicals!
|
||||
to_chat(hound, "<span class='notice'>Your stomach is currently too full of fluids to secrete more fluids of this kind.</span>")
|
||||
return
|
||||
patient.reagents.add_reagent(chem, 10)
|
||||
hound.cell.use(750) //-750 charge per injection
|
||||
var/units = round(patient.reagents.get_reagent_amount(chem))
|
||||
to_chat(hound, "<span class='notice'>Injecting [units] unit\s of [chem] into occupant.</span>") //If they were immersed, the reagents wouldn't leave with them.
|
||||
|
||||
/obj/item/dogborg/sleeper/medihound //Medihound sleeper
|
||||
name = "Mobile Sleeper"
|
||||
desc = "Equipment for medical hound. A mounted sleeper that stabilizes patients and can inject reagents in the borg's reserves."
|
||||
icon = 'icons/mob/dogborg.dmi'
|
||||
icon_state = "sleeper"
|
||||
breakout_time = 30 //Medical sleepers should be designed to be as easy as possible to get out of.
|
||||
|
||||
/obj/item/dogborg/sleeper/K9 //The K9 portabrig
|
||||
name = "Mobile Brig"
|
||||
desc = "Equipment for a K9 unit. A mounted portable-brig that holds criminals."
|
||||
icon = 'icons/mob/dogborg.dmi'
|
||||
icon_state = "sleeperb"
|
||||
inject_amount = 0
|
||||
min_health = -100
|
||||
injection_chems = null //So they don't have all the same chems as the medihound!
|
||||
breakout_time = 300
|
||||
|
||||
/obj/item/storage/attackby(obj/item/dogborg/sleeper/K9, mob/user, proximity)
|
||||
if(istype(K9))
|
||||
K9.afterattack(src, user ,1)
|
||||
else
|
||||
. = ..()
|
||||
|
||||
/obj/item/dogborg/sleeper/K9/afterattack(mob/living/carbon/target, mob/living/silicon/user, proximity)
|
||||
var/mob/living/silicon/robot/hound = get_host()
|
||||
if(!hound || !istype(target) || !proximity || target.anchored)
|
||||
return
|
||||
if (!target.devourable)
|
||||
to_chat(user, "The target registers an error code. Unable to insert into [src].")
|
||||
return
|
||||
if(patient)
|
||||
to_chat(user,"<span class='warning'>Your [src] is already occupied.</span>")
|
||||
return
|
||||
if(target.buckled)
|
||||
to_chat(user,"<span class='warning'>[target] is buckled and can not be put into your [src].</span>")
|
||||
return
|
||||
user.visible_message("<span class='warning'>[hound.name] is ingesting [target] into their [src].</span>", "<span class='notice'>You start ingesting [target] into your [src.name]...</span>")
|
||||
if(do_after(user, 30, target = target) && !patient && !target.buckled)
|
||||
target.forceMove(src)
|
||||
target.reset_perspective(src)
|
||||
update_gut(hound)
|
||||
user.visible_message("<span class='warning'>[hound.name]'s mobile brig clunks in series as [target] slips inside.</span>", "<span class='notice'>Your mobile brig groans lightly as [target] slips inside.</span>")
|
||||
playsound(hound, 'sound/effects/bin_close.ogg', 80, 1) // Really don't need ERP sound effects for robots
|
||||
|
||||
|
||||
/obj/item/dogborg/sleeper/compactor //Janihound gut.
|
||||
name = "garbage processor"
|
||||
desc = "A mounted garbage compactor unit with fuel processor."
|
||||
icon = 'icons/mob/dogborg.dmi'
|
||||
icon_state = "compactor"
|
||||
inject_amount = 0
|
||||
min_health = -100
|
||||
injection_chems = null //So they don't have all the same chems as the medihound!
|
||||
var/max_item_count = 30
|
||||
|
||||
/obj/item/storage/attackby(obj/item/dogborg/sleeper/compactor, mob/user, proximity) //GIT CIRCUMVENTED YO!
|
||||
if(istype(compactor))
|
||||
compactor.afterattack(src, user ,1)
|
||||
else
|
||||
. = ..()
|
||||
|
||||
/obj/item/dogborg/sleeper/compactor/afterattack(atom/movable/target, mob/living/silicon/user, proximity)//GARBO NOMS
|
||||
var/mob/living/silicon/robot/hound = get_host()
|
||||
if(!hound || !istype(target) || !proximity || target.anchored)
|
||||
return
|
||||
if(length(contents) > (max_item_count - 1))
|
||||
to_chat(user,"<span class='warning'>Your [src] is full. Eject or process contents to continue.</span>")
|
||||
return
|
||||
if(isitem(target))
|
||||
var/obj/item/I = target
|
||||
if(CheckAccepted(I))
|
||||
to_chat(user,"<span class='warning'>[I] registers an error code to your [src]</span>")
|
||||
return
|
||||
if(I.w_class > WEIGHT_CLASS_NORMAL)
|
||||
to_chat(user,"<span class='warning'>[I] is too large to fit into your [src]</span>")
|
||||
return
|
||||
user.visible_message("<span class='warning'>[hound.name] is ingesting [I] into their [src.name].</span>", "<span class='notice'>You start ingesting [target] into your [src.name]...</span>")
|
||||
if(do_after(user, 15, target = target) && length(contents) < max_item_count)
|
||||
I.forceMove(src)
|
||||
I.visible_message("<span class='warning'>[hound.name]'s garbage processor groans lightly as [I] slips inside.</span>", "<span class='notice'>Your garbage compactor groans lightly as [I] slips inside.</span>")
|
||||
playsound(hound, 'sound/machines/disposalflush.ogg', 50, 1)
|
||||
if(length(contents) > 11) //grow that tum after a certain junk amount
|
||||
hound.sleeper_r = 1
|
||||
hound.update_icons()
|
||||
else
|
||||
hound.sleeper_r = 0
|
||||
hound.update_icons()
|
||||
return
|
||||
|
||||
if(iscarbon(target) || issilicon(target))
|
||||
var/mob/living/trashman = target
|
||||
if(!trashman.devourable)
|
||||
to_chat(user, "<span class='warning'>[target] registers an error code to your [src]</span>")
|
||||
return
|
||||
if(patient)
|
||||
to_chat(user,"<span class='warning'>Your [src] is already occupied.</span>")
|
||||
return
|
||||
if(trashman.buckled)
|
||||
to_chat(user,"<span class='warning'>[trashman] is buckled and can not be put into your [src].</span>")
|
||||
return
|
||||
user.visible_message("<span class='warning'>[hound.name] is ingesting [trashman] into their [src].</span>", "<span class='notice'>You start ingesting [trashman] into your [src.name]...</span>")
|
||||
if(do_after(user, 30, target = trashman) && !patient && !trashman.buckled && length(contents) < max_item_count)
|
||||
trashman.forceMove(src)
|
||||
trashman.reset_perspective(src)
|
||||
update_gut()
|
||||
user.visible_message("<span class='warning'>[hound.name]'s garbage processor groans lightly as [trashman] slips inside.</span>", "<span class='notice'>Your garbage compactor groans lightly as [trashman] slips inside.</span>")
|
||||
playsound(hound, 'sound/effects/bin_close.ogg', 80, 1)
|
||||
|
||||
/obj/item/dogborg/sleeper/K9/flavour
|
||||
name = "Mobile Sleeper"
|
||||
desc = "A mounted, underslung sleeper, intended for holding willing occupants for leisurely purposes."
|
||||
@@ -0,0 +1,545 @@
|
||||
/obj/item/flashlight
|
||||
name = "flashlight"
|
||||
desc = "A hand-held emergency light."
|
||||
icon = 'icons/obj/lighting.dmi'
|
||||
icon_state = "flashlight"
|
||||
item_state = "flashlight"
|
||||
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
flags_1 = CONDUCT_1
|
||||
slot_flags = ITEM_SLOT_BELT
|
||||
materials = list(MAT_METAL=50, MAT_GLASS=20)
|
||||
actions_types = list(/datum/action/item_action/toggle_light)
|
||||
var/on = FALSE
|
||||
var/brightness_on = 4 //range of light when on
|
||||
var/flashlight_power = 0.8 //strength of the light when on
|
||||
light_color = "#FFCC66"
|
||||
|
||||
/obj/item/flashlight/Initialize()
|
||||
. = ..()
|
||||
if(icon_state == "[initial(icon_state)]-on")
|
||||
on = TRUE
|
||||
update_brightness()
|
||||
|
||||
/obj/item/flashlight/proc/update_brightness(mob/user = null)
|
||||
if(on)
|
||||
icon_state = "[initial(icon_state)]-on"
|
||||
if(flashlight_power)
|
||||
set_light(l_range = brightness_on, l_power = flashlight_power)
|
||||
else
|
||||
set_light(brightness_on)
|
||||
else
|
||||
icon_state = initial(icon_state)
|
||||
set_light(0)
|
||||
|
||||
/obj/item/flashlight/attack_self(mob/user)
|
||||
on = !on
|
||||
update_brightness(user)
|
||||
playsound(user, on ? 'sound/weapons/magin.ogg' : 'sound/weapons/magout.ogg', 40, 1)
|
||||
for(var/X in actions)
|
||||
var/datum/action/A = X
|
||||
A.UpdateButtonIcon()
|
||||
return 1
|
||||
|
||||
/obj/item/flashlight/suicide_act(mob/living/carbon/human/user)
|
||||
if (user.eye_blind)
|
||||
user.visible_message("<span class='suicide'>[user] is putting [src] close to [user.p_their()] eyes and turning it on ... but [user.p_theyre()] blind!</span>")
|
||||
return SHAME
|
||||
user.visible_message("<span class='suicide'>[user] is putting [src] close to [user.p_their()] eyes and turning it on! It looks like [user.p_theyre()] trying to commit suicide!</span>")
|
||||
return (FIRELOSS)
|
||||
|
||||
/obj/item/flashlight/attack(mob/living/carbon/M, mob/living/carbon/human/user)
|
||||
add_fingerprint(user)
|
||||
if(istype(M) && on && user.zone_selected in list(BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH))
|
||||
|
||||
if((HAS_TRAIT(user, TRAIT_CLUMSY) || HAS_TRAIT(user, TRAIT_DUMB)) && prob(50)) //too dumb to use flashlight properly
|
||||
return ..() //just hit them in the head
|
||||
|
||||
if(!user.IsAdvancedToolUser())
|
||||
to_chat(user, "<span class='warning'>You don't have the dexterity to do this!</span>")
|
||||
return
|
||||
|
||||
if(!M.get_bodypart(BODY_ZONE_HEAD))
|
||||
to_chat(user, "<span class='warning'>[M] doesn't have a head!</span>")
|
||||
return
|
||||
|
||||
if(flashlight_power < 0.3)
|
||||
to_chat(user, "<span class='warning'>\The [src] isn't bright enough to see anything!</span> ")
|
||||
return
|
||||
|
||||
switch(user.zone_selected)
|
||||
if(BODY_ZONE_PRECISE_EYES)
|
||||
if((M.head && M.head.flags_cover & HEADCOVERSEYES) || (M.wear_mask && M.wear_mask.flags_cover & MASKCOVERSEYES) || (M.glasses && M.glasses.flags_cover & GLASSESCOVERSEYES))
|
||||
to_chat(user, "<span class='notice'>You're going to need to remove that [(M.head && M.head.flags_cover & HEADCOVERSEYES) ? "helmet" : (M.wear_mask && M.wear_mask.flags_cover & MASKCOVERSEYES) ? "mask": "glasses"] first.</span>")
|
||||
return
|
||||
|
||||
var/obj/item/organ/eyes/E = M.getorganslot(ORGAN_SLOT_EYES)
|
||||
if(!E)
|
||||
to_chat(user, "<span class='danger'>[M] doesn't have any eyes!</span>")
|
||||
return
|
||||
|
||||
if(M == user) //they're using it on themselves
|
||||
if(M.flash_act(visual = 1))
|
||||
M.visible_message("[M] directs [src] to [M.p_their()] eyes.", "<span class='notice'>You wave the light in front of your eyes! Trippy!</span>")
|
||||
else
|
||||
M.visible_message("[M] directs [src] to [M.p_their()] eyes.", "<span class='notice'>You wave the light in front of your eyes.</span>")
|
||||
else
|
||||
user.visible_message("<span class='warning'>[user] directs [src] to [M]'s eyes.</span>", \
|
||||
"<span class='danger'>You direct [src] to [M]'s eyes.</span>")
|
||||
if(M.stat == DEAD || (HAS_TRAIT(M, TRAIT_BLIND)) || !M.flash_act(visual = 1)) //mob is dead or fully blind
|
||||
to_chat(user, "<span class='warning'>[M]'s pupils don't react to the light!</span>")
|
||||
else if(M.dna && M.dna.check_mutation(XRAY)) //mob has X-ray vision
|
||||
to_chat(user, "<span class='danger'>[M]'s pupils give an eerie glow!</span>")
|
||||
else //they're okay!
|
||||
to_chat(user, "<span class='notice'>[M]'s pupils narrow.</span>")
|
||||
|
||||
if(BODY_ZONE_PRECISE_MOUTH)
|
||||
|
||||
if(M.is_mouth_covered())
|
||||
to_chat(user, "<span class='notice'>You're going to need to remove that [(M.head && M.head.flags_cover & HEADCOVERSMOUTH) ? "helmet" : "mask"] first.</span>")
|
||||
return
|
||||
|
||||
var/their = M.p_their()
|
||||
|
||||
var/list/mouth_organs = new
|
||||
for(var/obj/item/organ/O in M.internal_organs)
|
||||
if(O.zone == BODY_ZONE_PRECISE_MOUTH)
|
||||
mouth_organs.Add(O)
|
||||
var/organ_list = ""
|
||||
var/organ_count = LAZYLEN(mouth_organs)
|
||||
if(organ_count)
|
||||
for(var/I in 1 to organ_count)
|
||||
if(I > 1)
|
||||
if(I == mouth_organs.len)
|
||||
organ_list += ", and "
|
||||
else
|
||||
organ_list += ", "
|
||||
var/obj/item/organ/O = mouth_organs[I]
|
||||
organ_list += (O.gender == "plural" ? O.name : "\an [O.name]")
|
||||
|
||||
var/pill_count = 0
|
||||
for(var/datum/action/item_action/hands_free/activate_pill/AP in M.actions)
|
||||
pill_count++
|
||||
|
||||
if(M == user)
|
||||
var/can_use_mirror = FALSE
|
||||
if(isturf(user.loc))
|
||||
var/obj/structure/mirror/mirror = locate(/obj/structure/mirror, user.loc)
|
||||
if(mirror)
|
||||
switch(user.dir)
|
||||
if(NORTH)
|
||||
can_use_mirror = mirror.pixel_y > 0
|
||||
if(SOUTH)
|
||||
can_use_mirror = mirror.pixel_y < 0
|
||||
if(EAST)
|
||||
can_use_mirror = mirror.pixel_x > 0
|
||||
if(WEST)
|
||||
can_use_mirror = mirror.pixel_x < 0
|
||||
|
||||
M.visible_message("[M] directs [src] to [their] mouth.", \
|
||||
"<span class='notice'>You point [src] into your mouth.</span>")
|
||||
if(!can_use_mirror)
|
||||
to_chat(user, "<span class='notice'>You can't see anything without a mirror.</span>")
|
||||
return
|
||||
if(organ_count)
|
||||
to_chat(user, "<span class='notice'>Inside your mouth [organ_count > 1 ? "are" : "is"] [organ_list].</span>")
|
||||
else
|
||||
to_chat(user, "<span class='notice'>There's nothing inside your mouth.</span>")
|
||||
if(pill_count)
|
||||
to_chat(user, "<span class='notice'>You have [pill_count] implanted pill[pill_count > 1 ? "s" : ""].</span>")
|
||||
|
||||
else
|
||||
user.visible_message("<span class='notice'>[user] directs [src] to [M]'s mouth.</span>",\
|
||||
"<span class='notice'>You direct [src] to [M]'s mouth.</span>")
|
||||
if(organ_count)
|
||||
to_chat(user, "<span class='notice'>Inside [their] mouth [organ_count > 1 ? "are" : "is"] [organ_list].</span>")
|
||||
else
|
||||
to_chat(user, "<span class='notice'>[M] doesn't have any organs in [their] mouth.</span>")
|
||||
if(pill_count)
|
||||
to_chat(user, "<span class='notice'>[M] has [pill_count] pill[pill_count > 1 ? "s" : ""] implanted in [their] teeth.</span>")
|
||||
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/item/flashlight/pen
|
||||
name = "penlight"
|
||||
desc = "A pen-sized light, used by medical staff. It can also be used to create a hologram to alert people of incoming medical assistance."
|
||||
icon_state = "penlight"
|
||||
item_state = ""
|
||||
flags_1 = CONDUCT_1
|
||||
brightness_on = 2
|
||||
light_color = "#FFDDCC"
|
||||
flashlight_power = 0.3
|
||||
var/holo_cooldown = 0
|
||||
|
||||
/obj/item/flashlight/pen/afterattack(atom/target, mob/user, proximity_flag)
|
||||
. = ..()
|
||||
if(!proximity_flag)
|
||||
if(holo_cooldown > world.time)
|
||||
to_chat(user, "<span class='warning'>[src] is not ready yet!</span>")
|
||||
return
|
||||
var/T = get_turf(target)
|
||||
if(locate(/mob/living) in T)
|
||||
new /obj/effect/temp_visual/medical_holosign(T,user) //produce a holographic glow
|
||||
holo_cooldown = world.time + 100
|
||||
return
|
||||
|
||||
/obj/effect/temp_visual/medical_holosign
|
||||
name = "medical holosign"
|
||||
desc = "A small holographic glow that indicates a medic is coming to treat a patient."
|
||||
icon_state = "medi_holo"
|
||||
duration = 30
|
||||
|
||||
/obj/effect/temp_visual/medical_holosign/Initialize(mapload, creator)
|
||||
. = ..()
|
||||
playsound(loc, 'sound/machines/ping.ogg', 50, 0) //make some noise!
|
||||
if(creator)
|
||||
visible_message("<span class='danger'>[creator] created a medical hologram!</span>")
|
||||
|
||||
|
||||
/obj/item/flashlight/seclite
|
||||
name = "seclite"
|
||||
desc = "A robust flashlight used by security."
|
||||
icon_state = "seclite"
|
||||
item_state = "seclite"
|
||||
lefthand_file = 'icons/mob/inhands/equipment/security_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/equipment/security_righthand.dmi'
|
||||
force = 9 // Not as good as a stun baton.
|
||||
brightness_on = 5 // A little better than the standard flashlight.
|
||||
light_color = "#CDDDFF"
|
||||
flashlight_power = 0.9
|
||||
hitsound = 'sound/weapons/genhit1.ogg'
|
||||
|
||||
// the desk lamps are a bit special
|
||||
/obj/item/flashlight/lamp
|
||||
name = "desk lamp"
|
||||
desc = "A desk lamp with an adjustable mount."
|
||||
icon_state = "lamp"
|
||||
item_state = "lamp"
|
||||
lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/items_righthand.dmi'
|
||||
force = 10
|
||||
brightness_on = 5
|
||||
light_color = "#FFDDBB"
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
flags_1 = CONDUCT_1
|
||||
materials = list()
|
||||
on = TRUE
|
||||
|
||||
|
||||
// green-shaded desk lamp
|
||||
/obj/item/flashlight/lamp/green
|
||||
desc = "A classic green-shaded desk lamp."
|
||||
icon_state = "lampgreen"
|
||||
item_state = "lampgreen"
|
||||
|
||||
|
||||
|
||||
/obj/item/flashlight/lamp/verb/toggle_light()
|
||||
set name = "Toggle light"
|
||||
set category = "Object"
|
||||
set src in oview(1)
|
||||
|
||||
if(!usr.stat)
|
||||
attack_self(usr)
|
||||
|
||||
//Bananalamp
|
||||
/obj/item/flashlight/lamp/bananalamp
|
||||
name = "banana lamp"
|
||||
desc = "Only a clown would think to make a ghetto banana-shaped lamp. Even has a goofy pullstring."
|
||||
icon_state = "bananalamp"
|
||||
item_state = "bananalamp"
|
||||
|
||||
// FLARES
|
||||
|
||||
/obj/item/flashlight/flare
|
||||
name = "flare"
|
||||
desc = "A red Nanotrasen issued flare. There are instructions on the side, it reads 'pull cord, make light'."
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
brightness_on = 7 // Pretty bright.
|
||||
light_color = "#FA421A"
|
||||
icon_state = "flare"
|
||||
item_state = "flare"
|
||||
actions_types = list()
|
||||
var/fuel = 0
|
||||
var/on_damage = 7
|
||||
var/produce_heat = 1500
|
||||
heat = 1000
|
||||
light_color = LIGHT_COLOR_FLARE
|
||||
grind_results = list("sulfur" = 15)
|
||||
|
||||
/obj/item/flashlight/flare/New()
|
||||
fuel = rand(800, 1000) // Sorry for changing this so much but I keep under-estimating how long X number of ticks last in seconds.
|
||||
..()
|
||||
|
||||
/obj/item/flashlight/flare/process()
|
||||
open_flame(heat)
|
||||
fuel = max(fuel - 1, 0)
|
||||
if(!fuel || !on)
|
||||
turn_off()
|
||||
if(!fuel)
|
||||
icon_state = "[initial(icon_state)]-empty"
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/flashlight/flare/ignition_effect(atom/A, mob/user)
|
||||
if(fuel && on)
|
||||
. = "<span class='notice'>[user] lights [A] with [src] like a real \
|
||||
badass.</span>"
|
||||
else
|
||||
. = ""
|
||||
|
||||
/obj/item/flashlight/flare/proc/turn_off()
|
||||
on = FALSE
|
||||
force = initial(src.force)
|
||||
damtype = initial(src.damtype)
|
||||
if(ismob(loc))
|
||||
var/mob/U = loc
|
||||
update_brightness(U)
|
||||
else
|
||||
update_brightness(null)
|
||||
|
||||
/obj/item/flashlight/flare/update_brightness(mob/user = null)
|
||||
..()
|
||||
if(on)
|
||||
item_state = "[initial(item_state)]-on"
|
||||
else
|
||||
item_state = "[initial(item_state)]"
|
||||
|
||||
/obj/item/flashlight/flare/attack_self(mob/user)
|
||||
|
||||
// Usual checks
|
||||
if(!fuel)
|
||||
to_chat(user, "<span class='warning'>[src] is out of fuel!</span>")
|
||||
return
|
||||
if(on)
|
||||
to_chat(user, "<span class='notice'>[src] is already on.</span>")
|
||||
return
|
||||
|
||||
. = ..()
|
||||
// All good, turn it on.
|
||||
if(.)
|
||||
user.visible_message("<span class='notice'>[user] lights \the [src].</span>", "<span class='notice'>You light \the [src]!</span>")
|
||||
force = on_damage
|
||||
damtype = "fire"
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/flashlight/flare/get_temperature()
|
||||
return on * heat
|
||||
|
||||
/obj/item/flashlight/flare/torch
|
||||
name = "torch"
|
||||
desc = "A torch fashioned from some leaves and a log."
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
brightness_on = 4
|
||||
light_color = "#FAA44B"
|
||||
icon_state = "torch"
|
||||
item_state = "torch"
|
||||
lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/items_righthand.dmi'
|
||||
light_color = LIGHT_COLOR_ORANGE
|
||||
on_damage = 10
|
||||
slot_flags = null
|
||||
|
||||
/obj/item/flashlight/lantern
|
||||
name = "lantern"
|
||||
icon_state = "lantern"
|
||||
item_state = "lantern"
|
||||
lefthand_file = 'icons/mob/inhands/equipment/mining_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/equipment/mining_righthand.dmi'
|
||||
desc = "A mining lantern."
|
||||
brightness_on = 6 // luminosity when on
|
||||
light_color = "#FFAA44"
|
||||
flashlight_power = 0.75
|
||||
|
||||
|
||||
/obj/item/flashlight/slime
|
||||
gender = PLURAL
|
||||
name = "glowing slime extract"
|
||||
desc = "Extract from a yellow slime. It emits a strong light when squeezed."
|
||||
icon = 'icons/obj/lighting.dmi'
|
||||
icon_state = "slime"
|
||||
item_state = "slime"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
slot_flags = ITEM_SLOT_BELT
|
||||
materials = list()
|
||||
brightness_on = 6 //luminosity when on
|
||||
light_color = "#FFEEAA"
|
||||
flashlight_power = 0.6
|
||||
|
||||
/obj/item/flashlight/emp
|
||||
var/emp_max_charges = 4
|
||||
var/emp_cur_charges = 4
|
||||
var/charge_tick = 0
|
||||
|
||||
|
||||
/obj/item/flashlight/emp/New()
|
||||
..()
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/flashlight/emp/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
. = ..()
|
||||
|
||||
/obj/item/flashlight/emp/process()
|
||||
charge_tick++
|
||||
if(charge_tick < 10)
|
||||
return FALSE
|
||||
charge_tick = 0
|
||||
emp_cur_charges = min(emp_cur_charges+1, emp_max_charges)
|
||||
return TRUE
|
||||
|
||||
/obj/item/flashlight/emp/attack(mob/living/M, mob/living/user)
|
||||
if(on && user.zone_selected in list(BODY_ZONE_PRECISE_EYES, BODY_ZONE_PRECISE_MOUTH)) // call original attack when examining organs
|
||||
..()
|
||||
return
|
||||
|
||||
/obj/item/flashlight/emp/afterattack(atom/movable/A, mob/user, proximity)
|
||||
. = ..()
|
||||
if(!proximity)
|
||||
return
|
||||
|
||||
if(emp_cur_charges > 0)
|
||||
emp_cur_charges -= 1
|
||||
|
||||
if(ismob(A))
|
||||
var/mob/M = A
|
||||
log_combat(user, M, "attacked", "EMP-light")
|
||||
M.visible_message("<span class='danger'>[user] blinks \the [src] at \the [A].", \
|
||||
"<span class='userdanger'>[user] blinks \the [src] at you.")
|
||||
else
|
||||
A.visible_message("<span class='danger'>[user] blinks \the [src] at \the [A].")
|
||||
to_chat(user, "\The [src] now has [emp_cur_charges] charge\s.")
|
||||
A.emp_act(EMP_HEAVY)
|
||||
else
|
||||
to_chat(user, "<span class='warning'>\The [src] needs time to recharge!</span>")
|
||||
return
|
||||
|
||||
/obj/item/flashlight/emp/debug //for testing emp_act()
|
||||
name = "debug EMP flashlight"
|
||||
emp_max_charges = 100
|
||||
emp_cur_charges = 100
|
||||
|
||||
// Glowsticks, in the uncomfortable range of similar to flares,
|
||||
// but not similar enough to make it worth a refactor
|
||||
/obj/item/flashlight/glowstick
|
||||
name = "glowstick"
|
||||
desc = "A military-grade glowstick."
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
brightness_on = 4
|
||||
color = LIGHT_COLOR_GREEN
|
||||
icon_state = "glowstick"
|
||||
item_state = "glowstick"
|
||||
grind_results = list("phenol" = 15, "hydrogen" = 10, "oxygen" = 5) //Meth-in-a-stick
|
||||
var/fuel = 0
|
||||
|
||||
/obj/item/flashlight/glowstick/Initialize()
|
||||
fuel = rand(1600, 2000)
|
||||
light_color = color
|
||||
. = ..()
|
||||
|
||||
/obj/item/flashlight/glowstick/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
. = ..()
|
||||
|
||||
/obj/item/flashlight/glowstick/process()
|
||||
fuel = max(fuel - 1, 0)
|
||||
if(!fuel)
|
||||
turn_off()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
update_icon()
|
||||
|
||||
/obj/item/flashlight/glowstick/proc/turn_off()
|
||||
on = FALSE
|
||||
update_icon()
|
||||
|
||||
/obj/item/flashlight/glowstick/update_icon()
|
||||
item_state = "glowstick"
|
||||
cut_overlays()
|
||||
if(!fuel)
|
||||
icon_state = "glowstick-empty"
|
||||
cut_overlays()
|
||||
set_light(0)
|
||||
else if(on)
|
||||
var/mutable_appearance/glowstick_overlay = mutable_appearance(icon, "glowstick-glow")
|
||||
glowstick_overlay.color = color
|
||||
add_overlay(glowstick_overlay)
|
||||
item_state = "glowstick-on"
|
||||
set_light(brightness_on)
|
||||
else
|
||||
icon_state = "glowstick"
|
||||
cut_overlays()
|
||||
|
||||
/obj/item/flashlight/glowstick/attack_self(mob/user)
|
||||
if(!fuel)
|
||||
to_chat(user, "<span class='notice'>[src] is spent.</span>")
|
||||
return
|
||||
if(on)
|
||||
to_chat(user, "<span class='notice'>[src] is already lit.</span>")
|
||||
return
|
||||
|
||||
. = ..()
|
||||
if(.)
|
||||
user.visible_message("<span class='notice'>[user] cracks and shakes [src].</span>", "<span class='notice'>You crack and shake [src], turning it on!</span>")
|
||||
activate()
|
||||
|
||||
/obj/item/flashlight/glowstick/proc/activate()
|
||||
if(!on)
|
||||
on = TRUE
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/flashlight/glowstick/red
|
||||
name = "red glowstick"
|
||||
color = LIGHT_COLOR_RED
|
||||
|
||||
/obj/item/flashlight/glowstick/blue
|
||||
name = "blue glowstick"
|
||||
color = LIGHT_COLOR_BLUE
|
||||
|
||||
/obj/item/flashlight/glowstick/cyan
|
||||
name = "cyan glowstick"
|
||||
color = LIGHT_COLOR_CYAN
|
||||
|
||||
/obj/item/flashlight/glowstick/orange
|
||||
name = "orange glowstick"
|
||||
color = LIGHT_COLOR_ORANGE
|
||||
|
||||
/obj/item/flashlight/glowstick/yellow
|
||||
name = "yellow glowstick"
|
||||
color = LIGHT_COLOR_YELLOW
|
||||
|
||||
/obj/item/flashlight/glowstick/pink
|
||||
name = "pink glowstick"
|
||||
color = LIGHT_COLOR_PINK
|
||||
|
||||
/obj/item/flashlight/spotlight //invisible lighting source
|
||||
name = "disco light"
|
||||
desc = "Groovy..."
|
||||
icon_state = null
|
||||
light_color = null
|
||||
brightness_on = 0
|
||||
flashlight_power = 1
|
||||
light_range = 0
|
||||
light_power = 10
|
||||
alpha = 0
|
||||
layer = 0
|
||||
on = TRUE
|
||||
anchored = TRUE
|
||||
var/range = null
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
|
||||
/obj/item/flashlight/flashdark
|
||||
name = "flashdark"
|
||||
desc = "A strange device manufactured with mysterious elements that somehow emits darkness. Or maybe it just sucks in light? Nobody knows for sure."
|
||||
icon_state = "flashdark"
|
||||
item_state = "flashdark"
|
||||
brightness_on = 2.5
|
||||
flashlight_power = -3
|
||||
|
||||
/obj/item/flashlight/eyelight
|
||||
name = "eyelight"
|
||||
desc = "This shouldn't exist outside of someone's head, how are you seeing this?"
|
||||
brightness_on = 15
|
||||
flags_1 = CONDUCT_1
|
||||
item_flags = DROPDEL
|
||||
actions_types = list()
|
||||
@@ -0,0 +1,230 @@
|
||||
#define RAD_LEVEL_NORMAL 9
|
||||
#define RAD_LEVEL_MODERATE 100
|
||||
#define RAD_LEVEL_HIGH 400
|
||||
#define RAD_LEVEL_VERY_HIGH 800
|
||||
#define RAD_LEVEL_CRITICAL 1500
|
||||
|
||||
#define RAD_MEASURE_SMOOTHING 5
|
||||
|
||||
#define RAD_GRACE_PERIOD 2
|
||||
|
||||
/obj/item/geiger_counter //DISCLAIMER: I know nothing about how real-life Geiger counters work. This will not be realistic. ~Xhuis
|
||||
name = "\improper Geiger counter"
|
||||
desc = "A handheld device used for detecting and measuring radiation pulses."
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "geiger_off"
|
||||
item_state = "multitool"
|
||||
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
slot_flags = ITEM_SLOT_BELT
|
||||
materials = list(MAT_METAL = 150, MAT_GLASS = 150)
|
||||
|
||||
var/grace = RAD_GRACE_PERIOD
|
||||
var/datum/looping_sound/geiger/soundloop
|
||||
|
||||
var/scanning = FALSE
|
||||
var/radiation_count = 0
|
||||
var/current_tick_amount = 0
|
||||
var/last_tick_amount = 0
|
||||
var/fail_to_receive = 0
|
||||
var/current_warning = 1
|
||||
|
||||
/obj/item/geiger_counter/Initialize()
|
||||
. = ..()
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
soundloop = new(list(src), FALSE)
|
||||
|
||||
/obj/item/geiger_counter/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/obj/item/geiger_counter/process()
|
||||
update_icon()
|
||||
update_sound()
|
||||
|
||||
if(!scanning)
|
||||
current_tick_amount = 0
|
||||
return
|
||||
|
||||
radiation_count -= radiation_count/RAD_MEASURE_SMOOTHING
|
||||
radiation_count += current_tick_amount/RAD_MEASURE_SMOOTHING
|
||||
|
||||
if(current_tick_amount)
|
||||
grace = RAD_GRACE_PERIOD
|
||||
last_tick_amount = current_tick_amount
|
||||
|
||||
else if(!(obj_flags & EMAGGED))
|
||||
grace--
|
||||
if(grace <= 0)
|
||||
radiation_count = 0
|
||||
|
||||
current_tick_amount = 0
|
||||
|
||||
/obj/item/geiger_counter/examine(mob/user)
|
||||
..()
|
||||
if(!scanning)
|
||||
return 1
|
||||
to_chat(user, "<span class='info'>Alt-click it to clear stored radiation levels.</span>")
|
||||
if(obj_flags & EMAGGED)
|
||||
to_chat(user, "<span class='warning'>The display seems to be incomprehensible.</span>")
|
||||
return 1
|
||||
switch(radiation_count)
|
||||
if(-INFINITY to RAD_LEVEL_NORMAL)
|
||||
to_chat(user, "<span class='notice'>Ambient radiation level count reports that all is well.</span>")
|
||||
if(RAD_LEVEL_NORMAL + 1 to RAD_LEVEL_MODERATE)
|
||||
to_chat(user, "<span class='disarm'>Ambient radiation levels slightly above average.</span>")
|
||||
if(RAD_LEVEL_MODERATE + 1 to RAD_LEVEL_HIGH)
|
||||
to_chat(user, "<span class='warning'>Ambient radiation levels above average.</span>")
|
||||
if(RAD_LEVEL_HIGH + 1 to RAD_LEVEL_VERY_HIGH)
|
||||
to_chat(user, "<span class='danger'>Ambient radiation levels highly above average.</span>")
|
||||
if(RAD_LEVEL_VERY_HIGH + 1 to RAD_LEVEL_CRITICAL)
|
||||
to_chat(user, "<span class='suicide'>Ambient radiation levels nearing critical level.</span>")
|
||||
if(RAD_LEVEL_CRITICAL + 1 to INFINITY)
|
||||
to_chat(user, "<span class='boldannounce'>Ambient radiation levels above critical level!</span>")
|
||||
|
||||
to_chat(user, "<span class='notice'>The last radiation amount detected was [last_tick_amount]</span>")
|
||||
|
||||
/obj/item/geiger_counter/update_icon()
|
||||
if(!scanning)
|
||||
icon_state = "geiger_off"
|
||||
return 1
|
||||
if(obj_flags & EMAGGED)
|
||||
icon_state = "geiger_on_emag"
|
||||
return 1
|
||||
switch(radiation_count)
|
||||
if(-INFINITY to RAD_LEVEL_NORMAL)
|
||||
icon_state = "geiger_on_1"
|
||||
if(RAD_LEVEL_NORMAL + 1 to RAD_LEVEL_MODERATE)
|
||||
icon_state = "geiger_on_2"
|
||||
if(RAD_LEVEL_MODERATE + 1 to RAD_LEVEL_HIGH)
|
||||
icon_state = "geiger_on_3"
|
||||
if(RAD_LEVEL_HIGH + 1 to RAD_LEVEL_VERY_HIGH)
|
||||
icon_state = "geiger_on_4"
|
||||
if(RAD_LEVEL_VERY_HIGH + 1 to RAD_LEVEL_CRITICAL)
|
||||
icon_state = "geiger_on_4"
|
||||
if(RAD_LEVEL_CRITICAL + 1 to INFINITY)
|
||||
icon_state = "geiger_on_5"
|
||||
..()
|
||||
|
||||
/obj/item/geiger_counter/proc/update_sound()
|
||||
var/datum/looping_sound/geiger/loop = soundloop
|
||||
if(!scanning)
|
||||
loop.stop()
|
||||
return
|
||||
if(!radiation_count)
|
||||
loop.stop()
|
||||
return
|
||||
loop.last_radiation = radiation_count
|
||||
loop.start()
|
||||
|
||||
/obj/item/geiger_counter/rad_act(amount)
|
||||
. = ..()
|
||||
if(amount <= RAD_BACKGROUND_RADIATION || !scanning)
|
||||
return
|
||||
current_tick_amount += amount
|
||||
update_icon()
|
||||
|
||||
/obj/item/geiger_counter/attack_self(mob/user)
|
||||
scanning = !scanning
|
||||
update_icon()
|
||||
to_chat(user, "<span class='notice'>[icon2html(src, user)] You switch [scanning ? "on" : "off"] [src].</span>")
|
||||
|
||||
/obj/item/geiger_counter/afterattack(atom/target, mob/user)
|
||||
. = ..()
|
||||
if(user.a_intent == INTENT_HELP)
|
||||
if(!(obj_flags & EMAGGED))
|
||||
user.visible_message("<span class='notice'>[user] scans [target] with [src].</span>", "<span class='notice'>You scan [target]'s radiation levels with [src]...</span>")
|
||||
addtimer(CALLBACK(src, .proc/scan, target, user), 20, TIMER_UNIQUE) // Let's not have spamming GetAllContents
|
||||
else
|
||||
user.visible_message("<span class='notice'>[user] scans [target] with [src].</span>", "<span class='danger'>You project [src]'s stored radiation into [target]!</span>")
|
||||
target.rad_act(radiation_count)
|
||||
radiation_count = 0
|
||||
return TRUE
|
||||
|
||||
/obj/item/geiger_counter/proc/scan(atom/A, mob/user)
|
||||
var/rad_strength = 0
|
||||
for(var/i in get_rad_contents(A)) // Yes it's intentional that you can't detect radioactive things under rad protection. Gives traitors a way to hide their glowing green rocks.
|
||||
var/atom/thing = i
|
||||
if(!thing)
|
||||
continue
|
||||
var/datum/component/radioactive/radiation = thing.GetComponent(/datum/component/radioactive)
|
||||
if(radiation)
|
||||
rad_strength += radiation.strength
|
||||
|
||||
if(isliving(A))
|
||||
var/mob/living/M = A
|
||||
if(!M.radiation)
|
||||
to_chat(user, "<span class='notice'>[icon2html(src, user)] Radiation levels within normal boundaries.</span>")
|
||||
else
|
||||
to_chat(user, "<span class='boldannounce'>[icon2html(src, user)] Subject is irradiated. Radiation levels: [M.radiation] rad.</span>")
|
||||
|
||||
if(rad_strength)
|
||||
to_chat(user, "<span class='boldannounce'>[icon2html(src, user)] Target contains radioactive contamination. Radioactive strength: [rad_strength]</span>")
|
||||
else
|
||||
to_chat(user, "<span class='notice'>[icon2html(src, user)] Target is free of radioactive contamination.</span>")
|
||||
|
||||
/obj/item/geiger_counter/attackby(obj/item/I, mob/user, params)
|
||||
if(istype(I, /obj/item/screwdriver) && (obj_flags & EMAGGED))
|
||||
if(scanning)
|
||||
to_chat(user, "<span class='warning'>Turn off [src] before you perform this action!</span>")
|
||||
return 0
|
||||
user.visible_message("<span class='notice'>[user] unscrews [src]'s maintenance panel and begins fiddling with its innards...</span>", "<span class='notice'>You begin resetting [src]...</span>")
|
||||
if(!I.use_tool(src, user, 40, volume=50))
|
||||
return 0
|
||||
user.visible_message("<span class='notice'>[user] refastens [src]'s maintenance panel!</span>", "<span class='notice'>You reset [src] to its factory settings!</span>")
|
||||
obj_flags &= ~EMAGGED
|
||||
radiation_count = 0
|
||||
update_icon()
|
||||
return 1
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/item/geiger_counter/AltClick(mob/living/user)
|
||||
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE))
|
||||
return ..()
|
||||
if(!scanning)
|
||||
to_chat(usr, "<span class='warning'>[src] must be on to reset its radiation level!</span>")
|
||||
return 0
|
||||
radiation_count = 0
|
||||
to_chat(usr, "<span class='notice'>You flush [src]'s radiation counts, resetting it to normal.</span>")
|
||||
update_icon()
|
||||
|
||||
/obj/item/geiger_counter/emag_act(mob/user)
|
||||
. = ..()
|
||||
if(obj_flags & EMAGGED)
|
||||
return
|
||||
if(scanning)
|
||||
to_chat(user, "<span class='warning'>Turn off [src] before you perform this action!</span>")
|
||||
return
|
||||
to_chat(user, "<span class='warning'>You override [src]'s radiation storing protocols. It will now generate small doses of radiation, and stored rads are now projected into creatures you scan.</span>")
|
||||
obj_flags |= EMAGGED
|
||||
return TRUE
|
||||
|
||||
/obj/item/geiger_counter/cyborg
|
||||
var/mob/listeningTo
|
||||
|
||||
/obj/item/geiger_counter/cyborg/equipped(mob/user)
|
||||
. = ..()
|
||||
if(listeningTo == user)
|
||||
return
|
||||
if(listeningTo)
|
||||
UnregisterSignal(listeningTo, COMSIG_ATOM_RAD_ACT)
|
||||
RegisterSignal(user, COMSIG_ATOM_RAD_ACT, .proc/redirect_rad_act)
|
||||
listeningTo = user
|
||||
|
||||
/obj/item/geiger_counter/cyborg/proc/redirect_rad_act(datum/source, amount)
|
||||
rad_act(amount)
|
||||
|
||||
/obj/item/geiger_counter/cyborg/dropped()
|
||||
. = ..()
|
||||
if(listeningTo)
|
||||
UnregisterSignal(listeningTo, COMSIG_ATOM_RAD_ACT)
|
||||
listeningTo = null
|
||||
|
||||
#undef RAD_LEVEL_NORMAL
|
||||
#undef RAD_LEVEL_MODERATE
|
||||
#undef RAD_LEVEL_HIGH
|
||||
#undef RAD_LEVEL_VERY_HIGH
|
||||
#undef RAD_LEVEL_CRITICAL
|
||||
@@ -0,0 +1,226 @@
|
||||
GLOBAL_LIST_EMPTY(GPS_list)
|
||||
/obj/item/gps
|
||||
name = "global positioning system"
|
||||
desc = "Helping lost spacemen find their way through the planets since 2016."
|
||||
icon = 'icons/obj/telescience.dmi'
|
||||
icon_state = "gps-c"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
slot_flags = ITEM_SLOT_BELT
|
||||
obj_flags = UNIQUE_RENAME
|
||||
var/gpstag = "COM0"
|
||||
var/emped = FALSE
|
||||
var/tracking = TRUE
|
||||
var/updating = TRUE //Automatic updating of GPS list. Can be set to manual by user.
|
||||
var/global_mode = TRUE //If disabled, only GPS signals of the same Z level are shown
|
||||
|
||||
/obj/item/gps/examine(mob/user)
|
||||
..()
|
||||
var/turf/curr = get_turf(src)
|
||||
to_chat(user, "The screen says: [get_area_name(curr, TRUE)] ([curr.x], [curr.y], [curr.z])")
|
||||
to_chat(user, "<span class='notice'>Alt-click to switch it [tracking ? "off":"on"].</span>")
|
||||
|
||||
/obj/item/gps/Initialize()
|
||||
. = ..()
|
||||
GLOB.GPS_list += src
|
||||
name = "global positioning system ([gpstag])"
|
||||
add_overlay("working")
|
||||
|
||||
/obj/item/gps/Destroy()
|
||||
GLOB.GPS_list -= src
|
||||
return ..()
|
||||
|
||||
/obj/item/gps/emp_act(severity)
|
||||
. = ..()
|
||||
if (. & EMP_PROTECT_SELF)
|
||||
return
|
||||
emped = TRUE
|
||||
cut_overlay("working")
|
||||
add_overlay("emp")
|
||||
addtimer(CALLBACK(src, .proc/reboot), 300, TIMER_UNIQUE|TIMER_OVERRIDE) //if a new EMP happens, remove the old timer so it doesn't reactivate early
|
||||
SStgui.close_uis(src) //Close the UI control if it is open.
|
||||
|
||||
/obj/item/gps/proc/reboot()
|
||||
emped = FALSE
|
||||
cut_overlay("emp")
|
||||
add_overlay("working")
|
||||
|
||||
/obj/item/gps/AltClick(mob/user)
|
||||
if(!user.canUseTopic(src, BE_CLOSE))
|
||||
return
|
||||
toggletracking(user)
|
||||
|
||||
/obj/item/gps/proc/toggletracking(mob/user)
|
||||
if(!user.canUseTopic(src, BE_CLOSE))
|
||||
return //user not valid to use gps
|
||||
if(emped)
|
||||
to_chat(user, "It's busted!")
|
||||
return
|
||||
if(tracking)
|
||||
cut_overlay("working")
|
||||
to_chat(user, "[src] is no longer tracking, or visible to other GPS devices.")
|
||||
tracking = FALSE
|
||||
else
|
||||
add_overlay("working")
|
||||
to_chat(user, "[src] is now tracking, and visible to other GPS devices.")
|
||||
tracking = TRUE
|
||||
|
||||
|
||||
/obj/item/gps/ui_interact(mob/user, ui_key = "gps", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) // Remember to use the appropriate state.
|
||||
if(emped)
|
||||
to_chat(user, "[src] fizzles weakly.")
|
||||
return
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
var/gps_window_height = 300 + GLOB.GPS_list.len * 20 // Variable window height, depending on how many GPS units there are to show
|
||||
ui = new(user, src, ui_key, "gps", "Global Positioning System", 600, gps_window_height, master_ui, state) //width, height
|
||||
ui.open()
|
||||
|
||||
ui.set_autoupdate(state = updating)
|
||||
|
||||
|
||||
/obj/item/gps/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["power"] = tracking
|
||||
data["tag"] = gpstag
|
||||
data["updating"] = updating
|
||||
data["globalmode"] = global_mode
|
||||
if(!tracking || emped) //Do not bother scanning if the GPS is off or EMPed
|
||||
return data
|
||||
|
||||
var/turf/curr = get_turf(src)
|
||||
data["current"] = "[get_area_name(curr, TRUE)] ([curr.x], [curr.y], [curr.z])"
|
||||
|
||||
var/list/signals = list()
|
||||
data["signals"] = list()
|
||||
|
||||
for(var/gps in GLOB.GPS_list)
|
||||
var/obj/item/gps/G = gps
|
||||
if(G.emped || !G.tracking || G == src)
|
||||
continue
|
||||
var/turf/pos = get_turf(G)
|
||||
if(!global_mode && pos.z != curr.z)
|
||||
continue
|
||||
var/list/signal = list()
|
||||
signal["entrytag"] = G.gpstag //Name or 'tag' of the GPS
|
||||
signal["area"] = get_area_name(G, TRUE)
|
||||
signal["coord"] = "[pos.x], [pos.y], [pos.z]"
|
||||
if(pos.z == curr.z) //Distance/Direction calculations for same z-level only
|
||||
signal["dist"] = max(get_dist(curr, pos), 0) //Distance between the src and remote GPS turfs
|
||||
signal["degrees"] = round(Get_Angle(curr, pos)) //0-360 degree directional bearing, for more precision.
|
||||
var/direction = uppertext(dir2text(get_dir(curr, pos))) //Direction text (East, etc). Not as precise, but still helpful.
|
||||
if(!direction)
|
||||
direction = "CENTER"
|
||||
signal["degrees"] = "N/A"
|
||||
signal["direction"] = direction
|
||||
|
||||
signals += list(signal) //Add this signal to the list of signals
|
||||
data["signals"] = signals
|
||||
return data
|
||||
|
||||
|
||||
|
||||
/obj/item/gps/ui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
switch(action)
|
||||
if("rename")
|
||||
var/a = input("Please enter desired tag.", name, gpstag) as text
|
||||
a = copytext(sanitize(a), 1, 20)
|
||||
gpstag = a
|
||||
. = TRUE
|
||||
name = "global positioning system ([gpstag])"
|
||||
|
||||
if("power")
|
||||
toggletracking(usr)
|
||||
. = TRUE
|
||||
if("updating")
|
||||
updating = !updating
|
||||
. = TRUE
|
||||
if("globalmode")
|
||||
global_mode = !global_mode
|
||||
. = TRUE
|
||||
|
||||
|
||||
/obj/item/gps/science
|
||||
icon_state = "gps-s"
|
||||
gpstag = "SCI0"
|
||||
|
||||
/obj/item/gps/engineering
|
||||
icon_state = "gps-e"
|
||||
gpstag = "ENG0"
|
||||
|
||||
/obj/item/gps/mining
|
||||
icon_state = "gps-m"
|
||||
gpstag = "MINE0"
|
||||
desc = "A positioning system helpful for rescuing trapped or injured miners, keeping one on you at all times while mining might just save your life."
|
||||
|
||||
/obj/item/gps/cyborg
|
||||
icon_state = "gps-b"
|
||||
gpstag = "BORG0"
|
||||
desc = "A mining cyborg internal positioning system. Used as a recovery beacon for damaged cyborg assets, or a collaboration tool for mining teams."
|
||||
|
||||
/obj/item/gps/cyborg/Initialize()
|
||||
. = ..()
|
||||
ADD_TRAIT(src, TRAIT_NODROP, CYBORG_ITEM_TRAIT)
|
||||
|
||||
/obj/item/gps/internal
|
||||
icon_state = null
|
||||
item_flags = ABSTRACT
|
||||
gpstag = "Eerie Signal"
|
||||
desc = "Report to a coder immediately."
|
||||
invisibility = INVISIBILITY_MAXIMUM
|
||||
var/obj/item/implant/gps/implant
|
||||
|
||||
/obj/item/gps/internal/Initialize(mapload, obj/item/implant/gps/_implant)
|
||||
. = ..()
|
||||
implant = _implant
|
||||
|
||||
/obj/item/gps/internal/Destroy()
|
||||
if(implant?.imp_in)
|
||||
qdel(implant)
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/item/gps/internal/mining
|
||||
icon_state = "gps-m"
|
||||
gpstag = "MINER"
|
||||
desc = "A positioning system helpful for rescuing trapped or injured miners, keeping one on you at all times while mining might just save your life."
|
||||
|
||||
/obj/item/gps/internal/base
|
||||
gpstag = "NT_AUX"
|
||||
desc = "A homing signal from Nanotrasen's mining base."
|
||||
|
||||
/obj/item/gps/visible_debug
|
||||
name = "visible GPS"
|
||||
gpstag = "ADMIN"
|
||||
desc = "This admin-spawn GPS unit leaves the coordinates visible \
|
||||
on any turf that it passes over, for debugging. Especially useful \
|
||||
for marking the area around the transition edges."
|
||||
var/list/turf/tagged
|
||||
|
||||
/obj/item/gps/visible_debug/Initialize()
|
||||
. = ..()
|
||||
tagged = list()
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
|
||||
/obj/item/gps/visible_debug/process()
|
||||
var/turf/T = get_turf(src)
|
||||
if(T)
|
||||
// I assume it's faster to color,tag and OR the turf in, rather
|
||||
// then checking if its there
|
||||
T.color = RANDOM_COLOUR
|
||||
T.maptext = "[T.x],[T.y],[T.z]"
|
||||
tagged |= T
|
||||
|
||||
/obj/item/gps/visible_debug/proc/clear()
|
||||
while(tagged.len)
|
||||
var/turf/T = pop(tagged)
|
||||
T.color = initial(T.color)
|
||||
T.maptext = initial(T.maptext)
|
||||
|
||||
/obj/item/gps/visible_debug/Destroy()
|
||||
if(tagged)
|
||||
clear()
|
||||
tagged = null
|
||||
STOP_PROCESSING(SSfastprocess, src)
|
||||
. = ..()
|
||||
@@ -0,0 +1,266 @@
|
||||
#define PROXIMITY_NONE ""
|
||||
#define PROXIMITY_ON_SCREEN "_red"
|
||||
#define PROXIMITY_NEAR "_yellow"
|
||||
|
||||
/**
|
||||
* Multitool -- A multitool is used for hacking electronic devices.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/obj/item/multitool
|
||||
name = "multitool"
|
||||
desc = "Used for pulsing wires to test which to cut. Not recommended by doctors."
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "multitool"
|
||||
item_state = "multitool"
|
||||
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
|
||||
force = 5
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
tool_behaviour = TOOL_MULTITOOL
|
||||
throwforce = 0
|
||||
throw_range = 7
|
||||
throw_speed = 3
|
||||
materials = list(MAT_METAL=50, MAT_GLASS=20)
|
||||
var/obj/machinery/buffer // simple machine buffer for device linkage
|
||||
toolspeed = 1
|
||||
tool_behaviour = TOOL_MULTITOOL
|
||||
usesound = 'sound/weapons/empty.ogg'
|
||||
var/datum/integrated_io/selected_io = null //functional for integrated circuits.
|
||||
var/mode = 0
|
||||
|
||||
/obj/item/multitool/chaplain
|
||||
name = "\improper hypertool"
|
||||
desc = "Used for pulsing wires to test which to cut. Also emits microwaves to fry some brains!"
|
||||
damtype = BRAIN
|
||||
force = 18
|
||||
armour_penetration = 35
|
||||
hitsound = 'sound/effects/sparks4.ogg'
|
||||
var/chaplain_spawnable = TRUE
|
||||
total_mass = TOTAL_MASS_MEDIEVAL_WEAPON
|
||||
throw_speed = 3
|
||||
throw_range = 4
|
||||
throwforce = 10
|
||||
obj_flags = UNIQUE_RENAME
|
||||
|
||||
/obj/item/multitool/chaplain/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/anti_magic, TRUE, TRUE, FALSE, null, null, FALSE)
|
||||
|
||||
/obj/item/multitool/examine(mob/user)
|
||||
..()
|
||||
if(selected_io)
|
||||
to_chat(user, "<span class='notice'>Activate [src] to detach the data wire.</span>")
|
||||
if(buffer)
|
||||
to_chat(user, "<span class='notice'>Its buffer contains [buffer].</span>")
|
||||
|
||||
/obj/item/multitool/suicide_act(mob/living/carbon/user)
|
||||
user.visible_message("<span class='suicide'>[user] puts the [src] to [user.p_their()] chest. It looks like [user.p_theyre()] trying to pulse [user.p_their()] heart off!</span>")
|
||||
return OXYLOSS//theres a reason it wasnt recommended by doctors
|
||||
|
||||
/obj/item/multitool/attack_self(mob/user)
|
||||
if(selected_io)
|
||||
selected_io = null
|
||||
to_chat(user, "<span class='notice'>You clear the wired connection from the multitool.</span>")
|
||||
update_icon()
|
||||
|
||||
/obj/item/multitool/update_icon()
|
||||
if(selected_io)
|
||||
icon_state = "multitool_red"
|
||||
else
|
||||
icon_state = "multitool"
|
||||
|
||||
/obj/item/multitool/proc/wire(var/datum/integrated_io/io, mob/user)
|
||||
if(!io.holder.assembly)
|
||||
to_chat(user, "<span class='warning'>\The [io.holder] needs to be secured inside an assembly first.</span>")
|
||||
return
|
||||
|
||||
if(selected_io)
|
||||
if(io == selected_io)
|
||||
to_chat(user, "<span class='warning'>Wiring \the [selected_io.holder]'s [selected_io.name] into itself is rather pointless.</span>")
|
||||
return
|
||||
if(io.io_type != selected_io.io_type)
|
||||
to_chat(user, "<span class='warning'>Those two types of channels are incompatible. The first is a [selected_io.io_type], \
|
||||
while the second is a [io.io_type].</span>")
|
||||
return
|
||||
if(io.holder.assembly && io.holder.assembly != selected_io.holder.assembly)
|
||||
to_chat(user, "<span class='warning'>Both \the [io.holder] and \the [selected_io.holder] need to be inside the same assembly.</span>")
|
||||
return
|
||||
io.connect_pin(selected_io)
|
||||
|
||||
to_chat(user, "<span class='notice'>You connect \the [selected_io.holder]'s [selected_io.name] to \the [io.holder]'s [io.name].</span>")
|
||||
selected_io.holder.interact(user) // This is to update the UI.
|
||||
selected_io = null
|
||||
|
||||
else
|
||||
selected_io = io
|
||||
to_chat(user, "<span class='notice'>You link \the multitool to \the [selected_io.holder]'s [selected_io.name] data channel.</span>")
|
||||
|
||||
update_icon()
|
||||
|
||||
|
||||
/obj/item/multitool/proc/unwire(var/datum/integrated_io/io1, var/datum/integrated_io/io2, mob/user)
|
||||
if(!io1.linked.len || !io2.linked.len)
|
||||
to_chat(user, "<span class='warning'>There is nothing connected to the data channel.</span>")
|
||||
return
|
||||
|
||||
if(!(io1 in io2.linked) || !(io2 in io1.linked) )
|
||||
to_chat(user, "<span class='warning'>These data pins aren't connected!</span>")
|
||||
return
|
||||
else
|
||||
io1.disconnect_pin(io2)
|
||||
to_chat(user, "<span class='notice'>You clip the data connection between the [io1.holder.displayed_name]'s \
|
||||
[io1.name] and the [io2.holder.displayed_name]'s [io2.name].</span>")
|
||||
io1.holder.interact(user) // This is to update the UI.
|
||||
update_icon()
|
||||
|
||||
|
||||
|
||||
// Syndicate device disguised as a multitool; it will turn red when an AI camera is nearby.
|
||||
|
||||
/obj/item/multitool/ai_detect
|
||||
var/track_cooldown = 0
|
||||
var/track_delay = 10 //How often it checks for proximity
|
||||
var/detect_state = PROXIMITY_NONE
|
||||
var/rangealert = 8 //Glows red when inside
|
||||
var/rangewarning = 20 //Glows yellow when inside
|
||||
var/hud_type = DATA_HUD_AI_DETECT
|
||||
var/hud_on = FALSE
|
||||
var/mob/camera/aiEye/remote/ai_detector/eye
|
||||
var/datum/action/item_action/toggle_multitool/toggle_action
|
||||
|
||||
/obj/item/multitool/ai_detect/Initialize()
|
||||
. = ..()
|
||||
START_PROCESSING(SSobj, src)
|
||||
eye = new /mob/camera/aiEye/remote/ai_detector()
|
||||
toggle_action = new /datum/action/item_action/toggle_multitool(src)
|
||||
|
||||
/obj/item/multitool/ai_detect/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
if(hud_on && ismob(loc))
|
||||
remove_hud(loc)
|
||||
QDEL_NULL(toggle_action)
|
||||
QDEL_NULL(eye)
|
||||
return ..()
|
||||
|
||||
/obj/item/multitool/ai_detect/ui_action_click()
|
||||
return
|
||||
|
||||
/obj/item/multitool/ai_detect/update_icon()
|
||||
if(selected_io)
|
||||
icon_state = "multitool_red"
|
||||
else
|
||||
icon_state = "[initial(icon_state)][detect_state]"
|
||||
|
||||
/obj/item/multitool/ai_detect/equipped(mob/living/carbon/human/user, slot)
|
||||
..()
|
||||
if(hud_on)
|
||||
show_hud(user)
|
||||
|
||||
/obj/item/multitool/ai_detect/dropped(mob/living/carbon/human/user)
|
||||
..()
|
||||
if(hud_on)
|
||||
remove_hud(user)
|
||||
|
||||
/obj/item/multitool/ai_detect/process()
|
||||
if(track_cooldown > world.time)
|
||||
return
|
||||
detect_state = PROXIMITY_NONE
|
||||
if(eye.eye_user)
|
||||
eye.setLoc(get_turf(src))
|
||||
multitool_detect()
|
||||
update_icon()
|
||||
track_cooldown = world.time + track_delay
|
||||
|
||||
/obj/item/multitool/ai_detect/proc/toggle_hud(mob/user)
|
||||
hud_on = !hud_on
|
||||
if(user)
|
||||
to_chat(user, "<span class='notice'>You toggle the ai detection HUD on [src] [hud_on ? "on" : "off"].</span>")
|
||||
if(hud_on)
|
||||
show_hud(user)
|
||||
else
|
||||
remove_hud(user)
|
||||
|
||||
/obj/item/multitool/ai_detect/proc/show_hud(mob/user)
|
||||
if(user && hud_type)
|
||||
var/obj/screen/plane_master/camera_static/PM = user.hud_used.plane_masters["[CAMERA_STATIC_PLANE]"]
|
||||
PM.alpha = 150
|
||||
var/datum/atom_hud/H = GLOB.huds[hud_type]
|
||||
if(!H.hudusers[user])
|
||||
H.add_hud_to(user)
|
||||
eye.eye_user = user
|
||||
eye.setLoc(get_turf(src))
|
||||
|
||||
/obj/item/multitool/ai_detect/proc/remove_hud(mob/user)
|
||||
if(user && hud_type)
|
||||
var/obj/screen/plane_master/camera_static/PM = user.hud_used.plane_masters["[CAMERA_STATIC_PLANE]"]
|
||||
PM.alpha = 255
|
||||
var/datum/atom_hud/H = GLOB.huds[hud_type]
|
||||
H.remove_hud_from(user)
|
||||
if(eye)
|
||||
eye.setLoc(null)
|
||||
eye.eye_user = null
|
||||
|
||||
/obj/item/multitool/ai_detect/proc/multitool_detect()
|
||||
var/turf/our_turf = get_turf(src)
|
||||
for(var/mob/living/silicon/ai/AI in GLOB.ai_list)
|
||||
if(AI.cameraFollow == src)
|
||||
detect_state = PROXIMITY_ON_SCREEN
|
||||
break
|
||||
|
||||
if(detect_state)
|
||||
return
|
||||
var/datum/camerachunk/chunk = GLOB.cameranet.chunkGenerated(our_turf.x, our_turf.y, our_turf.z)
|
||||
if(chunk && chunk.seenby.len)
|
||||
for(var/mob/camera/aiEye/A in chunk.seenby)
|
||||
if(!A.ai_detector_visible)
|
||||
continue
|
||||
var/turf/detect_turf = get_turf(A)
|
||||
if(get_dist(our_turf, detect_turf) < rangealert)
|
||||
detect_state = PROXIMITY_ON_SCREEN
|
||||
break
|
||||
if(get_dist(our_turf, detect_turf) < rangewarning)
|
||||
detect_state = PROXIMITY_NEAR
|
||||
break
|
||||
|
||||
/mob/camera/aiEye/remote/ai_detector
|
||||
name = "AI detector eye"
|
||||
ai_detector_visible = FALSE
|
||||
use_static = USE_STATIC_TRANSPARENT
|
||||
visible_icon = FALSE
|
||||
|
||||
/datum/action/item_action/toggle_multitool
|
||||
name = "Toggle AI detector HUD"
|
||||
check_flags = NONE
|
||||
|
||||
/datum/action/item_action/toggle_multitool/Trigger()
|
||||
if(!..())
|
||||
return 0
|
||||
if(target)
|
||||
var/obj/item/multitool/ai_detect/M = target
|
||||
M.toggle_hud(owner)
|
||||
return 1
|
||||
|
||||
/obj/item/multitool/cyborg
|
||||
name = "multitool"
|
||||
desc = "Optimised and stripped-down version of a regular multitool."
|
||||
icon = 'icons/obj/items_cyborg.dmi'
|
||||
icon_state = "multitool_cyborg"
|
||||
toolspeed = 0.5
|
||||
|
||||
/obj/item/multitool/abductor
|
||||
name = "alien multitool"
|
||||
desc = "An omni-technological interface."
|
||||
icon = 'icons/obj/abductor.dmi'
|
||||
icon_state = "multitool"
|
||||
toolspeed = 0.1
|
||||
|
||||
/obj/item/multitool/advanced
|
||||
name = "advanced multitool"
|
||||
desc = "The reproduction of an abductor's multitool, this multitool is a classy silver."
|
||||
icon = 'icons/obj/advancedtools.dmi'
|
||||
icon_state = "multitool"
|
||||
toolspeed = 0.2
|
||||
@@ -0,0 +1,165 @@
|
||||
/obj/item/paicard
|
||||
name = "personal AI device"
|
||||
icon = 'icons/obj/aicards.dmi'
|
||||
icon_state = "pai"
|
||||
item_state = "electronic"
|
||||
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
slot_flags = ITEM_SLOT_BELT
|
||||
var/mob/living/silicon/pai/pai
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF | INDESTRUCTIBLE
|
||||
|
||||
/obj/item/paicard/suicide_act(mob/living/user)
|
||||
user.visible_message("<span class='suicide'>[user] is staring sadly at [src]! [user.p_they()] can't keep living without real human intimacy!</span>")
|
||||
return OXYLOSS
|
||||
|
||||
/obj/item/paicard/Initialize()
|
||||
SSpai.pai_card_list += src
|
||||
add_overlay("pai-off")
|
||||
return ..()
|
||||
|
||||
/obj/item/paicard/Destroy()
|
||||
//Will stop people throwing friend pAIs into the singularity so they can respawn
|
||||
SSpai.pai_card_list -= src
|
||||
if (!QDELETED(pai))
|
||||
QDEL_NULL(pai)
|
||||
return ..()
|
||||
|
||||
/obj/item/paicard/attack_self(mob/user)
|
||||
if (!in_range(src, user))
|
||||
return
|
||||
user.set_machine(src)
|
||||
var/dat = "<TT><B>Personal AI Device</B><BR>"
|
||||
if(pai)
|
||||
if(!pai.master_dna || !pai.master)
|
||||
dat += "<a href='byond://?src=[REF(src)];setdna=1'>Imprint Master DNA</a><br>"
|
||||
dat += "Installed Personality: [pai.name]<br>"
|
||||
dat += "Prime directive: <br>[pai.laws.zeroth]<br>"
|
||||
for(var/slaws in pai.laws.supplied)
|
||||
dat += "Additional directives: <br>[slaws]<br>"
|
||||
dat += "<a href='byond://?src=[REF(src)];setlaws=1'>Configure Directives</a><br>"
|
||||
dat += "<br>"
|
||||
dat += "<h3>Device Settings</h3><br>"
|
||||
if(pai.radio)
|
||||
dat += "<b>Radio Uplink</b><br>"
|
||||
dat += "Transmit: <A href='byond://?src=[REF(src)];wires=[WIRE_TX]'>[(pai.radio.wires.is_cut(WIRE_TX)) ? "Disabled" : "Enabled"]</A><br>"
|
||||
dat += "Receive: <A href='byond://?src=[REF(src)];wires=[WIRE_RX]'>[(pai.radio.wires.is_cut(WIRE_RX)) ? "Disabled" : "Enabled"]</A><br>"
|
||||
else
|
||||
dat += "<b>Radio Uplink</b><br>"
|
||||
dat += "<font color=red><i>Radio firmware not loaded. Please install a pAI personality to load firmware.</i></font><br>"
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
if(H.real_name == pai.master || H.dna.unique_enzymes == pai.master_dna)
|
||||
dat += "<A href='byond://?src=[REF(src)];toggle_holo=1'>\[[pai.canholo? "Disable" : "Enable"] holomatrix projectors\]</a><br>"
|
||||
dat += "<A href='byond://?src=[REF(src)];wipe=1'>\[Wipe current pAI personality\]</a><br>"
|
||||
else
|
||||
dat += "No personality installed.<br>"
|
||||
dat += "Searching for a personality... Press view available personalities to notify potential candidates."
|
||||
dat += "<A href='byond://?src=[REF(src)];request=1'>\[View available personalities\]</a><br>"
|
||||
user << browse(dat, "window=paicard")
|
||||
onclose(user, "paicard")
|
||||
return
|
||||
|
||||
/obj/item/paicard/Topic(href, href_list)
|
||||
|
||||
if(!usr || usr.stat)
|
||||
return
|
||||
|
||||
if(href_list["request"])
|
||||
SSpai.findPAI(src, usr)
|
||||
|
||||
if(pai)
|
||||
if(!(loc == usr))
|
||||
return
|
||||
if(href_list["setdna"])
|
||||
if(pai.master_dna)
|
||||
return
|
||||
if(!iscarbon(usr))
|
||||
to_chat(usr, "<span class='warning'>You don't have any DNA, or your DNA is incompatible with this device!</span>")
|
||||
else
|
||||
var/mob/living/carbon/M = usr
|
||||
pai.master = M.real_name
|
||||
pai.master_dna = M.dna.unique_enzymes
|
||||
to_chat(pai, "<span class='notice'>You have been bound to a new master.</span>")
|
||||
pai.emittersemicd = FALSE
|
||||
if(href_list["wipe"])
|
||||
var/confirm = input("Are you CERTAIN you wish to delete the current personality? This action cannot be undone.", "Personality Wipe") in list("Yes", "No")
|
||||
if(confirm == "Yes")
|
||||
if(pai)
|
||||
to_chat(pai, "<span class='warning'>You feel yourself slipping away from reality.</span>")
|
||||
to_chat(pai, "<span class='danger'>Byte by byte you lose your sense of self.</span>")
|
||||
to_chat(pai, "<span class='userdanger'>Your mental faculties leave you.</span>")
|
||||
to_chat(pai, "<span class='rose'>oblivion... </span>")
|
||||
qdel(pai)
|
||||
if(href_list["wires"])
|
||||
var/wire = text2num(href_list["wires"])
|
||||
if(pai.radio)
|
||||
pai.radio.wires.cut(wire)
|
||||
if(href_list["setlaws"])
|
||||
var/newlaws = copytext(sanitize(input("Enter any additional directives you would like your pAI personality to follow. Note that these directives will not override the personality's allegiance to its imprinted master. Conflicting directives will be ignored.", "pAI Directive Configuration", pai.laws.supplied[1]) as message),1,MAX_MESSAGE_LEN)
|
||||
if(newlaws && pai)
|
||||
pai.add_supplied_law(0,newlaws)
|
||||
if(href_list["toggle_holo"])
|
||||
if(pai.canholo)
|
||||
to_chat(pai, "<span class='userdanger'>Your owner has disabled your holomatrix projectors!</span>")
|
||||
pai.canholo = FALSE
|
||||
to_chat(usr, "<span class='warning'>You disable your pAI's holomatrix!</span>")
|
||||
else
|
||||
to_chat(pai, "<span class='boldnotice'>Your owner has enabled your holomatrix projectors!</span>")
|
||||
pai.canholo = TRUE
|
||||
to_chat(usr, "<span class='notice'>You enable your pAI's holomatrix!</span>")
|
||||
|
||||
attack_self(usr)
|
||||
|
||||
// WIRE_SIGNAL = 1
|
||||
// WIRE_RECEIVE = 2
|
||||
// WIRE_TRANSMIT = 4
|
||||
|
||||
/obj/item/paicard/proc/setPersonality(mob/living/silicon/pai/personality)
|
||||
src.pai = personality
|
||||
src.add_overlay("pai-null")
|
||||
|
||||
playsound(loc, 'sound/effects/pai_boot.ogg', 50, 1, -1)
|
||||
audible_message("\The [src] plays a cheerful startup noise!")
|
||||
|
||||
/obj/item/paicard/proc/setEmotion(emotion)
|
||||
if(pai)
|
||||
src.cut_overlays()
|
||||
switch(emotion)
|
||||
if(1)
|
||||
src.add_overlay("pai-happy")
|
||||
if(2)
|
||||
src.add_overlay("pai-cat")
|
||||
if(3)
|
||||
src.add_overlay("pai-extremely-happy")
|
||||
if(4)
|
||||
src.add_overlay("pai-face")
|
||||
if(5)
|
||||
src.add_overlay("pai-laugh")
|
||||
if(6)
|
||||
src.add_overlay("pai-off")
|
||||
if(7)
|
||||
src.add_overlay("pai-sad")
|
||||
if(8)
|
||||
src.add_overlay("pai-angry")
|
||||
if(9)
|
||||
src.add_overlay("pai-what")
|
||||
if(10)
|
||||
src.add_overlay("pai-null")
|
||||
if(11)
|
||||
src.add_overlay("pai-exclamation")
|
||||
if(12)
|
||||
src.add_overlay("pai-question")
|
||||
if(13)
|
||||
src.add_overlay("pai-sunglasses")
|
||||
|
||||
/obj/item/paicard/proc/alertUpdate()
|
||||
visible_message("<span class ='info'>[src] flashes a message across its screen, \"Additional personalities available for download.\"", "<span class='notice'>[src] bleeps electronically.</span>")
|
||||
|
||||
/obj/item/paicard/emp_act(severity)
|
||||
. = ..()
|
||||
if (. & EMP_PROTECT_SELF)
|
||||
return
|
||||
if(pai && !pai.holoform)
|
||||
pai.emp_act(severity)
|
||||
@@ -0,0 +1,776 @@
|
||||
|
||||
/*
|
||||
|
||||
CONTAINS:
|
||||
T-RAY
|
||||
HEALTH ANALYZER
|
||||
GAS ANALYZER
|
||||
SLIME SCANNER
|
||||
|
||||
*/
|
||||
/obj/item/t_scanner
|
||||
name = "\improper T-ray scanner"
|
||||
desc = "A terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes."
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "t-ray0"
|
||||
var/on = FALSE
|
||||
slot_flags = ITEM_SLOT_BELT
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
item_state = "electronic"
|
||||
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
|
||||
materials = list(MAT_METAL=150)
|
||||
|
||||
/obj/item/t_scanner/suicide_act(mob/living/carbon/user)
|
||||
user.visible_message("<span class='suicide'>[user] begins to emit terahertz-rays into [user.p_their()] brain with [src]! It looks like [user.p_theyre()] trying to commit suicide!</span>")
|
||||
return TOXLOSS
|
||||
|
||||
/obj/item/t_scanner/attack_self(mob/user)
|
||||
|
||||
on = !on
|
||||
icon_state = copytext(icon_state, 1, length(icon_state))+"[on]"
|
||||
|
||||
if(on)
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/t_scanner/process()
|
||||
if(!on)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return null
|
||||
scan()
|
||||
|
||||
/obj/item/t_scanner/proc/scan()
|
||||
t_ray_scan(loc)
|
||||
|
||||
/proc/t_ray_scan(mob/viewer, flick_time = 8, distance = 3)
|
||||
if(!ismob(viewer) || !viewer.client)
|
||||
return
|
||||
var/list/t_ray_images = list()
|
||||
for(var/obj/O in orange(distance, viewer) )
|
||||
if(O.level != 1)
|
||||
continue
|
||||
|
||||
if(O.invisibility == INVISIBILITY_MAXIMUM)
|
||||
var/image/I = new(loc = get_turf(O))
|
||||
var/mutable_appearance/MA = new(O)
|
||||
MA.alpha = 128
|
||||
MA.dir = O.dir
|
||||
I.appearance = MA
|
||||
t_ray_images += I
|
||||
if(t_ray_images.len)
|
||||
flick_overlay(t_ray_images, list(viewer.client), flick_time)
|
||||
|
||||
/obj/item/healthanalyzer
|
||||
name = "health analyzer"
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "health"
|
||||
item_state = "healthanalyzer"
|
||||
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
|
||||
desc = "A hand-held body scanner able to distinguish vital signs of the subject."
|
||||
flags_1 = CONDUCT_1
|
||||
item_flags = NOBLUDGEON
|
||||
slot_flags = ITEM_SLOT_BELT
|
||||
throwforce = 3
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
throw_speed = 3
|
||||
throw_range = 7
|
||||
materials = list(MAT_METAL=200)
|
||||
var/mode = 1
|
||||
var/scanmode = 0
|
||||
var/advanced = FALSE
|
||||
|
||||
/obj/item/healthanalyzer/suicide_act(mob/living/carbon/user)
|
||||
user.visible_message("<span class='suicide'>[user] begins to analyze [user.p_them()]self with [src]! The display shows that [user.p_theyre()] dead!</span>")
|
||||
return BRUTELOSS
|
||||
|
||||
/obj/item/healthanalyzer/attack_self(mob/user)
|
||||
if(!scanmode)
|
||||
to_chat(user, "<span class='notice'>You switch the health analyzer to scan chemical contents.</span>")
|
||||
scanmode = 1
|
||||
else
|
||||
to_chat(user, "<span class='notice'>You switch the health analyzer to check physical health.</span>")
|
||||
scanmode = 0
|
||||
|
||||
/obj/item/healthanalyzer/attack(mob/living/M, mob/living/carbon/human/user)
|
||||
|
||||
// Clumsiness/brain damage check
|
||||
if ((HAS_TRAIT(user, TRAIT_CLUMSY) || HAS_TRAIT(user, TRAIT_DUMB)) && prob(50))
|
||||
to_chat(user, "<span class='notice'>You stupidly try to analyze the floor's vitals!</span>")
|
||||
user.visible_message("<span class='warning'>[user] has analyzed the floor's vitals!</span>")
|
||||
var/msg = "<span class='info'>*---------*\nAnalyzing results for The floor:\n\tOverall status: <b>Healthy</b>\n"
|
||||
msg += "Key: <font color='blue'>Suffocation</font>/<font color='green'>Toxin</font>/<font color='#FF8000'>Burn</font>/<font color='red'>Brute</font>\n"
|
||||
msg += "\tDamage specifics: <font color='blue'>0</font>-<font color='green'>0</font>-<font color='#FF8000'>0</font>-<font color='red'>0</font>\n"
|
||||
msg += "Body temperature: ???\n"
|
||||
msg += "*---------*</span>"
|
||||
to_chat(user, msg)
|
||||
return
|
||||
|
||||
user.visible_message("<span class='notice'>[user] has analyzed [M]'s vitals.</span>")
|
||||
|
||||
if(scanmode == 0)
|
||||
healthscan(user, M, mode, advanced)
|
||||
else if(scanmode == 1)
|
||||
chemscan(user, M)
|
||||
|
||||
add_fingerprint(user)
|
||||
|
||||
|
||||
// Used by the PDA medical scanner too
|
||||
/proc/healthscan(mob/user, mob/living/M, mode = 1, advanced = FALSE)
|
||||
if(isliving(user) && (user.incapacitated() || user.eye_blind))
|
||||
return
|
||||
//Damage specifics
|
||||
var/oxy_loss = M.getOxyLoss()
|
||||
var/tox_loss = M.getToxLoss()
|
||||
var/fire_loss = M.getFireLoss()
|
||||
var/brute_loss = M.getBruteLoss()
|
||||
var/mob_status = (M.stat == DEAD ? "<span class='alert'><b>Deceased</b></span>" : "<b>[round(M.health/M.maxHealth,0.01)*100] % healthy</b>")
|
||||
|
||||
if(HAS_TRAIT(M, TRAIT_FAKEDEATH) && !advanced)
|
||||
mob_status = "<span class='alert'><b>Deceased</b></span>"
|
||||
oxy_loss = max(rand(1, 40), oxy_loss, (300 - (tox_loss + fire_loss + brute_loss))) // Random oxygen loss
|
||||
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
if(H.undergoing_cardiac_arrest() && H.stat != DEAD)
|
||||
to_chat(user, "<span class='danger'>Subject suffering from heart attack: Apply defibrillation or other electric shock immediately!</span>")
|
||||
if(H.undergoing_liver_failure() && H.stat != DEAD) //might be depreciated BUG_PROBABLE_CAUSE
|
||||
to_chat(user, "<span class='danger'>Subject is suffering from liver failure: Apply Corazone and begin a liver transplant immediately!</span>")
|
||||
|
||||
var/msg = "<span class='info'>*---------*\nAnalyzing results for [M]:\n\tOverall status: [mob_status]\n"
|
||||
|
||||
// Damage descriptions
|
||||
if(brute_loss > 10)
|
||||
msg += "\t<span class='alert'>[brute_loss > 50 ? "Severe" : "Minor"] tissue damage detected.</span>\n"
|
||||
if(fire_loss > 10)
|
||||
msg += "\t<span class='alert'>[fire_loss > 50 ? "Severe" : "Minor"] burn damage detected.</span>\n"
|
||||
if(oxy_loss > 10)
|
||||
msg += "\t<span class='info'><span class='alert'>[oxy_loss > 50 ? "Severe" : "Minor"] oxygen deprivation detected.</span>\n"
|
||||
if(tox_loss > 10)
|
||||
msg += "\t<span class='alert'>[tox_loss > 50 ? "Severe" : "Minor"] amount of toxin damage detected.</span>\n"
|
||||
if(M.getStaminaLoss())
|
||||
msg += "\t<span class='alert'>Subject appears to be suffering from fatigue.</span>\n"
|
||||
if(advanced)
|
||||
msg += "\t<span class='info'>Fatigue Level: [M.getStaminaLoss()]%.</span>\n"
|
||||
if (M.getCloneLoss())
|
||||
msg += "\t<span class='alert'>Subject appears to have [M.getCloneLoss() > 30 ? "Severe" : "Minor"] cellular damage.</span>\n"
|
||||
if(advanced)
|
||||
msg += "\t<span class='info'>Cellular Damage Level: [M.getCloneLoss()].</span>\n"
|
||||
if (!M.getorgan(/obj/item/organ/brain))
|
||||
to_chat(user, "\t<span class='alert'>Subject lacks a brain.</span>") //Unsure how this won't proc for 50% of the cit playerbase (This is a joke everyone on cit a cute.)
|
||||
if(ishuman(M) && advanced) // Should I make this not advanced?
|
||||
var/mob/living/carbon/human/H = M
|
||||
var/obj/item/organ/liver/L = H.getorganslot("liver")
|
||||
if(L)
|
||||
if(L.swelling > 20)
|
||||
msg += "\t<span class='danger'>Subject is suffering from an enlarged liver.</span>\n" //i.e. shrink their liver or give them a transplant.
|
||||
else
|
||||
msg += "\t<span class='danger'>Subject's liver is missing.</span>\n"
|
||||
var/obj/item/organ/tongue/T = H.getorganslot("tongue")
|
||||
if(T)
|
||||
if(T.damage > 40)
|
||||
msg += "\t<span class='danger'>Subject is suffering from severe burn tissue on their tongue.</span>\n" //i.e. their tongue is shot
|
||||
if(T.name == "fluffy tongue")
|
||||
msg += "\t<span class='danger'>Subject is suffering from a fluffified tongue. Suggested cure: Yamerol or a tongue transplant.</span>\n"
|
||||
else
|
||||
msg += "\t<span class='danger'>Subject's tongue is missing.</span>\n"
|
||||
var/obj/item/organ/lungs/Lung = H.getorganslot("lungs")
|
||||
if(Lung)
|
||||
if(Lung.damage > 150)
|
||||
msg += "\t<span class='danger'>Subject is suffering from acute emphysema leading to trouble breathing.</span>\n" //i.e. Their lungs are shot
|
||||
else
|
||||
msg += "\t<span class='danger'>Subject's lungs have collapsed from trauma!</span>\n"
|
||||
var/obj/item/organ/genital/penis/P = H.getorganslot("penis")
|
||||
if(P)
|
||||
if(P.length>20)
|
||||
msg += "\t<span class='info'>Subject has a sizeable gentleman's organ at [P.length] inches.</span>\n"
|
||||
var/obj/item/organ/genital/breasts/Br = H.getorganslot("breasts")
|
||||
if(Br)
|
||||
if(Br.cached_size>5)
|
||||
msg += "\t<span class='info'>Subject has a sizeable bosom with a [Br.size] cup.</span>\n"
|
||||
|
||||
if (M.getOrganLoss(ORGAN_SLOT_BRAIN) >= 200 || !M.getorgan(/obj/item/organ/brain))
|
||||
msg += "\t<span class='alert'>Subject's brain function is non-existent.</span>\n"
|
||||
else if (M.getOrganLoss(ORGAN_SLOT_BRAIN) >= 120)
|
||||
msg += "\t<span class='alert'>Severe brain damage detected. Subject likely to have mental traumas.</span>\n"
|
||||
else if (M.getOrganLoss(ORGAN_SLOT_BRAIN) >= 45)
|
||||
msg += "\t<span class='alert'>Brain damage detected.</span>\n"
|
||||
|
||||
if(iscarbon(M))
|
||||
var/mob/living/carbon/C = M
|
||||
if(LAZYLEN(C.get_traumas()))
|
||||
var/list/trauma_text = list()
|
||||
for(var/datum/brain_trauma/B in C.get_traumas())
|
||||
var/trauma_desc = ""
|
||||
switch(B.resilience)
|
||||
if(TRAUMA_RESILIENCE_SURGERY)
|
||||
trauma_desc += "severe "
|
||||
if(TRAUMA_RESILIENCE_LOBOTOMY)
|
||||
trauma_desc += "deep-rooted "
|
||||
if(TRAUMA_RESILIENCE_MAGIC, TRAUMA_RESILIENCE_ABSOLUTE)
|
||||
trauma_desc += "permanent "
|
||||
trauma_desc += B.scan_desc
|
||||
trauma_text += trauma_desc
|
||||
msg += "\t<span class='alert'>Cerebral traumas detected: subject appears to be suffering from [english_list(trauma_text)].</span>\n"
|
||||
if(C.roundstart_quirks.len)
|
||||
msg += "\t<span class='info'>Subject has the following physiological traits: [C.get_trait_string()].</span>\n"
|
||||
if(advanced)
|
||||
msg += "\t<span class='info'>Brain Activity Level: [(200 - M.getOrganLoss(ORGAN_SLOT_BRAIN))/2]%.</span>\n"
|
||||
if(M.radiation)
|
||||
msg += "\t<span class='alert'>Subject is irradiated.</span>\n"
|
||||
msg += "\t<span class='info'>Radiation Level: [M.radiation] rad</span>\n"
|
||||
|
||||
if(advanced && M.hallucinating())
|
||||
msg += "\t<span class='info'>Subject is hallucinating.</span>\n"
|
||||
|
||||
//MKUltra
|
||||
if(advanced && M.has_status_effect(/datum/status_effect/chem/enthrall))
|
||||
msg += "\t<span class='info'>Subject has abnormal brain fuctions.</span>\n"
|
||||
|
||||
//Astrogen shenanigans
|
||||
if(advanced && M.reagents.has_reagent("astral"))
|
||||
if(M.mind)
|
||||
msg += "\t<span class='danger'>Warning: subject may be possesed.</span>\n"
|
||||
else
|
||||
msg += "\t<span class='notice'>Subject appears to be astrally projecting.</span>\n"
|
||||
|
||||
//Eyes and ears
|
||||
if(advanced)
|
||||
if(iscarbon(M))
|
||||
var/mob/living/carbon/C = M
|
||||
var/obj/item/organ/ears/ears = C.getorganslot(ORGAN_SLOT_EARS)
|
||||
msg += "\t<span class='info'><b>==EAR STATUS==</b></span>\n"
|
||||
if(istype(ears))
|
||||
var/healthy = TRUE
|
||||
if(HAS_TRAIT_FROM(C, TRAIT_DEAF, GENETIC_MUTATION))
|
||||
healthy = FALSE
|
||||
msg += "\t<span class='alert'>Subject is genetically deaf.</span>\n"
|
||||
else if(HAS_TRAIT(C, TRAIT_DEAF))
|
||||
healthy = FALSE
|
||||
msg += "\t<span class='alert'>Subject is deaf.</span>\n"
|
||||
else
|
||||
if(ears.damage)
|
||||
to_chat(user, "\t<span class='alert'>Subject has [ears.damage > ears.maxHealth ? "permanent ": "temporary "]hearing damage.</span>")
|
||||
healthy = FALSE
|
||||
if(ears.deaf)
|
||||
to_chat(user, "\t<span class='alert'>Subject is [ears.damage > ears.maxHealth ? "permanently ": "temporarily "] deaf.</span>")
|
||||
healthy = FALSE
|
||||
if(healthy)
|
||||
msg += "\t<span class='info'>Healthy.</span>\n"
|
||||
else
|
||||
msg += "\t<span class='alert'>Subject does not have ears.</span>\n"
|
||||
var/obj/item/organ/eyes/eyes = C.getorganslot(ORGAN_SLOT_EYES)
|
||||
msg += "\t<span class='info'><b>==EYE STATUS==</b></span>\n"
|
||||
if(istype(eyes))
|
||||
var/healthy = TRUE
|
||||
if(HAS_TRAIT(C, TRAIT_BLIND))
|
||||
msg += "\t<span class='alert'>Subject is blind.</span>\n"
|
||||
healthy = FALSE
|
||||
if(HAS_TRAIT(C, TRAIT_NEARSIGHT))
|
||||
msg += "\t<span class='alert'>Subject is nearsighted.</span>\n"
|
||||
healthy = FALSE
|
||||
if(eyes.damage > 30)
|
||||
msg += "\t<span class='alert'>Subject has severe eye damage.</span>\n"
|
||||
healthy = FALSE
|
||||
else if(eyes.damage > 20)
|
||||
msg += "\t<span class='alert'>Subject has significant eye damage.</span>\n"
|
||||
healthy = FALSE
|
||||
else if(eyes.damage)
|
||||
msg += "\t<span class='alert'>Subject has minor eye damage.</span>\n"
|
||||
healthy = FALSE
|
||||
if(healthy)
|
||||
msg += "\t<span class='info'>Healthy.</span>\n"
|
||||
else
|
||||
msg += "\t<span class='alert'>Subject does not have eyes.</span>\n"
|
||||
|
||||
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
var/ldamage = H.return_liver_damage()
|
||||
if(ldamage > 10)
|
||||
msg += "\t<span class='alert'>[ldamage > 45 ? "Severe" : "Minor"] liver damage detected.</span>\n"
|
||||
|
||||
// Body part damage report
|
||||
if(iscarbon(M) && mode == 1)
|
||||
var/mob/living/carbon/C = M
|
||||
var/list/damaged = C.get_damaged_bodyparts(1,1)
|
||||
if(length(damaged)>0 || oxy_loss>0 || tox_loss>0 || fire_loss>0)
|
||||
msg += "<span class='info'>\tDamage: <span class='info'><font color='red'>Brute</font></span>-<font color='#FF8000'>Burn</font>-<font color='green'>Toxin</font>-<font color='blue'>Suffocation</font>\n\t\tSpecifics: <font color='red'>[brute_loss]</font>-<font color='#FF8000'>[fire_loss]</font>-<font color='green'>[tox_loss]</font>-<font color='blue'>[oxy_loss]</font></span>\n"
|
||||
for(var/obj/item/bodypart/org in damaged)
|
||||
msg += "\t\t<span class='info'>[capitalize(org.name)]: [(org.brute_dam > 0) ? "<font color='red'>[org.brute_dam]</font></span>" : "<font color='red'>0</font>"]-[(org.burn_dam > 0) ? "<font color='#FF8000'>[org.burn_dam]</font>" : "<font color='#FF8000'>0</font>"]\n"
|
||||
|
||||
//Organ damages report
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
var/minor_damage
|
||||
var/major_damage
|
||||
var/max_damage
|
||||
var/report_organs = FALSE
|
||||
|
||||
//Piece together the lists to be reported
|
||||
for(var/O in H.internal_organs)
|
||||
var/obj/item/organ/organ = O
|
||||
if(organ.organ_flags & ORGAN_FAILING)
|
||||
report_organs = TRUE //if we report one organ, we report all organs, even if the lists are empty, just for consistency
|
||||
if(max_damage)
|
||||
max_damage += ", " //prelude the organ if we've already reported an organ
|
||||
max_damage += organ.name //this just slaps the organ name into the string of text
|
||||
else
|
||||
max_damage = "\t<span class='alert'>Non-Functional Organs: " //our initial statement
|
||||
max_damage += organ.name
|
||||
else if(organ.damage > organ.high_threshold)
|
||||
report_organs = TRUE
|
||||
if(major_damage)
|
||||
major_damage += ", "
|
||||
major_damage += organ.name
|
||||
else
|
||||
major_damage = "\t<span class='info'>Severely Damaged Organs: "
|
||||
major_damage += organ.name
|
||||
else if(organ.damage > organ.low_threshold)
|
||||
report_organs = TRUE
|
||||
if(minor_damage)
|
||||
minor_damage += ", "
|
||||
minor_damage += organ.name
|
||||
else
|
||||
minor_damage = "\t<span class='info'>Mildly Damaged Organs: "
|
||||
minor_damage += organ.name
|
||||
|
||||
if(report_organs) //we either finish the list, or set it to be empty if no organs were reported in that category
|
||||
if(!max_damage)
|
||||
max_damage = "\t<span class='alert'>Non-Functional Organs: </span>\n"
|
||||
else
|
||||
max_damage += "</span>\n"
|
||||
if(!major_damage)
|
||||
major_damage = "\t<span class='info'>Severely Damaged Organs: </span>\n"
|
||||
else
|
||||
major_damage += "</span>\n"
|
||||
if(!minor_damage)
|
||||
minor_damage = "\t<span class='info'>Mildly Damaged Organs: </span>\n"
|
||||
else
|
||||
minor_damage += "</span>\n"
|
||||
msg += "[minor_damage]"
|
||||
msg += "[major_damage]"
|
||||
msg += "[max_damage]"
|
||||
|
||||
|
||||
// Species and body temperature
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
var/datum/species/S = H.dna.species
|
||||
var/mutant = FALSE
|
||||
if (H.dna.check_mutation(HULK))
|
||||
mutant = TRUE
|
||||
else if (S.mutantlungs != initial(S.mutantlungs))
|
||||
mutant = TRUE
|
||||
else if (S.mutant_brain != initial(S.mutant_brain))
|
||||
mutant = TRUE
|
||||
else if (S.mutant_heart != initial(S.mutant_heart))
|
||||
mutant = TRUE
|
||||
else if (S.mutanteyes != initial(S.mutanteyes))
|
||||
mutant = TRUE
|
||||
else if (S.mutantears != initial(S.mutantears))
|
||||
mutant = TRUE
|
||||
else if (S.mutanthands != initial(S.mutanthands))
|
||||
mutant = TRUE
|
||||
else if (S.mutanttongue != initial(S.mutanttongue))
|
||||
mutant = TRUE
|
||||
else if (S.mutanttail != initial(S.mutanttail))
|
||||
mutant = TRUE
|
||||
else if (S.mutantliver != initial(S.mutantliver))
|
||||
mutant = TRUE
|
||||
else if (S.mutantstomach != initial(S.mutantstomach))
|
||||
mutant = TRUE
|
||||
|
||||
msg += "\t<span class='info'>Reported Species: [H.dna.custom_species ? H.dna.custom_species : S.name]</span>\n"
|
||||
msg += "\t<span class='info'>Base Species: [S.name]</span>\n"
|
||||
if(mutant)
|
||||
msg += "\t<span class='info'>Subject has mutations present.</span>\n"
|
||||
msg += "\t<span class='info'>Body temperature: [round(M.bodytemperature-T0C,0.1)] °C ([round(M.bodytemperature*1.8-459.67,0.1)] °F)</span>\n"
|
||||
|
||||
// Time of death
|
||||
if(M.tod && (M.stat == DEAD || ((HAS_TRAIT(M, TRAIT_FAKEDEATH)) && !advanced)))
|
||||
msg += "<span class='info'>Time of Death:</span> [M.tod]\n"
|
||||
var/tdelta = round(world.time - M.timeofdeath)
|
||||
if(tdelta < (DEFIB_TIME_LIMIT * 10))
|
||||
msg += "<span class='danger'>Subject died [DisplayTimeText(tdelta)] ago, defibrillation may be possible!</span>\n"
|
||||
|
||||
for(var/thing in M.diseases)
|
||||
var/datum/disease/D = thing
|
||||
if(!(D.visibility_flags & HIDDEN_SCANNER))
|
||||
msg += "<span class='alert'><b>Warning: [D.form] detected</b>\nName: [D.name].\nType: [D.spread_text].\nStage: [D.stage]/[D.max_stages].\nPossible Cure: [D.cure_text]</span>\n"
|
||||
|
||||
// Blood Level
|
||||
if(M.has_dna())
|
||||
var/mob/living/carbon/C = M
|
||||
var/blood_id = C.get_blood_id()
|
||||
if(blood_id)
|
||||
if(ishuman(C))
|
||||
var/mob/living/carbon/human/H = C
|
||||
if(H.bleed_rate)
|
||||
msg += "<span class='danger'>Subject is bleeding!</span>\n"
|
||||
var/blood_percent = round((C.blood_volume / (BLOOD_VOLUME_NORMAL * C.blood_ratio))*100)
|
||||
var/blood_type = C.dna.blood_type
|
||||
if(blood_id != ("blood" || "jellyblood"))//special blood substance
|
||||
var/datum/reagent/R = GLOB.chemical_reagents_list[blood_id]
|
||||
if(R)
|
||||
blood_type = R.name
|
||||
else
|
||||
blood_type = blood_id
|
||||
if(C.blood_volume <= (BLOOD_VOLUME_SAFE*C.blood_ratio) && C.blood_volume > (BLOOD_VOLUME_OKAY*C.blood_ratio))
|
||||
msg += "<span class='danger'>LOW blood level [blood_percent] %, [C.blood_volume] cl,</span> <span class='info'>type: [blood_type]</span>\n"
|
||||
else if(C.blood_volume <= (BLOOD_VOLUME_OKAY*C.blood_ratio))
|
||||
msg += "<span class='danger'>CRITICAL blood level [blood_percent] %, [C.blood_volume] cl,</span> <span class='info'>type: [blood_type]</span>\n"
|
||||
else
|
||||
msg += "<span class='info'>Blood level [blood_percent] %, [C.blood_volume] cl, type: [blood_type]</span>\n"
|
||||
|
||||
var/cyberimp_detect
|
||||
for(var/obj/item/organ/cyberimp/CI in C.internal_organs)
|
||||
if(CI.status == ORGAN_ROBOTIC && !CI.syndicate_implant)
|
||||
cyberimp_detect += "[C.name] is modified with a [CI.name].<br>"
|
||||
if(cyberimp_detect)
|
||||
msg += "<span class='notice'>Detected cybernetic modifications:</span>\n"
|
||||
msg += "<span class='notice'>[cyberimp_detect]</span>\n"
|
||||
msg += "*---------*</span>"
|
||||
to_chat(user, msg)
|
||||
SEND_SIGNAL(M, COMSIG_NANITE_SCAN, user, FALSE)
|
||||
|
||||
/proc/chemscan(mob/living/user, mob/living/M)
|
||||
if(istype(M))
|
||||
if(M.reagents)
|
||||
var/msg = "<span class='info'>*---------*\n"
|
||||
if(M.reagents.reagent_list.len)
|
||||
var/list/datum/reagent/reagents = list()
|
||||
for(var/datum/reagent/R in M.reagents.reagent_list)
|
||||
if(R.chemical_flags & REAGENT_INVISIBLE)
|
||||
continue
|
||||
reagents += R
|
||||
|
||||
if(length(reagents))
|
||||
msg += "<span class='notice'>Subject contains the following reagents:</span>\n"
|
||||
for(var/datum/reagent/R in reagents)
|
||||
msg += "<span class='notice'>[R.volume] units of [R.name][R.overdosed == 1 ? "</span> - <span class='boldannounce'>OVERDOSING</span>" : ".</span>"]\n"
|
||||
else
|
||||
msg += "<span class='notice'>Subject contains no reagents.</span>\n"
|
||||
|
||||
else
|
||||
msg += "<span class='notice'>Subject contains no reagents.</span>\n"
|
||||
if(M.reagents.addiction_list.len)
|
||||
msg += "<span class='boldannounce'>Subject is addicted to the following reagents:</span>\n"
|
||||
for(var/datum/reagent/R in M.reagents.addiction_list)
|
||||
msg += "<span class='danger'>[R.name]</span>\n"
|
||||
else
|
||||
msg += "<span class='notice'>Subject is not addicted to any reagents.</span>\n"
|
||||
|
||||
if(M.reagents.has_reagent("fermiTox"))
|
||||
var/datum/reagent/fermiTox = M.reagents.has_reagent("fermiTox")
|
||||
switch(fermiTox.volume)
|
||||
if(5 to 10)
|
||||
msg += "<span class='notice'>Subject contains a low amount of toxic isomers.</span>\n"
|
||||
if(10 to 25)
|
||||
msg += "<span class='danger'>Subject contains toxic isomers.</span>\n"
|
||||
if(25 to 50)
|
||||
msg += "<span class='danger'>Subject contains a substantial amount of toxic isomers.</span>\n"
|
||||
if(50 to 95)
|
||||
msg += "<span class='danger'>Subject contains a high amount of toxic isomers.</span>\n"
|
||||
if(95 to INFINITY)
|
||||
msg += "<span class='danger'>Subject contains a extremely dangerous amount of toxic isomers.</span>\n"
|
||||
|
||||
msg += "*---------*</span>"
|
||||
to_chat(user, msg)
|
||||
|
||||
/obj/item/healthanalyzer/verb/toggle_mode()
|
||||
set name = "Switch Verbosity"
|
||||
set category = "Object"
|
||||
|
||||
if(usr.stat || !usr.canmove || usr.restrained())
|
||||
return
|
||||
|
||||
mode = !mode
|
||||
switch (mode)
|
||||
if(1)
|
||||
to_chat(usr, "The scanner now shows specific limb damage.")
|
||||
if(0)
|
||||
to_chat(usr, "The scanner no longer shows limb damage.")
|
||||
|
||||
/obj/item/healthanalyzer/advanced
|
||||
name = "advanced health analyzer"
|
||||
icon_state = "health_adv"
|
||||
desc = "A hand-held body scanner able to distinguish vital signs of the subject with high accuracy."
|
||||
advanced = TRUE
|
||||
|
||||
/obj/item/analyzer
|
||||
desc = "A hand-held environmental scanner which reports current gas levels. Alt-Click to use the built in barometer function."
|
||||
name = "analyzer"
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "analyzer"
|
||||
item_state = "analyzer"
|
||||
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
flags_1 = CONDUCT_1
|
||||
item_flags = NOBLUDGEON
|
||||
slot_flags = ITEM_SLOT_BELT
|
||||
throwforce = 0
|
||||
throw_speed = 3
|
||||
throw_range = 7
|
||||
tool_behaviour = TOOL_ANALYZER
|
||||
materials = list(MAT_METAL=30, MAT_GLASS=20)
|
||||
grind_results = list("mercury" = 5, "iron" = 5, "silicon" = 5)
|
||||
var/cooldown = FALSE
|
||||
var/cooldown_time = 250
|
||||
var/accuracy // 0 is the best accuracy.
|
||||
|
||||
/obj/item/analyzer/examine(mob/user)
|
||||
. = ..()
|
||||
to_chat(user, "<span class='notice'>Alt-click [src] to activate the barometer function.</span>")
|
||||
|
||||
/obj/item/analyzer/suicide_act(mob/living/carbon/user)
|
||||
user.visible_message("<span class='suicide'>[user] begins to analyze [user.p_them()]self with [src]! The display shows that [user.p_theyre()] dead!</span>")
|
||||
return BRUTELOSS
|
||||
|
||||
/obj/item/analyzer/attack_self(mob/user)
|
||||
add_fingerprint(user)
|
||||
|
||||
if (user.stat || user.eye_blind)
|
||||
return
|
||||
|
||||
var/turf/location = user.loc
|
||||
if(!istype(location))
|
||||
return
|
||||
|
||||
var/datum/gas_mixture/environment = location.return_air()
|
||||
|
||||
var/pressure = environment.return_pressure()
|
||||
var/total_moles = environment.total_moles()
|
||||
|
||||
to_chat(user, "<span class='info'><B>Results:</B></span>")
|
||||
if(abs(pressure - ONE_ATMOSPHERE) < 10)
|
||||
to_chat(user, "<span class='info'>Pressure: [round(pressure, 0.01)] kPa</span>")
|
||||
else
|
||||
to_chat(user, "<span class='alert'>Pressure: [round(pressure, 0.01)] kPa</span>")
|
||||
if(total_moles)
|
||||
var/list/env_gases = environment.gases
|
||||
|
||||
var/o2_concentration = env_gases[/datum/gas/oxygen]/total_moles
|
||||
var/n2_concentration = env_gases[/datum/gas/nitrogen]/total_moles
|
||||
var/co2_concentration = env_gases[/datum/gas/carbon_dioxide]/total_moles
|
||||
var/plasma_concentration = env_gases[/datum/gas/plasma]/total_moles
|
||||
|
||||
if(abs(n2_concentration - N2STANDARD) < 20)
|
||||
to_chat(user, "<span class='info'>Nitrogen: [round(n2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/nitrogen], 0.01)] mol)</span>")
|
||||
else
|
||||
to_chat(user, "<span class='alert'>Nitrogen: [round(n2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/nitrogen], 0.01)] mol)</span>")
|
||||
|
||||
if(abs(o2_concentration - O2STANDARD) < 2)
|
||||
to_chat(user, "<span class='info'>Oxygen: [round(o2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/oxygen], 0.01)] mol)</span>")
|
||||
else
|
||||
to_chat(user, "<span class='alert'>Oxygen: [round(o2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/oxygen], 0.01)] mol)</span>")
|
||||
|
||||
if(co2_concentration > 0.01)
|
||||
to_chat(user, "<span class='alert'>CO2: [round(co2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/carbon_dioxide], 0.01)] mol)</span>")
|
||||
else
|
||||
to_chat(user, "<span class='info'>CO2: [round(co2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/carbon_dioxide], 0.01)] mol)</span>")
|
||||
|
||||
if(plasma_concentration > 0.005)
|
||||
to_chat(user, "<span class='alert'>Plasma: [round(plasma_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/plasma], 0.01)] mol)</span>")
|
||||
else
|
||||
to_chat(user, "<span class='info'>Plasma: [round(plasma_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/plasma], 0.01)] mol)</span>")
|
||||
|
||||
GAS_GARBAGE_COLLECT(environment.gases)
|
||||
|
||||
for(var/id in env_gases)
|
||||
if(id in GLOB.hardcoded_gases)
|
||||
continue
|
||||
var/gas_concentration = env_gases[id]/total_moles
|
||||
to_chat(user, "<span class='alert'>[GLOB.meta_gas_names[id]]: [round(gas_concentration*100, 0.01)] % ([round(env_gases[id], 0.01)] mol)</span>")
|
||||
to_chat(user, "<span class='info'>Temperature: [round(environment.temperature-T0C, 0.01)] °C ([round(environment.temperature, 0.01)] K)</span>")
|
||||
|
||||
/obj/item/analyzer/AltClick(mob/user) //Barometer output for measuring when the next storm happens
|
||||
..()
|
||||
|
||||
if(user.canUseTopic(src))
|
||||
|
||||
if(cooldown)
|
||||
to_chat(user, "<span class='warning'>[src]'s barometer function is preparing itself.</span>")
|
||||
return
|
||||
|
||||
var/turf/T = get_turf(user)
|
||||
if(!T)
|
||||
return
|
||||
|
||||
playsound(src, 'sound/effects/pop.ogg', 100)
|
||||
var/area/user_area = T.loc
|
||||
var/datum/weather/ongoing_weather = null
|
||||
|
||||
if(!user_area.outdoors)
|
||||
to_chat(user, "<span class='warning'>[src]'s barometer function won't work indoors!</span>")
|
||||
return
|
||||
|
||||
for(var/V in SSweather.processing)
|
||||
var/datum/weather/W = V
|
||||
if(W.barometer_predictable && (T.z in W.impacted_z_levels) && W.area_type == user_area.type && !(W.stage == END_STAGE))
|
||||
ongoing_weather = W
|
||||
break
|
||||
|
||||
if(ongoing_weather)
|
||||
if((ongoing_weather.stage == MAIN_STAGE) || (ongoing_weather.stage == WIND_DOWN_STAGE))
|
||||
to_chat(user, "<span class='warning'>[src]'s barometer function can't trace anything while the storm is [ongoing_weather.stage == MAIN_STAGE ? "already here!" : "winding down."]</span>")
|
||||
return
|
||||
|
||||
to_chat(user, "<span class='notice'>The next [ongoing_weather] will hit in [butchertime(ongoing_weather.next_hit_time - world.time)].</span>")
|
||||
if(ongoing_weather.aesthetic)
|
||||
to_chat(user, "<span class='warning'>[src]'s barometer function says that the next storm will breeze on by.</span>")
|
||||
else
|
||||
var/next_hit = SSweather.next_hit_by_zlevel["[T.z]"]
|
||||
var/fixed = next_hit ? next_hit - world.time : -1
|
||||
if(fixed < 0)
|
||||
to_chat(user, "<span class='warning'>[src]'s barometer function was unable to trace any weather patterns.</span>")
|
||||
else
|
||||
to_chat(user, "<span class='warning'>[src]'s barometer function says a storm will land in approximately [butchertime(fixed)].</span>")
|
||||
cooldown = TRUE
|
||||
addtimer(CALLBACK(src,/obj/item/analyzer/proc/ping), cooldown_time)
|
||||
|
||||
/obj/item/analyzer/proc/ping()
|
||||
if(isliving(loc))
|
||||
var/mob/living/L = loc
|
||||
to_chat(L, "<span class='notice'>[src]'s barometer function is ready!</span>")
|
||||
playsound(src, 'sound/machines/click.ogg', 100)
|
||||
cooldown = FALSE
|
||||
|
||||
/obj/item/analyzer/proc/butchertime(amount)
|
||||
if(!amount)
|
||||
return
|
||||
if(accuracy)
|
||||
var/inaccurate = round(accuracy*(1/3))
|
||||
if(prob(50))
|
||||
amount -= inaccurate
|
||||
if(prob(50))
|
||||
amount += inaccurate
|
||||
return DisplayTimeText(max(1,amount))
|
||||
|
||||
/proc/atmosanalyzer_scan(mixture, mob/living/user, atom/target = src)
|
||||
var/icon = target
|
||||
user.visible_message("[user] has used the analyzer on [icon2html(icon, viewers(user))] [target].", "<span class='notice'>You use the analyzer on [icon2html(icon, user)] [target].</span>")
|
||||
to_chat(user, "<span class='boldnotice'>Results of analysis of [icon2html(icon, user)] [target].</span>")
|
||||
|
||||
var/list/airs = islist(mixture) ? mixture : list(mixture)
|
||||
for(var/g in airs)
|
||||
if(airs.len > 1) //not a unary gas mixture
|
||||
to_chat(user, "<span class='boldnotice'>Node [airs.Find(g)]</span>")
|
||||
var/datum/gas_mixture/air_contents = g
|
||||
|
||||
var/total_moles = air_contents.total_moles()
|
||||
var/pressure = air_contents.return_pressure()
|
||||
var/volume = air_contents.return_volume() //could just do mixture.volume... but safety, I guess?
|
||||
var/temperature = air_contents.temperature
|
||||
var/cached_scan_results = air_contents.analyzer_results
|
||||
|
||||
if(total_moles > 0)
|
||||
to_chat(user, "<span class='notice'>Moles: [round(total_moles, 0.01)] mol</span>")
|
||||
to_chat(user, "<span class='notice'>Volume: [volume] L</span>")
|
||||
to_chat(user, "<span class='notice'>Pressure: [round(pressure,0.01)] kPa</span>")
|
||||
|
||||
var/list/cached_gases = air_contents.gases
|
||||
for(var/id in cached_gases)
|
||||
var/gas_concentration = cached_gases[id]/total_moles
|
||||
to_chat(user, "<span class='notice'>[GLOB.meta_gas_names[id]]: [round(gas_concentration*100, 0.01)] % ([round(cached_gases[id], 0.01)] mol)</span>")
|
||||
to_chat(user, "<span class='notice'>Temperature: [round(temperature - T0C,0.01)] °C ([round(temperature, 0.01)] K)</span>")
|
||||
|
||||
else
|
||||
if(airs.len > 1)
|
||||
to_chat(user, "<span class='notice'>This node is empty!</span>")
|
||||
else
|
||||
to_chat(user, "<span class='notice'>[target] is empty!</span>")
|
||||
|
||||
if(cached_scan_results && cached_scan_results["fusion"]) //notify the user if a fusion reaction was detected
|
||||
var/fusion_power = round(cached_scan_results["fusion"], 0.01)
|
||||
var/tier = fusionpower2text(fusion_power)
|
||||
to_chat(user, "<span class='boldnotice'>Large amounts of free neutrons detected in the air indicate that a fusion reaction took place.</span>")
|
||||
to_chat(user, "<span class='notice'>Power of the last fusion reaction: [fusion_power]\n This power indicates it was a [tier]-tier fusion reaction.</span>")
|
||||
return
|
||||
|
||||
//slime scanner
|
||||
|
||||
/obj/item/slime_scanner
|
||||
name = "slime scanner"
|
||||
desc = "A device that analyzes a slime's internal composition and measures its stats."
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "adv_spectrometer"
|
||||
item_state = "analyzer"
|
||||
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
flags_1 = CONDUCT_1
|
||||
throwforce = 0
|
||||
throw_speed = 3
|
||||
throw_range = 7
|
||||
materials = list(MAT_METAL=30, MAT_GLASS=20)
|
||||
|
||||
/obj/item/slime_scanner/attack(mob/living/M, mob/living/user)
|
||||
if(user.stat || user.eye_blind)
|
||||
return
|
||||
if (!isslime(M))
|
||||
to_chat(user, "<span class='warning'>This device can only scan slimes!</span>")
|
||||
return
|
||||
var/mob/living/simple_animal/slime/T = M
|
||||
slime_scan(T, user)
|
||||
|
||||
/proc/slime_scan(mob/living/simple_animal/slime/T, mob/living/user)
|
||||
to_chat(user, "========================")
|
||||
to_chat(user, "<b>Slime scan results:</b>")
|
||||
to_chat(user, "<span class='notice'>[T.colour] [T.is_adult ? "adult" : "baby"] slime</span>")
|
||||
to_chat(user, "Nutrition: [T.nutrition]/[T.get_max_nutrition()]")
|
||||
if (T.nutrition < T.get_starve_nutrition())
|
||||
to_chat(user, "<span class='warning'>Warning: slime is starving!</span>")
|
||||
else if (T.nutrition < T.get_hunger_nutrition())
|
||||
to_chat(user, "<span class='warning'>Warning: slime is hungry</span>")
|
||||
to_chat(user, "Electric change strength: [T.powerlevel]")
|
||||
to_chat(user, "Health: [round(T.health/T.maxHealth,0.01)*100]%")
|
||||
if (T.slime_mutation[4] == T.colour)
|
||||
to_chat(user, "This slime does not evolve any further.")
|
||||
else
|
||||
if (T.slime_mutation[3] == T.slime_mutation[4])
|
||||
if (T.slime_mutation[2] == T.slime_mutation[1])
|
||||
to_chat(user, "Possible mutation: [T.slime_mutation[3]]")
|
||||
to_chat(user, "Genetic destability: [T.mutation_chance/2] % chance of mutation on splitting")
|
||||
else
|
||||
to_chat(user, "Possible mutations: [T.slime_mutation[1]], [T.slime_mutation[2]], [T.slime_mutation[3]] (x2)")
|
||||
to_chat(user, "Genetic destability: [T.mutation_chance] % chance of mutation on splitting")
|
||||
else
|
||||
to_chat(user, "Possible mutations: [T.slime_mutation[1]], [T.slime_mutation[2]], [T.slime_mutation[3]], [T.slime_mutation[4]]")
|
||||
to_chat(user, "Genetic destability: [T.mutation_chance] % chance of mutation on splitting")
|
||||
if (T.cores > 1)
|
||||
to_chat(user, "Multiple cores detected")
|
||||
to_chat(user, "Growth progress: [T.amount_grown]/[SLIME_EVOLUTION_THRESHOLD]")
|
||||
if(T.effectmod)
|
||||
to_chat(user, "<span class='notice'>Core mutation in progress: [T.effectmod]</span>")
|
||||
to_chat(user, "<span class = 'notice'>Progress in core mutation: [T.applied] / [SLIME_EXTRACT_CROSSING_REQUIRED]</span>")
|
||||
to_chat(user, "========================")
|
||||
|
||||
|
||||
/obj/item/nanite_scanner
|
||||
name = "nanite scanner"
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "nanite_scanner"
|
||||
item_state = "nanite_remote"
|
||||
lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi'
|
||||
desc = "A hand-held body scanner able to detect nanites and their programming."
|
||||
flags_1 = CONDUCT_1
|
||||
item_flags = NOBLUDGEON
|
||||
slot_flags = ITEM_SLOT_BELT
|
||||
throwforce = 3
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
throw_speed = 3
|
||||
throw_range = 7
|
||||
materials = list(MAT_METAL=200)
|
||||
|
||||
/obj/item/nanite_scanner/attack(mob/living/M, mob/living/carbon/human/user)
|
||||
user.visible_message("<span class='notice'>[user] has analyzed [M]'s nanites.</span>")
|
||||
|
||||
add_fingerprint(user)
|
||||
|
||||
var/response = SEND_SIGNAL(M, COMSIG_NANITE_SCAN, user, TRUE)
|
||||
if(!response)
|
||||
to_chat(user, "<span class='info'>No nanites detected in the subject.</span>")
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
|
||||
Miscellaneous traitor devices
|
||||
|
||||
BATTERER
|
||||
|
||||
RADIOACTIVE MICROLASER
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
The Batterer, like a flashbang but 50% chance to knock people over. Can be either very
|
||||
effective or pretty fucking useless.
|
||||
|
||||
*/
|
||||
|
||||
/obj/item/batterer
|
||||
name = "mind batterer"
|
||||
desc = "A strange device with twin antennas."
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "batterer"
|
||||
throwforce = 5
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
throw_speed = 3
|
||||
throw_range = 7
|
||||
flags_1 = CONDUCT_1
|
||||
item_state = "electronic"
|
||||
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
|
||||
|
||||
var/times_used = 0 //Number of times it's been used.
|
||||
var/max_uses = 2
|
||||
|
||||
|
||||
/obj/item/batterer/attack_self(mob/living/carbon/user, flag = 0, emp = 0)
|
||||
if(!user) return
|
||||
if(times_used >= max_uses)
|
||||
to_chat(user, "<span class='danger'>The mind batterer has been burnt out!</span>")
|
||||
return
|
||||
|
||||
log_combat(user, null, "knocked down people in the area", src)
|
||||
|
||||
for(var/mob/living/carbon/human/M in urange(10, user, 1))
|
||||
if(prob(50))
|
||||
|
||||
M.Knockdown(rand(200,400))
|
||||
to_chat(M, "<span class='userdanger'>You feel a tremendous, paralyzing wave flood your mind.</span>")
|
||||
|
||||
else
|
||||
to_chat(M, "<span class='userdanger'>You feel a sudden, electric jolt travel through your head.</span>")
|
||||
|
||||
playsound(src.loc, 'sound/misc/interference.ogg', 50, 1)
|
||||
to_chat(user, "<span class='notice'>You trigger [src].</span>")
|
||||
times_used += 1
|
||||
if(times_used >= max_uses)
|
||||
icon_state = "battererburnt"
|
||||
|
||||
/*
|
||||
The radioactive microlaser, a device disguised as a health analyzer used to irradiate people.
|
||||
|
||||
The strength of the radiation is determined by the 'intensity' setting, while the delay between
|
||||
the scan and the irradiation kicking in is determined by the wavelength.
|
||||
|
||||
Each scan will cause the microlaser to have a brief cooldown period. Higher intensity will increase
|
||||
the cooldown, while higher wavelength will decrease it.
|
||||
|
||||
Wavelength is also slightly increased by the intensity as well.
|
||||
*/
|
||||
|
||||
/obj/item/healthanalyzer/rad_laser
|
||||
materials = list(MAT_METAL=400)
|
||||
var/irradiate = 1
|
||||
var/intensity = 10 // how much damage the radiation does
|
||||
var/wavelength = 10 // time it takes for the radiation to kick in, in seconds
|
||||
var/used = 0 // is it cooling down?
|
||||
var/stealth = FALSE
|
||||
|
||||
/obj/item/healthanalyzer/rad_laser/attack(mob/living/M, mob/living/user)
|
||||
if(!stealth || !irradiate)
|
||||
..()
|
||||
if(!irradiate)
|
||||
return
|
||||
if(!used)
|
||||
log_combat(user, M, "irradiated", src)
|
||||
var/cooldown = GetCooldown()
|
||||
used = 1
|
||||
icon_state = "health1"
|
||||
handle_cooldown(cooldown) // splits off to handle the cooldown while handling wavelength
|
||||
to_chat(user, "<span class='warning'>Successfully irradiated [M].</span>")
|
||||
spawn((wavelength+(intensity*4))*5)
|
||||
if(M)
|
||||
if(intensity >= 5)
|
||||
M.apply_effect(round(intensity/0.075), EFFECT_UNCONSCIOUS)
|
||||
M.rad_act(intensity*10)
|
||||
else
|
||||
to_chat(user, "<span class='warning'>The radioactive microlaser is still recharging.</span>")
|
||||
|
||||
/obj/item/healthanalyzer/rad_laser/proc/handle_cooldown(cooldown)
|
||||
spawn(cooldown)
|
||||
used = 0
|
||||
icon_state = "health"
|
||||
|
||||
/obj/item/healthanalyzer/rad_laser/attack_self(mob/user)
|
||||
interact(user)
|
||||
|
||||
/obj/item/healthanalyzer/rad_laser/proc/GetCooldown()
|
||||
return round(max(10, (stealth*30 + intensity*5 - wavelength/4)))
|
||||
|
||||
/obj/item/healthanalyzer/rad_laser/interact(mob/user)
|
||||
ui_interact(user)
|
||||
|
||||
/obj/item/healthanalyzer/rad_laser/ui_interact(mob/user)
|
||||
. = ..()
|
||||
|
||||
var/dat = "Irradiation: <A href='?src=[REF(src)];rad=1'>[irradiate ? "On" : "Off"]</A><br>"
|
||||
dat += "Stealth Mode (NOTE: Deactivates automatically while Irradiation is off): <A href='?src=[REF(src)];stealthy=[TRUE]'>[stealth ? "On" : "Off"]</A><br>"
|
||||
dat += "Scan Mode: <a href='?src=[REF(src)];mode=1'>"
|
||||
if(!scanmode)
|
||||
dat += "Scan Health"
|
||||
else if(scanmode == 1)
|
||||
dat += "Scan Reagents"
|
||||
else
|
||||
dat += "Disabled"
|
||||
dat += "</a><br><br>"
|
||||
|
||||
dat += {"
|
||||
Radiation Intensity:
|
||||
<A href='?src=[REF(src)];radint=-5'>-</A><A href='?src=[REF(src)];radint=-1'>-</A>
|
||||
[intensity]
|
||||
<A href='?src=[REF(src)];radint=1'>+</A><A href='?src=[REF(src)];radint=5'>+</A><BR>
|
||||
|
||||
Radiation Wavelength:
|
||||
<A href='?src=[REF(src)];radwav=-5'>-</A><A href='?src=[REF(src)];radwav=-1'>-</A>
|
||||
[(wavelength+(intensity*4))]
|
||||
<A href='?src=[REF(src)];radwav=1'>+</A><A href='?src=[REF(src)];radwav=5'>+</A><BR>
|
||||
Laser Cooldown: [DisplayTimeText(GetCooldown())]<BR>
|
||||
"}
|
||||
|
||||
var/datum/browser/popup = new(user, "radlaser", "Radioactive Microlaser Interface", 400, 240)
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
|
||||
/obj/item/healthanalyzer/rad_laser/Topic(href, href_list)
|
||||
if(!usr.canUseTopic(src))
|
||||
return 1
|
||||
|
||||
usr.set_machine(src)
|
||||
if(href_list["rad"])
|
||||
irradiate = !irradiate
|
||||
|
||||
else if(href_list["stealthy"])
|
||||
stealth = !stealth
|
||||
|
||||
else if(href_list["mode"])
|
||||
scanmode += 1
|
||||
if(scanmode > 2)
|
||||
scanmode = 0
|
||||
|
||||
else if(href_list["radint"])
|
||||
var/amount = text2num(href_list["radint"])
|
||||
amount += intensity
|
||||
intensity = max(1,(min(20,amount)))
|
||||
|
||||
else if(href_list["radwav"])
|
||||
var/amount = text2num(href_list["radwav"])
|
||||
amount += wavelength
|
||||
wavelength = max(0,(min(120,amount)))
|
||||
|
||||
attack_self(usr)
|
||||
add_fingerprint(usr)
|
||||
return
|
||||
|
||||
/obj/item/shadowcloak
|
||||
name = "cloaker belt"
|
||||
desc = "Makes you invisible for short periods of time. Recharges in darkness."
|
||||
icon = 'icons/obj/clothing/belts.dmi'
|
||||
icon_state = "utilitybelt"
|
||||
item_state = "utility"
|
||||
slot_flags = ITEM_SLOT_BELT
|
||||
attack_verb = list("whipped", "lashed", "disciplined")
|
||||
|
||||
var/mob/living/carbon/human/user = null
|
||||
var/charge = 300
|
||||
var/max_charge = 300
|
||||
var/on = FALSE
|
||||
var/old_alpha = 0
|
||||
actions_types = list(/datum/action/item_action/toggle)
|
||||
|
||||
/obj/item/shadowcloak/ui_action_click(mob/user)
|
||||
if(user.get_item_by_slot(SLOT_BELT) == src)
|
||||
if(!on)
|
||||
Activate(usr)
|
||||
else
|
||||
Deactivate()
|
||||
return
|
||||
|
||||
/obj/item/shadowcloak/item_action_slot_check(slot, mob/user, datum/action/A)
|
||||
if(slot == SLOT_BELT)
|
||||
return 1
|
||||
|
||||
/obj/item/shadowcloak/proc/Activate(mob/living/carbon/human/user)
|
||||
if(!user)
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You activate [src].</span>")
|
||||
src.user = user
|
||||
START_PROCESSING(SSobj, src)
|
||||
old_alpha = user.alpha
|
||||
on = TRUE
|
||||
|
||||
/obj/item/shadowcloak/proc/Deactivate()
|
||||
to_chat(user, "<span class='notice'>You deactivate [src].</span>")
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
if(user)
|
||||
user.alpha = old_alpha
|
||||
on = FALSE
|
||||
user = null
|
||||
|
||||
/obj/item/shadowcloak/dropped(mob/user)
|
||||
..()
|
||||
if(user && user.get_item_by_slot(SLOT_BELT) != src)
|
||||
Deactivate()
|
||||
|
||||
/obj/item/shadowcloak/process()
|
||||
if(user.get_item_by_slot(SLOT_BELT) != src)
|
||||
Deactivate()
|
||||
return
|
||||
var/turf/T = get_turf(src)
|
||||
if(on)
|
||||
var/lumcount = T.get_lumcount()
|
||||
if(lumcount > 0.3)
|
||||
charge = max(0,charge - 25)//Quick decrease in light
|
||||
else
|
||||
charge = min(max_charge,charge + 50) //Charge in the dark
|
||||
animate(user,alpha = CLAMP(255 - charge,0,255),time = 10)
|
||||
|
||||
|
||||
/obj/item/jammer
|
||||
name = "radio jammer"
|
||||
desc = "Device used to disrupt nearby radio communication."
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "jammer"
|
||||
var/active = FALSE
|
||||
var/range = 12
|
||||
|
||||
/obj/item/jammer/attack_self(mob/user)
|
||||
to_chat(user,"<span class='notice'>You [active ? "deactivate" : "activate"] [src].</span>")
|
||||
active = !active
|
||||
if(active)
|
||||
GLOB.active_jammers |= src
|
||||
else
|
||||
GLOB.active_jammers -= src
|
||||
update_icon()
|
||||
Reference in New Issue
Block a user