Revert "PLASE"

This reverts commit 8af225e6e3.
This commit is contained in:
Fermi
2019-11-24 03:06:08 +00:00
parent aeb8606ce0
commit ba4fa1ef67
2145 changed files with 1387321 additions and 5257 deletions
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"
+775
View File
@@ -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
+226
View File
@@ -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
+165
View File
@@ -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)
+776
View File
@@ -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)] &deg;C ([round(M.bodytemperature*1.8-459.67,0.1)] &deg;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)] &deg;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)] &deg;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()