Files
runecap 052c9a0d53 Commune can send popups of paper & photo contents (#22295)
Commune can now send snapshot popups of paper & photo contents to
targets, similar to how Show-Held-Item can let others see the items'
contents. This was originally planned for the initial PR and only didn't
make it in because I didn't know how to get the functionality. There's
more unique psi-sensitivity interactions in this.

There is a 45% chance for psi-deaf targets to even get the photo popup.
Photo scribbles fittingly appear scrambled to the psi-deaf.
<img width="969" height="232" alt="image"
src="https://github.com/user-attachments/assets/84105c37-6c1d-4da2-83bb-738ce6651977"
/>

On the chance psi-deaf even get the photo popup, there is a 60% chance
the photo will be in greyscale.
<img width="476" height="195" alt="image"
src="https://github.com/user-attachments/assets/030407b7-ce51-4b20-a1e2-6eb93fb21be7"
/>


Psi-sensitive targets receiving a paper popup will have it translated
based on what the sender understands. This is a fun form of translation
because it's possible for the target to _not_ understand a written
language they actually know simply because the sender doesn't.

<img width="1411" height="656" alt="image"
src="https://github.com/user-attachments/assets/9d4fe196-2593-4535-b062-bbdfa8e1e819"
/>


There is also a QoL adjacency check for writing scribbles on photos now
because it bugged me I needed to use both hands while testing.

NEW: If a psi-deaf target doesn't get it in greyscale, they may also get
photos with randomized colors

<img width="443" height="444" alt="image"
src="https://github.com/user-attachments/assets/f9a1f1c0-a6af-4d1e-95e3-260231d57643"
/>
 
This can seriously reframe some photos. Lots of fun with possible
misunderstandings. Both the above and below are the same photo with
Skrell blood, but the above looks like vaurca blood while the below one
doesn't look to even have blood at first glance.
 
<img width="444" height="447" alt="image"
src="https://github.com/user-attachments/assets/166b40f2-6ed7-49af-95e1-989ffbb69e60"
/>

---------

Signed-off-by: runecap <43975590+runecap@users.noreply.github.com>
Co-authored-by: VMSolidus <evilexecutive@gmail.com>
2026-05-15 17:13:33 +00:00

233 lines
11 KiB
Plaintext

/singleton/psionic_power/commune
name = "Commune"
desc = "Psionically commune with the target. Use on an adjacent object to store a mental image for sending. Use in hand with a mental image to warp and add to its description, or clear a warped one."
icon_state = "tech_audibledeception"
point_cost = 0
ability_flags = PSI_FLAG_FOUNDATIONAL
spell_path = /obj/item/spell/commune
/obj/item/spell/commune
name = "commune"
desc = "Déjà-vu."
icon_state = "overload"
item_icons = null
cast_methods = CAST_RANGED|CAST_MELEE|CAST_USE
aspect = ASPECT_PSIONIC
cooldown = 10
psi_cost = 15
//For shared/remote-examine Communes, similar to Show-Held-Object
var/object
var/note //desc
var/image //chat icon
var/warpdesc //Custom text added right after normal note desc
var/popup //Remote popups like paper & photos
var/obj/item/photo/psiphoto
var/savedscribble
var/realscribble
var/obj/item/paper/psipaper
var/savedinfo
var/realinfo
/obj/item/spell/commune/proc/ShowSavedScribble(mob/target, saved, psi_deaf = FALSE)
//Likely a better way, but the trick is quickly swapping real and saved
realscribble = "[psiphoto.scribble]"
psiphoto.scribble = "[(saved)]"
if(psi_deaf && prob(60)) //Psi-deaf targets that do receieve a photo, have it in black & white more than not
var/icon/realimg = icon(psiphoto.img)
psiphoto.img.MapColors(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0))
psiphoto.show(target)
psiphoto.img = icon(realimg)
to_chat(target, SPAN_ALIEN("...Even the colors are half-formed."))
else if(psi_deaf && prob(60)) //Or with random colors
var/icon/realimg = icon(psiphoto.img)
psiphoto.img.MapColors(rgb(rand(0,255), rand(0,255), rand(0,255)), rgb(rand(0,255), rand(0, 255), rand(0,255)), rgb(rand(0,255), rand(0,255), rand(0,255)), rgb(rand(0,255), rand(0,255), rand(0,255)))
psiphoto.show(target)
psiphoto.img = icon(realimg)
to_chat(target, SPAN_ALIEN("...Even the colors are off."))
else if(prob(10) && (SSatlas.current_sector.name in list(SECTOR_LEMURIAN_SEA, SECTOR_LEMURIAN_SEA_FAR))) //Lemurian Sea jumpscare!
var/icon/realimg = icon(psiphoto.img)
psiphoto.img.MapColors(rgb(0,0,0), rgb(0,0,0), rgb(0,0,0), rgb(0,0,0))
target.eye_blind += 20
psiphoto.show(target)
psiphoto.img = icon(realimg)
to_chat(target, SPAN_ALIEN("An eerie void momentarily blanks your perception."))
sleep(50)
target.eye_blind -= 20
else
psiphoto.show(target)
psiphoto.scribble = "[realscribble]"
/obj/item/spell/commune/proc/ShowSavedInfo(var/mob/target, saved)
realinfo = "[psipaper.info]"
psipaper.info = "[(saved)]"
psipaper.show_content(target)
psipaper.info = "[realinfo]"
/obj/item/spell/commune/proc/TranslateWritten(mob/target, mob/user, can_read = TRUE)
//This is just parse_languages() & paperwork reuse
var/regex/written_lang_regex = new(@"(\[lang=([#_a-zA-Z0-9\^]{1})])(.*?)(\[\/lang])", "g")
while(written_lang_regex.Find(psipaper.info))
var/datum/language/L = GLOB.language_keys[written_lang_regex.group[2]]
if(!L || !L.written_style)
continue
var/content = written_lang_regex.group[3]
var/user_understands = user.say_understands(null, L)
if(!user_understands)
content = L.scramble(content)
psipaper.info = replacetext(psipaper.info, written_lang_regex.match, "<span class='[L.written_style] [user_understands ? "understood" : "scramble"]'>[L.short && user_understands ? "([L.short]) [content]" : content]</span>")
simple_asset_ensure_is_sent(target, /datum/asset/simple/paper)
var/datum/browser/paper_win = new(target, "Mental Image", null, 450, 500, null, TRUE)
paper_win.set_content("<head><title>(Mental Image)</title><style>body {background-color: [color];}</style></head><body>[can_read ? psipaper.info : stars(psipaper.info)][psipaper.stamps]</body>")
paper_win.add_stylesheet("paper_languages", 'html/browser/paper_languages.css')
paper_win.open()
/obj/item/spell/commune/Destroy()
object = null
note = null
psiphoto = null
psipaper = null
return ..()
/obj/item/spell/commune/on_use_cast(mob/user)
. = ..()
if(warpdesc)
warpdesc = null
to_chat(user, SPAN_NOTICE("You rein back to your initial mental image of \the [object], clearing the warped additions."))
else if(warpdesc == null && (object))
warpdesc = tgui_input_text(user, "How would you warp the mental image of \the [object]? This will only add to its existing description, never override or delete.", "Warp Mental Image", "", MAX_MESSAGE_LEN, TRUE)
if(!warpdesc)
warpdesc = null
return
to_chat(user, SPAN_NOTICE("You rethink your mental image of \the [object], warping in additions to its details."))
else return
/obj/item/spell/commune/on_melee_cast(atom/hit_atom, mob/living/user, def_zone)
. = ..()
if(!.)
return
if(object)
to_chat(user, SPAN_NOTICE("You already have a mental image in mind of \the [object]."))
return
else if(istype(hit_atom, /obj/item/photo) && !object)
psiphoto = hit_atom
object = "[hit_atom.name]"
note = ("[hit_atom.desc]" + " [psiphoto.picture_desc]")
savedscribble = "[psiphoto.scribble]" //To later compare & prevent displaying a new scribble the psion never saved
image = icon2html(psiphoto, user)
popup = TRUE
to_chat(user, SPAN_NOTICE("You note and refine a mental replica of \the [object] for sending."))
else if(istype(hit_atom, /obj/item/paper) && !object)
psipaper = hit_atom
object = "[hit_atom.name]"
note = "[hit_atom.desc]"
savedinfo = "[psipaper.info]"
image = icon2html(psipaper, user)
popup = TRUE
to_chat(user, SPAN_NOTICE("You note and refine a mental copy of \the [object] for sending."))
else if(isobj(hit_atom) && !object)
object = "[hit_atom.name]" //Save a snapshot of its info so they don't auto-update.
note = ("[hit_atom.desc]" + " [hit_atom.desc_feedback]")
image = icon2html(hit_atom, user)
to_chat(user, SPAN_NOTICE("You note and refine a mental image of \the [object] for sending."))
/obj/item/spell/commune/on_ranged_cast(atom/hit_atom, mob/user)
. = ..()
if(!.)
return
commune(hit_atom, user)
/obj/item/spell/commune/proc/commune(atom/hit_atom, mob/user)
if(!isliving(hit_atom))
return
var/mob/living/target = hit_atom
if(target.stat == DEAD)
to_chat(user, SPAN_WARNING("Not even a psion of your level can speak to the dead."))
return
var/psi_blocked = target.is_psi_blocked(user, FALSE)
if(psi_blocked)
to_chat(user, psi_blocked)
return
//Checks psi-sensitivity for remote item examines, because normal Commune gets blocked
if(object)
to_chat(user, SPAN_CULT("You psionically project to [target]: [image] [object]\n[note]" + " [warpdesc]"))
var/mob/living/carbon/human/T = target
var/target_sensitivity = T.check_psi_sensitivity()
if(target_sensitivity >= 1) //Small detail, psions can subtly tell apart warped descriptions easier with the new line.
to_chat(T, SPAN_NOTICE("<i>[user] blinks, their eyes briefly developing an unnatural shine.</i>"))
to_chat(T, SPAN_CULT("What must be [user]'s mental image of \a [image] [object] appears in your mind.\n[note]\n[warpdesc]"))
if(popup)
if(psipaper)
realinfo = "[psipaper.info]"
psipaper.info = "[savedinfo]"
TranslateWritten(T, user) //High-sensitives get papers translated to what the psion knew
to_chat(T, SPAN_NOTICE("You sense [user]'s psyche link so well, you understand the sheet just as they do."))
psipaper.info = "[realinfo]"
else
ShowSavedScribble(T, savedscribble)
else if(target_sensitivity >= 0) //Info span so it feels like an actual examine at a glance
to_chat(T, SPAN_INFO("<b>A mental image of \a [image] [object] suddenly builds in your mind.</b>\n[note]" + " [warpdesc]"))
if(popup)
if(psipaper)
ShowSavedInfo(T, savedinfo)
else
ShowSavedScribble(T, savedscribble)
else //60% chance low-targets don't even get the icon. 35% (per point) of the object's name is scrambled, but base desc can still clue the item
var/scrmbldobject = stars(object, (abs(target_sensitivity) * 35))
var/scrmbldnote = stars(note, (abs(target_sensitivity) * 25))
var/scrmbldwarp = stars(warpdesc, (abs(target_sensitivity) * 45)) //Warped descriptions are even more abnormal, so extra scrambled
to_chat(T, SPAN_ALIEN("<b>A mental image of \a [prob(40)?"[image] ":""][scrmbldobject] attempts to form in your mind.</b>\n[scrmbldnote]" + " [scrmbldwarp]"))
if(prob(45) && popup)
if(psipaper)
var/scrmbldinfo = stars(psipaper.info, (abs(target_sensitivity) * 35)) //upped ratio as pencode itself gets scrambled too
ShowSavedInfo(T, scrmbldinfo) //that technically makes text in other langs slightly decipherable ICly, buuut...
to_chat(T, SPAN_ALIEN("It's so nonsensical, you can't tell what's even real.")) //plant doubt by saying it's nonsense.
else
var/scrmbldscribble = stars(psiphoto.scribble, (abs(target_sensitivity) * 25))
ShowSavedScribble(T, scrmbldscribble, TRUE)
log_say("[key_name(user)] communed to [key_name(target)]: [image] [object]\n[note]" + " [warpdesc]")
for(var/mob/M as anything in GLOB.player_list)
if(M.stat == DEAD && M.client.prefs.toggles & CHAT_GHOSTEARS)
to_chat(M, "<span class='notice'>[user] psionically says to [target]:</span> [image] [object]\n[note]" + " [warpdesc]")
return
var/text = tgui_input_text(user, "What would you like to say?", "Commune", "", MAX_MESSAGE_LEN, TRUE)
if(!text)
return
text = formalize_text(text)
if(target.stat == DEAD)
to_chat(user, SPAN_WARNING("Not even a psion of your level can speak to the dead."))
return
log_say("[key_name(user)] communed to [key_name(target)]: [text]")
to_chat(user, SPAN_CULT("You psionically say to [target]: [text]"))
for (var/mob/M in GLOB.player_list)
if (istype(M, /mob/abstract/new_player))
continue
else if(M.stat == DEAD && M.client.prefs.toggles & CHAT_GHOSTEARS)
to_chat(M, "<span class='notice'>[user] psionically says to [target]:</span> [text]")
var/mob/living/carbon/human/H = target
var/target_sensitivity = H.check_psi_sensitivity()
if(target_sensitivity >= 1)
// Augmented case for anyone with enhancements to their psi-sensitivity.
to_chat(H, SPAN_NOTICE("<i>[user] blinks, their eyes briefly developing an unnatural shine.</i>"))
to_chat(H, SPAN_CULT("<b>You instinctively sense [user] passing a thought into your mind:</b> [text]"))
else if (target_sensitivity >= 0)
// Standard case for most characters.
to_chat(H, SPAN_ALIEN("<b>A thought from outside your consciousness slips into your mind:</b> [text]"))
else
// Negative sensitivity case, message arrives scrambled.
// 25% of the message is scrambled per negative point of psi-sensitivity. Allowing fractional points.
var/scrambled_message = stars(text, (abs(target_sensitivity) * 25))
to_chat(H, SPAN_ALIEN("<b>A half-formed thought passes through your mind:</b> [scrambled_message]"))