diff --git a/beestation.dme b/beestation.dme index b821820446fe3..c42e283f1e289 100644 --- a/beestation.dme +++ b/beestation.dme @@ -340,6 +340,7 @@ #include "code\_globalvars\lists\client.dm" #include "code\_globalvars\lists\flavor_misc.dm" #include "code\_globalvars\lists\icons.dm" +#include "code\_globalvars\lists\keybindings.dm" #include "code\_globalvars\lists\maintenance_loot.dm" #include "code\_globalvars\lists\mapping.dm" #include "code\_globalvars\lists\mobs.dm" @@ -917,6 +918,7 @@ #include "code\datums\keybinding\carbon.dm" #include "code\datums\keybinding\client.dm" #include "code\datums\keybinding\communication.dm" +#include "code\datums\keybinding\emote.dm" #include "code\datums\keybinding\human.dm" #include "code\datums\keybinding\keybinding.dm" #include "code\datums\keybinding\living.dm" diff --git a/code/__DEFINES/cooldowns.dm b/code/__DEFINES/cooldowns.dm index 4a0502d319458..b9568ebdbf5db 100644 --- a/code/__DEFINES/cooldowns.dm +++ b/code/__DEFINES/cooldowns.dm @@ -48,6 +48,8 @@ #define COOLDOWN_MECHA_MELEE_ATTACK "mecha_melee" #define COOLDOWN_MECHA_SMOKE "mecha_smoke" +#define COOLDOWN_EMOTE_WINDOW "emote_window" + //TIMER COOLDOWN MACROS #define COMSIG_CD_STOP(cd_index) "cooldown_[cd_index]" diff --git a/code/__DEFINES/keybinding.dm b/code/__DEFINES/keybinding.dm index 7fad19e2aa6d3..17a654b8fc4c4 100644 --- a/code/__DEFINES/keybinding.dm +++ b/code/__DEFINES/keybinding.dm @@ -2,6 +2,7 @@ //General #define COMSIG_KB_ACTIVATED (1<<0) +#define COMSIG_KB_EMOTE "keybinding_emote_down" //Admin #define COMSIG_KB_ADMIN_ASAY_DOWN "keybinding_admin_asay_down" diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index 9198ea42d2bb7..b65a99f086ba3 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -73,13 +73,7 @@ GLOB.hair_gradients_list[H.name] = H // Keybindings - for(var/KB in subtypesof(/datum/keybinding)) - var/datum/keybinding/keybinding = KB - if(!initial(keybinding.keys) || !initial(keybinding.keybind_signal)) - continue - var/datum/keybinding/instance = new keybinding - GLOB.keybindings_by_name[instance.name] = instance - LAZYADD(GLOB.keybindings_by_name_to_key[instance.name], LAZYCOPY(instance.keys)) + init_keybindings() init_crafting_recipes(GLOB.crafting_recipes) diff --git a/code/_globalvars/lists/keybindings.dm b/code/_globalvars/lists/keybindings.dm new file mode 100644 index 0000000000000..2bf6abc648409 --- /dev/null +++ b/code/_globalvars/lists/keybindings.dm @@ -0,0 +1,22 @@ +/// Creates and sorts all the keybinding datums +/proc/init_keybindings() + for(var/KB in subtypesof(/datum/keybinding)) + var/datum/keybinding/keybinding = KB + if(!initial(keybinding.keybind_signal) || !initial(keybinding.name)) + continue + add_keybinding(new keybinding) + init_emote_keybinds() + +/// Adds an instanced keybinding to the global tracker +/proc/add_keybinding(datum/keybinding/instance) + GLOB.keybindings_by_name[instance.name] = instance + LAZYADD(GLOB.keybindings_by_name_to_key[instance.name], LAZYCOPY(instance.keys)) + +/proc/init_emote_keybinds() + for(var/i in subtypesof(/datum/emote)) + var/datum/emote/faketype = i + if(!initial(faketype.key)) + continue + var/datum/keybinding/emote/emote_kb = new + emote_kb.link_to_emote(faketype) + add_keybinding(emote_kb) diff --git a/code/datums/brain_damage/split_personality.dm b/code/datums/brain_damage/split_personality.dm index d88793bc03b9a..c193fd55f9845 100644 --- a/code/datums/brain_damage/split_personality.dm +++ b/code/datums/brain_damage/split_personality.dm @@ -170,7 +170,7 @@ CREATION_TEST_IGNORE_SUBTYPES(/mob/living/split_personality) return FALSE /mob/living/split_personality/emote(act, m_type = null, message = null, intentional = FALSE) - return + return FALSE ///////////////BRAINWASHING//////////////////// diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm index 7eeed5c3d7f8d..ce17ae29792a6 100644 --- a/code/datums/emotes.dm +++ b/code/datums/emotes.dm @@ -26,9 +26,21 @@ var/sound /// Volume to play the sound at var/sound_volume = 50 - /// Whether to vary the pitch of the sound played + /// Do we vary the pitch of the sound played var/vary = FALSE var/only_forced_audio = FALSE //can only code call this event instead of the player. + /// The cooldown between the uses of the emote. + var/cooldown = 0.5 SECONDS + /// How long is the shared emote cooldown triggered by this emote? + var/general_emote_audio_cooldown = 7 SECONDS + /// How long is the specific emote cooldown triggered by this emote? + var/specific_emote_audio_cooldown = 10 SECONDS + /// Every time a emote is made, it increases this counter by one. When the integer equals cooldown_integer_ceiling, the mob is forced into a cooldown period + var/cooldown_integer = 0 + /// Maximum amount of emotes that can be made + var/cooldown_integer_ceiling = 3 + /// Does this emote's sound ignore walls? + var/sound_wall_ignore = FALSE // Animated emote stuff // ~~~~~~~~~~~~~~~~~~~ @@ -61,18 +73,27 @@ if(!name) name = key +/** + * Handles the modifications and execution of emotes. + * + * Arguments: + * * user - Person that is trying to send the emote. + * * params - Parameters added after the emote. + * * type_override - Override to the current emote_type. + * * intentional - Bool that says whether the emote was forced (FALSE) or not (TRUE). + * + */ /datum/emote/proc/run_emote(mob/user, params, type_override, intentional = FALSE) SHOULD_CALL_PARENT(TRUE) - if(!can_run_emote(user, TRUE, intentional)) - return FALSE if((emote_type & EMOTE_ANIMATED) && emote_length > 0) var/image/I = image(overlay_icon, user, overlay_icon_state, ABOVE_MOB_LAYER, 0, overlay_x_offset, overlay_y_offset) flick_overlay_view(I, user, emote_length) var/tmp_sound = get_sound(user) - if(tmp_sound && (!only_forced_audio || !intentional)) - playsound(user, tmp_sound, sound_volume, vary) + if(tmp_sound && should_play_sound(user, intentional) && !TIMER_COOLDOWN_CHECK(user, "audible_emote_cooldown") && !TIMER_COOLDOWN_CHECK(user, type)) + run_cooldown_integer(user) + playsound(source = user, soundin = tmp_sound, vol = sound_volume, vary = vary, ignore_walls = sound_wall_ignore) var/msg = select_message_type(user, intentional) if(params && message_param) @@ -86,7 +107,7 @@ I.trigger(key, L) if(!msg) - return TRUE + return user.log_message(msg, LOG_EMOTE) @@ -124,7 +145,7 @@ viewer.show_message("[user] [msg]", MSG_AUDIBLE) else if(is_visual) viewer.show_message("[user] [msg]", MSG_VISUAL) - return TRUE // Early exit so no dchat message + return // Early exit so no dchat message // The emote has some important information, and should always be shown to the user else if(is_important) @@ -168,6 +189,51 @@ if(!ghost?.client.prefs?.read_player_preference(/datum/preference/toggle/chat_ghostsight)) continue to_chat(ghost, "[FOLLOW_LINK(ghost, user)] [dchatmsg]") + return + +/datum/emote/proc/run_cooldown_integer(mob/user) + //If we do one audible emote, then do nothing, we eventually should purge our list + cooldown_integer_window(user) + cooldown_integer += 1 + //debug + //user.balloon_alert(user, "[cooldown_integer]") + check_cooldown_integer(user) + +/datum/emote/proc/check_cooldown_integer(mob/user) + if(cooldown_integer >= cooldown_integer_ceiling) + to_chat(user, "[name] emote limit reached") + TIMER_COOLDOWN_START(user, type, specific_emote_audio_cooldown) + TIMER_COOLDOWN_START(user, "general_emote_audio_cooldown", general_emote_audio_cooldown) + //We used up all our usable emotes, now we set the integer back to zero and wait the long wait + cooldown_integer = initial(cooldown_integer) + +/datum/emote/proc/cooldown_integer_window(mob/user) + if(TIMER_COOLDOWN_CHECK(user, COOLDOWN_EMOTE_WINDOW)) + TIMER_COOLDOWN_START(user, COOLDOWN_EMOTE_WINDOW, 4 SECONDS) + else + //We did a few emotes, but didnt use them all up, reset our integer + cooldown_integer = initial(cooldown_integer) + +/** + * For handling emote cooldown, return true to allow the emote to happen. + * + * Arguments: + * * user - Person that is trying to send the emote. + * * intentional - Bool that says whether the emote was forced (FALSE) or not (TRUE). + * + * Returns FALSE if the cooldown is not over, TRUE if the cooldown is over. + */ +/datum/emote/proc/check_cooldown(mob/user, intentional) + if(!intentional) + return TRUE + + if(user.emotes_used && user.emotes_used[src] + cooldown > world.time) + return FALSE + + if(!user.emotes_used) + user.emotes_used = list() + + user.emotes_used[src] = world.time return TRUE /datum/emote/proc/get_sound(mob/living/user) @@ -208,7 +274,7 @@ /datum/emote/proc/select_param(mob/user, params) return replacetext(message_param, "%t", params) -/datum/emote/proc/can_run_emote(mob/user, status_check = TRUE, intentional = FALSE) +/datum/emote/proc/can_run_emote(mob/user, status_check = TRUE, intentional = FALSE, params) . = TRUE if(!is_type_in_typecache(user, mob_type_allowed_typecache)) return FALSE @@ -237,6 +303,39 @@ if(HAS_TRAIT(L, TRAIT_EMOTEMUTE)) return FALSE +/** + * Check to see if the user should play a sound when performing the emote. + * + * Arguments: + * * user - Person that is doing the emote. + * * intentional - Bool that says whether the emote was forced (FALSE) or not (TRUE). + * + * Returns a bool about whether or not the user should play a sound when performing the emote. + */ +/datum/emote/proc/should_play_sound(mob/user, intentional = FALSE) + if(emote_type & EMOTE_AUDIBLE && !hands_use_check) + if(HAS_TRAIT(user, TRAIT_MUTE)) + return FALSE + if(ishuman(user)) + var/mob/living/carbon/human/loud_mouth = user + if(loud_mouth.mind?.miming) // vow of silence prevents outloud noises + return FALSE + if(!loud_mouth.getorganslot(ORGAN_SLOT_TONGUE)) + return FALSE + + if(only_forced_audio && intentional) + return FALSE + return TRUE + +/** +* Allows the intrepid coder to send a basic emote +* Takes text as input, sends it out to those who need to know after some light parsing +* If you need something more complex, make it into a datum emote +* Arguments: +* * text - The text to send out +* +* Returns TRUE if it was able to run the emote, FALSE otherwise. +*/ /mob/proc/manual_emote(text) //Just override the song and dance . = TRUE if(stat != CONSCIOUS) diff --git a/code/datums/keybinding/_defines.dm b/code/datums/keybinding/_defines.dm index fcca453b332b5..f9bd57f107578 100644 --- a/code/datums/keybinding/_defines.dm +++ b/code/datums/keybinding/_defines.dm @@ -1,4 +1,5 @@ #define CATEGORY_CLIENT "CLIENT" +#define CATEGORY_EMOTE "EMOTE" #define CATEGORY_ADMIN "ADMIN" #define CATEGORY_CARBON "CARBON" #define CATEGORY_HUMAN "HUMAN" @@ -8,11 +9,12 @@ #define CATEGORY_COMMUNICATION "COMMUNICATION" #define WEIGHT_HIGHEST 0 -#define WEIGHT_CLIENT 10 -#define WEIGHT_ADMIN 20 -#define WEIGHT_MOB 30 -#define WEIGHT_LIVING 40 -#define WEIGHT_DEAD 50 -#define WEIGHT_ROBOT 60 +#define WEIGHT_ADMIN 10 +#define WEIGHT_CLIENT 20 +#define WEIGHT_ROBOT 30 +#define WEIGHT_MOB 40 +#define WEIGHT_LIVING 50 +#define WEIGHT_DEAD 60 +#define WEIGHT_EMOTE 70 #define WEIGHT_AI 80 #define WEIGHT_LOWEST 999 diff --git a/code/datums/keybinding/emote.dm b/code/datums/keybinding/emote.dm new file mode 100644 index 0000000000000..6c58aba8733a8 --- /dev/null +++ b/code/datums/keybinding/emote.dm @@ -0,0 +1,17 @@ +/datum/keybinding/emote + category = CATEGORY_EMOTE + weight = WEIGHT_EMOTE + keybind_signal = COMSIG_KB_EMOTE + var/emote_key + +/datum/keybinding/emote/proc/link_to_emote(datum/emote/faketype) + keys = list("Unbound") + emote_key = initial(faketype.key) + name = initial(faketype.key) + full_name = capitalize(initial(faketype.key)) + +/datum/keybinding/emote/down(client/user) + . = ..() + if(.) + return + return user.mob.emote(emote_key, intentional=TRUE) diff --git a/code/modules/mob/camera/camera.dm b/code/modules/mob/camera/camera.dm index 34ff413b6aae9..02213718f884c 100644 --- a/code/modules/mob/camera/camera.dm +++ b/code/modules/mob/camera/camera.dm @@ -28,7 +28,7 @@ return FALSE /mob/camera/emote(act, m_type=1, message = null, intentional = FALSE) - return + return FALSE // Cameras can't fall /mob/camera/has_gravity(turf/T) diff --git a/code/modules/mob/emote.dm b/code/modules/mob/emote.dm index edff4142e2d86..2a3ffed3b9c0e 100644 --- a/code/modules/mob/emote.dm +++ b/code/modules/mob/emote.dm @@ -1,24 +1,32 @@ //The code execution of the emote datum is located at code/datums/emotes.dm /mob/proc/emote(act, m_type, message, intentional = FALSE) - act = LOWER_TEXT(act) var/param = message var/custom_param = findchar(act, " ") if(custom_param) param = copytext(act, custom_param + length(act[custom_param])) act = copytext(act, 1, custom_param) + act = LOWER_TEXT(act) var/list/key_emotes = GLOB.emote_list[act] if(!length(key_emotes)) if(intentional) to_chat(src, "'[act]' emote does not exist. Say *help for a list.") - return - for(var/datum/emote/P in key_emotes) - if(P.run_emote(src, param, m_type, intentional)) - SEND_SIGNAL(src, COMSIG_MOB_EMOTE, P, act, m_type, message, intentional) - return - if(intentional) + return FALSE + var/silenced = FALSE + for(var/datum/emote/emote in key_emotes) + if(!emote.check_cooldown(src, intentional)) + silenced = TRUE + continue + if(!emote.can_run_emote(src, TRUE, intentional, param)) + continue + emote.run_emote(src, param, m_type, intentional) + SEND_SIGNAL(src, COMSIG_MOB_EMOTE, emote, act, m_type, message, intentional) + //SEND_SIGNAL(src, COMSIG_MOB_EMOTED(emote.key)) + return TRUE + if(intentional && !silenced) to_chat(src, "Unusable emote '[act]'. Say *help for a list.") + return FALSE /datum/emote/flip key = "flip" @@ -29,12 +37,33 @@ emote_type = EMOTE_VISIBLE /datum/emote/flip/run_emote(mob/user, params , type_override, intentional) + . = ..() + user.SpinAnimation(7,1) + if(isliving(user)) + var/mob/living/L = user + L.confused += 2 + +/datum/emote/flip/check_cooldown(mob/user, intentional) . = ..() if(.) - user.SpinAnimation(7,1) - if(isliving(user) && intentional) - var/mob/living/L = user - L.confused += 2 + return + if(!can_run_emote(user, intentional=intentional)) + return + if(isliving(user)) + var/mob/living/flippy_mcgee = user + if(prob(20)) + flippy_mcgee.Knockdown(1 SECONDS) + flippy_mcgee.visible_message( + "[flippy_mcgee] attempts to do a flip and falls over, what a doofus!", + "You attempt to do a flip while still off balance from the last flip and fall down!" + ) + if(prob(50)) + flippy_mcgee.adjustBruteLoss(1) + else + flippy_mcgee.visible_message( + "[flippy_mcgee] stumbles a bit after their flip.", + "You stumble a bit from still being off balance from your last flip." + ) /datum/emote/spin key = "spin" @@ -46,19 +75,18 @@ /datum/emote/spin/run_emote(mob/user, params , type_override, intentional) . = ..() - if(.) - user.spin(20, 1) - if(isliving(user) && intentional) - var/mob/living/L = user - L.confused += 2 - if(iscyborg(user) && user.has_buckled_mobs()) - var/mob/living/silicon/robot/R = user - var/datum/component/riding/riding_datum = R.GetComponent(/datum/component/riding) - if(riding_datum) - for(var/mob/M in R.buckled_mobs) - riding_datum.force_dismount(M) - else - R.unbuckle_all_mobs() + user.spin(20, 1) + if(isliving(user)) + var/mob/living/L = user + L.confused += 2 + if(iscyborg(user) && user.has_buckled_mobs()) + var/mob/living/silicon/robot/R = user + var/datum/component/riding/riding_datum = R.GetComponent(/datum/component/riding) + if(riding_datum) + for(var/mob/M in R.buckled_mobs) + riding_datum.force_dismount(M) + else + R.unbuckle_all_mobs() /datum/emote/inhale key = "inhale" diff --git a/code/modules/mob/living/brain/emote.dm b/code/modules/mob/living/brain/emote.dm index 75599e892a25d..c93e3acf4b24a 100644 --- a/code/modules/mob/living/brain/emote.dm +++ b/code/modules/mob/living/brain/emote.dm @@ -3,7 +3,7 @@ mob_type_blacklist_typecache = list() emote_type = EMOTE_AUDIBLE -/datum/emote/brain/can_run_emote(mob/user, status_check = TRUE, intentional) +/datum/emote/brain/can_run_emote(mob/user, status_check = TRUE, intentional, params) . = ..() var/mob/living/brain/B = user if(!istype(B) || (!(B.container && istype(B.container, /obj/item/mmi)))) diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index 953f5ee00dbee..f7890fed9be1a 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -120,13 +120,15 @@ key_third_person = "screams" message = "screams" emote_type = EMOTE_AUDIBLE | EMOTE_VISIBLE + specific_emote_audio_cooldown = 5 SECONDS + cooldown_integer_ceiling = 2 vary = TRUE -/datum/emote/living/carbon/human/scream/get_sound(mob/living/user) - if(!ishuman(user) || user.mind?.miming) +/datum/emote/living/carbon/human/scream/get_sound(mob/living/carbon/human/user) + if(!istype(user)) return - var/mob/living/carbon/H = user - return H.dna?.species?.get_scream_sound(H) + + return user.dna.species.get_scream_sound(user) /datum/emote/living/carbon/human/pale key = "pale" @@ -168,8 +170,6 @@ /datum/emote/living/carbon/human/wag/run_emote(mob/user, params, type_override, intentional) . = ..() - if(!.) - return var/mob/living/carbon/human/H = user var/obj/item/organ/tail/tail = H?.getorganslot(ORGAN_SLOT_TAIL) if(!tail) @@ -177,8 +177,6 @@ tail.toggle_wag(H) /datum/emote/living/carbon/human/wag/can_run_emote(mob/user, status_check = TRUE , intentional) - if(!..()) - return FALSE var/mob/living/carbon/human/H = user return istype(H?.getorganslot(ORGAN_SLOT_TAIL), /obj/item/organ/tail) @@ -197,9 +195,8 @@ /datum/emote/living/carbon/human/wing/run_emote(mob/user, params, type_override, intentional) . = ..() - if(.) - var/mob/living/carbon/human/H = user - H.Togglewings() + var/mob/living/carbon/human/H = user + H.Togglewings() /datum/emote/living/carbon/human/wing/select_message_type(mob/user, intentional) . = ..() @@ -209,9 +206,7 @@ else . = "closes " + message -/datum/emote/living/carbon/human/wing/can_run_emote(mob/user, status_check = TRUE, intentional) - if(!..()) - return FALSE +/datum/emote/living/carbon/human/wing/can_run_emote(mob/user, status_check = TRUE, intentional, params) var/mob/living/carbon/human/H = user if(H.dna && H.dna.species) if(H.dna.features["wings"] != "None") diff --git a/code/modules/mob/living/emote.dm b/code/modules/mob/living/emote.dm index 2bc9d918a7cef..22b956bbc14e6 100644 --- a/code/modules/mob/living/emote.dm +++ b/code/modules/mob/living/emote.dm @@ -76,9 +76,9 @@ /datum/emote/living/collapse/run_emote(mob/user, params, type_override, intentional) . = ..() - if(. && isliving(user) && intentional) - var/mob/living/L = user - L.Unconscious(40) + if(isliving(user) && intentional) + var/mob/living/living = user + living.Unconscious(4 SECONDS) /datum/emote/living/dance key = "dance" @@ -99,16 +99,16 @@ message_ipc = "gives one shrill beep before falling limp, their monitor flashing blue before completely shutting off" message_simple = "stops moving" emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE | EMOTE_IMPORTANT + cooldown = (7.5 SECONDS) stat_allowed = HARD_CRIT -/datum/emote/living/deathgasp/run_emote(mob/user, params, type_override, intentional) +/datum/emote/living/deathgasp/run_emote(mob/living/user, params, type_override, intentional) var/mob/living/simple_animal/S = user if(istype(S) && S.deathmessage) message_simple = S.deathmessage . = ..() message_simple = initial(message_simple) - var/mob/living/living_user = user - if(!. && !living_user.can_speak_vocal() || living_user.getOxyLoss() >= 50) + if(!user.can_speak_vocal() || user.getOxyLoss() >= 50) return //stop the sound if oxyloss too high/cant speak var/mob/living/carbon/carbon_user = user // For masks that give unique death sounds @@ -132,9 +132,9 @@ /datum/emote/living/faint/run_emote(mob/user, params, type_override, intentional) . = ..() - if(. && isliving(user) && intentional) - var/mob/living/L = user - L.SetSleeping(200) + if(isliving(user) && intentional) + var/mob/living/living = user + living.SetSleeping(20 SECONDS) /datum/emote/living/flap key = "flap" @@ -146,7 +146,7 @@ /datum/emote/living/flap/run_emote(mob/user, params, type_override, intentional) . = ..() - if(. && ishuman(user)) + if(ishuman(user)) var/mob/living/carbon/human/H = user var/obj/item/organ/wings/wings = H.getorganslot(ORGAN_SLOT_WINGS) if(H.Togglewings()) @@ -224,10 +224,11 @@ /datum/emote/living/jump/run_emote(mob/living/user, params, type_override, intentional) . = ..() - if(!.) - return FALSE animate(user, pixel_y = user.pixel_y + 4, time = 0.1 SECONDS) animate(pixel_y = user.pixel_y - 4, time = 0.1 SECONDS) + if(iscarbon(user)) + var/mob/living/carbon/jumps_till_drops = user + jumps_till_drops.adjustStaminaLoss(10, forced = TRUE) /datum/emote/living/jump/get_sound(mob/living/user) return 'sound/weapons/thudswoosh.ogg' @@ -245,12 +246,12 @@ message = "laughs" message_mime = "laughs silently" emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE + specific_emote_audio_cooldown = 5 SECONDS + cooldown_integer_ceiling = 3 vary = TRUE /datum/emote/living/laugh/can_run_emote(mob/living/user, status_check = TRUE , intentional) . = ..() - if(!.) - return FALSE if(iscarbon(user)) var/mob/living/carbon/C = user return !C.silent @@ -309,6 +310,7 @@ message_mime = "acts out a scream" emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE mob_type_blacklist_typecache = list(/mob/living/carbon/human) //Humans get specialized scream. + sound_wall_ignore = TRUE /datum/emote/living/scream/select_message_type(mob/user, intentional) . = ..() @@ -336,8 +338,6 @@ #define SHIVER_LOOP_DURATION (1 SECONDS) /datum/emote/living/shiver/run_emote(mob/living/user, params, type_override, intentional) . = ..() - if(!.) - return FALSE animate(user, pixel_x = user.pixel_x + 1, time = 0.1 SECONDS) for(var/i in 1 to SHIVER_LOOP_DURATION / (0.2 SECONDS)) //desired total duration divided by the iteration duration to give the necessary iteration count animate(pixel_x = user.pixel_x - 1, time = 0.1 SECONDS) @@ -404,9 +404,9 @@ /datum/emote/living/surrender/run_emote(mob/user, params, type_override, intentional) . = ..() - if(. && isliving(user) && intentional) - var/mob/living/L = user - L.Paralyze(200) + if(isliving(user) && intentional) + var/mob/living/living = user + living.Paralyze(20 SECONDS) /datum/emote/living/sway key = "sway" @@ -416,8 +416,6 @@ /datum/emote/living/sway/run_emote(mob/living/user, params, type_override, intentional) . = ..() - if(!.) - return FALSE animate(user, pixel_x = user.pixel_x + 2, time = 0.5 SECONDS) for(var/i in 1 to 2) animate(pixel_x = user.pixel_x - 4, time = 1.0 SECONDS) @@ -433,8 +431,6 @@ #define TREMBLE_LOOP_DURATION (4.4 SECONDS) /datum/emote/living/tremble/run_emote(mob/living/user, params, type_override, intentional) . = ..() - if(!.) - return FALSE animate(user, pixel_x = user.pixel_x + 2, time = 0.2 SECONDS) for(var/i in 1 to TREMBLE_LOOP_DURATION / (0.4 SECONDS)) //desired total duration divided by the iteration duration to give the necessary iteration count animate(pixel_x = user.pixel_x - 2, time = 0.2 SECONDS) @@ -450,8 +446,6 @@ /datum/emote/living/twitch/run_emote(mob/living/user, params, type_override, intentional) . = ..() - if(!.) - return FALSE animate(user, pixel_x = user.pixel_x - 1, time = 0.1 SECONDS) animate(pixel_x = user.pixel_x + 1, time = 0.1 SECONDS) animate(time = 0.1 SECONDS) @@ -466,8 +460,6 @@ /datum/emote/living/twitch_s/run_emote(mob/living/user, params, type_override, intentional) . = ..() - if(!.) - return FALSE animate(user, pixel_x = user.pixel_x - 1, time = 0.1 SECONDS) animate(pixel_x = user.pixel_x + 1, time = 0.1 SECONDS) @@ -504,28 +496,23 @@ mob_type_blacklist_typecache = /mob/living/brain emote_type = EMOTE_VISIBLE | EMOTE_AUDIBLE -/datum/emote/living/custom/can_run_emote(mob/user, status_check, intentional) - . = ..() && intentional - -/datum/emote/living/custom/proc/check_invalid(mob/user, input) - var/static/regex/stop_bad_mime = regex(@"says|exclaims|yells|asks") - if(stop_bad_mime.Find(input, 1, 1)) - to_chat(user, "Invalid emote.") - return TRUE - return FALSE - -/datum/emote/living/custom/run_emote(mob/user, params, type_override = null, intentional = FALSE) - if(!can_run_emote(user, TRUE, intentional)) +/datum/emote/living/custom/can_run_emote(mob/user, status_check, intentional, params) + . = ..() + if(!. || !intentional) return FALSE - if(is_banned_from(user.ckey, "Emote")) + + if(!isnull(user.ckey) && is_banned_from(user.ckey, "Emote")) to_chat(user, "You cannot send custom emotes (banned).") return FALSE - else if(QDELETED(user)) + + if(QDELETED(user)) return FALSE - else if(user.client && user.client.prefs.muted & MUTE_IC) + + if(user.client && user.client.prefs.muted & MUTE_IC) to_chat(user, "You cannot send IC messages (muted).") return FALSE - else if(!params) + + if(!params) var/custom_emote = stripped_input(user, "Choose an emote to display.") if(custom_emote && !check_invalid(user, custom_emote)) var/list/emote_list = list("Audible", "Visible", "Both") @@ -540,8 +527,17 @@ message = user.say_emphasis(custom_emote) else message = params - if(type_override) - emote_type = type_override + +/datum/emote/living/custom/proc/check_invalid(mob/user, input) + var/static/regex/stop_bad_mime = regex(@"says|exclaims|yells|asks") + if(stop_bad_mime.Find(input, 1, 1)) + to_chat(user, "Invalid emote.") + return TRUE + return FALSE + +/datum/emote/living/custom/run_emote(mob/user, params, type_override = null, intentional = FALSE) + if(params && type_override) + emote_type = type_override . = ..() message = null emote_type = null diff --git a/code/modules/mob/living/silicon/ai/emote.dm b/code/modules/mob/living/silicon/ai/emote.dm index 7114c04d502c3..ab600d808ee57 100644 --- a/code/modules/mob/living/silicon/ai/emote.dm +++ b/code/modules/mob/living/silicon/ai/emote.dm @@ -9,8 +9,6 @@ /datum/emote/ai/emotion_display/run_emote(mob/user, params, type_override, intentional) . = ..() - if(!.) - return var/mob/living/silicon/ai/ai = user var/turf/ai_turf = get_turf(ai) @@ -80,9 +78,6 @@ /datum/emote/ai/emotion_display/friend_computer/run_emote(mob/user, params, type_override, intentional) . = ..() - if(!.) - return - var/datum/radio_frequency/frequency = SSradio.return_frequency(FREQ_STATUS_DISPLAYS) if(!frequency) diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 67f9136df2696..8f2fc41b34612 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -374,8 +374,8 @@ /mob/living/simple_animal/emote(act, m_type=1, message = null, intentional = FALSE) if(stat) - return - . = ..() + return FALSE + return ..() /mob/living/simple_animal/proc/set_varspeed(var_value) speed = var_value diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 9969981ccadf7..b72f36346b73e 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -216,6 +216,9 @@ CREATION_TEST_IGNORE_SELF(/mob) var/memory_throttle_time = 0 + /// Used for tracking last uses of emotes for cooldown purposes + var/list/emotes_used + ///Whether the mob is updating glide size when movespeed updates or not var/updating_glide_size = TRUE diff --git a/code/modules/multiz/movement/mob/living_zmove.dm b/code/modules/multiz/movement/mob/living_zmove.dm index e2e70f180612d..ed700a757f5f9 100644 --- a/code/modules/multiz/movement/mob/living_zmove.dm +++ b/code/modules/multiz/movement/mob/living_zmove.dm @@ -64,7 +64,7 @@ if(MOVETYPE_NONE_JUMP) visible_message("[src] jumps into the air, as if [p_they()] expected to float... Gravity pulls [p_them()] back down quickly.", "You try jumping into the space above you. Gravity pulls you back down quickly.") do_jump_animation() - adjustStaminaLoss(15, forced = TRUE) + adjustStaminaLoss(10, forced = TRUE) return FALSE if(MOVETYPE_JAUNT) move_verb = "moving" diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 7788bda1ba68c..d92e2d64a9e60 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -27,6 +27,7 @@ */ #include "dcs_get_id_from_elements.dm" #include "dynamic_ruleset_sanity.dm" +#include "emoting.dm" #include "enumerables.dm" #ifdef REFERENCE_TRACKING_DEBUG //Don't try and parse this file if ref tracking isn't turned on. IE: don't parse ref tracking please mr linter diff --git a/code/modules/unit_tests/emoting.dm b/code/modules/unit_tests/emoting.dm new file mode 100644 index 0000000000000..a9a0bb51e9c40 --- /dev/null +++ b/code/modules/unit_tests/emoting.dm @@ -0,0 +1,23 @@ +/datum/unit_test/emoting + var/emotes_used = 0 + +/datum/unit_test/emoting/Run() + var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human/consistent) + RegisterSignal(human, COMSIG_MOB_EMOTE, PROC_REF(on_emote_used)) + + human.say("*shrug") + TEST_ASSERT_EQUAL(emotes_used, 1, "Human did not shrug") + + human.say("*beep") + TEST_ASSERT_EQUAL(emotes_used, 1, "Human beeped, when that should be restricted to silicons") + + human.setOxyLoss(140) + + TEST_ASSERT(human.stat != CONSCIOUS, "Human is somehow conscious after receiving suffocation damage") + + human.say("*shrug") + TEST_ASSERT_EQUAL(emotes_used, 1, "Human shrugged while unconscious") + +/datum/unit_test/emoting/proc/on_emote_used() + SIGNAL_HANDLER + emotes_used += 1