diff --git a/code/controllers/subsystem/garbage.dm.rej b/code/controllers/subsystem/garbage.dm.rej new file mode 100644 index 0000000000..2075155c49 --- /dev/null +++ b/code/controllers/subsystem/garbage.dm.rej @@ -0,0 +1,9 @@ +diff a/code/controllers/subsystem/garbage.dm b/code/controllers/subsystem/garbage.dm (rejected hunks) +@@ -725,7 +725,6 @@ var/datum/controller/subsystem/garbage_collector/SSgarbage + SearchVar(multiverse) + SearchVar(announcement_systems) + SearchVar(doppler_arrays) +- SearchVar(HOLOPAD_MODE) + SearchVar(holopads) + SearchVar(news_network) + SearchVar(allCasters) diff --git a/code/datums/holocall.dm b/code/datums/holocall.dm new file mode 100644 index 0000000000..0153e41414 --- /dev/null +++ b/code/datums/holocall.dm @@ -0,0 +1,154 @@ +#define HOLOPAD_MAX_DIAL_TIME 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/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 + LAZYADD(H.holo_calls, src) + + if(!dialed_holopads.len) + calling_pad.say("Connection failure.") + qdel(src) + + testing("Holocall started") + +//cleans up ALL references :) +/datum/holocall/Destroy() + QDEL_NULL(eye) + + user.reset_perspective() + + user = null + hologram.HC = null + hologram = null + calling_holopad.outgoing_call = null + + 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.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) + calling_holopad.say("[usr] disconnected.") + else if(H == calling_holopad && connected_holopad) + connected_holopad.say("[usr] 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) + 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") + + connected_holopad = H + for(var/I in dialed_holopads) + if(I == H) + continue + var/obj/machinery/holopad/Holo = I + LAZYREMOVE(Holo.holo_calls, src) + dialed_holopads -= Holo + + 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) + +//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) + . = !QDELETED(connected_holopad) && connected_holopad.is_operational() + else + . = world.time < (call_start_time + HOLOPAD_MAX_DIAL_TIME) + if(!.) + calling_holopad.say("No answer recieved.") + calling_holopad.temp = "" + + if(!.) + testing("Holocall Check fail") + qdel(src) diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index ae8c1ac1c4..bc73c41f08 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -26,14 +26,13 @@ Possible to do for anyone motivated enough: #define HOLOPAD_PASSIVE_POWER_USAGE 1 #define HOLOGRAM_POWER_USAGE 2 -#define RANGE_BASED 4 -#define AREA_BASED 6 +GLOBAL_LIST_EMPTY(holopads) #define HOLOPAD_MODE RANGE_BASED /obj/machinery/holopad - name = "\improper AI holopad" - desc = "It's a floor-mounted device for projecting holographic images. It is activated remotely." + name = "Holopad" + desc = "It's a floor-mounted device for projecting holographic images." icon_state = "holopad0" layer = LOW_OBJ_LAYER flags = HEAR @@ -44,21 +43,32 @@ Possible to do for anyone motivated enough: obj_integrity = 300 max_integrity = 300 armor = list(melee = 50, bullet = 20, laser = 20, energy = 20, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 0) - var/list/masters = list()//List of AIs that use the holopad + var/list/masters = list()//List of living mobs that use the holopad var/last_request = 0 //to prevent request spam. ~Carn var/holo_range = 5 // Change to change how far the AI can move away from the holopad before deactivating. var/temp = "" + var/list/holo_calls //array of /datum/holocalls + var/datum/holocall/outgoing_call //do not modify the datums only check and call the public procs + var/static/force_answer_call = FALSE //Calls will be automatically answered after a couple rings, here for debugging var/static/list/holopads = list() -/obj/machinery/holopad/New() +/obj/machinery/holopad/Initialize() ..() var/obj/item/weapon/circuitboard/machine/B = new /obj/item/weapon/circuitboard/machine/holopad(null) B.apply_default_parts(src) holopads += src /obj/machinery/holopad/Destroy() - for (var/mob/living/silicon/ai/master in masters) - clear_holo(master) + if(outgoing_call) + LAZYADD(holo_calls, outgoing_call) + + for(var/I in holo_calls) + var/datum/holocall/HC = I + HC.ConnectionFailure(src) + LAZYCLEARLIST(holo_calls) + + for (var/I in masters) + clear_holo(I) holopads -= src return ..() @@ -91,20 +101,58 @@ Possible to do for anyone motivated enough: return return ..() +/obj/machinery/holopad/proc/CheckCallClose() + for(var/I in holo_calls) + var/datum/holocall/HC = I + if(usr == HC.eye) + HC.Disconnect(HC.calling_holopad) //disconnect via clicking the called holopad + return TRUE + return FALSE + +/obj/machinery/holopad/Click(location,control,params) + if(!CheckCallClose()) + return ..() + /obj/machinery/holopad/AltClick(mob/living/carbon/human/user) - interact(user) + if(!CheckCallClose()) + interact(user) /obj/machinery/holopad/interact(mob/living/carbon/human/user) //Carn: Hologram requests. if(!istype(user)) return - if(user.stat || !is_operational()) + + if(outgoing_call || user.incapacitated() || !is_operational()) return + user.set_machine(src) var/dat if(temp) dat = temp else - dat = "request an AI's presence." + dat = "Request an AI's presence.
" + dat += "Call another holopad.
" + + if(LAZYLEN(holo_calls)) + dat += "=====================================================
" + + var/one_answered_call = FALSE + var/one_unanswered_call = FALSE + for(var/I in holo_calls) + var/datum/holocall/HC = I + if(HC.connected_holopad != src) + dat += "Answer call from [get_area(HC.calling_holopad)].
" + one_unanswered_call = TRUE + else + one_answered_call = TRUE + + if(one_answered_call && one_unanswered_call) + dat += "=====================================================
" + //we loop twice for formatting + for(var/I in holo_calls) + var/datum/holocall/HC = I + if(HC.connected_holopad == src) + dat += "Disconnect call from [HC.user].
" + var/datum/browser/popup = new(user, "holopad", name, 300, 130) popup.set_content(dat) @@ -112,7 +160,10 @@ Possible to do for anyone motivated enough: popup.open() /obj/machinery/holopad/Topic(href, href_list) - if(..() || !is_operational()) + if(..() || isAI(usr)) + return + add_fingerprint(usr) + if(!is_operational()) return if (href_list["AIrequest"]) if(last_request + 200 < world.time) @@ -120,7 +171,7 @@ Possible to do for anyone motivated enough: temp = "You requested an AI's presence.
" temp += "Main Menu" var/area/area = get_area(src) - for(var/mob/living/silicon/ai/AI in GLOB.living_mob_list) + for(var/mob/living/silicon/ai/AI in GLOB.silicon_mobs) if(!AI.client) continue to_chat(AI, "Your presence is requested at \the [area].") @@ -128,12 +179,49 @@ Possible to do for anyone motivated enough: temp = "A request for AI presence was already sent recently.
" temp += "Main Menu" - else if(href_list["mainmenu"]) + else if(href_list["Holocall"]) + if(outgoing_call) + return + + temp = "You must stand on the holopad to make a call!
" + temp += "Main Menu" + 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 callnames + if(QDELETED(usr) || !result || outgoing_call) + return + + if(usr.loc == loc) + temp = "Dialing...
" + temp += "Main Menu" + new /datum/holocall(usr, src, callnames[result]) + + else if(href_list["connectcall"]) + var/datum/holocall/call_to_connect = locate(href_list["connectcall"]) + if(!QDELETED(call_to_connect)) + call_to_connect.Answer(src) temp = "" - updateDialog() - add_fingerprint(usr) + else if(href_list["disconnectcall"]) + var/datum/holocall/call_to_disconnect = locate(href_list["disconnectcall"]) + if(!QDELETED(call_to_disconnect)) + call_to_disconnect.Disconnect(src) + temp = "" + else if(href_list["mainmenu"]) + temp = "" + if(outgoing_call) + outgoing_call.Disconnect() + + updateDialog() + +//do not allow AIs to answer calls or people will use it to meta the AI sattelite /obj/machinery/holopad/attack_ai(mob/living/silicon/ai/user) if (!istype(user)) return @@ -148,39 +236,75 @@ Possible to do for anyone motivated enough: clear_holo(user) /obj/machinery/holopad/process() - if(masters.len)//If there is a hologram. - for (var/mob/living/silicon/ai/master in masters) - if(master && !master.stat && master.client && master.eyeobj)//If there is an AI attached, it's not incapacitated, it has a client, and the client eye is centered on the projector. - if(!(stat & NOPOWER))//If the machine has power. - if(HOLOPAD_MODE == RANGE_BASED) - if(get_dist(master.eyeobj, src) <= holo_range) - return TRUE - else - var/obj/machinery/holopad/pad_close = get_closest_atom(/obj/machinery/holopad, holopads, master.eyeobj) - if(get_dist(pad_close, master.eyeobj) <= holo_range) - var/obj/effect/overlay/holo_pad_hologram/h = masters[master] - unset_holo(master) - pad_close.set_holo(master, h) - return TRUE + for(var/I in masters) + var/mob/living/master = I + var/mob/living/silicon/ai/AI = master + if(!istype(AI)) + AI = null - else if (HOLOPAD_MODE == AREA_BASED) + if(!QDELETED(master) && !master.incapacitated() && master.client && (!AI || AI.eyeobj))//If there is an AI attached, it's not incapacitated, it has a client, and the client eye is centered on the projector. + if(is_operational())//If the machine has power. + if(AI) //ais are range based + if(get_dist(AI.eyeobj, src) <= holo_range) + continue + else + var/obj/machinery/holopad/pad_close = get_closest_atom(/obj/machinery/holopad, holopads, AI.eyeobj) + if(get_dist(pad_close, AI.eyeobj) <= holo_range) + var/obj/effect/overlay/holo_pad_hologram/h = masters[master] + unset_holo(master) + pad_close.set_holo(master, h) + continue + else + continue + clear_holo(master)//If not, we want to get rid of the hologram. - var/area/holo_area = get_area(src) - var/area/eye_area = get_area(master.eyeobj) + if(outgoing_call) + outgoing_call.Check() - if(eye_area in holo_area.related) - return TRUE + 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(outgoing_call) + HC.Disconnect(src)//can't answer calls while calling + else + playsound(src, 'sound/machines/twobeep.ogg', 100) //bring, bring! - clear_holo(master)//If not, we want to get rid of the hologram. - return TRUE +/obj/machinery/holopad/proc/activate_holo(mob/living/user) + var/mob/living/silicon/ai/AI = user + if(!istype(AI)) + AI = null -/obj/machinery/holopad/proc/activate_holo(mob/living/silicon/ai/user) - if(!(stat & NOPOWER) && user.eyeobj.loc == src.loc)//If the projector has power and client eye is on it - if (istype(user.current, /obj/machinery/holopad)) + if(is_operational() && (!AI || AI.eyeobj.loc == loc))//If the projector has power and client eye is on it + if (AI && istype(AI.current, /obj/machinery/holopad)) to_chat(user, "ERROR: \black Image feed in progress.") return - create_holo(user)//Create one. - src.visible_message("A holographic image of [user] flicks to life right before your eyes!") + + var/obj/effect/overlay/holo_pad_hologram/Hologram = new(loc)//Spawn a blank effect at the location. + if(AI) + Hologram.icon = AI.holo_icon + 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.languages = user.languages + Hologram.mouse_opacity = 0//So you can't click on it. + Hologram.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them. + Hologram.anchored = 1//So space wind cannot drag it. + Hologram.name = "[user.name] (Hologram)"//If someone decides to right click. + Hologram.set_light(2) //hologram lighting + + set_holo(user, Hologram) + visible_message("A holographic image of [user] flicks to life right before your eyes!") + + return Hologram else to_chat(user, "ERROR: \black Unable to project hologram.") @@ -192,58 +316,80 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ if(masters[master] && speaker != master) master.relay_speech(message, speaker, message_language, raw_message, radio_freq, spans, message_mode) -/obj/machinery/holopad/proc/create_holo(mob/living/silicon/ai/A, turf/T = loc) - var/obj/effect/overlay/holo_pad_hologram/h = new(T)//Spawn a blank effect at the location. - h.icon = A.holo_icon - h.mouse_opacity = 0//So you can't click on it. - h.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them. - h.anchored = 1//So space wind cannot drag it. - h.name = "[A.name] (Hologram)"//If someone decides to right click. - h.set_light(2) //hologram lighting - set_holo(A, h) + for(var/I in holo_calls) + var/datum/holocall/HC = I + if(HC.connected_holopad == src && speaker != HC.hologram) + HC.user.Hear(message, speaker, message_language, raw_message, radio_freq, spans) + + if(outgoing_call && speaker == outgoing_call.user) + outgoing_call.hologram.say(raw_message) + +/obj/machinery/holopad/proc/SetLightsAndPower() + var/total_users = masters.len + LAZYLEN(holo_calls) + use_power = HOLOPAD_PASSIVE_POWER_USAGE + HOLOGRAM_POWER_USAGE * total_users + if(total_users) + set_light(2) + icon_state = "holopad1" + else + set_light(0) + icon_state = "holopad0" + +/obj/machinery/holopad/proc/set_holo(mob/living/user, var/obj/effect/overlay/holo_pad_hologram/h) + masters[user] = h + var/mob/living/silicon/ai/AI = user + if(istype(AI)) + AI.current = src + SetLightsAndPower() return TRUE -/obj/machinery/holopad/proc/set_holo(mob/living/silicon/ai/A, var/obj/effect/overlay/holo_pad_hologram/h) - masters[A] = h - set_light(2) // pad lighting - icon_state = "holopad1" - A.current = src - use_power += HOLOGRAM_POWER_USAGE - return TRUE - -/obj/machinery/holopad/proc/clear_holo(mob/living/silicon/ai/user) +/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/silicon/ai/user) - if(user.current == src) - user.current = null +/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 masters -= user // Discard AI from the list of those who use holopad - use_power = max(HOLOPAD_PASSIVE_POWER_USAGE, use_power - HOLOGRAM_POWER_USAGE)//Reduce power usage - if (!masters.len) // If no users left - set_light(0) // pad lighting (hologram lighting will be handled automatically since its owner was deleted) - icon_state = "holopad0" - use_power = HOLOPAD_PASSIVE_POWER_USAGE + SetLightsAndPower() return TRUE -/obj/machinery/holopad/proc/move_hologram(mob/living/silicon/ai/user) +/obj/machinery/holopad/proc/move_hologram(mob/living/user, turf/new_turf) if(masters[user]) - step_to(masters[user], user.eyeobj) var/obj/effect/overlay/holo_pad_hologram/H = masters[user] - H.loc = get_turf(user.eyeobj) + step_to(H, new_turf) + H.loc = new_turf + var/area/holo_area = get_area(src) + var/area/eye_area = new_turf.loc + + if(!(eye_area in holo_area.related)) + clear_holo(user) return TRUE +/obj/effect/overlay/holo_pad_hologram + var/mob/living/Impersonation + var/datum/holocall/HC + +/obj/effect/overlay/holo_pad_hologram/Destroy() + Impersonation = null + if(HC) + HC.Disconnect(HC.calling_holopad) + return ..() + /obj/effect/overlay/holo_pad_hologram/Process_Spacemove(movement_dir = 0) return 1 +/obj/effect/overlay/holo_pad_hologram/examine(mob/user) + if(Impersonation) + return Impersonation.examine(user) + return ..() + /obj/item/weapon/circuitboard/machine/holopad name = "AI Holopad (Machine Board)" build_path = /obj/machinery/holopad origin_tech = "programming=1" req_components = list(/obj/item/weapon/stock_parts/capacitor = 1) -#undef RANGE_BASED -#undef AREA_BASED #undef HOLOPAD_PASSIVE_POWER_USAGE -#undef HOLOGRAM_POWER_USAGE +#undef HOLOGRAM_POWER_USAGE \ No newline at end of file diff --git a/code/game/machinery/hologram.dm.rej b/code/game/machinery/hologram.dm.rej new file mode 100644 index 0000000000..5dcbcc9d54 --- /dev/null +++ b/code/game/machinery/hologram.dm.rej @@ -0,0 +1,226 @@ +diff a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm (rejected hunks) +@@ -26,16 +26,12 @@ Possible to do for anyone motivated enough: + + #define HOLOPAD_PASSIVE_POWER_USAGE 1 + #define HOLOGRAM_POWER_USAGE 2 +-#define RANGE_BASED 4 +-#define AREA_BASED 6 +- +-var/const/HOLOPAD_MODE = RANGE_BASED + + var/list/holopads = list() + + /obj/machinery/holopad +- name = "\improper AI holopad" +- desc = "It's a floor-mounted device for projecting holographic images. It is activated remotely." ++ name = "Holopad" ++ desc = "It's a floor-mounted device for projecting holographic images." + icon_state = "holopad0" + layer = LOW_OBJ_LAYER + flags = HEAR +@@ -48,10 +44,13 @@ var/list/holopads = list() + obj_integrity = 300 + max_integrity = 300 + armor = list(melee = 50, bullet = 20, laser = 20, energy = 20, bomb = 0, bio = 0, rad = 0, fire = 50, acid = 0) +- var/list/masters = list()//List of AIs that use the holopad ++ var/list/masters = list()//List of living mobs that use the holopad + var/last_request = 0 //to prevent request spam. ~Carn + var/holo_range = 5 // Change to change how far the AI can move away from the holopad before deactivating. + var/temp = "" ++ var/list/holo_calls //array of /datum/holocalls ++ var/datum/holocall/outgoing_call //do not modify the datums only check and call the public procs ++ var/static/force_answer_call = FALSE //Calls will be automatically answered after a couple rings, here for debugging + + /obj/machinery/holopad/Initialize() + ..() +@@ -94,20 +101,60 @@ var/list/holopads = list() + return + return ..() + ++/obj/machinery/holopad/proc/CheckCallClose() ++ for(var/I in holo_calls) ++ var/datum/holocall/HC = I ++ if(usr == HC.eye) ++ HC.Disconnect(HC.calling_holopad) //disconnect via clicking the called holopad ++ return TRUE ++ return FALSE ++ ++/obj/machinery/holopad/Click(location,control,params) ++ if(!CheckCallClose()) ++ return ..() ++ + /obj/machinery/holopad/AltClick(mob/living/carbon/human/user) +- interact(user) ++ if(!CheckCallClose()) ++ interact(user) + + /obj/machinery/holopad/interact(mob/living/carbon/human/user) //Carn: Hologram requests. + if(!istype(user)) + return +- if(user.stat || stat & (NOPOWER|BROKEN)) ++ if(user.incapacitated() || !is_operational()) ++ return ++ ++ if(outgoing_call) //someone is making a call, leave them be + return ++ + user.set_machine(src) + var/dat + if(temp) + dat = temp + else +- dat = "request an AI's presence." ++ dat = "Request an AI's presence.
" ++ dat += "Call another holopad.
" ++ ++ if(LAZYLEN(holo_calls)) ++ dat += "=====================================================
" ++ ++ var/one_answered_call = FALSE ++ var/one_unanswered_call = FALSE ++ for(var/I in holo_calls) ++ var/datum/holocall/HC = I ++ if(HC.connected_holopad != src) ++ dat += "Answer call from [get_area(HC.calling_holopad)].
" ++ one_unanswered_call = TRUE ++ else ++ one_answered_call = TRUE ++ ++ if(one_answered_call && one_unanswered_call) ++ dat += "=====================================================
" ++ //we loop twice for formatting ++ for(var/I in holo_calls) ++ var/datum/holocall/HC = I ++ if(HC.connected_holopad == src) ++ dat += "Disconnect call from [HC.user].
" ++ + + var/datum/browser/popup = new(user, "holopad", name, 300, 130) + popup.set_content(dat) +@@ -115,7 +162,10 @@ var/list/holopads = list() + popup.open() + + /obj/machinery/holopad/Topic(href, href_list) +- if(..()) ++ if(..() || isAI(usr)) ++ return ++ add_fingerprint(usr) ++ if(!is_operational()) + return + if (href_list["AIrequest"]) + if(last_request + 200 < world.time) +@@ -194,59 +317,81 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ + for(var/mob/living/silicon/ai/master in masters) + if(masters[master] && speaker != master) + master.relay_speech(message, speaker, message_langs, raw_message, radio_freq, spans) ++ ++ for(var/I in holo_calls) ++ var/datum/holocall/HC = I ++ if(HC.connected_holopad == src && speaker != HC.hologram) ++ HC.user.Hear(message, speaker, message_langs, raw_message, radio_freq, spans) ++ ++ if(outgoing_call && speaker == outgoing_call.user) ++ outgoing_call.hologram.say(raw_message) ++ ++/obj/machinery/holopad/proc/SetLightsAndPower() ++ var/total_users = masters.len + LAZYLEN(holo_calls) ++ use_power = HOLOPAD_PASSIVE_POWER_USAGE + HOLOGRAM_POWER_USAGE * total_users ++ if(total_users) ++ set_light(2) ++ icon_state = "holopad1" ++ else ++ set_light(0) ++ icon_state = "holopad0" + +-/obj/machinery/holopad/proc/create_holo(mob/living/silicon/ai/A, turf/T = loc) +- var/obj/effect/overlay/holo_pad_hologram/h = new(T)//Spawn a blank effect at the location. +- h.icon = A.holo_icon +- h.mouse_opacity = 0//So you can't click on it. +- h.layer = FLY_LAYER//Above all the other objects/mobs. Or the vast majority of them. +- h.anchored = 1//So space wind cannot drag it. +- h.name = "[A.name] (Hologram)"//If someone decides to right click. +- h.set_light(2) //hologram lighting +- set_holo(A, h) +- return TRUE +- +-/obj/machinery/holopad/proc/set_holo(mob/living/silicon/ai/A, var/obj/effect/overlay/holo_pad_hologram/h) +- masters[A] = h +- set_light(2) // pad lighting +- icon_state = "holopad1" +- A.current = src +- use_power += HOLOGRAM_POWER_USAGE ++/obj/machinery/holopad/proc/set_holo(mob/living/user, var/obj/effect/overlay/holo_pad_hologram/h) ++ masters[user] = h ++ var/mob/living/silicon/ai/AI = user ++ if(istype(AI)) ++ AI.current = src ++ SetLightsAndPower() + return TRUE + +-/obj/machinery/holopad/proc/clear_holo(mob/living/silicon/ai/user) ++/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/silicon/ai/user) +- if(user.current == src) +- user.current = null ++/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 + masters -= user // Discard AI from the list of those who use holopad +- use_power = max(HOLOPAD_PASSIVE_POWER_USAGE, use_power - HOLOGRAM_POWER_USAGE)//Reduce power usage +- if (!masters.len) // If no users left +- set_light(0) // pad lighting (hologram lighting will be handled automatically since its owner was deleted) +- icon_state = "holopad0" +- use_power = HOLOPAD_PASSIVE_POWER_USAGE ++ SetLightsAndPower() + return TRUE + +-/obj/machinery/holopad/proc/move_hologram(mob/living/silicon/ai/user) ++/obj/machinery/holopad/proc/move_hologram(mob/living/user, turf/new_turf) + if(masters[user]) +- step_to(masters[user], user.eyeobj) + var/obj/effect/overlay/holo_pad_hologram/H = masters[user] +- H.loc = get_turf(user.eyeobj) ++ step_to(H, new_turf) ++ H.loc = new_turf ++ var/area/holo_area = get_area(src) ++ var/area/eye_area = new_turf.loc ++ ++ if(!(eye_area in holo_area.related)) ++ clear_holo(user) + return TRUE + ++/obj/effect/overlay/holo_pad_hologram ++ var/mob/living/Impersonation ++ var/datum/holocall/HC ++ ++/obj/effect/overlay/holo_pad_hologram/Destroy() ++ Impersonation = null ++ if(HC) ++ HC.Disconnect(HC.calling_holopad) ++ return ..() ++ + /obj/effect/overlay/holo_pad_hologram/Process_Spacemove(movement_dir = 0) + return 1 + ++/obj/effect/overlay/holo_pad_hologram/examine(mob/user) ++ if(Impersonation) ++ return Impersonation.examine(user) ++ return ..() ++ + /obj/item/weapon/circuitboard/machine/holopad + name = "AI Holopad (Machine Board)" + build_path = /obj/machinery/holopad + origin_tech = "programming=1" + req_components = list(/obj/item/weapon/stock_parts/capacitor = 1) + +-#undef RANGE_BASED +-#undef AREA_BASED + #undef HOLOPAD_PASSIVE_POWER_USAGE +-#undef HOLOGRAM_POWER_USAGE ++#undef HOLOGRAM_POWER_USAGE +\ No newline at end of file diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm index dce38bd664..56c43211e4 100644 --- a/code/modules/mob/living/silicon/ai/freelook/eye.dm +++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm @@ -28,7 +28,7 @@ //Holopad if(istype(ai.current, /obj/machinery/holopad)) var/obj/machinery/holopad/H = ai.current - H.move_hologram(ai) + H.move_hologram(ai, T) /mob/camera/aiEye/Move() return 0 diff --git a/tgstation.dme b/tgstation.dme index 1f3ef27802..bff0d9a902 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -244,6 +244,7 @@ #include "code\datums\dog_fashion.dm" #include "code\datums\emotes.dm" #include "code\datums\forced_movement.dm" +#include "code\datums\holocall.dm" #include "code\datums\hud.dm" #include "code\datums\map_config.dm" #include "code\datums\martial.dm" diff --git a/tgstation.dme.rej b/tgstation.dme.rej new file mode 100644 index 0000000000..0f29d8f023 --- /dev/null +++ b/tgstation.dme.rej @@ -0,0 +1,9 @@ +diff a/tgstation.dme b/tgstation.dme (rejected hunks) +@@ -206,6 +206,7 @@ + #include "code\datums\emotes.dm" + #include "code\datums\forced_movement.dm" + #include "code\datums\gas_overrides.dm" ++#include "code\datums\holocall.dm" + #include "code\datums\hud.dm" + #include "code\datums\map_config.dm" + #include "code\datums\martial.dm"