Files
Paradise/code/modules/mob/mob_emote.dm
T
d558a69583 Ports a character customization option: Quirks (#28388)
* Makes the datum

* Initial Commit

* Gets TGUI menu not crashing

* Autodoc

* quirk menu bugged

* oh.

* Working menu + buttons

* trait defintes pt 1/?

* Moves traits around

* ACTUALLY START ON THE QUIRKS THEMSELVES

* TGUI Bundle Rebuild

* TGUI properly saves now

* Removes allergies for scope reasons, make foreigner work properly

* TGUI tweaks

* Reworks TGUI Menu

* quirks store the mob, not mind

* Adds asthma and mute

* minor tweaks

* Cloning re-adds quirks

* Adds functionality to frail

* Adds crafty

* TGUI fix

* Fixes a frail bug

* Makes sure the list is in the correct format

* TGUI size change

* Makes skittish work

* Adds glutton, removes unused traits

* Changes description and adds function to glutton

* Freerunner functioning

* Crafty buff

* Clear quirk list when changing species

* Forgot to remove light step

* Delete

* Refactor item-giving

* TGUI Bundle Rebuild

* whoops

* Apply quirks when changing species

* Removes some unneeded defines/paths

* Replaces improved charging cord with a bad charger

* coment

* Guard clause

* Changes how quirks add and remove organs

* unused var

* adding organs works, removing them doesn't yet

* oh that fixed it

* Hopefully finally once and for all fix the json issues

* Buffs asthma

* switches the processor to ssobj since it's slower

* actually spawns the items whoops

* maybe actually fixes json issues I really hope this doesn't come up again?

* moves sql to the right directory

* CULPRIT FOUND, JSON ISSUES ACTUALLY SOLVED

* adds admin tooling for quirks

* Ups the cost of lungs

* Adds removal for the drinking ones

* Fixes some duplicate code

* Removing foreigner works

* wops

* bumps sql version to 65

* prettier

* semicolon

* Foreigners can understand galcomm but not speak it, as per headmin request

* Different solution was agreed upon

* CC announcements now get auto-translated

* Apply suggestions from code review

Lewc quick suggestions

Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com>
Signed-off-by: BiancaWilkson <42818125+BiancaWilkson@users.noreply.github.com>

* Move alcohol tolerance basetype

* accomodates for reset_styles

* Swap to species flags, and being dead is a universal process stopper

* adds a foreigner trait

* Xenos and the like can't understand CC's translated announcements

* TGUI Bundle Rebuild

* Lewc quick fixes

Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com>
Signed-off-by: BiancaWilkson <42818125+BiancaWilkson@users.noreply.github.com>

* i forgor

* i forgor pt 2

* process properly returns true

* TGUI Bundle Rebuild

* sql update for fixing merge conflicts

* bundle changes

* actual sql fix

* Not test tiny in prod

* sql fix?

* tgui update, first pass

* Adds schema changes to main quirk branch

* persistent data works now

* TGUI Bundle Rebuild

* Fixes conflict with job assignment datumization

* TGUI Bundle Rebuild

* Adds pacifism by request, -3 points

* gives skittish a small cd

* Makes the lunch randomizer  I was thinking about doing

* GC returns a qdel hint now

* Removes hard refs

* SQL save fix

* Fixes admin tools

* Fixes wizards getting quirked up

* ragin too

* Lightweight is more lightweight

* Adds admin logging for when someone makes a languagesless character

* TGUI Bundle Rebuild

* Adds colorblind, hungry, and buffs asthma

* fixed vv post-merge conflict

* behold, my monster

* rebuild and merge master

* Adds more positive quirks, and makes monochrome give more points

* adds nearsighted, and no whispering

* sorry linter

* Adds the cool quirk, thanks to pumpkin312

* Oh dear that signal gets sent to everyone every time someone spawns in

* i forgot that slimes dont have eyes

* tgui fix

* bundle rebuild probably

* prettier

* prettier

* Skittish package fix

* force add bundle

* Makes plasmemes be unable to take foreigner

* Makes advanced lungs respect the owner's species

* makes breaking open wrapped crates/lockers a do_after_once

* Makes plasmeme/vox advanced lungs actually help

* You get glasses anyway whoops

* bundle rebuild

---------

Signed-off-by: BiancaWilkson <42818125+BiancaWilkson@users.noreply.github.com>
Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>
Co-authored-by: Luc <89928798+lewcc@users.noreply.github.com>
Co-authored-by: warriorstar-orion <orion@snowfrost.garden>
2025-11-04 00:18:52 +00:00

250 lines
8.8 KiB
Plaintext

// The datum in use is defined in code/datums/emotes.dm
/**
* Send an emote.
*
* * emote_key: Key of the emote being triggered
* * m_type: Type of the emote, like EMOTE_AUDIBLE. If this is not null, the default type of the emote will be overridden.
* * message: Custom parameter for the emote. This should be used if you want to pass something like a target programmatically.
* * intentional: Whether or not the emote was deliberately triggered by the mob. If true, it's forced, which skips some checks when calling the emote.
* * force_silence: If true, unusable/nonexistent emotes will not notify the user.
*/
/mob/proc/emote(emote_key, type_override = null, message = null, intentional = FALSE, force_silence = FALSE)
emote_key = lowertext(emote_key)
var/param = message
var/custom_param_offset = findtext(emote_key, EMOTE_PARAM_SEPARATOR, 1, null)
if(custom_param_offset)
param = copytext(emote_key, custom_param_offset + length(emote_key[custom_param_offset]))
emote_key = copytext(emote_key, 1, custom_param_offset)
var/list/key_emotes = GLOB.emote_list[emote_key]
if(!length(key_emotes))
if(intentional && !force_silence)
to_chat(src, "<span class='notice'>'[emote_key]' emote does not exist. Say *help for a list.</span>")
else if(!intentional)
CRASH("Emote with key [emote_key] was attempted to be called, though doesn't exist!")
return FALSE
var/silenced = FALSE
for(var/datum/emote/P in key_emotes)
// can this mob run the emote at all?
if(!P.can_run_emote(src, intentional = intentional))
continue
if(!P.check_cooldown(src, intentional))
// if an emote's on cooldown, don't spam them with messages of not being able to use it
silenced = TRUE
continue
if(P.try_run_emote(src, param, type_override, intentional))
return TRUE
if(intentional && !silenced && !force_silence)
to_chat(src, "<span class='notice'>Unusable emote '[emote_key]'. Say *help for a list.</span>")
return FALSE
/**
* Perform a custom emote.
*
* * m_type: Type of message to send.
* * message: Content of the message. If none is provided, the user will be prompted to choose the input.
* * intentional: Whether or not the user intendeded to perform the emote.
*/
/mob/proc/custom_emote(m_type = EMOTE_VISIBLE, message = null, intentional = FALSE)
var/input = ""
if(!message && !client)
CRASH("An empty custom emote was called from a client-less mob.")
else if(!message)
input = tgui_input_text(src, "Choose an emote to display.", "Custom Emote")
else
input = message
emote("me", m_type, input, intentional)
/**
* Get a list of all emote keys usable by the current mob.
*
* * intentional_use: Whether or not to check based on if the action was intentional.
*/
/mob/proc/usable_emote_keys(intentional_use = TRUE)
var/list/all_keys = list()
for(var/key in GLOB.emote_list)
for(var/datum/emote/P in GLOB.emote_list[key])
if(P.key in all_keys)
continue
if(P.can_run_emote(src, status_check = FALSE, intentional = null))
all_keys += P.key
if(P.key_third_person)
all_keys += P.key_third_person
return all_keys
/datum/emote/help
key = "help"
mob_type_ignore_stat_typecache = list(/mob/dead/observer, /mob/living/silicon/ai)
/datum/emote/help/run_emote(mob/user, params, type_override, intentional)
. = ..()
var/list/base_keys = list()
var/list/all_keys = list()
var/list/species_emotes = list()
var/list/message = list("Available emotes, you can use them with say \"*emote\": ")
var/mob/living/carbon/human/H = user
for(var/key in GLOB.emote_list)
for(var/datum/emote/P in GLOB.emote_list[key])
var/full_key = P.key
if(P.key in all_keys)
continue
if(P.can_run_emote(user, status_check = FALSE, intentional = TRUE))
if(P.message_param && P.param_desc)
// Add our parameter description, like flap-user
full_key = P.key + "\[[EMOTE_PARAM_SEPARATOR][P.param_desc]\]"
if(istype(H) && P.species_type_whitelist_typecache && H.dna && is_type_in_typecache(H.dna.species, P.species_type_whitelist_typecache))
species_emotes += full_key
else
base_keys += full_key
all_keys += P.key
base_keys = sortList(base_keys)
message += base_keys.Join(", ")
message += "."
message = message.Join("")
if(length(species_emotes) > 0)
species_emotes = sortList(species_emotes)
message += "\n<u>[user?.dna?.species.name] specific emotes</u> :- "
message += species_emotes.Join(", ")
message += "."
to_chat(user, message)
/datum/emote/flip
key = "flip"
key_third_person = "flips"
message = "does a flip!"
hands_use_check = TRUE
emote_type = EMOTE_VISIBLE | EMOTE_FORCE_NO_RUNECHAT // don't need an emote to see that
mob_type_allowed_typecache = list(/mob/living, /mob/dead/observer) // okay but what if we allowed ghosts to flip as well
mob_type_blacklist_typecache = list(/mob/living/brain, /mob/camera, /mob/living/silicon/ai)
mob_type_ignore_stat_typecache = list(/mob/dead/observer)
/datum/emote/flip/run_emote(mob/user, params, type_override, intentional)
if(isobserver(user))
user.SpinAnimation(5, 1)
return TRUE
var/mob/living/L = user
if(IS_HORIZONTAL(L))
var/turf = get_turf(L)
message = "flops and flails around [isspaceturf(turf) ? "in space" : "on the floor"]."
return ..()
else if(params)
message_param = "flips in %t's general direction."
else if(ishuman(user))
var/mob/living/carbon/human/H = user
if(istype(H.get_active_hand(), /obj/item/grab))
var/obj/item/grab/G = H.get_active_hand()
if(G && G.affecting)
if(H.buckled || G.affecting.buckled)
to_chat(user, "<span class='warning'>[G.affecting] is buckled, you can't flip around [G.affecting.p_them()]!</span>")
return TRUE
var/turf/oldloc = user.loc
var/turf/newloc = G.affecting.loc
if(isturf(oldloc) && isturf(newloc))
user.SpinAnimation(5, 1)
var/old_pass = user.pass_flags
user.pass_flags |= (PASSTABLE)
step(user, get_dir(oldloc, newloc))
user.pass_flags = old_pass
message = "flips over [G.affecting]!"
return ..()
user.SpinAnimation(5, 1)
if(isrobot(user))
var/mob/living/silicon/robot/borg = user
if(borg.drop_hat())
borg.visible_message("<span class='warning'><span class='name'>[user]</span> drops their hat!</span>",
"<span class='warning'>As you flip your hat falls off!</span>")
if(prob(5) && ishuman(user) && !HAS_TRAIT(user, TRAIT_COOL))
var/turf = get_turf(L)
message = "attempts a flip and [isspaceturf(turf) ? "loses balance" : "crashes to the floor"]!"
if(istype(L))
addtimer(CALLBACK(L, TYPE_PROC_REF(/mob/living, Weaken), 4 SECONDS), 0.3 SECONDS)
return ..()
. = ..()
/datum/emote/spin
key = "spin"
key_third_person = "spins"
hands_use_check = TRUE
emote_type = EMOTE_VISIBLE | EMOTE_FORCE_NO_RUNECHAT
mob_type_allowed_typecache = list(/mob/living, /mob/dead/observer)
mob_type_blacklist_typecache = list(/mob/living/brain, /mob/camera, /mob/living/silicon/ai)
mob_type_ignore_stat_typecache = list(/mob/dead/observer)
cooldown = 2 SECONDS // how long the spin takes, any faster and mobs can spin
/datum/emote/spin/run_emote(mob/user, params, type_override, intentional)
. = ..()
if(!.)
return FALSE
if(prob(95) || isobserver(user) || !ishuman(user) || HAS_TRAIT(user, TRAIT_COOL))
user.spin(20, 1)
return TRUE
user.spin(32, 1)
to_chat(user, "<span class='warning'>You spin too much!</span>")
var/mob/living/L = user
if(istype(L))
L.Dizzy(24 SECONDS)
L.Confused(24 SECONDS)
/datum/emote/jump
key = "jump"
key_third_person = "jumps"
message = "jumps!"
emote_type = EMOTE_VISIBLE | EMOTE_FORCE_NO_RUNECHAT
mob_type_allowed_typecache = list(/mob/living, /mob/dead/observer)
mob_type_blacklist_typecache = list(/mob/living/brain, /mob/camera, /mob/living/silicon/ai)
mob_type_ignore_stat_typecache = list(/mob/dead/observer)
cooldown = 0.25 SECONDS // bhop until you're stamina-less if you want lmao
audio_cooldown = 0.25 SECONDS
/datum/emote/jump/run_emote(mob/user, params, type_override, intentional)
if(isobserver(user))
jump_animation(user)
return TRUE
. = ..()
if(!.)
return FALSE
jump_animation(user)
if(isliving(user) && !HAS_TRAIT(user, TRAIT_BOOTS_OF_JUMPING))
var/mob/living/L = user
L.adjustStaminaLoss(30)
/datum/emote/jump/proc/jump_animation(mob/user)
var/original_transform = user.transform
animate(user, transform = user.transform.Translate(0, 8), time = 0.15 SECONDS, flags = ANIMATION_PARALLEL, easing = QUAD_EASING|EASE_OUT)
animate(transform = original_transform, time = 0.15 SECONDS, easing = QUAD_EASING|EASE_IN)
/datum/emote/jump/get_sound(mob/user)
return 'sound/weapons/thudswoosh.ogg'
// Avoids playing sounds if we're a ghost
/datum/emote/jump/should_play_sound(mob/user, intentional)
if(isliving(user))
return ..()
return FALSE
/datum/emote/jump/can_run_emote(mob/user, status_check, intentional)
if(user.buckled || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED) || !isturf(user.loc))
return FALSE
if(isliving(user))
var/mob/living/L = user
if(IS_HORIZONTAL(L))
return FALSE
return ..()