From 02f46322ad53876d80dd87a98bf29f624021628f Mon Sep 17 00:00:00 2001 From: Timothy Teakettle <59849408+timothyteakettle@users.noreply.github.com> Date: Thu, 9 Jul 2020 18:32:45 +0100 Subject: [PATCH] port examine --- code/__DEFINES/dcs/signals.dm | 14 ++-- code/__DEFINES/mobs.dm | 6 ++ code/datums/mind.dm | 2 + code/datums/traits/negative.dm | 46 +++++++++++++ code/game/atoms.dm | 14 ++++ code/game/machinery/computer/arcade/battle.dm | 9 +++ code/game/objects/items/cards_ids.dm | 20 ++++++ code/modules/client/client_defines.dm | 2 + code/modules/mob/mob.dm | 69 ++++++++++++++++--- code/modules/paperwork/paper.dm | 3 + 10 files changed, 172 insertions(+), 13 deletions(-) diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index d9a6452f38..5c28694005 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -37,12 +37,14 @@ #define COMSIG_ATOM_HULK_ATTACK "hulk_attack" //from base of atom/attack_hulk(): (/mob/living/carbon/human) #define COMSIG_ATOM_ATTACK_ANIMAL "attack_animal" //from base of atom/animal_attack(): (/mob/user) #define COMSIG_PARENT_EXAMINE "atom_examine" //from base of atom/examine(): (/mob, list/examine_return_text) -#define COMSIG_ATOM_GET_EXAMINE_NAME "atom_examine_name" //from base of atom/get_examine_name(): (/mob, list/overrides) +///from base of atom/get_examine_name(): (/mob, list/overrides) +#define COMSIG_ATOM_GET_EXAMINE_NAME "atom_examine_name" +#define COMSIG_PARENT_EXAMINE_MORE "atom_examine_more" ///from base of atom/examine_more(): (/mob) //Positions for overrides list - #define EXAMINE_POSITION_ARTICLE 1 - #define EXAMINE_POSITION_BEFORE 2 + #define EXAMINE_POSITION_ARTICLE (1<<0) + #define EXAMINE_POSITION_BEFORE (1<<1) //End positions - #define COMPONENT_EXNAME_CHANGED 1 + #define COMPONENT_EXNAME_CHANGED (1<<0) #define COMSIG_ATOM_UPDATE_ICON "atom_update_icon" //from base of atom/update_icon(): () #define COMSIG_ATOM_NO_UPDATE_ICON_STATE 1 #define COMSIG_ATOM_NO_UPDATE_OVERLAYS 2 @@ -179,6 +181,10 @@ #define COMSIG_MOB_THROW "mob_throw" //from base of /mob/throw_item(): (atom/target) #define COMSIG_MOB_KEY_CHANGE "mob_key_change" //from base of /mob/transfer_ckey(): (new_character, old_character) #define COMSIG_MOB_PRE_PLAYER_CHANGE "mob_pre_player_change" //sent to the target mob from base of /mob/transfer_ckey() and /mind/transfer_to(): (our_character, their_character) +///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) // #define COMPONENT_STOP_MIND_TRANSFER 1 #define COMSIG_MOB_UPDATE_SIGHT "mob_update_sight" //from base of /mob/update_sight(): () #define COMSIG_MOB_ON_NEW_MIND "mob_on_new_mind" //called when a new mind is assigned to a mob: () diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index fa7758be1f..42139cdeda 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -310,3 +310,9 @@ #define FOV_90_DEGREES 90 #define FOV_180_DEGREES 180 #define FOV_270_DEGREES 270 + +/// How far away you can be to make eye contact with someone while examining +#define EYE_CONTACT_RANGE 5 + +/// If you examine the same atom twice in this timeframe, we call examine_more() instead of examine() +#define EXAMINE_MORE_TIME 1 SECONDS \ No newline at end of file diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 8ac9bc4276..d02249fb38 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -124,6 +124,8 @@ transfer_martial_arts(new_character) if(active || force_key_move) new_character.key = key //now transfer the key to link the client to our new body + if(new_character.client) + LAZYCLEARLIST(new_character.client.recent_examines) current.update_atom_languages() //CIT CHANGE - makes arousal update when transfering bodies diff --git a/code/datums/traits/negative.dm b/code/datums/traits/negative.dm index 9c2128163f..40b71eec0a 100644 --- a/code/datums/traits/negative.dm +++ b/code/datums/traits/negative.dm @@ -314,6 +314,13 @@ GLOBAL_LIST_EMPTY(family_heirlooms) medical_record_text = "Patient is usually anxious in social encounters and prefers to avoid them." 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)) @@ -330,6 +337,45 @@ GLOBAL_LIST_EMPTY(family_heirlooms) dumb_thing = FALSE //only once per life if(prob(1)) new/obj/item/reagent_containers/food/snacks/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, "You make eye contact with [A]."), 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, "[msg]"), 3) // so the examine signal has time to fire and this will print after + return COMSIG_BLOCK_EYECONTACT + +/datum/mood_event/anxiety_eyecontact + description = "Sometimes eye contact makes me so nervous...\n" + mood_change = -5 + timeout = 3 MINUTES /datum/quirk/phobia name = "Phobia" diff --git a/code/game/atoms.dm b/code/game/atoms.dm index bbd38e9021..5d29ef1598 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -366,6 +366,20 @@ SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, .) +/** + * Called when a mob examines (shift click or verb) this atom twice (or more) within EXAMINE_MORE_TIME (default 1.5 seconds) + * + * This is where you can put extra information on something that may be superfluous or not important in critical gameplay + * moments, while allowing people to manually double-examine to take a closer look + * + * Produces a signal [COMSIG_PARENT_EXAMINE_MORE] + */ +/atom/proc/examine_more(mob/user) + . = list() + SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE_MORE, user, .) + if(!LAZYLEN(.)) // lol ..length + return list("You examine [src] closer, but find nothing of interest...") + /// Updates the icon of the atom /atom/proc/update_icon() // I expect we're going to need more return flags and options in this proc diff --git a/code/game/machinery/computer/arcade/battle.dm b/code/game/machinery/computer/arcade/battle.dm index b906b1afb5..fc99edd3eb 100644 --- a/code/game/machinery/computer/arcade/battle.dm +++ b/code/game/machinery/computer/arcade/battle.dm @@ -184,6 +184,15 @@ blocked = FALSE return +/obj/machinery/computer/arcade/battle/examine_more(mob/user) + to_chat(user, "Scribbled on the side of the Arcade Machine you notice some writing...\ + \nmagical -> >=50 power\ + \nsmart -> defend, defend, light attack\ + \nshotgun -> defend, defend, power attack\ + \nshort temper -> counter, counter, counter\ + \npoisonous -> light attack, light attack, light attack\ + \nchonker -> power attack, power attack, power attack") + return ..() /obj/machinery/computer/arcade/battle/emag_act(mob/user) . = ..() diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm index 58d907e14b..60e869815b 100644 --- a/code/game/objects/items/cards_ids.dm +++ b/code/game/objects/items/cards_ids.dm @@ -113,6 +113,26 @@ . = ..() . += "It has [uses ? uses : "no"] charges left." +/obj/item/card/id/examine_more(mob/user) + var/list/msg = list("You examine [src] closer, and note the following...") + + if(mining_points) + msg += "There's [mining_points] mining equipment redemption point\s loaded onto this card." + if(registered_account) + msg += "The account linked to the ID belongs to '[registered_account.account_holder]' and reports a balance of [registered_account.account_balance] cr." + if(registered_account.account_job) + var/datum/bank_account/D = SSeconomy.get_dep_account(registered_account.account_job.paycheck_department) + if(D) + msg += "The [D.account_holder] reports a balance of [D.account_balance] cr." + msg += "Alt-Click the ID to pull money from the linked account in the form of holochips." + msg += "You can insert credits into the linked account by pressing holochips, cash, or coins against the ID." + if(registered_account.account_holder == user.real_name) + msg += "If you lose this ID card, you can reclaim your account by Alt-Clicking a blank ID card while holding it and entering your account ID number." + else + msg += "There is no registered account linked to this card. Alt-Click to add one." + + return msg + /obj/item/card/emag/attackby(obj/item/W, mob/user, params) if(istype(W, /obj/item/emagrecharge)) var/obj/item/emagrecharge/ER = W diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index 7f670e756f..7f1ba86ff5 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -119,6 +119,8 @@ /// Messages currently seen by this client var/list/seen_messages + ///A lazy list of atoms we've examined in the last EXAMINE_MORE_TIME (default 1.5) seconds, so that we will call [atom/proc/examine_more()] instead of [atom/proc/examine()] on them when examining + var/list/recent_examines ///When was the last time we warned them about not cryoing without an ahelp, set to -5 minutes so that rounstart cryo still warns var/cryo_warned = -5 MINUTES diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index fd9afc91cd..dda7d68046 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -288,8 +288,14 @@ mob/visible_message(message, self_message, blind_message, vision_distance = DEFA . = view(dist, src) SEND_SIGNAL(src, COMSIG_MOB_FOV_VIEW, .) -//mob verbs are faster than object verbs. See https://secure.byond.com/forum/?post=1326139&page=2#comment8198716 for why this isn't atom/verb/examine() -/mob/verb/examinate(atom/A as mob|obj|turf in fov_view()) //It used to be oview(12), but I can't really say why +/** + * Examine a mob + * + * mob verbs are faster than object verbs. See + * [this byond forum post](https://secure.byond.com/forum/?post=1326139&page=2#comment8198716) + * for why this isn't atom/verb/examine() + */ +/mob/verb/examinate(atom/A as mob|obj|turf in view()) //It used to be oview(12), but I can't really say why set name = "Examine" set category = "IC" @@ -297,18 +303,63 @@ mob/visible_message(message, self_message, blind_message, vision_distance = DEFA // shift-click catcher may issue examinate() calls for out-of-sight turfs return - if(is_blind(src)) + if(is_blind()) to_chat(src, "Something is there but you can't see it!") return face_atom(A) - var/flags = SEND_SIGNAL(src, COMSIG_MOB_EXAMINATE, A) - if(flags & COMPONENT_DENY_EXAMINATE) - if(flags & COMPONENT_EXAMINATE_BLIND) - to_chat(src, "Something is there but you can't see it!") - return - var/list/result = A.examine(src) + var/list/result + if(client) + LAZYINITLIST(client.recent_examines) + 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 + result = A.examine(src) // if a tree is examined but no client is there to see it, did the tree ever really exist? + to_chat(src, result.Join("\n")) + SEND_SIGNAL(src, COMSIG_MOB_EXAMINATE, A) + +/mob/proc/clear_from_recent_examines(atom/A) + 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 + + var/mob/living/carbon/examined_carbon = examined_mob + // check to see if their face is blocked (or if they're not a carbon, in which case they can't block their face anyway) + if(!istype(examined_carbon) || (!(examined_carbon.wear_mask && examined_carbon.wear_mask.flags_inv & HIDEFACE) && !(examined_carbon.head && examined_carbon.head.flags_inv & HIDEFACE))) + if(SEND_SIGNAL(src, COMSIG_MOB_EYECONTACT, examined_mob, TRUE) != COMSIG_BLOCK_EYECONTACT) + var/msg = "You make eye contact with [examined_mob]." + addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, src, msg), 3) // so the examine signal has time to fire and this will print after + + var/mob/living/carbon/us_as_carbon = src // i know >casting as subtype, but this isn't really an inheritable check + if(!istype(us_as_carbon) || (!(us_as_carbon.wear_mask && us_as_carbon.wear_mask.flags_inv & HIDEFACE) && !(us_as_carbon.head && us_as_carbon.head.flags_inv & HIDEFACE))) + if(SEND_SIGNAL(examined_mob, COMSIG_MOB_EYECONTACT, src, FALSE) != COMSIG_BLOCK_EYECONTACT) + var/msg = "[src] makes eye contact with you." + addtimer(CALLBACK(GLOBAL_PROC, .proc/to_chat, examined_mob, msg), 3) //same as above //note: ghosts can point, this is intended diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index aed3ff5848..4afc2f1d5b 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -96,6 +96,9 @@ else . += "You're too far away to read it!" +/obj/item/paper/examine_more(mob/user) + ui_interact(user) + /obj/item/paper/proc/show_content(mob/user) user.examinate(src)