From e0bcd2538fa41ccc655c7f34ae214f71be87e68e Mon Sep 17 00:00:00 2001 From: TheChosenEvilOne <34602646+TheChosenEvilOne@users.noreply.github.com> Date: Wed, 23 Oct 2019 23:00:22 +0300 Subject: [PATCH] [Ready] Deadchat control component, also a chance for deadchat controlled birdboat. (#47110) * Dead chat controlled singularity! It just can't go wrong. Signed-off-by: TheChosenEvilOne * Renames the input_cooldown var to make more sense. Signed-off-by: TheChosenEvilOne * Begone ugly checks. Signed-off-by: TheChosenEvilOne * oh yeah, I can just do this. Signed-off-by: TheChosenEvilOne * Replaced the deadchat control with a component. Signed-off-by: TheChosenEvilOne * Autodoc Signed-off-by: TheChosenEvilOne * Removed the leftover global list. Signed-off-by: TheChosenEvilOne * Possessed goose, bye singularity. Signed-off-by: TheChosenEvilOne * vomit Signed-off-by: TheChosenEvilOne * Deadchat plays singularity Signed-off-by: TheChosenEvilOne * Removed global mode completely, also reviews. Signed-off-by: TheChosenEvilOne * Reviews. Signed-off-by: TheChosenEvilOne * alrighty then Signed-off-by: TheChosenEvilOne --- code/__DEFINES/components.dm | 5 +- code/datums/components/deadchat_control.dm | 106 ++++++++++++++++++ code/datums/components/orbiter.dm | 2 + .../mob/living/simple_animal/hostile/goose.dm | 14 +++ code/modules/mob/say.dm | 2 + code/modules/power/singularity/singularity.dm | 11 ++ tgstation.dme | 1 + 7 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 code/datums/components/deadchat_control.dm diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm index ec6030c87d9..1cc7ea12d42 100644 --- a/code/__DEFINES/components.dm +++ b/code/__DEFINES/components.dm @@ -114,6 +114,8 @@ #define COMPONENT_BLOCK_TOOL_ATTACK 1 #define COMSIG_ATOM_INTERCEPT_TELEPORT "intercept_teleport" //called when teleporting into a protected turf: (channel, turf/origin) #define COMPONENT_BLOCK_TELEPORT 1 +#define COMSIG_ATOM_ORBIT_BEGIN "atom_orbit_begin" //called when an atom starts orbiting another atom: (atom) +#define COMSIG_ATOM_ORBIT_STOP "atom_orbit_stop" //called when an atom stops orbiting another atom: (atom) ///////////////// #define COMSIG_ATOM_ATTACK_GHOST "atom_attack_ghost" //from base of atom/attack_ghost(): (mob/dead/observer/ghost) #define COMSIG_ATOM_ATTACK_HAND "atom_attack_hand" //from base of atom/attack_hand(): (mob/user) @@ -204,7 +206,8 @@ #define SPEECH_LANGUAGE 5 #define SPEECH_IGNORE_SPAM 6 #define SPEECH_FORCED 7 */ - +#define COMSIG_MOB_DEADSAY "mob_deadsay" // from /mob/say_dead(): (mob/speaker, message) + #define MOB_DEADSAY_SIGNAL_INTERCEPT 1 // /mob/living signals #define COMSIG_LIVING_RESIST "living_resist" //from base of mob/living/resist() (/mob/living) #define COMSIG_LIVING_IGNITED "living_ignite" //from base of mob/living/IgniteMob() (/mob/living) diff --git a/code/datums/components/deadchat_control.dm b/code/datums/components/deadchat_control.dm new file mode 100644 index 00000000000..7c6398e51ab --- /dev/null +++ b/code/datums/components/deadchat_control.dm @@ -0,0 +1,106 @@ +#define DEMOCRACY_MODE "democracy" +#define ANARCHY_MODE "anarchy" + +/datum/component/deadchat_control + dupe_mode = COMPONENT_DUPE_UNIQUE + var/timerid + + var/list/datum/callback/inputs = list() + var/list/ckey_to_cooldown = list() + var/orbiters = list() + var/deadchat_mode + var/input_cooldown + +/datum/component/deadchat_control/Initialize(_deadchat_mode, _inputs, _input_cooldown = 12 SECONDS) + if(!isatom(parent)) + return COMPONENT_INCOMPATIBLE + RegisterSignal(parent, COMSIG_ATOM_ORBIT_BEGIN, .proc/orbit_begin) + RegisterSignal(parent, COMSIG_ATOM_ORBIT_STOP, .proc/orbit_stop) + deadchat_mode = _deadchat_mode + inputs = _inputs + input_cooldown = _input_cooldown + if(deadchat_mode == DEMOCRACY_MODE) + timerid = addtimer(CALLBACK(src, .proc/democracy_loop), input_cooldown, TIMER_STOPPABLE | TIMER_LOOP) + notify_ghosts("[parent] is now deadchat controllable!", source = parent, action = NOTIFY_ORBIT, header="Something Interesting!") + + +/datum/component/deadchat_control/Destroy(force, silent) + inputs = null + orbiters = null + ckey_to_cooldown = null + return ..() + +/datum/component/deadchat_control/proc/deadchat_react(mob/source, message) + message = lowertext(message) + if(!inputs[message]) + return + if(deadchat_mode == ANARCHY_MODE) + var/cooldown = ckey_to_cooldown[source.ckey] + if(cooldown) + return MOB_DEADSAY_SIGNAL_INTERCEPT + inputs[message].Invoke() + ckey_to_cooldown[source.ckey] = TRUE + addtimer(CALLBACK(src, .proc/remove_cooldown, source.ckey), input_cooldown) + else if(deadchat_mode == DEMOCRACY_MODE) + ckey_to_cooldown[source.ckey] = message + return MOB_DEADSAY_SIGNAL_INTERCEPT + +/datum/component/deadchat_control/proc/remove_cooldown(ckey) + ckey_to_cooldown.Remove(ckey) + +/datum/component/deadchat_control/proc/democracy_loop() + if(QDELETED(parent) || deadchat_mode != DEMOCRACY_MODE) + deltimer(timerid) + return + var/result = count_democracy_votes() + if(!isnull(result)) + inputs[result].Invoke() + var/message = "[parent] has done action [result]!
New vote started. It will end in [input_cooldown/10] seconds.
" + for(var/M in orbiters) + to_chat(M, message) + else + var/message = "No votes were cast this cycle." + for(var/M in orbiters) + to_chat(M, message) + +/datum/component/deadchat_control/proc/count_democracy_votes() + if(!length(ckey_to_cooldown)) + return + var/list/votes = list() + for(var/command in inputs) + votes["[command]"] = 0 + for(var/vote in ckey_to_cooldown) + votes[ckey_to_cooldown[vote]]++ + ckey_to_cooldown.Remove(vote) + + // Solve which had most votes. + var/prev_value = 0 + var/result + for(var/vote in votes) + if(votes[vote] > prev_value) + prev_value = votes[vote] + result = vote + + if(result in inputs) + return result + +/datum/component/deadchat_control/vv_edit_var(var_name, var_value) + . = ..() + if(!.) + return + if(var_name != NAMEOF(src, deadchat_mode)) + return + ckey_to_cooldown = list() + if(var_value == DEMOCRACY_MODE) + timerid = addtimer(CALLBACK(src, .proc/democracy_loop), input_cooldown, TIMER_STOPPABLE | TIMER_LOOP) + else + deltimer(timerid) + +/datum/component/deadchat_control/proc/orbit_begin(atom/source, atom/orbiter) + RegisterSignal(orbiter, COMSIG_MOB_DEADSAY, .proc/deadchat_react) + orbiters |= orbiter + +/datum/component/deadchat_control/proc/orbit_stop(atom/source, atom/orbiter) + if(orbiter in orbiters) + UnregisterSignal(orbiter, COMSIG_MOB_DEADSAY) + orbiters -= orbiter diff --git a/code/datums/components/orbiter.dm b/code/datums/components/orbiter.dm index efa0fd14d55..2e4d34e716c 100644 --- a/code/datums/components/orbiter.dm +++ b/code/datums/components/orbiter.dm @@ -60,6 +60,7 @@ orbiters[orbiter] = TRUE orbiter.orbiting = src RegisterSignal(orbiter, COMSIG_MOVABLE_MOVED, .proc/orbiter_move_react) + SEND_SIGNAL(parent, COMSIG_ATOM_ORBIT_BEGIN, orbiter) var/matrix/initial_transform = matrix(orbiter.transform) // Head first! @@ -86,6 +87,7 @@ if(!orbiters[orbiter]) return UnregisterSignal(orbiter, COMSIG_MOVABLE_MOVED) + SEND_SIGNAL(parent, COMSIG_ATOM_ORBIT_STOP, orbiter) orbiter.SpinAnimation(0, 0) orbiters -= orbiter orbiter.stop_orbit(src) diff --git a/code/modules/mob/living/simple_animal/hostile/goose.dm b/code/modules/mob/living/simple_animal/hostile/goose.dm index 69163bb3acb..d9347c9d344 100644 --- a/code/modules/mob/living/simple_animal/hostile/goose.dm +++ b/code/modules/mob/living/simple_animal/hostile/goose.dm @@ -64,6 +64,10 @@ goosevomit = new goosevomit.Grant(src) RegisterSignal(src, COMSIG_MOVABLE_MOVED, .proc/goosement) + // 5% chance every round to have anarchy mode deadchat control on birdboat. + if(prob(5)) + desc = "[initial(desc)] It's waddling more than usual. It seems to be possessed." + deadchat_plays_goose() /mob/living/simple_animal/hostile/retaliate/goose/vomit/Destroy() UnregisterSignal(src, COMSIG_MOVABLE_MOVED) @@ -155,6 +159,16 @@ vomitCoefficient = 1 vomitTimeBonus = 0 +/// A proc to make it easier for admins to make the goose playable by deadchat. +/mob/living/simple_animal/hostile/retaliate/goose/vomit/proc/deadchat_plays_goose() + stop_automated_movement = TRUE + AddComponent(/datum/component/deadchat_control, ANARCHY_MODE, list( + "up" = CALLBACK(GLOBAL_PROC, .proc/_step, src, NORTH), + "down" = CALLBACK(GLOBAL_PROC, .proc/_step, src, SOUTH), + "left" = CALLBACK(GLOBAL_PROC, .proc/_step, src, WEST), + "right" = CALLBACK(GLOBAL_PROC, .proc/_step, src, EAST), + "vomit" = CALLBACK(src, .proc/vomit_prestart, 25)), 12 SECONDS, 4 SECONDS) + /datum/action/cooldown/vomit name = "Vomit" check_flags = AB_CHECK_CONSCIOUS diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index cc12a56eb70..250aefefa82 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -78,6 +78,8 @@ var/source = "DEAD: [name][alt_name]" var/rendered = " [emoji_parse(spanned)]" log_talk(message, LOG_SAY, tag="DEAD") + if(SEND_SIGNAL(src, COMSIG_MOB_DEADSAY, message) & MOB_DEADSAY_SIGNAL_INTERCEPT) + return deadchat_broadcast(rendered, source, follow_target = src, speaker_key = key) ///Check if this message is an emote diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index 27ab3ed300b..d3865389d89 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -446,3 +446,14 @@ explosion(src.loc,(dist),(dist*2),(dist*4)) qdel(src) return(gain) + +/obj/singularity/deadchat_controlled + move_self = FALSE + +/obj/singularity/deadchat_controlled/Initialize(mapload, starting_energy) + . = ..() + AddComponent(/datum/component/deadchat_control, DEMOCRACY_MODE, list( + "up" = CALLBACK(GLOBAL_PROC, .proc/_step, src, NORTH), + "down" = CALLBACK(GLOBAL_PROC, .proc/_step, src, SOUTH), + "left" = CALLBACK(GLOBAL_PROC, .proc/_step, src, WEST), + "right" = CALLBACK(GLOBAL_PROC, .proc/_step, src, EAST))) diff --git a/tgstation.dme b/tgstation.dme index 909db17444c..3ca8ae3b07c 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -363,6 +363,7 @@ #include "code\datums\components\chasm.dm" #include "code\datums\components\construction.dm" #include "code\datums\components\creamed.dm" +#include "code\datums\components\deadchat_control.dm" #include "code\datums\components\decal.dm" #include "code\datums\components\dejavu.dm" #include "code\datums\components\earprotection.dm"