Adds eye contact (#52108)

* doesn't do it for me

* signal and hijinx

* creep stare

* less lazy code
This commit is contained in:
Ryll Ryll
2020-07-11 13:39:57 -04:00
committed by GitHub
parent 24bc8c5f9c
commit 4a481bc861
7 changed files with 99 additions and 4 deletions

View File

@@ -312,6 +312,10 @@
#define COMSIG_MOB_THROW "mob_throw"
///from base of /mob/verb/examinate(): (atom/target)
#define COMSIG_MOB_EXAMINATE "mob_examinate"
///from /mob/living/handle_eye_contact(): (mob/living/other_mob)
#define COMSIG_MOB_EYECONTACT "mob_eyecontact"
/// return this if you want to block printing this message to this person, if you want to print your own (does not affect the other person's message)
#define COMSIG_BLOCK_EYECONTACT (1<<0)
///from base of /mob/update_sight(): ()
#define COMSIG_MOB_UPDATE_SIGHT "mob_update_sight"
////from /mob/living/say(): ()

View File

@@ -366,5 +366,7 @@
/// If you examine the same atom twice in this timeframe, we call examine_more() instead of examine()
#define EXAMINE_MORE_TIME 1 SECONDS
/// How far away you can be to make eye contact with someone while examining
#define EYE_CONTACT_RANGE 5
#define SILENCE_RANGED_MESSAGE (1<<0)

View File

@@ -29,6 +29,7 @@
owner.mind.add_antag_datum(/datum/antagonist/obsessed)
antagonist = owner.mind.has_antag_datum(/datum/antagonist/obsessed)
antagonist.trauma = src
RegisterSignal(obsession, COMSIG_MOB_EYECONTACT, .proc/stare)
..()
//antag stuff//
antagonist.forge_objectives(obsession.mind)
@@ -65,6 +66,8 @@
/datum/brain_trauma/special/obsessed/on_lose()
..()
owner.mind.remove_antag_datum(/datum/antagonist/obsessed)
if(obsession)
UnregisterSignal(obsession, COMSIG_MOB_EYECONTACT)
/datum/brain_trauma/special/obsessed/handle_speech(datum/source, list/speech_args)
if(!viewing)
@@ -100,6 +103,13 @@
fail = TRUE
return fail
// if the creep examines first, then the obsession examines them, have a 50% chance to possibly blow their cover. wearing a mask avoids this risk
/datum/brain_trauma/special/obsessed/proc/stare(datum/source, mob/living/examining_mob, triggering_examiner)
if(examining_mob != owner || !triggering_examiner || prob(50))
return
addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, obsession, "<span class='warning'>You catch [examining_mob] staring at you...</span>", 3))
return COMSIG_BLOCK_EYECONTACT
/datum/brain_trauma/special/obsessed/proc/find_obsession()
var/chosen_victim

View File

@@ -96,7 +96,6 @@
/datum/quirk/brainproblems/on_process()
if(HAS_TRAIT(quirk_holder, TRAIT_TUMOR_SUPPRESSED))
return
quirk_holder.adjustOrganLoss(ORGAN_SLOT_BRAIN, 0.2)
/datum/quirk/deafness
@@ -484,6 +483,13 @@
hardcore_value = 4
var/dumb_thing = TRUE
/datum/quirk/social_anxiety/add()
RegisterSignal(quirk_holder, COMSIG_MOB_EYECONTACT, .proc/eye_contact)
RegisterSignal(quirk_holder, COMSIG_MOB_EXAMINATE, .proc/looks_at_floor)
/datum/quirk/social_anxiety/remove()
UnregisterSignal(quirk_holder, list(COMSIG_MOB_EYECONTACT, COMSIG_MOB_EXAMINATE))
/datum/quirk/social_anxiety/on_process()
var/nearby_people = 0
for(var/mob/living/carbon/human/H in oview(3, quirk_holder))
@@ -501,6 +507,43 @@
if(prob(1))
new/obj/item/reagent_containers/food/snacks/spaghetti/pastatomato(get_turf(H)) //now that's what I call spaghetti code
// small chance to make eye contact with inanimate objects/mindless mobs because of nerves
/datum/quirk/social_anxiety/proc/looks_at_floor(datum/source, atom/A)
var/mob/living/mind_check = A
if(prob(85) || (istype(mind_check) && mind_check.mind))
return
addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, quirk_holder, "<span class='smallnotice'>You make eye contact with [A].</span>"), 3)
/datum/quirk/social_anxiety/proc/eye_contact(datum/source, mob/living/other_mob, triggering_examiner)
if(prob(75))
return
var/msg
if(triggering_examiner)
msg = "You make eye contact with [other_mob], "
else
msg = "[other_mob] makes eye contact with you, "
switch(rand(1,3))
if(1)
quirk_holder.Jitter(10)
msg += "causing you to start fidgeting!"
if(2)
quirk_holder.stuttering = max(3, quirk_holder.stuttering)
msg += "causing you to start stuttering!"
if(3)
quirk_holder.Stun(2 SECONDS)
msg += "causing you to freeze up!"
SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, "anxiety_eyecontact", /datum/mood_event/anxiety_eyecontact)
addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, quirk_holder, "<span class='userdanger'>[msg]</span>"), 3) // so the examine signal has time to fire and this will print after
return COMSIG_BLOCK_EYECONTACT
/datum/mood_event/anxiety_eyecontact
description = "<span class='warning'>Sometimes eye contact makes me so nervous...</span>\n"
mood_change = -5
timeout = 3 MINUTES
/datum/quirk/junkie
name = "Junkie"
desc = "You can't get enough of hard drugs."

View File

@@ -1184,3 +1184,6 @@
S.generate(BP, W)
S.fake = TRUE
QDEL_NULL(W)
/mob/living/carbon/is_face_visible()
return !(wear_mask?.flags_inv & HIDEFACE) && !(head?.flags_inv & HIDEFACE)

View File

@@ -1556,3 +1556,7 @@
switch(stat) //Current stat.
if(UNCONSCIOUS)
become_blind(UNCONSCIOUS_BLIND)
/// Only defined for carbons who can wear masks and helmets, we just assume other mobs have visible faces
/mob/living/proc/is_face_visible()
return TRUE

View File

@@ -419,10 +419,12 @@
var/list/result
if(client)
LAZYINITLIST(client.recent_examines)
if(isnull(client.recent_examines[A]) || client.recent_examines[A] < world.time) // originally this wasn't an assoc list, but sometimes the timer failed and atoms stayed in a client's recent_examines, so we check here manually
client.recent_examines[A] = world.time + EXAMINE_MORE_TIME
if(isnull(client.recent_examines[A]) || client.recent_examines[A] < world.time)
result = A.examine(src)
client.recent_examines[A] = world.time + EXAMINE_MORE_TIME // set the value to when the examine cooldown ends
RegisterSignal(A, COMSIG_PARENT_QDELETING, .proc/clear_from_recent_examines, override=TRUE) // to flush the value if deleted early
addtimer(CALLBACK(src, .proc/clear_from_recent_examines, A), EXAMINE_MORE_TIME)
handle_eye_contact(A)
else
result = A.examine_more(src)
else
@@ -432,10 +434,37 @@
SEND_SIGNAL(src, COMSIG_MOB_EXAMINATE, A)
/mob/proc/clear_from_recent_examines(atom/A)
if(QDELETED(A) || !client)
if(!client)
return
UnregisterSignal(A, COMSIG_PARENT_QDELETING)
LAZYREMOVE(client.recent_examines, A)
/**
* handle_eye_contact() is called when we examine() something. If we examine an alive mob with a mind who has examined us in the last second within 5 tiles, we make eye contact!
*
* Note that if either party has their face obscured, the other won't get the notice about the eye contact
* Also note that examine_more() doesn't proc this or extend the timer, just because it's simpler this way and doesn't lose much.
* The nice part about relying on examining is that we don't bother checking visibility, because we already know they were both visible to each other within the last second, and the one who triggers it is currently seeing them
*/
/mob/proc/handle_eye_contact(mob/living/examined_mob)
return
/mob/living/handle_eye_contact(mob/living/examined_mob)
if(!istype(examined_mob) || src == examined_mob || examined_mob.stat >= UNCONSCIOUS || !client || !examined_mob.client?.recent_examines || !(src in examined_mob.client.recent_examines))
return
if(get_dist(src, examined_mob) > EYE_CONTACT_RANGE)
return
// check to see if their face is blocked or, if not, a signal blocks it
if(examined_mob.is_face_visible() && SEND_SIGNAL(src, COMSIG_MOB_EYECONTACT, examined_mob, TRUE) != COMSIG_BLOCK_EYECONTACT)
var/msg = "<span class='smallnotice'>You make eye contact with [examined_mob].</span>"
addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, src, msg), 3) // so the examine signal has time to fire and this will print after
if(is_face_visible() && SEND_SIGNAL(examined_mob, COMSIG_MOB_EYECONTACT, src, FALSE) != COMSIG_BLOCK_EYECONTACT)
var/msg = "<span class='smallnotice'>[src] makes eye contact with you.</span>"
addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, examined_mob, msg), 3)
/**
* Point at an atom
*