diff --git a/code/datums/holocall.dm b/code/datums/holocall.dm
index abad0dba1d..75abc87080 100644
--- a/code/datums/holocall.dm
+++ b/code/datums/holocall.dm
@@ -1,5 +1,14 @@
#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
@@ -184,3 +193,122 @@
/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
+
+/obj/item/disk/holodisk
+ name = "holorecord disk"
+ desc = "Stores recorder holocalls."
+ icon_state = "holodisk"
+ 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/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)
+ . = getFlatIcon(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/gorilla
+ nonhuman_mobtype = /mob/living/simple_animal/hostile/gorilla
+
+/datum/preset_holoimage/clown
+ outfit_type = /datum/outfit/job/clown
+
diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm
index afc463a60e..d6d8c93ed1 100644
--- a/code/game/machinery/hologram.dm
+++ b/code/game/machinery/hologram.dm
@@ -47,6 +47,12 @@ Possible to do for anyone motivated enough:
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/obj/item/disk/holodisk/disk //Record disk
+ var/replay_mode = FALSE //currently replaying a recording
+ var/record_mode = FALSE //currently recording
+ var/record_start = 0 //recording start time
+ var/record_user //user that inititiated the recording
+ var/obj/effect/overlay/holo_pad_hologram/replay_holo //replay hologram
var/static/force_answer_call = FALSE //Calls will be automatically answered after a couple rings, here for debugging
var/static/list/holopads = list()
@@ -64,6 +70,14 @@ Possible to do for anyone motivated enough:
for (var/I in masters)
clear_holo(I)
+
+ if(replay_mode)
+ replay_stop()
+ if(record_mode)
+ record_stop()
+
+ QDEL_NULL(disk)
+
holopads -= src
return ..()
@@ -72,6 +86,10 @@ Possible to do for anyone motivated enough:
stat &= ~NOPOWER
else
stat |= NOPOWER
+ if(replay_mode)
+ replay_stop()
+ if(record_mode)
+ record_stop()
if(outgoing_call)
outgoing_call.ConnectionFailure(src)
@@ -101,6 +119,18 @@ Possible to do for anyone motivated enough:
if(default_deconstruction_crowbar(P))
return
+
+ if(istype(P,/obj/item/disk/holodisk))
+ if(disk)
+ to_chat(user,"There's already a disk inside [src]")
+ return
+ if (!user.transferItemToLoc(P,src))
+ return
+ to_chat(user,"You insert [P] into [src]")
+ disk = P
+ updateDialog()
+ return
+
return ..()
/obj/machinery/holopad/AltClick(mob/living/carbon/human/user)
@@ -122,6 +152,17 @@ Possible to do for anyone motivated enough:
else
dat = "Request an AI's presence.
"
dat += "Call another holopad.
"
+ if(disk)
+ if(disk.record)
+ //Replay
+ dat += "Replay disk recording.
"
+ //Clear
+ dat += "Clear disk recording.
"
+ else
+ //Record
+ dat += "Start new recording.
"
+ //Eject
+ dat += "Eject disk.
"
if(LAZYLEN(holo_calls))
dat += "=====================================================
"
@@ -145,7 +186,7 @@ Possible to do for anyone motivated enough:
dat += "Disconnect call from [HC.user].
"
- var/datum/browser/popup = new(user, "holopad", name, 300, 130)
+ var/datum/browser/popup = new(user, "holopad", name, 300, 150)
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
@@ -215,6 +256,22 @@ Possible to do for anyone motivated enough:
temp = ""
if(outgoing_call)
outgoing_call.Disconnect()
+
+ else if(href_list["disk_eject"])
+ if(disk && !replay_mode)
+ disk.forceMove(drop_location())
+ disk = null
+
+ else if(href_list["replay_stop"])
+ replay_stop()
+ else if(href_list["replay_start"])
+ replay_start()
+ else if(href_list["record_start"])
+ record_start(usr)
+ else if(href_list["record_stop"])
+ record_stop()
+ else if(href_list["record_clear"])
+ record_clear()
updateDialog()
@@ -269,6 +326,7 @@ Possible to do for anyone motivated enough:
else
playsound(src, 'sound/machines/twobeep.ogg', 100) //bring, bring!
+
/obj/machinery/holopad/proc/activate_holo(mob/living/user)
var/mob/living/silicon/ai/AI = user
if(!istype(AI))
@@ -321,15 +379,24 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
if(outgoing_call && 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 = masters.len + 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)
+ if(total_users || replay_mode)
set_light(2)
- icon_state = "holopad1"
else
set_light(0)
+ update_icon()
+
+/obj/machinery/holopad/update_icon()
+ var/total_users = masters.len + LAZYLEN(holo_calls)
+ if(total_users || replay_mode)
+ icon_state = "holopad1"
+ else
icon_state = "holopad0"
/obj/machinery/holopad/proc/set_holo(mob/living/user, var/obj/effect/overlay/holo_pad_hologram/h)
@@ -365,6 +432,128 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
clear_holo(user)
return TRUE
+// 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
+ Hologram.grant_all_languages(omnitongue=TRUE)
+ var/datum/language_holder/holder = Hologram.get_language_holder()
+ holder.selected_default_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.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("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)
+ temp = "Replaying...
"
+ temp += "End replay."
+ SetLightsAndPower()
+ replay_entry(1)
+ return
+
+/obj/machinery/holopad/proc/replay_stop()
+ if(replay_mode)
+ replay_mode = FALSE
+ temp = null
+ QDEL_NULL(replay_holo)
+ SetLightsAndPower()
+ updateDialog()
+
+/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.caller_image = get_record_icon(user)
+ temp = "Recording...
"
+ temp += "End recording."
+
+/obj/machinery/holopad/proc/get_record_icon(mob/living/user)
+ var/olddir = user.dir
+ user.setDir(SOUTH)
+ . = getFlatIcon(user)
+ user.setDir(olddir)
+
+/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 && 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 < entry_number)
+ 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,1)
+ 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_default_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
+ temp = null
+ record_user = null
+ updateDialog()
+
+/obj/machinery/holopad/proc/record_clear()
+ if(disk && disk.record)
+ QDEL_NULL(disk.record)
+ updateDialog()
+
/obj/effect/overlay/holo_pad_hologram
var/mob/living/Impersonation
var/datum/holocall/HC
diff --git a/code/modules/research/designs/autolathe_designs.dm b/code/modules/research/designs/autolathe_designs.dm
index 03424346e8..1c0c12a02b 100644
--- a/code/modules/research/designs/autolathe_designs.dm
+++ b/code/modules/research/designs/autolathe_designs.dm
@@ -799,3 +799,22 @@
materials = list(MAT_METAL = 300, MAT_GLASS = 200)
build_path = /obj/item/device/slime_scanner
category = list("initial", "Misc")
+<<<<<<< HEAD
+=======
+
+/datum/design/miniature_power_cell
+ name = "Light Fixture Battery"
+ id = "miniature_power_cell"
+ build_type = AUTOLATHE
+ materials = list(MAT_GLASS = 20)
+ build_path = /obj/item/stock_parts/cell/emergency_light
+ category = list("initial", "Electronics")
+
+/datum/design/holodisk
+ name = "Holodisk"
+ id = "holodisk"
+ build_type = AUTOLATHE
+ materials = list(MAT_METAL = 1000)
+ build_path = /obj/item/disk/holodisk
+ category = list("initial", "Misc")
+>>>>>>> 78fe2bc... Holorecord disks. (#32868)
diff --git a/icons/obj/module.dmi b/icons/obj/module.dmi
index c24f7c4c16..f5bd612c22 100644
Binary files a/icons/obj/module.dmi and b/icons/obj/module.dmi differ