From 40b3b4c2513bb790d80575c01617211a10e4f299 Mon Sep 17 00:00:00 2001 From: Jacquerel Date: Sun, 2 Jul 2023 22:25:42 +0100 Subject: [PATCH] Rat RP expansion (#76455) This fixes a vile and long-standing bug where putting a mouse inside your hat would not allow the mouse to control your movements, as it would pop out of the hat whenever it tried to move. Additionally as a feature this allows a mouse sitting on your head to convey complicated instructions such as "scream" or "do a flip", via emoting. Through drift compatibility, the rat's living mech will also perform this action. I could have made this into a component but there's no fucking way any other item is going to have this behaviour, so I didn't. This feature was already in the game but broken and I want it not to be broken. The mouse should be able to control your entire life. :cl: fix: Placing a mouse inside your chef hat will once more allow it to pilot you around. add: A player-controlled mouse inside your chef hat can compel you to perform complex actions, such as flipping and spinning. You will obey because the mouse knows better than you do. /:cl: --- .../signals/signals_mob/signals_mob_main.dm | 5 +- code/datums/emotes.dm | 2 + code/modules/clothing/head/jobs.dm | 78 +++++++++++++++---- code/modules/mob/mob_movement.dm | 2 +- 4 files changed, 71 insertions(+), 16 deletions(-) diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm index a7a04b7dfdd3..f0622d183127 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm @@ -35,7 +35,7 @@ /// Should we stop the current living movement attempt #define COMSIG_MOB_CLIENT_BLOCK_PRE_LIVING_MOVE COMPONENT_MOVABLE_BLOCK_PRE_MOVE -/// From base of /client/Move(): (list/move_args) +/// From base of /client/Move(): (new_loc, direction) #define COMSIG_MOB_CLIENT_PRE_MOVE "mob_client_pre_move" /// Should always match COMPONENT_MOVABLE_BLOCK_PRE_MOVE as these are interchangeable and used to block movement. #define COMSIG_MOB_CLIENT_BLOCK_PRE_MOVE COMPONENT_MOVABLE_BLOCK_PRE_MOVE @@ -140,6 +140,9 @@ ///Mob is trying to open the wires of a target [/atom], from /datum/wires/interactable(): (atom/target) #define COMSIG_TRY_WIRES_INTERACT "try_wires_interact" #define COMPONENT_CANT_INTERACT_WIRES (1<<0) +///Mob is trying to emote, from /datum/emote/proc/run_emote(): (key, params, type_override, intentional) +#define COMSIG_MOB_PRE_EMOTED "mob_pre_emoted" + #define COMPONENT_CANT_EMOTE (1<<0) #define COMSIG_MOB_EMOTED(emote_key) "mob_emoted_[emote_key]" ///sent when a mob/login() finishes: (client) #define COMSIG_MOB_CLIENT_LOGIN "comsig_mob_client_login" diff --git a/code/datums/emotes.dm b/code/datums/emotes.dm index c8c1af309f7c..feca02e6e6d4 100644 --- a/code/datums/emotes.dm +++ b/code/datums/emotes.dm @@ -91,6 +91,8 @@ . = TRUE if(!can_run_emote(user, TRUE, intentional)) return FALSE + if(SEND_SIGNAL(user, COMSIG_MOB_PRE_EMOTED, key, params, type_override, intentional) & COMPONENT_CANT_EMOTE) + return // We don't return FALSE because the error output would be incorrect, provide your own if necessary. var/msg = select_message_type(user, message, intentional) if(params && message_param) msg = select_param(user, params) diff --git a/code/modules/clothing/head/jobs.dm b/code/modules/clothing/head/jobs.dm index fc018e15a0dc..d22f03dc261e 100644 --- a/code/modules/clothing/head/jobs.dm +++ b/code/modules/clothing/head/jobs.dm @@ -12,19 +12,77 @@ desc = "The commander in chef's head wear." strip_delay = 10 equip_delay_other = 10 - dog_fashion = /datum/dog_fashion/head/chef - ///the chance that the movements of a mouse inside of this hat get relayed to the human wearing the hat + /// The chance that the movements of a mouse inside of this hat get relayed to the human wearing the hat var/mouse_control_probability = 20 + /// Allowed time between movements + COOLDOWN_DECLARE(move_cooldown) + +/// Admin variant of the chef hat where every mouse pilot input will always be transferred to the wearer +/obj/item/clothing/head/utility/chefhat/i_am_assuming_direct_control + desc = "The commander in chef's head wear. Upon closer inspection, there seem to be dozens of tiny levers, buttons, dials, and screens inside of this hat. What the hell...?" + mouse_control_probability = 100 /obj/item/clothing/head/utility/chefhat/Initialize(mapload) . = ..() - create_storage(storage_type = /datum/storage/pockets/chefhat) -/obj/item/clothing/head/utility/chefhat/i_am_assuming_direct_control - desc = "The commander in chef's head wear. Upon closer inspection, there seem to be dozens of tiny levers, buttons, dials, and screens inside of this hat. What the hell...?" - mouse_control_probability = 100 +/obj/item/clothing/head/utility/chefhat/Entered(atom/movable/arrived, atom/old_loc, list/atom/old_locs) + . = ..() + var/mob/living/basic/new_boss = get_mouse(arrived) + if(!new_boss) + return + RegisterSignal(new_boss, COMSIG_MOB_PRE_EMOTED, PROC_REF(on_mouse_emote)) + RegisterSignal(new_boss, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(on_mouse_moving)) + RegisterSignal(new_boss, COMSIG_MOB_CLIENT_PRE_LIVING_MOVE, PROC_REF(on_mouse_moving)) + +/obj/item/clothing/head/utility/chefhat/Exited(atom/movable/gone, direction) + . = ..() + var/mob/living/basic/old_boss = get_mouse(gone) + if(!old_boss) + return + UnregisterSignal(old_boss, list(COMSIG_MOB_PRE_EMOTED, COMSIG_MOVABLE_PRE_MOVE, COMSIG_MOB_CLIENT_PRE_LIVING_MOVE)) + +/// Returns a mob stored inside a mob container, if there is one +/obj/item/clothing/head/utility/chefhat/proc/get_mouse(atom/possible_mouse) + if (!ispickedupmob(possible_mouse)) + return + var/obj/item/clothing/head/mob_holder/mousey_holder = possible_mouse + return locate(/mob/living/basic) in mousey_holder.contents + +/// Relays emotes emoted by your boss to the hat wearer for full immersion +/obj/item/clothing/head/utility/chefhat/proc/on_mouse_emote(mob/living/source, key, emote_message, type_override) + SIGNAL_HANDLER + var/mob/living/carbon/wearer = loc + if(!wearer || wearer.incapacitated(IGNORE_RESTRAINTS)) + return + if (!prob(mouse_control_probability)) + return COMPONENT_CANT_EMOTE + INVOKE_ASYNC(wearer, TYPE_PROC_REF(/mob, emote), key, type_override, emote_message, FALSE) + return COMPONENT_CANT_EMOTE + +/// Relays movement made by the mouse in your hat to the wearer of the hat +/obj/item/clothing/head/utility/chefhat/proc/on_mouse_moving(mob/living/source, atom/moved_to) + SIGNAL_HANDLER + if (!prob(mouse_control_probability) || !COOLDOWN_FINISHED(src, move_cooldown)) + return COMPONENT_MOVABLE_BLOCK_PRE_MOVE // Didn't roll well enough or on cooldown + + var/mob/living/carbon/wearer = loc + if(!wearer || wearer.incapacitated(IGNORE_RESTRAINTS)) + return COMPONENT_MOVABLE_BLOCK_PRE_MOVE // Not worn or can't move + + var/move_direction = get_dir(wearer, moved_to) + if(!wearer.Process_Spacemove(move_direction)) + return COMPONENT_MOVABLE_BLOCK_PRE_MOVE // Currently drifting in space + if(!has_gravity() || !isturf(wearer.loc)) + return COMPONENT_MOVABLE_BLOCK_PRE_MOVE // Not in a location where we can move + + step_towards(wearer, moved_to) + var/move_delay = wearer.cached_multiplicative_slowdown + if (ISDIAGONALDIR(move_direction)) + move_delay *= sqrt(2) + COOLDOWN_START(src, move_cooldown, move_delay) + return COMPONENT_MOVABLE_BLOCK_PRE_MOVE /obj/item/clothing/head/utility/chefhat/suicide_act(mob/living/user) user.visible_message(span_suicide("[user] is donning [src]! It looks like [user.p_theyre()] trying to become a chef.")) @@ -35,14 +93,6 @@ playsound(user, 'sound/machines/ding.ogg', 50, TRUE) return FIRELOSS -/obj/item/clothing/head/utility/chefhat/relaymove(mob/living/user, direction) - if(!ismouse(user) || !isliving(loc) || !prob(mouse_control_probability)) - return - var/mob/living/L = loc - if(L.incapacitated(IGNORE_RESTRAINTS)) //just in case - return - step_towards(L, get_step(L, direction)) - //Captain /obj/item/clothing/head/hats/caphat name = "captain's hat" diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index fd7c0cda9268..7fd0130ba7f7 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -84,7 +84,7 @@ if(mob.stat == DEAD) mob.ghostize() return FALSE - if(SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_PRE_LIVING_MOVE) & COMSIG_MOB_CLIENT_BLOCK_PRE_LIVING_MOVE) + if(SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_PRE_LIVING_MOVE, new_loc, direct) & COMSIG_MOB_CLIENT_BLOCK_PRE_LIVING_MOVE) return FALSE var/mob/living/L = mob //Already checked for isliving earlier