Files
Yogstation/code/datums/holocall.dm
Cruix 73f4d04975 AI detection multitools can show camera static and AI eyes (#38255)
The old color-change functionality of the multitool remains unchanged. The hud is toggled with an action button, because multitools already have an attack_self() that does something. When toggled on, you get the hud as long as the multitool is in any slot on your body.

The only way I could find to reliably show exactly where an AI is looking was to place an image on every turf they are looking at. Because AI eyes move around a lot and I do not want them placing images all over the place when they do not have to, if there is nobody in the world currently using an ai detection multitool, they will not update the images until someone is.

Only AI eyes and multicamera zones currently appear. Advanced camera consoles do not appear on the hud, and no longer trigger the proximity color change. Also fixed multitools not appearing in-hands if their icon state changed.
2018-07-08 06:20:11 +01:00

353 lines
10 KiB
Plaintext

#define HOLOPAD_MAX_DIAL_TIME 200
#define HOLORECORD_DELAY "delay"
#define HOLORECORD_SAY "say"
#define HOLORECORD_SOUND "sound"
#define HOLORECORD_LANGUAGE "lang"
#define HOLORECORD_PRESET "preset"
#define HOLORECORD_RENAME "rename"
#define HOLORECORD_MAX_LENGTH 200
/mob/camera/aiEye/remote/holo/setLoc()
. = ..()
var/obj/machinery/holopad/H = origin
H.move_hologram(eye_user, loc)
//this datum manages it's own references
/datum/holocall
var/mob/living/user //the one that called
var/obj/machinery/holopad/calling_holopad //the one that sent the call
var/obj/machinery/holopad/connected_holopad //the one that answered the call (may be null)
var/list/dialed_holopads //all things called, will be cleared out to just connected_holopad once answered
var/mob/camera/aiEye/remote/holo/eye //user's eye, once connected
var/obj/effect/overlay/holo_pad_hologram/hologram //user's hologram, once connected
var/datum/action/innate/end_holocall/hangup //hangup action
var/call_start_time
//creates a holocall made by `caller` from `calling_pad` to `callees`
/datum/holocall/New(mob/living/caller, obj/machinery/holopad/calling_pad, list/callees)
call_start_time = world.time
user = caller
calling_pad.outgoing_call = src
calling_holopad = calling_pad
dialed_holopads = list()
for(var/I in callees)
var/obj/machinery/holopad/H = I
if(!QDELETED(H) && H.is_operational())
dialed_holopads += H
H.say("Incoming call.")
LAZYADD(H.holo_calls, src)
if(!dialed_holopads.len)
calling_pad.say("Connection failure.")
qdel(src)
return
testing("Holocall started")
//cleans up ALL references :)
/datum/holocall/Destroy()
QDEL_NULL(hangup)
var/user_good = !QDELETED(user)
if(user_good)
user.reset_perspective()
user.remote_control = null
if(!QDELETED(eye))
QDEL_NULL(eye)
if(connected_holopad && !QDELETED(hologram))
hologram = null
connected_holopad.clear_holo(user)
user = null
//Hologram survived holopad destro
if(!QDELETED(hologram))
hologram.HC = null
QDEL_NULL(hologram)
for(var/I in dialed_holopads)
var/obj/machinery/holopad/H = I
LAZYREMOVE(H.holo_calls, src)
dialed_holopads.Cut()
if(calling_holopad)
calling_holopad.outgoing_call = null
calling_holopad.SetLightsAndPower()
calling_holopad = null
if(connected_holopad)
connected_holopad.SetLightsAndPower()
connected_holopad = null
testing("Holocall destroyed")
return ..()
//Gracefully disconnects a holopad `H` from a call. Pads not in the call are ignored. Notifies participants of the disconnection
/datum/holocall/proc/Disconnect(obj/machinery/holopad/H)
testing("Holocall disconnect")
if(H == connected_holopad)
var/area/A = get_area(connected_holopad)
calling_holopad.say("[A] holopad disconnected.")
else if(H == calling_holopad && connected_holopad)
connected_holopad.say("[user] disconnected.")
ConnectionFailure(H, TRUE)
//Forcefully disconnects a holopad `H` from a call. Pads not in the call are ignored.
/datum/holocall/proc/ConnectionFailure(obj/machinery/holopad/H, graceful = FALSE)
testing("Holocall connection failure: graceful [graceful]")
if(H == connected_holopad || H == calling_holopad)
if(!graceful && H != calling_holopad)
calling_holopad.say("Connection failure.")
qdel(src)
return
LAZYREMOVE(H.holo_calls, src)
dialed_holopads -= H
if(!dialed_holopads.len)
if(graceful)
calling_holopad.say("Call rejected.")
testing("No recipients, terminating")
qdel(src)
//Answers a call made to a holopad `H` which cannot be the calling holopad. Pads not in the call are ignored
/datum/holocall/proc/Answer(obj/machinery/holopad/H)
testing("Holocall answer")
if(H == calling_holopad)
CRASH("How cute, a holopad tried to answer itself.")
if(!(H in dialed_holopads))
return
if(connected_holopad)
CRASH("Multi-connection holocall")
for(var/I in dialed_holopads)
if(I == H)
continue
Disconnect(I)
for(var/I in H.holo_calls)
var/datum/holocall/HC = I
if(HC != src)
HC.Disconnect(H)
connected_holopad = H
if(!Check())
return
hologram = H.activate_holo(user)
hologram.HC = src
//eyeobj code is horrid, this is the best copypasta I could make
eye = new
eye.origin = H
eye.eye_initialized = TRUE
eye.eye_user = user
eye.name = "Camera Eye ([user.name])"
user.remote_control = eye
user.reset_perspective(eye)
eye.setLoc(H.loc)
hangup = new(eye, src)
hangup.Grant(user)
//Checks the validity of a holocall and qdels itself if it's not. Returns TRUE if valid, FALSE otherwise
/datum/holocall/proc/Check()
for(var/I in dialed_holopads)
var/obj/machinery/holopad/H = I
if(!H.is_operational())
ConnectionFailure(H)
if(QDELETED(src))
return FALSE
. = !QDELETED(user) && !user.incapacitated() && !QDELETED(calling_holopad) && calling_holopad.is_operational() && user.loc == calling_holopad.loc
if(.)
if(!connected_holopad)
. = world.time < (call_start_time + HOLOPAD_MAX_DIAL_TIME)
if(!.)
calling_holopad.say("No answer received.")
calling_holopad.temp = ""
if(!.)
testing("Holocall Check fail")
qdel(src)
/datum/action/innate/end_holocall
name = "End Holocall"
icon_icon = 'icons/mob/actions/actions_silicon.dmi'
button_icon_state = "camera_off"
var/datum/holocall/hcall
/datum/action/innate/end_holocall/New(Target, datum/holocall/HC)
..()
hcall = HC
/datum/action/innate/end_holocall/Activate()
hcall.Disconnect(hcall.calling_holopad)
//RECORDS
/datum/holorecord
var/caller_name = "Unknown" //Caller name
var/image/caller_image
var/list/entries = list()
var/language = /datum/language/common //Initial language, can be changed by HOLORECORD_LANGUAGE entries
/datum/holorecord/proc/set_caller_image(mob/user)
var/olddir = user.dir
user.setDir(SOUTH)
caller_image = image(user)
user.setDir(olddir)
/obj/item/disk/holodisk
name = "holorecord disk"
desc = "Stores recorder holocalls."
icon_state = "holodisk"
obj_flags = UNIQUE_RENAME
materials = list(MAT_METAL = 100, MAT_GLASS = 100)
var/datum/holorecord/record
//Preset variables
var/preset_image_type
var/preset_record_text
/obj/item/disk/holodisk/Initialize(mapload)
. = ..()
if(preset_record_text)
build_record()
/obj/item/disk/holodisk/Destroy()
QDEL_NULL(record)
return ..()
/obj/item/disk/holodisk/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/disk/holodisk))
var/obj/item/disk/holodisk/holodiskOriginal = W
if (holodiskOriginal.record)
if (!record)
record = new
record.caller_name = holodiskOriginal.record.caller_name
record.caller_image = holodiskOriginal.record.caller_image
record.entries = holodiskOriginal.record.entries.Copy()
record.language = holodiskOriginal.record.language
to_chat(user, "You copy the record from [holodiskOriginal] to [src] by connecting the ports!")
name = holodiskOriginal.name
else
to_chat(user, "[holodiskOriginal] has no record on it!")
..()
/obj/item/disk/holodisk/proc/build_record()
record = new
var/list/lines = splittext(preset_record_text,"\n")
for(var/line in lines)
var/prepared_line = trim(line)
if(!length(prepared_line))
continue
var/splitpoint = findtext(prepared_line," ")
if(!splitpoint)
continue
var/command = copytext(prepared_line,1,splitpoint)
var/value = copytext(prepared_line,splitpoint+1)
switch(command)
if("DELAY")
var/delay_value = text2num(value)
if(!delay_value)
continue
record.entries += list(list(HOLORECORD_DELAY,delay_value))
if("NAME")
if(!record.caller_name)
record.caller_name = value
else
record.entries += list(list(HOLORECORD_RENAME,value))
if("SAY")
record.entries += list(list(HOLORECORD_SAY,value))
if("SOUND")
record.entries += list(list(HOLORECORD_SOUND,value))
if("LANGUAGE")
var/lang_type = text2path(value)
if(ispath(lang_type,/datum/language))
record.entries += list(list(HOLORECORD_LANGUAGE,lang_type))
if("PRESET")
var/preset_type = text2path(value)
if(ispath(preset_type,/datum/preset_holoimage))
record.entries += list(list(HOLORECORD_PRESET,preset_type))
if(!preset_image_type)
record.caller_image = image('icons/mob/animal.dmi',"old")
else
var/datum/preset_holoimage/H = new preset_image_type
record.caller_image = H.build_image()
//These build caller image from outfit and some additional data, for use by mappers for ruin holorecords
/datum/preset_holoimage
var/nonhuman_mobtype //Fill this if you just want something nonhuman
var/outfit_type
var/species_type = /datum/species/human
/datum/preset_holoimage/proc/build_image()
if(nonhuman_mobtype)
var/mob/living/L = nonhuman_mobtype
. = image(initial(L.icon),initial(L.icon_state))
else
var/mob/living/carbon/human/dummy/mannequin = generate_or_wait_for_human_dummy("HOLODISK_PRESET")
if(species_type)
mannequin.set_species(species_type)
if(outfit_type)
mannequin.equipOutfit(outfit_type,TRUE)
mannequin.setDir(SOUTH)
COMPILE_OVERLAYS(mannequin)
. = image(mannequin)
unset_busy_human_dummy("HOLODISK_PRESET")
/obj/item/disk/holodisk/example
preset_image_type = /datum/preset_holoimage/clown
preset_record_text = {"
NAME Clown
DELAY 10
SAY Why did the chaplain cross the maint ?
DELAY 20
SAY He wanted to get to the other side!
SOUND clownstep
DELAY 30
LANGUAGE /datum/language/narsie
SAY Helped him get there!
DELAY 10
SAY ALSO IM SECRETLY A GORILLA
DELAY 10
PRESET /datum/preset_holoimage/gorilla
NAME Gorilla
LANGUAGE /datum/language/common
SAY OOGA
DELAY 20"}
/datum/preset_holoimage/engineer
outfit_type = /datum/outfit/job/engineer/gloved/rig
/datum/preset_holoimage/researcher
outfit_type = /datum/outfit/job/scientist
/datum/preset_holoimage/captain
outfit_type = /datum/outfit/job/captain
/datum/preset_holoimage/nanotrasenprivatesecurity
outfit_type = /datum/outfit/nanotrasensoldiercorpse2
/datum/preset_holoimage/gorilla
nonhuman_mobtype = /mob/living/simple_animal/hostile/gorilla
/datum/preset_holoimage/corgi
nonhuman_mobtype = /mob/living/simple_animal/pet/dog/corgi
/datum/preset_holoimage/clown
outfit_type = /datum/outfit/job/clown