diff --git a/code/_onclick/hud/action_button.dm b/code/_onclick/hud/action_button.dm
index 6d609299a05..94c8c461997 100644
--- a/code/_onclick/hud/action_button.dm
+++ b/code/_onclick/hud/action_button.dm
@@ -12,8 +12,13 @@
var/ordered = TRUE //If the button gets placed into the default bar
/atom/movable/screen/movable/action_button/proc/can_use(mob/user)
- if (linked_action)
- return linked_action.owner == user
+ if(linked_action)
+ if(linked_action.owner == user)
+ return TRUE
+ for(var/datum/weakref/reference as anything in linked_action.sharers)
+ if(IS_WEAKREF_OF(user, reference))
+ return TRUE
+ return FALSE
else if (isobserver(user))
var/mob/dead/observer/O = user
return !O.observetarget
diff --git a/code/datums/action.dm b/code/datums/action.dm
index 542f64efee9..ac9bdda9707 100644
--- a/code/datums/action.dm
+++ b/code/datums/action.dm
@@ -19,6 +19,8 @@
var/icon_icon = 'icons/hud/actions.dmi' //This is the file for the ACTION icon
var/button_icon_state = "default" //And this is the state for the action icon
var/mob/owner
+ ///All mobs that are sharing our action button.
+ var/list/sharers = list()
/datum/action/New(Target)
link_to(Target)
@@ -78,6 +80,12 @@
Remove(owner)
/datum/action/proc/Remove(mob/M)
+ for(var/datum/weakref/reference as anything in sharers)
+ var/mob/freeloader = reference.resolve()
+ if(!freeloader)
+ continue
+ Unshare(freeloader)
+ sharers = null
if(M)
if(M.client)
M.client.screen -= button
@@ -149,6 +157,25 @@
SIGNAL_HANDLER
UpdateButtonIcon()
+//Adds our action button to the screen of another player
+/datum/action/proc/Share(mob/freeloader)
+ if(!freeloader.client)
+ return
+ sharers += WEAKREF(freeloader)
+ freeloader.client.screen += button
+ freeloader.update_action_buttons()
+
+//Removes our action button from the screen of another player
+/datum/action/proc/Unshare(mob/freeloader)
+ if(!freeloader.client)
+ return
+ for(var/freeloader_reference in sharers)
+ if(IS_WEAKREF_OF(freeloader, freeloader_reference))
+ sharers -= freeloader_reference
+ break
+ freeloader.client.screen -= button
+ freeloader.update_action_buttons()
+
//Presets for item actions
/datum/action/item_action
check_flags = AB_CHECK_HANDS_BLOCKED|AB_CHECK_CONSCIOUS
@@ -672,6 +699,27 @@
if(next_use_time > world.time)
START_PROCESSING(SSfastprocess, src)
+///Like a cooldown action, but with an associated proc holder.
+/datum/action/cooldown/spell_like
+
+/datum/action/cooldown/spell_like/New(Target)
+ ..()
+ var/obj/effect/proc_holder/our_proc_holder = target
+ our_proc_holder.action = src
+ name = our_proc_holder.name
+ desc = our_proc_holder.desc
+ icon_icon = our_proc_holder.action_icon
+ button_icon_state = our_proc_holder.action_icon_state
+ background_icon_state = our_proc_holder.action_background_icon_state
+ button.name = name
+
+/datum/action/cooldown/spell_like/Trigger()
+ if(!..())
+ return FALSE
+ if(target)
+ var/obj/effect/proc_holder/our_proc_holder = target
+ our_proc_holder.Click()
+ return TRUE
//Stickmemes
/datum/action/item_action/stickmen
diff --git a/code/datums/components/riding/riding_mob.dm b/code/datums/components/riding/riding_mob.dm
index eb35cc9a7ec..a5ae08ce68c 100644
--- a/code/datums/components/riding/riding_mob.dm
+++ b/code/datums/components/riding/riding_mob.dm
@@ -5,6 +5,7 @@
var/can_be_driven = TRUE
/// If TRUE, this creature's abilities can be triggered by the rider while mounted
var/can_use_abilities = FALSE
+ var/list/shared_action_buttons = list()
/datum/component/riding/creature/Initialize(mob/living/riding_mob, force = FALSE, ride_check_flags = NONE, potion_boost = FALSE)
if(!isliving(parent))
@@ -129,24 +130,32 @@
force_dismount(yeet_mob, (!user.combat_mode)) // gentle on help, byeeee if not
/// If the ridden creature has abilities, and some var yet to be made is set to TRUE, the rider will be able to control those abilities
-/datum/component/riding/creature/proc/setup_abilities(mob/living/M)
- var/mob/living/ridden_creature = parent
-
- for(var/i in ridden_creature.abilities)
- var/obj/effect/proc_holder/proc_holder = i
- M.AddAbility(proc_holder)
-
-/// Takes away the riding parent's abilities from the rider
-/datum/component/riding/creature/proc/remove_abilities(mob/living/M)
+/datum/component/riding/creature/proc/setup_abilities(mob/living/rider)
if(!istype(parent, /mob/living))
return
var/mob/living/ridden_creature = parent
- for(var/i in ridden_creature.abilities)
- var/obj/effect/proc_holder/proc_holder = i
- M.RemoveAbility(proc_holder)
+ for(var/ability in ridden_creature.abilities)
+ var/obj/effect/proc_holder/proc_holder = ability
+ if(!proc_holder.action)
+ return
+ proc_holder.action.Share(rider)
+/// Takes away the riding parent's abilities from the rider
+/datum/component/riding/creature/proc/remove_abilities(mob/living/rider)
+ if(!istype(parent, /mob/living))
+ return
+
+ var/mob/living/ridden_creature = parent
+
+ for(var/ability in ridden_creature.abilities)
+ var/obj/effect/proc_holder/proc_holder = ability
+ if(!proc_holder.action)
+ return
+ if(rider == proc_holder.ranged_ability_user)
+ proc_holder.remove_ranged_ability()
+ proc_holder.action.Unshare(rider)
///////Yes, I said humans. No, this won't end well...//////////
/datum/component/riding/creature/human
diff --git a/code/modules/mob/living/simple_animal/hostile/vatbeast.dm b/code/modules/mob/living/simple_animal/hostile/vatbeast.dm
index b7a7773bbaa..79b3b9ed172 100644
--- a/code/modules/mob/living/simple_animal/hostile/vatbeast.dm
+++ b/code/modules/mob/living/simple_animal/hostile/vatbeast.dm
@@ -54,8 +54,16 @@
action_icon_state = "tentacle_slap"
action_background_icon_state = "bg_revenant"
ranged_mousepointer = 'icons/effects/mouse_pointers/supplypod_target.dmi'
+ base_action = /datum/action/cooldown/spell_like
+ ///How long cooldown before we can use the ability again
var/cooldown = 12 SECONDS
- var/current_cooldown = 0
+
+/obj/effect/proc_holder/tentacle_slap/Initialize(mapload, mob/living/new_owner)
+ . = ..()
+ if(!action)
+ return
+ var/datum/action/cooldown/our_action = action
+ our_action.cooldown_time = cooldown
/obj/effect/proc_holder/tentacle_slap/Click(location, control, params)
. = ..()
@@ -63,14 +71,11 @@
return TRUE
fire(usr)
-/obj/effect/proc_holder/tentacle_slap/fire(mob/living/carbon/user)
- if(current_cooldown > world.time)
- to_chat(user, span_notice("This ability is still on cooldown."))
- return
+/obj/effect/proc_holder/tentacle_slap/fire(mob/living/user)
if(active)
remove_ranged_ability(span_notice("You stop preparing to tentacle slap."))
else
- add_ranged_ability(user, span_notice("You prepare your pimp-tentacle. Left-click to slap a target!"), TRUE)
+ add_ranged_ability(user, span_notice("You prepare [(IS_WEAKREF_OF(user, owner)) ? "your" : "their"] pimp-tentacle. Left-click to slap a target!"), TRUE)
/obj/effect/proc_holder/tentacle_slap/InterceptClickOn(mob/living/caller, params, atom/target)
. = ..()
@@ -86,7 +91,7 @@
remove_ranged_ability()
return
- if(!caller.Adjacent(target))
+ if(!beast_owner.Adjacent(target))
return
if(!isliving(target))
@@ -94,11 +99,20 @@
var/mob/living/living_target = target
+ if(!action.IsAvailable()) //extra check for safety since the ability is shared
+ remove_ranged_ability()
+ to_chat(caller, span_notice("This ability is still on cooldown."))
+ return
+
beast_owner.visible_message("