mirror of
https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13.git
synced 2025-12-09 16:07:40 +00:00
750 lines
23 KiB
Plaintext
750 lines
23 KiB
Plaintext
/* Holograms!
|
|
* Contains:
|
|
* Holopad
|
|
* Hologram
|
|
* Other stuff
|
|
*/
|
|
|
|
/*
|
|
Revised. Original based on space ninja hologram code. Which is also mine. /N
|
|
How it works:
|
|
AI clicks on holopad in camera view. View centers on holopad.
|
|
AI clicks again on the holopad to display a hologram. Hologram stays as long as AI is looking at the pad and it (the hologram) is in range of the pad.
|
|
AI can use the directional keys to move the hologram around, provided the above conditions are met and the AI in question is the holopad's master.
|
|
Any number of AIs can use a holopad. /Lo6
|
|
AI may cancel the hologram at any time by clicking on the holopad once more.
|
|
|
|
Possible to do for anyone motivated enough:
|
|
Give an AI variable for different hologram icons.
|
|
Itegrate EMP effect to disable the unit.
|
|
*/
|
|
|
|
|
|
/*
|
|
* Holopad
|
|
*/
|
|
|
|
#define HOLOPAD_PASSIVE_POWER_USAGE 1
|
|
#define HOLOGRAM_POWER_USAGE 2
|
|
|
|
/obj/machinery/holopad
|
|
name = "holopad"
|
|
desc = "It's a floor-mounted device for projecting holographic images."
|
|
icon_state = "holopad0"
|
|
base_icon_state = "holopad"
|
|
layer = LOW_OBJ_LAYER
|
|
plane = FLOOR_PLANE
|
|
flags_1 = HEAR_1
|
|
req_access = list(ACCESS_KEYCARD_AUTH) //Used to allow for forced connecting to other (not secure) holopads. Anyone can make a call, though.
|
|
use_power = IDLE_POWER_USE
|
|
idle_power_usage = 5
|
|
active_power_usage = 100
|
|
max_integrity = 300
|
|
armor = list(MELEE = 50, BULLET = 20, LASER = 20, ENERGY = 20, BOMB = 0, BIO = 0, RAD = 0, FIRE = 50, ACID = 0)
|
|
circuit = /obj/item/circuitboard/machine/holopad
|
|
/// List of living mobs that use the holopad
|
|
var/list/masters
|
|
/// Holoray-mob link
|
|
var/list/holorays
|
|
/// To prevent request spam. ~Carn
|
|
var/last_request = 0
|
|
/// Change to change how far the AI can move away from the holopad before deactivating
|
|
var/holo_range = 5
|
|
/// Array of /datum/holocalls
|
|
var/list/holo_calls
|
|
/// Currently outgoing holocall, do not modify the datums only check and call the public procs
|
|
var/datum/holocall/outgoing_call
|
|
/// Record disk
|
|
var/obj/item/disk/holodisk/disk
|
|
/// Currently replaying a recording
|
|
var/replay_mode = FALSE
|
|
/// Currently looping a recording
|
|
var/loop_mode = FALSE
|
|
/// Currently recording
|
|
var/record_mode = FALSE
|
|
/// Recording start time
|
|
var/record_start = 0
|
|
/// User that inititiated the recording
|
|
var/record_user
|
|
/// Replay hologram
|
|
var/obj/effect/overlay/holo_pad_hologram/replay_holo
|
|
/// Calls will be automatically answered after a couple rings, here for debugging
|
|
var/static/force_answer_call = FALSE
|
|
var/static/list/holopads = list()
|
|
var/obj/effect/overlay/holoray/ray
|
|
var/ringing = FALSE
|
|
var/offset = FALSE
|
|
var/on_network = TRUE
|
|
/// For pads in secure areas; do not allow forced connecting
|
|
var/secure = FALSE
|
|
/// If we are currently calling another holopad
|
|
var/calling = FALSE
|
|
|
|
/obj/machinery/holopad/secure
|
|
name = "secure holopad"
|
|
desc = "It's a floor-mounted device for projecting holographic images. This one will refuse to auto-connect incoming calls."
|
|
secure = TRUE
|
|
|
|
/obj/machinery/holopad/secure/Initialize(mapload)
|
|
. = ..()
|
|
var/obj/item/circuitboard/machine/holopad/board = circuit
|
|
board.secure = TRUE
|
|
board.build_path = /obj/machinery/holopad/secure
|
|
|
|
/obj/machinery/holopad/tutorial
|
|
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
|
flags_1 = NODECONSTRUCT_1
|
|
on_network = FALSE
|
|
var/proximity_range = 1
|
|
|
|
/obj/machinery/holopad/tutorial/Initialize(mapload)
|
|
. = ..()
|
|
if(proximity_range)
|
|
proximity_monitor = new(src, proximity_range)
|
|
if(mapload)
|
|
var/obj/item/disk/holodisk/new_disk = locate(/obj/item/disk/holodisk) in src.loc
|
|
if(new_disk && !disk)
|
|
new_disk.forceMove(src)
|
|
disk = new_disk
|
|
|
|
/obj/machinery/holopad/Moved(atom/OldLoc, Dir)
|
|
. = ..()
|
|
if(!loc)
|
|
return
|
|
// move any relevant holograms, basically non-AI, and rays with the pad
|
|
if(replay_holo)
|
|
replay_holo.abstract_move(loc)
|
|
for(var/i in holorays)
|
|
var/obj/effect/overlay/holoray/ray = holorays[i]
|
|
ray.abstract_move(loc)
|
|
var/list/non_call_masters = masters?.Copy()
|
|
for(var/datum/holocall/holocall as anything in holo_calls)
|
|
if(!holocall.user || !LAZYACCESS(masters, holocall.user))
|
|
continue
|
|
non_call_masters -= holocall.user
|
|
// moving the eye moves the holo which updates the ray too
|
|
holocall.eye.setLoc(locate(clamp(x + (holocall.hologram.x - OldLoc.x), 1, world.maxx), clamp(y + (holocall.hologram.y - OldLoc.y), 1, world.maxy), z))
|
|
for(var/mob/living/holo_master as anything in non_call_masters)
|
|
var/obj/effect/holo = masters[holo_master]
|
|
update_holoray(holo_master, holo.loc)
|
|
|
|
/obj/machinery/holopad/tutorial/attack_hand(mob/user, list/modifiers)
|
|
if(!istype(user))
|
|
return
|
|
if(user.incapacitated() || !is_operational())
|
|
return
|
|
if(replay_mode)
|
|
replay_stop()
|
|
else if(disk?.record)
|
|
replay_start()
|
|
|
|
/obj/machinery/holopad/tutorial/HasProximity(atom/movable/AM)
|
|
if (!isliving(AM))
|
|
return
|
|
if(!replay_mode && (disk?.record))
|
|
replay_start()
|
|
|
|
/obj/machinery/holopad/Initialize(mapload)
|
|
. = ..()
|
|
if(on_network)
|
|
holopads += src
|
|
|
|
/obj/machinery/holopad/Destroy()
|
|
if(outgoing_call)
|
|
outgoing_call.ConnectionFailure(src)
|
|
|
|
for(var/I in holo_calls)
|
|
var/datum/holocall/HC = I
|
|
HC.ConnectionFailure(src)
|
|
|
|
for (var/I in masters)
|
|
clear_holo(I)
|
|
|
|
if(replay_mode)
|
|
replay_stop()
|
|
if(record_mode)
|
|
record_stop()
|
|
|
|
QDEL_NULL(disk)
|
|
|
|
holopads -= src
|
|
return ..()
|
|
|
|
/obj/machinery/holopad/power_change()
|
|
. = ..()
|
|
if (!powered())
|
|
if(replay_mode)
|
|
replay_stop()
|
|
if(record_mode)
|
|
record_stop()
|
|
if(outgoing_call)
|
|
outgoing_call.ConnectionFailure(src)
|
|
|
|
/obj/machinery/holopad/obj_break()
|
|
. = ..()
|
|
if(outgoing_call)
|
|
outgoing_call.ConnectionFailure(src)
|
|
|
|
/obj/machinery/holopad/RefreshParts()
|
|
var/holograph_range = 4
|
|
for(var/obj/item/stock_parts/capacitor/B in component_parts)
|
|
holograph_range += 1 * B.rating
|
|
holo_range = holograph_range
|
|
|
|
/obj/machinery/holopad/examine(mob/user)
|
|
. = ..()
|
|
if(isAI(user))
|
|
. += span_notice("The status display reads: Current projection range: <b>[holo_range]</b> units. Use :h to speak through the projection. Right-click to project or cancel a projection. Alt-click to hangup all active and incomming calls. Ctrl-click to end projection without jumping to your last location.")
|
|
else if(in_range(user, src) || isobserver(user))
|
|
. += span_notice("The status display reads: Current projection range: <b>[holo_range]</b> units.")
|
|
|
|
/obj/machinery/holopad/attackby(obj/item/P, mob/user, params)
|
|
if(default_deconstruction_screwdriver(user, "holopad_open", "holopad0", P))
|
|
return
|
|
|
|
if(default_pry_open(P))
|
|
return
|
|
|
|
if(default_unfasten_wrench(user, P))
|
|
return
|
|
|
|
if(default_deconstruction_crowbar(P))
|
|
return
|
|
|
|
if(istype(P,/obj/item/disk/holodisk))
|
|
if(disk)
|
|
to_chat(user,span_warning("There's already a disk inside [src]!"))
|
|
return
|
|
if (!user.transferItemToLoc(P,src))
|
|
return
|
|
to_chat(user,span_notice("You insert [P] into [src]."))
|
|
disk = P
|
|
return
|
|
|
|
return ..()
|
|
|
|
/obj/machinery/holopad/ui_status(mob/user)
|
|
if(!is_operational())
|
|
return UI_CLOSE
|
|
if(outgoing_call && !calling)
|
|
return UI_CLOSE
|
|
return ..()
|
|
|
|
/obj/machinery/holopad/ui_interact(mob/user, datum/tgui/ui)
|
|
ui = SStgui.try_update_ui(user, src, ui)
|
|
if(!ui)
|
|
ui = new(user, src, "Holopad", name)
|
|
ui.open()
|
|
|
|
/obj/machinery/holopad/ui_data(mob/user)
|
|
var/list/data = list()
|
|
data["calling"] = calling
|
|
data["on_network"] = on_network
|
|
data["on_cooldown"] = last_request + 200 < world.time ? FALSE : TRUE
|
|
data["allowed"] = allowed(user)
|
|
data["disk"] = disk ? TRUE : FALSE
|
|
data["disk_record"] = disk?.record ? TRUE : FALSE
|
|
data["replay_mode"] = replay_mode
|
|
data["loop_mode"] = loop_mode
|
|
data["record_mode"] = record_mode
|
|
data["holo_calls"] = list()
|
|
for(var/I in holo_calls)
|
|
var/datum/holocall/HC = I
|
|
var/list/call_data = list(
|
|
caller = HC.user,
|
|
connected = HC.connected_holopad == src ? TRUE : FALSE,
|
|
ref = REF(HC)
|
|
)
|
|
data["holo_calls"] += list(call_data)
|
|
return data
|
|
|
|
/obj/machinery/holopad/ui_act(action, list/params)
|
|
. = ..()
|
|
if(.)
|
|
return
|
|
|
|
switch(action)
|
|
if("AIrequest")
|
|
if(isAI(usr))
|
|
var/mob/living/silicon/ai/ai_user = usr
|
|
ai_user.eyeobj.setLoc(get_turf(src))
|
|
to_chat(usr, span_info("AIs can not request AI presence. Jumping instead."))
|
|
return
|
|
if(last_request + 200 < world.time)
|
|
last_request = world.time
|
|
to_chat(usr, span_info("You requested an AI's presence."))
|
|
var/area/area = get_area(src)
|
|
for(var/mob/living/silicon/ai/AI in GLOB.silicon_mobs)
|
|
if(!AI.client)
|
|
continue
|
|
to_chat(AI, span_info("Your presence is requested at <a href='?src=[REF(AI)];jumptoholopad=[REF(src)]'>\the [area]</a>.</span>")) // <a href='?src=[REF(AI)];project_to_holopad=[REF(src)]'>Project Hologram?</a>"))
|
|
return TRUE
|
|
else
|
|
to_chat(usr, span_info("A request for AI presence was already sent recently."))
|
|
return
|
|
if("holocall")
|
|
if(outgoing_call)
|
|
return
|
|
if(usr.loc == loc)
|
|
var/list/callnames = list()
|
|
for(var/I in holopads)
|
|
var/area/A = get_area(I)
|
|
if(A)
|
|
LAZYADD(callnames[A], I)
|
|
callnames -= get_area(src)
|
|
var/result = input(usr, "Choose an area to call", "Holocall") as null|anything in sortNames(callnames)
|
|
if(QDELETED(usr) || !result || outgoing_call)
|
|
return
|
|
if(usr.loc == loc)
|
|
var/input = text2num(params["headcall"])
|
|
var/headcall = input == 1 ? TRUE : FALSE
|
|
new /datum/holocall(usr, src, callnames[result], headcall)
|
|
calling = TRUE
|
|
return TRUE
|
|
else
|
|
to_chat(usr, span_warning("You must stand on the holopad to make a call!"))
|
|
if("connectcall")
|
|
var/datum/holocall/call_to_connect = locate(params["holopad"]) in holo_calls
|
|
if(!QDELETED(call_to_connect))
|
|
call_to_connect.Answer(src)
|
|
return TRUE
|
|
if("disconnectcall")
|
|
var/datum/holocall/call_to_disconnect = locate(params["holopad"]) in holo_calls
|
|
if(!QDELETED(call_to_disconnect))
|
|
call_to_disconnect.Disconnect(src)
|
|
return TRUE
|
|
if("rejectall")
|
|
for(var/datum/holocall/call_to_reject as anything in holo_calls)
|
|
if(call_to_reject.connected_holopad == src) // do not kill the current connection
|
|
continue
|
|
call_to_reject.Disconnect(src)
|
|
return TRUE
|
|
if("disk_eject")
|
|
if(disk && !replay_mode)
|
|
disk.forceMove(drop_location())
|
|
disk = null
|
|
return TRUE
|
|
if("replay_mode")
|
|
if(replay_mode)
|
|
replay_stop()
|
|
return TRUE
|
|
else
|
|
replay_start()
|
|
return TRUE
|
|
if("loop_mode")
|
|
loop_mode = !loop_mode
|
|
return TRUE
|
|
if("record_mode")
|
|
if(record_mode)
|
|
record_stop()
|
|
return TRUE
|
|
else
|
|
record_start(usr)
|
|
return TRUE
|
|
if("record_clear")
|
|
record_clear()
|
|
return TRUE
|
|
if("offset")
|
|
offset++
|
|
if(offset > 4)
|
|
offset = FALSE
|
|
var/turf/new_turf
|
|
if(!offset)
|
|
new_turf = get_turf(src)
|
|
else
|
|
new_turf = get_step(src, GLOB.cardinals[offset])
|
|
replay_holo.forceMove(new_turf)
|
|
return TRUE
|
|
if("hang_up")
|
|
if(outgoing_call)
|
|
outgoing_call.Disconnect(src)
|
|
return TRUE
|
|
|
|
/**
|
|
* hangup_all_calls: Disconnects all current holocalls from the holopad
|
|
*/
|
|
/obj/machinery/holopad/proc/hangup_all_calls()
|
|
for(var/I in holo_calls)
|
|
var/datum/holocall/HC = I
|
|
HC.Disconnect(src)
|
|
|
|
/obj/machinery/holopad/attack_ai(mob/living/silicon/ai/user)
|
|
if (!istype(user))
|
|
return
|
|
if (!on_network)
|
|
return
|
|
/*There are pretty much only three ways to interact here.
|
|
I don't need to check for client since they're clicking on an object.
|
|
This may change in the future but for now will suffice.*/
|
|
if(!LAZYLEN(masters) || !masters[user])//If there is no hologram, possibly make one.
|
|
activate_holo(user)
|
|
else//If there is a hologram, remove it, and jump to your last location.
|
|
clear_holo(user)
|
|
// if(user.lastloc)//only jump to your last location if your lastloc is set, which only sets if you projected from a request message.
|
|
// user.eyeobj.setLoc(user.lastloc)
|
|
// user.lastloc = null
|
|
|
|
/obj/machinery/holopad/AICtrlClick(mob/living/silicon/ai/user)
|
|
if (!istype(user))
|
|
return
|
|
if (!on_network)
|
|
return
|
|
if(!LAZYLEN(masters) || !masters[user])//If there is no hologram, then this button does nothing.
|
|
return
|
|
else//If there is a hologram, remove it, but dont jump to your last location.
|
|
// user.lastloc = null
|
|
clear_holo(user)
|
|
return
|
|
|
|
/obj/machinery/holopad/process()
|
|
if(LAZYLEN(masters))
|
|
for(var/I in masters)
|
|
var/mob/living/master = I
|
|
var/mob/living/silicon/ai/AI = master
|
|
if(!istype(AI))
|
|
AI = null
|
|
|
|
if(!is_operational() || !validate_user(master))
|
|
clear_holo(master)
|
|
|
|
if(outgoing_call)
|
|
outgoing_call.Check()
|
|
|
|
ringing = FALSE
|
|
|
|
for(var/I in holo_calls)
|
|
var/datum/holocall/HC = I
|
|
if(HC.connected_holopad != src)
|
|
if(force_answer_call && world.time > (HC.call_start_time + (HOLOPAD_MAX_DIAL_TIME / 2)))
|
|
HC.Answer(src)
|
|
break
|
|
if(HC.head_call && !secure)
|
|
HC.Answer(src)
|
|
break
|
|
if(outgoing_call)
|
|
HC.Disconnect(src)//can't answer calls while calling
|
|
else
|
|
playsound(src, 'sound/machines/twobeep.ogg', 100) //bring, bring!
|
|
ringing = TRUE
|
|
|
|
update_appearance()
|
|
|
|
/obj/machinery/holopad/proc/activate_holo(mob/living/user)
|
|
var/mob/living/silicon/ai/AI = user
|
|
if(!istype(AI))
|
|
AI = null
|
|
|
|
if(is_operational())//If the projector has power
|
|
if(AI && istype(AI.current, /obj/machinery/holopad))
|
|
to_chat(user, "[span_danger("ERROR:")] \black Image feed in progress.")
|
|
return
|
|
|
|
var/obj/effect/overlay/holo_pad_hologram/Hologram = new(loc)//Spawn a blank effect at the location.
|
|
if(AI)
|
|
Hologram.icon = AI.holo_icon
|
|
AI.eyeobj.setLoc(get_turf(src)) //ensure the AI camera moves to the holopad
|
|
else //make it like real life
|
|
Hologram.icon = user.icon
|
|
Hologram.icon_state = user.icon_state
|
|
Hologram.copy_overlays(user, TRUE)
|
|
//codersprite some holo effects here
|
|
Hologram.alpha = 100
|
|
Hologram.add_atom_colour("#77abff", FIXED_COLOUR_PRIORITY)
|
|
Hologram.Impersonation = user
|
|
|
|
Hologram.mouse_opacity = MOUSE_OPACITY_TRANSPARENT//So you can't click on it.
|
|
Hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them.
|
|
Hologram.set_anchored(TRUE)//So space wind cannot drag it.
|
|
Hologram.name = "[user.name] (Hologram)"//If someone decides to right click.
|
|
Hologram.set_light(2) //hologram lighting
|
|
move_hologram()
|
|
|
|
set_holo(user, Hologram)
|
|
visible_message(span_notice("A holographic image of [user] flickers to life before your eyes!"))
|
|
|
|
return Hologram
|
|
else
|
|
to_chat(user, "[span_danger("ERROR:")] Unable to project hologram.")
|
|
|
|
/*This is the proc for special two-way communication between AI and holopad/people talking near holopad.
|
|
For the other part of the code, check silicon say.dm. Particularly robot talk.*/
|
|
/obj/machinery/holopad/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods = list())
|
|
. = ..()
|
|
if(speaker && LAZYLEN(masters) && !radio_freq)//Master is mostly a safety in case lag hits or something. Radio_freq so AIs dont hear holopad stuff through radios.
|
|
for(var/mob/living/silicon/ai/master in masters)
|
|
if(masters[master] && speaker != master)
|
|
master.relay_speech(message, speaker, message_language, raw_message, radio_freq, spans, message_mods)
|
|
|
|
for(var/I in holo_calls)
|
|
var/datum/holocall/HC = I
|
|
if(HC.connected_holopad == src)
|
|
if(speaker == HC.hologram && HC.user.client?.prefs.chat_on_map)
|
|
HC.user.create_chat_message(speaker, message_language, raw_message, spans)
|
|
else
|
|
HC.user.Hear(message, speaker, message_language, raw_message, radio_freq, spans, message_mods)
|
|
|
|
if(outgoing_call?.hologram && speaker == outgoing_call.user)
|
|
outgoing_call.hologram.say(raw_message)
|
|
|
|
if(record_mode && speaker == record_user)
|
|
record_message(speaker,raw_message,message_language)
|
|
|
|
/obj/machinery/holopad/proc/SetLightsAndPower()
|
|
var/total_users = LAZYLEN(masters) + LAZYLEN(holo_calls)
|
|
use_power = total_users > 0 ? ACTIVE_POWER_USE : IDLE_POWER_USE
|
|
active_power_usage = HOLOPAD_PASSIVE_POWER_USAGE + (HOLOGRAM_POWER_USAGE * total_users)
|
|
if(total_users || replay_mode)
|
|
set_light(2)
|
|
else
|
|
set_light(0)
|
|
update_appearance()
|
|
|
|
/obj/machinery/holopad/update_icon_state()
|
|
var/total_users = LAZYLEN(masters) + LAZYLEN(holo_calls)
|
|
if(ringing)
|
|
icon_state = "[base_icon_state]_ringing"
|
|
return ..()
|
|
icon_state = "[base_icon_state][(total_users || replay_mode) ? 1 : 0]"
|
|
return ..()
|
|
|
|
/obj/machinery/holopad/proc/set_holo(mob/living/user, obj/effect/overlay/holo_pad_hologram/h)
|
|
LAZYSET(masters, user, h)
|
|
LAZYSET(holorays, user, new /obj/effect/overlay/holoray(loc))
|
|
var/mob/living/silicon/ai/AI = user
|
|
if(istype(AI))
|
|
AI.current = src
|
|
SetLightsAndPower()
|
|
update_holoray(user, get_turf(loc))
|
|
return TRUE
|
|
|
|
/obj/machinery/holopad/proc/clear_holo(mob/living/user)
|
|
qdel(masters[user]) // Get rid of user's hologram
|
|
unset_holo(user)
|
|
return TRUE
|
|
|
|
/obj/machinery/holopad/proc/unset_holo(mob/living/user)
|
|
var/mob/living/silicon/ai/AI = user
|
|
if(istype(AI) && AI.current == src)
|
|
AI.current = null
|
|
LAZYREMOVE(masters, user) // Discard AI from the list of those who use holopad
|
|
qdel(holorays[user])
|
|
LAZYREMOVE(holorays, user)
|
|
SetLightsAndPower()
|
|
return TRUE
|
|
|
|
//Try to transfer hologram to another pad that can project on T
|
|
/obj/machinery/holopad/proc/transfer_to_nearby_pad(turf/T,mob/holo_owner)
|
|
var/obj/effect/overlay/holo_pad_hologram/h = masters[holo_owner]
|
|
if(!h || h.HC) //Holocalls can't change source.
|
|
return FALSE
|
|
for(var/pad in holopads)
|
|
var/obj/machinery/holopad/another = pad
|
|
if(another == src)
|
|
continue
|
|
if(another.validate_location(T))
|
|
unset_holo(holo_owner)
|
|
if(another.masters && another.masters[holo_owner])
|
|
another.clear_holo(holo_owner)
|
|
another.set_holo(holo_owner, h)
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/obj/machinery/holopad/proc/validate_user(mob/living/user)
|
|
if(QDELETED(user) || user.incapacitated() || !user.client)
|
|
return FALSE
|
|
return TRUE
|
|
|
|
//Can we display holos there
|
|
//Area check instead of line of sight check because this is a called a lot if AI wants to move around.
|
|
/obj/machinery/holopad/proc/validate_location(turf/T,check_los = FALSE)
|
|
if(T.z == z && get_dist(T, src) <= holo_range && T.loc == get_area(src))
|
|
return TRUE
|
|
else
|
|
return FALSE
|
|
|
|
/obj/machinery/holopad/proc/move_hologram(mob/living/user, turf/new_turf)
|
|
if(LAZYLEN(masters) && masters[user])
|
|
var/obj/effect/overlay/holo_pad_hologram/holo = masters[user]
|
|
var/transfered = FALSE
|
|
if(!validate_location(new_turf))
|
|
if(!transfer_to_nearby_pad(new_turf,user))
|
|
clear_holo(user)
|
|
return FALSE
|
|
else
|
|
transfered = TRUE
|
|
//All is good.
|
|
holo.abstract_move(new_turf)
|
|
if(!transfered)
|
|
update_holoray(user,new_turf)
|
|
return TRUE
|
|
|
|
|
|
/obj/machinery/holopad/proc/update_holoray(mob/living/user, turf/new_turf)
|
|
var/obj/effect/overlay/holo_pad_hologram/holo = masters[user]
|
|
var/obj/effect/overlay/holoray/ray = holorays[user]
|
|
var/disty = holo.y - ray.y
|
|
var/distx = holo.x - ray.x
|
|
var/newangle
|
|
if(!disty)
|
|
if(distx >= 0)
|
|
newangle = 90
|
|
else
|
|
newangle = 270
|
|
else
|
|
newangle = arctan(distx/disty)
|
|
if(disty < 0)
|
|
newangle += 180
|
|
else if(distx < 0)
|
|
newangle += 360
|
|
var/matrix/M = matrix()
|
|
if (get_dist(get_turf(holo),new_turf) <= 1)
|
|
animate(ray, transform = turn(M.Scale(1,sqrt(distx*distx+disty*disty)),newangle),time = 1)
|
|
else
|
|
ray.transform = turn(M.Scale(1,sqrt(distx*distx+disty*disty)),newangle)
|
|
|
|
// RECORDED MESSAGES
|
|
|
|
/obj/machinery/holopad/proc/setup_replay_holo(datum/holorecord/record)
|
|
var/obj/effect/overlay/holo_pad_hologram/Hologram = new(loc)//Spawn a blank effect at the location.
|
|
Hologram.add_overlay(record.caller_image)
|
|
Hologram.alpha = 170
|
|
Hologram.add_atom_colour("#77abff", FIXED_COLOUR_PRIORITY)
|
|
Hologram.dir = SOUTH //for now
|
|
var/datum/language_holder/holder = Hologram.get_language_holder()
|
|
holder.selected_language = record.language
|
|
Hologram.mouse_opacity = MOUSE_OPACITY_TRANSPARENT//So you can't click on it.
|
|
Hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them.
|
|
Hologram.set_anchored(TRUE)//So space wind cannot drag it.
|
|
Hologram.name = "[record.caller_name] (Hologram)"//If someone decides to right click.
|
|
Hologram.set_light(2) //hologram lighting
|
|
visible_message(span_notice("A holographic image of [record.caller_name] flickers to life before your eyes!"))
|
|
return Hologram
|
|
|
|
/obj/machinery/holopad/proc/replay_start()
|
|
if(!replay_mode)
|
|
replay_mode = TRUE
|
|
replay_holo = setup_replay_holo(disk.record)
|
|
SetLightsAndPower()
|
|
replay_entry(1)
|
|
|
|
/obj/machinery/holopad/proc/replay_stop()
|
|
if(replay_mode)
|
|
replay_mode = FALSE
|
|
offset = FALSE
|
|
QDEL_NULL(replay_holo)
|
|
SetLightsAndPower()
|
|
|
|
/obj/machinery/holopad/proc/record_start(mob/living/user)
|
|
if(!user || !disk || disk.record)
|
|
return
|
|
disk.record = new
|
|
record_mode = TRUE
|
|
record_start = world.time
|
|
record_user = user
|
|
disk.record.set_caller_image(user)
|
|
|
|
/obj/machinery/holopad/proc/record_message(mob/living/speaker,message,language)
|
|
if(!record_mode)
|
|
return
|
|
//make this command so you can have multiple languages in single record
|
|
if((!disk.record.caller_name || disk.record.caller_name == "Unknown") && istype(speaker))
|
|
disk.record.caller_name = speaker.name
|
|
if(!disk.record.language)
|
|
disk.record.language = language
|
|
else if(language != disk.record.language)
|
|
disk.record.entries += list(list(HOLORECORD_LANGUAGE,language))
|
|
|
|
var/current_delay = 0
|
|
for(var/E in disk.record.entries)
|
|
var/list/entry = E
|
|
if(entry[1] != HOLORECORD_DELAY)
|
|
continue
|
|
current_delay += entry[2]
|
|
|
|
var/time_delta = world.time - record_start - current_delay
|
|
|
|
if(time_delta >= 1)
|
|
disk.record.entries += list(list(HOLORECORD_DELAY,time_delta))
|
|
disk.record.entries += list(list(HOLORECORD_SAY,message))
|
|
if(disk.record.entries.len >= HOLORECORD_MAX_LENGTH)
|
|
record_stop()
|
|
|
|
/obj/machinery/holopad/proc/replay_entry(entry_number)
|
|
if(!replay_mode)
|
|
return
|
|
if (!disk.record.entries.len) // check for zero entries such as photographs and no text recordings
|
|
return // and pretty much just display them statically untill manually stopped
|
|
if(disk.record.entries.len < entry_number)
|
|
if(loop_mode)
|
|
entry_number = 1
|
|
else
|
|
replay_stop()
|
|
return
|
|
var/list/entry = disk.record.entries[entry_number]
|
|
var/command = entry[1]
|
|
switch(command)
|
|
if(HOLORECORD_SAY)
|
|
var/message = entry[2]
|
|
if(replay_holo)
|
|
replay_holo.say(message)
|
|
if(HOLORECORD_SOUND)
|
|
playsound(src,entry[2],50,TRUE)
|
|
if(HOLORECORD_DELAY)
|
|
addtimer(CALLBACK(src,.proc/replay_entry,entry_number+1),entry[2])
|
|
return
|
|
if(HOLORECORD_LANGUAGE)
|
|
var/datum/language_holder/holder = replay_holo.get_language_holder()
|
|
holder.selected_language = entry[2]
|
|
if(HOLORECORD_PRESET)
|
|
var/preset_type = entry[2]
|
|
var/datum/preset_holoimage/H = new preset_type
|
|
replay_holo.cut_overlays()
|
|
replay_holo.add_overlay(H.build_image())
|
|
if(HOLORECORD_RENAME)
|
|
replay_holo.name = entry[2] + " (Hologram)"
|
|
.(entry_number+1)
|
|
|
|
/obj/machinery/holopad/proc/record_stop()
|
|
if(record_mode)
|
|
record_mode = FALSE
|
|
record_user = null
|
|
|
|
/obj/machinery/holopad/proc/record_clear()
|
|
if(disk?.record)
|
|
QDEL_NULL(disk.record)
|
|
|
|
/obj/effect/overlay/holo_pad_hologram
|
|
initial_language_holder = /datum/language_holder/universal
|
|
var/mob/living/Impersonation
|
|
var/datum/holocall/HC
|
|
|
|
/obj/effect/overlay/holo_pad_hologram/Destroy()
|
|
Impersonation = null
|
|
if(!QDELETED(HC))
|
|
HC.Disconnect(HC.calling_holopad)
|
|
HC = null
|
|
return ..()
|
|
|
|
/obj/effect/overlay/holo_pad_hologram/Process_Spacemove(movement_dir = 0)
|
|
return TRUE
|
|
|
|
/obj/effect/overlay/holo_pad_hologram/examine(mob/user)
|
|
if(Impersonation)
|
|
return Impersonation.examine(user)
|
|
return ..()
|
|
|
|
/obj/effect/overlay/holoray
|
|
name = "holoray"
|
|
icon = 'icons/effects/96x96.dmi'
|
|
icon_state = "holoray"
|
|
layer = FLY_LAYER
|
|
density = FALSE
|
|
anchored = TRUE
|
|
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
|
|
pixel_x = -32
|
|
pixel_y = -32
|
|
alpha = 100
|
|
|
|
#undef HOLOPAD_PASSIVE_POWER_USAGE
|
|
#undef HOLOGRAM_POWER_USAGE
|