Datumized Emotes (#17544)

* Add raw tg emote datums

* Some more initial implementation

* First (big) commit

* More reworks, most emotes seem to work but cooldowns are weird

* Add the remaining emote species

* Add simple mobs

* Update defines, add more comments

* Remove unnecessary intentional calls

* Fix some bugs, add more functionality
- Silicon mobs/bots can't use all the living emotes
- Sound volume can now be specified for emotes
- Added a define for emotes to not go thru runechat
- Reorganized some emotes
- Some human emotes that need breath will gasp
- IPCs can beep boop
- Restore power warn for robits
- Sneezing and coughing have sound effects

* Fixes emote species separation not working in *help

* Reorganize emotes based on what's available on paradise

* mouthful mode

* Update implants to use emote signals

* Update species whitelist to use a typecache

* Cleanups

* More small changes
- Remove old extraneous typecache
- Reconfigure drask emotes
- Make all mime emotes visible (since they're silent)

* Remove old emote functions

* Update emote sounds, volume

* Rework more emotes into EMOTE_VISIBLE

* *rumble*

* More slight testing and cleanup
- Drop audio emote cooldown from 10 to 5 seconds
- Change clapping behaivor slightly, putting it into run_emote
- Update some emote flags here and there
- Fix up johnny, though remind me why this one exists?

* Fix indentation, missing typecast

* fix some returns

* More review

* Rename emote files

* Add'l review

* Even more emote fixes!

- Move defines out into an emote define file
- Integrate audio and general emote cooldowns
- change some 1 and 2 into visible/audible
- Try to fix monkey screech

* Add ability for admins to mute users' emotes.

* Rename cooldowns, add general mob emote cooldown.

* Fix flip not always geting the right message

* Add some emote target handling, docs

* Fix admin rights, indentation

* Update emote.dm

set default mode to ANY

* General implant fixes

- The way implant triggers are handled is now improved, with separate checks for death and emotes instead of just checking deathgasp.
- Implants can choose to be triggered for the first death or for every death.
- Voice of God play dead now activates sad trombone

read: voice of god can now no longer lowtiergod nukies

* Bunch of other changes and bugfixes

- Adds number flag for behavior
- Reworks how muzzling/vocalizing emotes works
- Breaks out sound effects
- Drops cooldown to 1.5s

* Fix people being able to snore/nightmare while awake.

* Bump paralysis on living emotes

* First review pass

* Clean up implants (while we're here) and fix compile errors

* in living error

* More outstanding review fixes

* use more isx() checks

* Add pre-emote signal, try_run_emote()

* Prevent silicons from playing their deathgasp multiple times

* Add emote postfix behavior for adding parameters to non-message params.

Also adds a signal to intercept emote actions.

* Fix linter complaints

* Remove new player checks on GLOB.dead_mob_list

* Fix species emotes not being distinguished, remove weird legacy code

* monkey ball

* better docs == better code

* Fix audio cooldown, silicon emotes

* Fix ghost emotes (don't worry they're staying)

* Restore spin to 2 seconds

* oh johnny boy

* Make fainting last two seconds instead of .2 seconds

* Remove extra highfive message

* Tick friendly emotes

* Fix up friendly emotes, ghost emotes

* Add some emote message safeguards, prevent flip from showing twice

* Fix ghost vision message format

* Fix hands_use_check on non-carbon mobs

* Bring emotes in line with say re. oxyloss

* Add option for death implants to not trigger on gib

* Try adding some unit testing

* Include emote unit tests

* More attempts at unit testing

* More attempts at unit testing?

* forget it this is fine

* Housekeeping

* Little bit more

* Remove extra bolding from ghostsight emotes

* More sanity checks, fix snore

* Remove stack trace when unintentional emote fails

* Update code/modules/mob/living/silicon/silicon_emote.dm

oops

Co-authored-by: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com>

* More implant cleanups

* Oh I missed this

* Silence wagging, more review fixes

* Oops 2

* ensure everything can swear

* High-five fixes and status effect stuff
- Adds new on_timeout() function that gets triggered when a status effect expires
- Fixes up passing arguments to status effects
- Ensures high-five explosions don't kill the wizards

* More review stuff
- Add a stat to text to make things easier
- Make it harder to delete emotes
- BIG DANGER
- pose can no longer be set while unconscious

* Remove extra high five logic, godmode changes

* forgor

* Fix gasp not working

* Update docs for good measure

* Some review and custom emote fixes

* Fixes death alarms being broken

* Better handle stat_allowed, more review comments.

* ..() conventions

* This is why unit tests are nice

* Remove drone snowflake emote stuff

* Linting

* No more flipping on the ground

* Snap doesn't require hands free

* Does it make complete sense? no, but it's Fun

* Apply suggestions from code review

Co-authored-by: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com>

* Fix revenant messages, power warning

* epic webedit fail, laugh at this user

Co-authored-by: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com>
This commit is contained in:
Luc
2022-06-14 15:06:24 -07:00
committed by GitHub
parent 9dc65f5cf0
commit 747d8111ca
82 changed files with 3035 additions and 2331 deletions
@@ -1,3 +1,28 @@
/// If used, an implant will trigger when an emote is intentionally used.
#define IMPLANT_EMOTE_TRIGGER_INTENTIONAL (1<<0)
/// If used, an implant will trigger when an emote is forced/unintentionally used.
#define IMPLANT_EMOTE_TRIGGER_UNINTENTIONAL (1<<1)
/// If used, an implant will always trigger when the user makes an emote.
#define IMPLANT_EMOTE_TRIGGER_ALWAYS (IMPLANT_EMOTE_TRIGGER_UNINTENTIONAL | IMPLANT_EMOTE_TRIGGER_INTENTIONAL)
/// If used, an implant will trigger on the user's first death.
#define IMPLANT_TRIGGER_DEATH_ONCE (1<<2)
/// If used, an implant will trigger any time a user dies.
#define IMPLANT_TRIGGER_DEATH_ANY (1<<3)
/// If used, an implant will NOT trigger on death when a user is gibbed.
#define IMPLANT_TRIGGER_NOT_WHEN_GIBBED (1<<4)
// Defines related to the way that the implant is activated. This is the value for implant.activated
/// The implant is passively active (like a mindshield)
#define IMPLANT_ACTIVATED_PASSIVE 0
/// The implant is activated manually by a trigger
#define IMPLANT_ACTIVATED_ACTIVE 1
/**
* # Implants
*
* Code for implants that can be inserted into a person and have some sort of passive or triggered action.
*
*/
/obj/item/implant
name = "implant"
icon = 'icons/obj/implants.dmi'
@@ -5,31 +30,121 @@
origin_tech = "materials=2;biotech=3;programming=2"
actions_types = list(/datum/action/item_action/hands_free/activate)
var/activated = 1 //1 for implant types that can be activated, 0 for ones that are "always on" like mindshield implants
/// How the implant is activated.
var/activated = IMPLANT_ACTIVATED_ACTIVE
/// Whether the implant is implanted. Null if it's never been inserted, TRUE if it's currently inside someone, or FALSE if it's been removed.
var/implanted = null
/// Who the implant is inside of.
var/mob/living/imp_in = null
item_color = "b"
var/allow_multiple = 0
/// Whether multiple implants of this same type can be inserted into someone.
var/allow_multiple = FALSE
/// Amount of times that the implant can be triggered by the user. If the implant can't be used, it can't be inserted.
var/uses = -1
flags = DROPDEL
flags = DROPDEL // By default, don't let implants be harvestable.
/// List of emote keys that activate this implant when used.
var/list/trigger_emotes
/// What type of action will trigger this emote. Bitfield of IMPLANT_EMOTE_* defines.
var/trigger_causes
/// Whether this implant has already triggered on death or not, to prevent it firing multiple times.
var/has_triggered_on_death = FALSE
/obj/item/implant/proc/trigger(emote, mob/source, force)
/obj/item/implant/proc/unregister_emotes()
if(imp_in && LAZYLEN(trigger_emotes))
for(var/emote in trigger_emotes)
UnregisterSignal(imp_in, COMSIG_MOB_EMOTED(emote))
/**
* Set the emote that will trigger the implant.
* * user - User who is trying to associate the implant to themselves.
* * emote_key - Key of the emote that should trigger the implant.
* * on_implant - Whether this proc is being called during the implantation of the implant.
* * silent - If true, the user won't get any to_chat messages if an implantation fails.
*/
/obj/item/implant/proc/set_trigger(mob/user, emote_key, on_implant = FALSE, silent = TRUE)
if(imp_in != user)
return FALSE
if(!emote_key)
return FALSE
if(LAZYIN(trigger_emotes, emote_key) && !on_implant)
if(!silent)
to_chat(user, "<span class='warning'> You've already registered [emote_key]!")
return FALSE
if(emote_key == "me" || emote_key == "custom")
if(!silent)
to_chat(user, "<span class='warning'> You can't trigger [src] with a custom emote.")
return FALSE
if(!(emote_key in user.usable_emote_keys(trigger_causes & IMPLANT_EMOTE_TRIGGER_INTENTIONAL)))
if(!silent)
to_chat(user, "<span class='warning'> You can't trigger [src] with that emote! Try *help to see emotes you can use.</span>")
return FALSE
if(!(emote_key in user.usable_emote_keys(trigger_causes & IMPLANT_EMOTE_TRIGGER_UNINTENTIONAL)))
CRASH("User was given an implant for an unintentional emote that they can't use.")
LAZYADD(trigger_emotes, emote_key)
RegisterSignal(user, COMSIG_MOB_EMOTED(emote_key), .proc/on_emote)
/obj/item/implant/proc/on_emote(mob/living/user, datum/emote/fired_emote, key, emote_type, message, intentional)
SIGNAL_HANDLER
if(!implanted || !imp_in)
return
if(!(intentional && (trigger_causes & IMPLANT_EMOTE_TRIGGER_INTENTIONAL)) && !(!intentional && (trigger_causes & IMPLANT_EMOTE_TRIGGER_UNINTENTIONAL)))
return
add_attack_logs(user, user, "[intentional ? "intentionally" : "unintentionally"] [src] was [intentional ? "intentionally" : "unintentionally"] triggered with the emote [fired_emote].")
emote_trigger(key, user, intentional)
/obj/item/implant/proc/on_death(mob/source, gibbed)
SIGNAL_HANDLER
if(!implanted || !imp_in)
return
if(gibbed && (trigger_causes & IMPLANT_TRIGGER_NOT_WHEN_GIBBED))
return
// This should help avoid infinite recursion for things like dust that call death()
if(has_triggered_on_death && (trigger_causes & IMPLANT_TRIGGER_DEATH_ONCE))
return
has_triggered_on_death = TRUE
add_attack_logs(source, source, "had their [src] implant triggered on [gibbed ? "gib" : "death"].")
death_trigger(source, gibbed)
/obj/item/implant/proc/emote_trigger(emote, mob/source, force)
return
/obj/item/implant/proc/activate()
/obj/item/implant/proc/death_trigger(mob/source, gibbed)
return
/obj/item/implant/proc/activate(cause)
return
/obj/item/implant/ui_action_click()
activate("action_button")
//What does the implant do upon injection?
//return 1 if the implant injects
//return -1 if the implant fails to inject
//return 0 if there is no room for implant
/**
* Try to implant ourselves into a mob.
*
* * source - The person the implant is being administered to.
* * user - The person who is doing the implanting.
*
* Returns
* 1 if the implant injects successfully
* -1 if the implant fails to inject
* 0 if there's no room for the implant.
*/
/obj/item/implant/proc/implant(mob/source, mob/user)
var/obj/item/implant/imp_e = locate(src.type) in source
var/obj/item/implant/imp_e = locate(type) in source
if(!allow_multiple && imp_e && imp_e != src)
if(imp_e.uses < initial(imp_e.uses)*2)
if(uses == -1)
@@ -42,13 +157,23 @@
return 0
src.loc = source
loc = source
imp_in = source
implanted = 1
implanted = TRUE
if(trigger_emotes)
if(!(trigger_causes & IMPLANT_EMOTE_TRIGGER_INTENTIONAL | IMPLANT_EMOTE_TRIGGER_UNINTENTIONAL))
CRASH("Implant [src] has trigger emotes defined but no trigger cause with which to use them!")
if(!activated && (trigger_causes & IMPLANT_EMOTE_TRIGGER_INTENTIONAL))
CRASH("Implant [src] has intentional emote triggers on a passive implant")
// If you can't activate the implant manually, you shouldn't be able to deliberately activate it with an emote
for(var/emote in trigger_emotes)
set_trigger(source, emote, TRUE, TRUE)
if(activated)
for(var/X in actions)
var/datum/action/A = X
A.Grant(source)
if(trigger_causes & (IMPLANT_TRIGGER_DEATH_ONCE | IMPLANT_TRIGGER_DEATH_ANY))
RegisterSignal(source, COMSIG_MOB_DEATH, .proc/on_death)
if(ishuman(source))
var/mob/living/carbon/human/H = source
H.sec_hud_set_implants()
@@ -58,10 +183,14 @@
return 1
/**
* Clean up when an implant is removed.
* * source - the user who the implant was removed from.
*/
/obj/item/implant/proc/removed(mob/source)
src.loc = null
loc = null
imp_in = null
implanted = 0
implanted = FALSE
for(var/X in actions)
var/datum/action/A = X
@@ -71,7 +200,12 @@
var/mob/living/carbon/human/H = source
H.sec_hud_set_implants()
return 1
if(trigger_causes & (IMPLANT_TRIGGER_DEATH_ONCE | IMPLANT_TRIGGER_DEATH_ANY))
UnregisterSignal(source, COMSIG_MOB_DEATH)
unregister_emotes()
return TRUE
/obj/item/implant/Destroy()
if(imp_in)
@@ -83,5 +217,5 @@
return "No information available"
/obj/item/implant/dropped(mob/user)
. = 1
. = TRUE
..()
@@ -3,7 +3,7 @@
desc = "Returns you to the mothership."
icon = 'icons/obj/abductor.dmi'
icon_state = "implant"
activated = 1
activated = IMPLANT_ACTIVATED_ACTIVE
origin_tech = "materials=2;biotech=7;magnets=4;bluespace=4;abductor=5"
var/obj/machinery/abductor/pad/home
var/cooldown = 30
@@ -4,6 +4,7 @@
icon_state = "reagents"
origin_tech = "materials=3;biotech=4"
container_type = OPENCONTAINER
trigger_causes = IMPLANT_TRIGGER_DEATH_ANY
/obj/item/implant/chem/get_data()
var/dat = {"<b>Implant Specifications:</b><BR>
@@ -30,12 +31,8 @@
GLOB.tracked_implants -= src
return ..()
/obj/item/implant/chem/trigger(emote, mob/source, force)
if(force && emote == "deathgasp")
activate(reagents.total_volume)
/obj/item/implant/chem/death_trigger(mob/victim, gibbed)
activate(reagents.total_volume)
/obj/item/implant/chem/activate(cause)
if(!cause || !imp_in) return 0
@@ -1,6 +1,9 @@
/obj/item/implant/sad_trombone
name = "sad trombone implant"
activated = FALSE
trigger_emotes = list("deathgasp")
// If something forces the clown to fake death, it's pretty funny to still see the sad trombone played
trigger_causes = IMPLANT_EMOTE_TRIGGER_UNINTENTIONAL | IMPLANT_TRIGGER_DEATH_ANY
/obj/item/implant/sad_trombone/get_data()
var/dat = {"<b>Implant Specifications:</b><BR>
@@ -9,9 +12,15 @@
"}
return dat
/obj/item/implant/sad_trombone/trigger(emote, mob/source, force)
if(force && emote == "deathgasp")
playsound(loc, 'sound/misc/sadtrombone.ogg', 50, FALSE)
/obj/item/implant/sad_trombone/emote_trigger(emote, mob/source, force)
activate(emote)
/obj/item/implant/sad_trombone/death_trigger(mob/user, gibbed)
activate(gibbed)
/obj/item/implant/sad_trombone/activate()
playsound(loc, 'sound/misc/sadtrombone.ogg', 50, FALSE)
/obj/item/implanter/sad_trombone
name = "implanter (sad trombone)"
@@ -2,8 +2,9 @@
name = "death alarm implant"
desc = "An alarm which monitors host vital signs and transmits a radio message upon death."
var/mobname = "Will Robinson"
activated = 0
activated = IMPLANT_ACTIVATED_PASSIVE
var/static/list/stealth_areas = typecacheof(list(/area/syndicate_mothership, /area/shuttle/syndicate_elite))
trigger_causes = IMPLANT_TRIGGER_DEATH_ANY
/obj/item/implant/death_alarm/get_data()
var/dat = {"<b>Implant Specifications:</b><BR>
@@ -17,9 +18,10 @@
<b>Integrity:</b> Implant will occasionally be degraded by the body's immune system and thus will occasionally malfunction."}
return dat
/obj/item/implant/death_alarm/Destroy()
UnregisterSignal(imp_in, COMSIG_MOB_DEATH)
return ..()
/obj/item/implant/death_alarm/implant(mob/target)
. = ..()
if(.)
mobname = target.real_name
/obj/item/implant/death_alarm/activate(cause) // Death signal sends name followed by the gibbed / not gibbed check
var/mob/M = imp_in
@@ -48,14 +50,7 @@
/obj/item/implant/death_alarm/emp_act(severity) //for some reason alarms stop going off in case they are emp'd, even without this
activate("emp") //let's shout that this dude is dead
/obj/item/implant/death_alarm/implant(mob/target)
if(..())
mobname = target.real_name
RegisterSignal(target, COMSIG_MOB_DEATH, /obj/item/implant/death_alarm.proc/check_gibbed_activate)
return 1
return 0
/obj/item/implant/death_alarm/proc/check_gibbed_activate(datum/source, gibbed)
/obj/item/implant/death_alarm/death_trigger(mob/source, gibbed)
if(gibbed)
activate("gib")
else
@@ -9,6 +9,7 @@
var/medium = 0.8
var/heavy = 0.4
var/delay = 7
trigger_causes = IMPLANT_TRIGGER_DEATH_ONCE // Not surviving that
/obj/item/implant/explosive/get_data()
var/dat = {"<b>Implant Specifications:</b><BR>
@@ -22,9 +23,8 @@
"}
return dat
/obj/item/implant/explosive/trigger(emote, mob/source, force)
if(force && emote == "deathgasp")
activate("death")
/obj/item/implant/explosive/death_trigger(mob/source, gibbed)
activate("death")
/obj/item/implant/explosive/activate(cause)
if(!cause || !imp_in)
@@ -142,6 +142,7 @@
icon = 'icons/effects/blood.dmi'
icon_state = "remains"
actions_types = list(/datum/action/item_action/hands_free/activate/always)
trigger_causes = IMPLANT_TRIGGER_DEATH_ONCE | IMPLANT_TRIGGER_NOT_WHEN_GIBBED
/obj/item/implant/dust/get_data()
var/dat = {"<b>Implant Specifications:</b><BR>
@@ -155,9 +156,8 @@
"}
return dat
/obj/item/implant/dust/trigger(emote, mob/source, force)
if(force && emote == "deathgasp")
activate("death")
/obj/item/implant/dust/death_trigger(emote, mob/source, force)
activate("death")
/obj/item/implant/dust/activate(cause)
if(!cause || !imp_in || cause == "emp")
@@ -3,7 +3,7 @@
desc = "Teaches you the arts of Krav Maga in 5 short instructional videos beamed directly into your eyeballs."
icon = 'icons/obj/wizard.dmi'
icon_state ="scroll2"
activated = 1
activated = IMPLANT_ACTIVATED_ACTIVE
origin_tech = "materials=2;biotech=4;combat=5;syndicate=4"
var/datum/martial_art/krav_maga/style = new
@@ -2,7 +2,7 @@
name = "mindshield implant"
desc = "Stops people messing with your mind."
origin_tech = "materials=2;biotech=4;programming=4"
activated = 0
activated = IMPLANT_ACTIVATED_PASSIVE
/obj/item/implant/mindshield/get_data()
var/dat = {"<b>Implant Specifications:</b><BR>
@@ -3,7 +3,7 @@
desc = "Lets you shoot your guns"
icon_state = "auth"
origin_tech = "magnets=2;programming=7;biotech=5;syndicate=5"
activated = 0
activated = IMPLANT_ACTIVATED_PASSIVE
/obj/item/implant/weapons_auth/get_data()
var/dat = {"<b>Implant Specifications:</b><BR>
@@ -1,7 +1,7 @@
/obj/item/implant/tracking
name = "tracking implant"
desc = "Track with this."
activated = 0
activated = IMPLANT_ACTIVATED_PASSIVE
origin_tech = "materials=2;magnets=2;programming=2;biotech=2"
var/id = 1
+1 -1
View File
@@ -105,7 +105,7 @@
/obj/attack_animal(mob/living/simple_animal/M)
if((M.a_intent == INTENT_HELP && M.ckey) || (!M.melee_damage_upper && !M.obj_damage))
M.custom_emote(1, "[M.friendly] [src].")
M.custom_emote(EMOTE_VISIBLE, "[M.friendly] [src].")
return 0
else
var/play_soundeffect = 1