Files
Bubberstation/code/datums/components/riding/riding_mob.dm
LemonInTheDark 50689f89a4 Action button refactor/rework: Enhanced Dragging (#65180)
About The Pull Request

I noticed a lot of strange and un-intuitive behavior in action buttons, and got stung by the bloat bug. Damn it hug #58027
I'll do my best to explain what I've changed and why, might get a bit long.
If you want a better idea, read the commits. Most of em are pretty solid, if long.

Whelp. Here we go.
How do action buttons currently work

All action buttons are draggable, to any place on the screen. They're held in an actions list on the player's mob.
Their location in this list determines their position on the top of the screen. If one is dragged away from the top, its position in the list is "saved". This looks really bad.
If two buttons are dragged over each other, their positions swap. (inside the actions list too)
If a button is shift clicked, it is brought back to the position it started at.
If the action collapse button that you likely just mentally edit out is alt clicked, it resets the position of all action buttons on the screen.
If an action is ctrl clicked, it is "locked". This prevents any future position changes, and also enables a saving feature. With this saving feature, locked button positions persist between rounds. So your first o2 canister will always start where you saved it, etc.
Actions and buttons are a one to one link. While there is functionality to share action buttons between two players, this means showing the same object to both. So one player can move a button on another's screen. Horrendous.
This also makes code that modifies properties of the screen object itself very clunky.
Why is this bad

A: None knew pretty much any of this information. It is actually documented, just in a horribly formatted screen tip on the collapse button, you know the one we all mentally delete from the hud.
B: None of this is intuitive. Dragging buttons makes the hud look much worse, and you get no feedback that you even can drag them. Depressing
C: We use actions to make new options clear to the player. This means players can have a lot of action buttons on the hud. This gets cluttery
D: The collapse button is useless. It lets you clear your screen if someone like me fucks up and gives you 2000 actions, but outside of that it just hides all information from you. You never want to see none of your action buttons, just a filtered list of them.
E: On a technical level, they're quite messy, and not fully functionally complete. This is depressing.
What I've done

Assuming the above to be true, how do we fix them?
Well first I'm going to go over everything I changed, including links to major commits. I'll then describe the finished product, and why I made the decisions I did.

Oh and I've moved some of the more niche or technical discussion to dropdowns. Hopefully this makes finding the major functional changes easier

Adds helper procs for turning screen_loc strings into more manageable arrays. This doesn't fully support all of the screen_loc spec, but it's enough for what I'm doing. (f54865f)

Uses these helper procs to improve existing code (6273b93)

Fixes an issue with tooltip code itself. If you tried to hold down a mouse button while dragging onto a tooltip enabled object, it would silently fail. The js made assumptions about the order args came in, which broke when buttons were held down (e0e42f6)

Adds a signal linked to /client/Click(). Surprised we didn't have this before honestly (c491a4a)

Makes /client/MouseDrag() return parent. If we don't do this, any overrides of MouseDrag will never actually be called (2190b2a)
Refactors how action buttons work under the hood (53ccce2)
Basically, rather then generating one button per action, we generate one button per viewer

Starts to change button behavior, more cleanup

Changes the mouse cursor when an action button is dragged. Hopefully
this makes moving things feel less like an accident, and makes you doing
it more clear

Removes the moved and locked vars. This will be more relevant later, but
for now:

Moved exists as a sort of budget "We've been dragged" variable. We can
handle this more cleanly, and the movable type doesn't care about it

Locked is a very old variable that is also not something that the
movable type "owns". It's more an action button thing that's been moved
down.
It exists so an action can be locked in place, and in that locking, be
treated as a "saved location"
(21e20fc)

Because I've nuked move, we don't need to directly set our button's
position. We can use the default_button_position var instead. This is
quite handy.

Please ignore position_action, I will explain that later
(83e265e)

Removes the buttons locked pref

It was another obscure part of action buttons, basically do buttons
start "locked" or not. See previous discussion of locked
(b58b1bd)

Major rework starts here

Alright. Sorry for this, this is where me not commiting regularly starts
to suck. I'll do my best though.

Rather then figuring out an action button's position via a combination
of the moved and ordered vars, we use a separate location var to store
one of a few defines. This makes life later much easier.

Adds tooltip support for dragging action buttons. The way the tooltip
just froze in place when dragging really bugged me, and lead to some
nasty visual artifacts.
This is a bit messy because the drag procs are horrible, but it's
workable

Dropping a button on another button will no longer swap their positions
Behavior instead depends on the target button.

If it's a part of a group (A concept I will explain later) the dragged
button is simply inserted before it in the group's list.

If it's floating on the general hud, we instead position the dragged
button to its right. There's extra logic here to ensure buttons will
never overflow the screen, but I'll get into that later.

Alright. That's most of the refactoring. Time for the larger behavior
changes.

Adds a button palette. This is a separate dropdown that renders
underneath buttons.

image

The idea is to allow for a conceptual separation between "important"
buttons and the ones that end up cluttering the screen.

You can click on the dropdown to open it, then any later clicks that
don't involve actions in some way will autoclose it.

My goal is to come up with an alternative for the action button that
just acted as a way to hide all buttons on screen. Not convinced it saw
much use.

As a side effect of removing that, I've moved its tooltip stuff to the
palette. I've properly formatted it, so hopefully it's easier to read
then the jumble that we used to have.

(You can alt click the palette button to reset all button positions)

Oh and the palette can scroll, since as you'll see later it has a
limited size.
image

Moving on from that, I've added what amounts to action landing buttons.
These allow buttons to rejoin groups, or be positioned at the end of a
line of buttons.
image

They've got a 32x32 hitbox, and only show up when dragging. Hopefully
this makes the system more clear just by dragging an action.

Oh and I've changed how button position updating works. The old system
of calling update_action_buttons on mob every time an action button
changes position is gone, mostly because I've setup more robust
grouping. Will discuss when I get to huds

(0d1e93f)
Adds the backbone behind action button position changes (94133bd)

Moves hud defines to the global folder, safer this way (7260117)

Adds color changing to the palette button, giving some heads up for buttons being inserted into the palette automatically
image
image
Ensures a landing button is always shown, even if it needs to break the
max row rule
Makes palettes auto contract if they have no buttons inside them
Prevents palettes from being opened if they have no buttons inside them
(f9417f3)
How it looks
2022-02-26.02-30-10.mp4
Why It's Good For The Game

Players have more control over the clutter on their screen.
Buttons are available, but not in the way,
Since any player move of a button saves it, any lack of clarity in the way buttons work will be forced out by buttons not just resetting when a new game starts.
We don't overlap any existing screen elements, unless the upper button list gets really long.
The code is much less crummy (I think, may have made it worse it's hard for me to judge my own work)

If it ends up not being as usable as I'd like, I'll rip out the existing changes and just implement the qol and backend stuff. I think it's worth doing though.
Changelog

cl
add: Expanded heavily on action buttons
add: Adds an action button dropdown that sits just under the normal list in the top left. You can drag new buttons onto it to insert them. Click on it to show its contents, do what you want to do, then click again anywhere to contract it. Alt click it to reset all button positions
add: Action buttons will now remember their position between rounds. So if you really like your flashlight right next to your player for some reason, we support that now
add: When you start to drag an action button, docking ports will appear in places that it can be inserted into. (Outside of just floating somewhere on your screen of course)
del: Removed action button locking, and the associated preference. I'm reasonably sure literally none uses this, but if you do hit me up
qol: Dragging an action button will now give you an outline of its size around your cursor
fix: You can no longer cause the screen to expand by putting an action button on the edge of widescreen, and then resizing to standard.
refactor: Refactors action and button code significantly. lots of little things.
/cl
2022-04-01 09:40:20 +13:00

394 lines
17 KiB
Plaintext

// For any mob that can be ridden
/datum/component/riding/creature
/// If TRUE, this creature's movements can be controlled by the rider while mounted (as opposed to riding cyborgs and humans, which is passive)
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))
return COMPONENT_INCOMPATIBLE
. = ..()
var/mob/living/living_parent = parent
living_parent.stop_pulling() // was only used on humans previously, may change some other behavior
log_riding(living_parent, riding_mob)
riding_mob.set_glide_size(living_parent.glide_size)
handle_vehicle_offsets(living_parent.dir)
if(can_use_abilities)
setup_abilities(riding_mob)
if(isanimal(parent))
var/mob/living/simple_animal/simple_parent = parent
simple_parent.stop_automated_movement = TRUE
/datum/component/riding/creature/Destroy(force, silent)
unequip_buckle_inhands(parent)
if(isanimal(parent))
var/mob/living/simple_animal/simple_parent = parent
simple_parent.stop_automated_movement = FALSE
return ..()
/datum/component/riding/creature/RegisterWithParent()
. = ..()
RegisterSignal(parent, COMSIG_MOB_EMOTE, .proc/check_emote)
if(can_be_driven)
RegisterSignal(parent, COMSIG_RIDDEN_DRIVER_MOVE, .proc/driver_move) // this isn't needed on riding humans or cyborgs since the rider can't control them
/// Creatures need to be logged when being mounted
/datum/component/riding/creature/proc/log_riding(mob/living/living_parent, mob/living/rider)
if(!istype(living_parent) || !istype(rider))
return
living_parent.log_message("is now being ridden by [rider]", LOG_ATTACK, color="pink")
rider.log_message("started riding [living_parent]", LOG_ATTACK, color="pink")
// this applies to humans and most creatures, but is replaced again for cyborgs
/datum/component/riding/creature/ride_check(mob/living/rider, consequences = TRUE)
. = TRUE
var/mob/living/living_parent = parent
if(living_parent.body_position != STANDING_UP) // if we move while on the ground, the rider falls off
. = FALSE
// for piggybacks and (redundant?) borg riding, check if the rider is stunned/restrained
else if((ride_check_flags & RIDER_NEEDS_ARMS) && (HAS_TRAIT(rider, TRAIT_RESTRAINED) || rider.incapacitated(IGNORE_RESTRAINTS|IGNORE_GRAB)))
. = FALSE
// for fireman carries, check if the ridden is stunned/restrained
else if((ride_check_flags & CARRIER_NEEDS_ARM) && (HAS_TRAIT(living_parent, TRAIT_RESTRAINED) || living_parent.incapacitated(IGNORE_RESTRAINTS|IGNORE_GRAB)))
. = FALSE
if(. || !consequences)
return
rider.visible_message(span_warning("[rider] falls off of [living_parent]!"), \
span_warning("You fall off of [living_parent]!"))
rider.Paralyze(1 SECONDS)
rider.Knockdown(4 SECONDS)
living_parent.unbuckle_mob(rider)
/datum/component/riding/creature/vehicle_mob_buckle(datum/source, mob/living/rider, force = FALSE)
// Ensure that the /mob/post_buckle_mob(mob/living/M) does not mess us up with layers
// If we do not do this override we'll be stuck with the above proc (+ 0.1)-ing our rider's layer incorrectly
rider.layer = initial(rider.layer)
return ..()
/datum/component/riding/creature/vehicle_mob_unbuckle(mob/living/living_parent, mob/living/former_rider, force = FALSE)
if(istype(living_parent) && istype(former_rider))
living_parent.log_message("is no longer being ridden by [former_rider]", LOG_ATTACK, color="pink")
former_rider.log_message("is no longer riding [living_parent]", LOG_ATTACK, color="pink")
remove_abilities(former_rider)
// We gotta reset those layers at some point, don't we?
former_rider.layer = MOB_LAYER
living_parent.layer = MOB_LAYER
return ..()
/datum/component/riding/creature/driver_move(atom/movable/movable_parent, mob/living/user, direction)
if(!COOLDOWN_FINISHED(src, vehicle_move_cooldown))
return COMPONENT_DRIVER_BLOCK_MOVE
if(!keycheck(user))
if(ispath(keytype, /obj/item))
var/obj/item/key = keytype
to_chat(user, span_warning("You need a [initial(key.name)] to ride [movable_parent]!"))
return COMPONENT_DRIVER_BLOCK_MOVE
var/mob/living/living_parent = parent
var/turf/next = get_step(living_parent, direction)
step(living_parent, direction)
last_move_diagonal = ((direction & (direction - 1)) && (living_parent.loc == next))
COOLDOWN_START(src, vehicle_move_cooldown, (last_move_diagonal? 2 : 1) * vehicle_move_delay)
return ..()
/// Yeets the rider off, used for animals and cyborgs, redefined for humans who shove their piggyback rider off
/datum/component/riding/creature/proc/force_dismount(mob/living/rider, gentle = FALSE)
var/atom/movable/movable_parent = parent
movable_parent.unbuckle_mob(rider)
if(!isanimal(movable_parent) && !iscyborg(movable_parent))
return
var/turf/target = get_edge_target_turf(movable_parent, movable_parent.dir)
var/turf/targetm = get_step(get_turf(movable_parent), movable_parent.dir)
rider.Move(targetm)
rider.Knockdown(3 SECONDS)
if(gentle)
rider.visible_message(span_warning("[rider] is thrown clear of [movable_parent]!"), \
span_warning("You're thrown clear of [movable_parent]!"))
rider.throw_at(target, 8, 3, movable_parent, gentle = TRUE)
else
rider.visible_message(span_warning("[rider] is thrown violently from [movable_parent]!"), \
span_warning("You're thrown violently from [movable_parent]!"))
rider.throw_at(target, 14, 5, movable_parent, gentle = FALSE)
/// If we're a cyborg or animal and we spin, we yeet whoever's on us off us
/datum/component/riding/creature/proc/check_emote(mob/living/user, datum/emote/emote)
SIGNAL_HANDLER
if((!iscyborg(user) && !isanimal(user)) || !istype(emote, /datum/emote/spin))
return
for(var/mob/yeet_mob in user.buckled_mobs)
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/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
proc_holder.action.GiveAction(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.HideFrom(rider)
/datum/component/riding/creature/riding_can_z_move(atom/movable/movable_parent, direction, turf/start, turf/destination, z_move_flags, mob/living/rider)
if(!(z_move_flags & ZMOVE_CAN_FLY_CHECKS))
return COMPONENT_RIDDEN_ALLOW_Z_MOVE
if(!can_be_driven)
if(z_move_flags & ZMOVE_FEEDBACK)
to_chat(rider, span_warning("[movable_parent] cannot be driven around. Unbuckle from [movable_parent.p_them()] first."))
return COMPONENT_RIDDEN_STOP_Z_MOVE
if(!ride_check(rider, FALSE))
if(z_move_flags & ZMOVE_FEEDBACK)
to_chat(rider, span_warning("You're unable to ride [movable_parent] right now!"))
return COMPONENT_RIDDEN_STOP_Z_MOVE
return COMPONENT_RIDDEN_ALLOW_Z_MOVE
///////Yes, I said humans. No, this won't end well...//////////
/datum/component/riding/creature/human
can_be_driven = FALSE
/datum/component/riding/creature/human/Initialize(mob/living/riding_mob, force = FALSE, ride_check_flags = NONE, potion_boost = FALSE)
. = ..()
var/mob/living/carbon/human/human_parent = parent
human_parent.add_movespeed_modifier(/datum/movespeed_modifier/human_carry)
if(ride_check_flags & RIDER_NEEDS_ARMS) // piggyback
human_parent.buckle_lying = 0
// the riding mob is made nondense so they don't bump into any dense atoms the carrier is pulling,
// since pulled movables are moved before buckled movables
riding_mob.set_density(FALSE)
else if(ride_check_flags & CARRIER_NEEDS_ARM) // fireman
human_parent.buckle_lying = 90
/datum/component/riding/creature/human/RegisterWithParent()
. = ..()
RegisterSignal(parent, COMSIG_HUMAN_EARLY_UNARMED_ATTACK, .proc/on_host_unarmed_melee)
RegisterSignal(parent, COMSIG_LIVING_SET_BODY_POSITION, .proc/check_carrier_fall_over)
/datum/component/riding/creature/human/log_riding(mob/living/living_parent, mob/living/rider)
if(!istype(living_parent) || !istype(rider))
return
if(ride_check_flags & RIDER_NEEDS_ARMS) // piggyback
living_parent.log_message("started giving [rider] a piggyback ride", LOG_ATTACK, color="pink")
rider.log_message("started piggyback riding [living_parent]", LOG_ATTACK, color="pink")
else if(ride_check_flags & CARRIER_NEEDS_ARM) // fireman
living_parent.log_message("started fireman carrying [rider]", LOG_ATTACK, color="pink")
rider.log_message("was fireman carried by [living_parent]", LOG_ATTACK, color="pink")
/datum/component/riding/creature/human/vehicle_mob_unbuckle(datum/source, mob/living/former_rider, force = FALSE)
unequip_buckle_inhands(parent)
var/mob/living/carbon/human/H = parent
H.remove_movespeed_modifier(/datum/movespeed_modifier/human_carry)
former_rider.set_density(!former_rider.body_position)
return ..()
/// If the carrier shoves the person they're carrying, force the carried mob off
/datum/component/riding/creature/human/proc/on_host_unarmed_melee(mob/living/carbon/human/human_parent, atom/target, proximity, modifiers)
SIGNAL_HANDLER
if(LAZYACCESS(modifiers, RIGHT_CLICK) && (target in human_parent.buckled_mobs))
force_dismount(target)
return COMPONENT_CANCEL_ATTACK_CHAIN
/// If the carrier gets knocked over, force the rider(s) off and see if someone got hurt
/datum/component/riding/creature/human/proc/check_carrier_fall_over(mob/living/carbon/human/human_parent)
SIGNAL_HANDLER
for(var/i in human_parent.buckled_mobs)
var/mob/living/rider = i
human_parent.unbuckle_mob(rider)
rider.Paralyze(1 SECONDS)
rider.Knockdown(4 SECONDS)
human_parent.visible_message(span_danger("[rider] topples off of [human_parent] as they both fall to the ground!"), \
span_warning("You fall to the ground, bringing [rider] with you!"), span_hear("You hear two consecutive thuds."), COMBAT_MESSAGE_RANGE, ignored_mobs=rider)
to_chat(rider, span_danger("[human_parent] falls to the ground, bringing you with [human_parent.p_them()]!"))
/datum/component/riding/creature/human/handle_vehicle_layer(dir)
var/atom/movable/AM = parent
if(!AM.buckled_mobs || !AM.buckled_mobs.len)
AM.layer = MOB_LAYER
return
for(var/mob/M in AM.buckled_mobs) //ensure proper layering of piggyback and carry, sometimes weird offsets get applied
M.layer = MOB_LAYER
if(!AM.buckle_lying) // rider is vertical, must be piggybacking
if(dir == SOUTH)
AM.layer = MOB_ABOVE_PIGGYBACK_LAYER
else
AM.layer = MOB_BELOW_PIGGYBACK_LAYER
else // laying flat, we must be firemanning the rider
if(dir == NORTH)
AM.layer = MOB_BELOW_PIGGYBACK_LAYER
else
AM.layer = MOB_ABOVE_PIGGYBACK_LAYER
/datum/component/riding/creature/human/get_offsets(pass_index)
var/mob/living/carbon/human/H = parent
if(H.buckle_lying)
return list(TEXT_NORTH = list(0, 6), TEXT_SOUTH = list(0, 6), TEXT_EAST = list(0, 6), TEXT_WEST = list(0, 6))
else
return list(TEXT_NORTH = list(0, 6), TEXT_SOUTH = list(0, 6), TEXT_EAST = list(-6, 4), TEXT_WEST = list( 6, 4))
/datum/component/riding/creature/human/force_dismount(mob/living/dismounted_rider)
var/atom/movable/AM = parent
AM.unbuckle_mob(dismounted_rider)
dismounted_rider.Paralyze(1 SECONDS)
dismounted_rider.Knockdown(4 SECONDS)
dismounted_rider.visible_message(span_warning("[AM] pushes [dismounted_rider] off of [AM.p_them()]!"), \
span_warning("[AM] pushes you off of [AM.p_them()]!"))
//Now onto cyborg riding//
/datum/component/riding/creature/cyborg
can_be_driven = FALSE
/datum/component/riding/creature/cyborg/ride_check(mob/living/user, consequences = TRUE)
var/mob/living/silicon/robot/robot_parent = parent
if(!iscarbon(user))
return TRUE
. = user.usable_hands
if(!. && consequences)
Unbuckle(user)
to_chat(user, span_warning("You can't grab onto [robot_parent] with no hands!"))
/datum/component/riding/creature/cyborg/handle_vehicle_layer(dir)
var/atom/movable/robot_parent = parent
if(dir == SOUTH)
robot_parent.layer = MOB_ABOVE_PIGGYBACK_LAYER
else
robot_parent.layer = MOB_BELOW_PIGGYBACK_LAYER
/datum/component/riding/creature/cyborg/get_offsets(pass_index) // list(dir = x, y, layer)
return list(TEXT_NORTH = list(0, 4), TEXT_SOUTH = list(0, 4), TEXT_EAST = list(-6, 3), TEXT_WEST = list( 6, 3))
/datum/component/riding/creature/cyborg/handle_vehicle_offsets(dir)
var/mob/living/silicon/robot/robot_parent = parent
for(var/mob/living/rider in robot_parent.buckled_mobs)
rider.setDir(dir)
if(istype(robot_parent.model))
rider.pixel_x = robot_parent.model.ride_offset_x[dir2text(dir)]
rider.pixel_y = robot_parent.model.ride_offset_y[dir2text(dir)]
//now onto every other ridable mob//
/datum/component/riding/creature/mulebot/handle_specials()
. = ..()
var/atom/movable/movable_parent = parent
set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(0, 12), TEXT_SOUTH = list(0, 12), TEXT_EAST = list(0, 12), TEXT_WEST = list(0, 12)))
set_vehicle_dir_layer(SOUTH, movable_parent.layer) //vehicles default to ABOVE_MOB_LAYER while moving, let's make sure that doesn't happen while a mob is riding us.
set_vehicle_dir_layer(NORTH, movable_parent.layer)
set_vehicle_dir_layer(EAST, movable_parent.layer)
set_vehicle_dir_layer(WEST, movable_parent.layer)
/datum/component/riding/creature/cow/handle_specials()
. = ..()
set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(0, 8), TEXT_SOUTH = list(0, 8), TEXT_EAST = list(-2, 8), TEXT_WEST = list(2, 8)))
set_vehicle_dir_layer(SOUTH, ABOVE_MOB_LAYER)
set_vehicle_dir_layer(NORTH, OBJ_LAYER)
set_vehicle_dir_layer(EAST, OBJ_LAYER)
set_vehicle_dir_layer(WEST, OBJ_LAYER)
/datum/component/riding/creature/bear/handle_specials()
. = ..()
set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(1, 8), TEXT_SOUTH = list(1, 8), TEXT_EAST = list(-3, 6), TEXT_WEST = list(3, 6)))
set_vehicle_dir_layer(SOUTH, ABOVE_MOB_LAYER)
set_vehicle_dir_layer(NORTH, OBJ_LAYER)
set_vehicle_dir_layer(EAST, ABOVE_MOB_LAYER)
set_vehicle_dir_layer(WEST, ABOVE_MOB_LAYER)
/datum/component/riding/creature/carp
override_allow_spacemove = TRUE
/datum/component/riding/creature/carp/handle_specials()
. = ..()
set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(0, 13), TEXT_SOUTH = list(0, 15), TEXT_EAST = list(-2, 12), TEXT_WEST = list(2, 12)))
set_vehicle_dir_layer(SOUTH, ABOVE_MOB_LAYER)
set_vehicle_dir_layer(NORTH, OBJ_LAYER)
set_vehicle_dir_layer(EAST, OBJ_LAYER)
set_vehicle_dir_layer(WEST, OBJ_LAYER)
/datum/component/riding/creature/megacarp/handle_specials()
. = ..()
var/atom/movable/movable_parent = parent
set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(1, 8), TEXT_SOUTH = list(1, 8), TEXT_EAST = list(-3, 6), TEXT_WEST = list(3, 6)))
set_vehicle_dir_offsets(SOUTH, movable_parent.pixel_x, 0)
set_vehicle_dir_offsets(NORTH, movable_parent.pixel_x, 0)
set_vehicle_dir_offsets(EAST, movable_parent.pixel_x, 0)
set_vehicle_dir_offsets(WEST, movable_parent.pixel_x, 0)
set_vehicle_dir_layer(SOUTH, ABOVE_MOB_LAYER)
set_vehicle_dir_layer(NORTH, OBJ_LAYER)
set_vehicle_dir_layer(EAST, OBJ_LAYER)
set_vehicle_dir_layer(WEST, OBJ_LAYER)
/datum/component/riding/creature/vatbeast
override_allow_spacemove = TRUE
can_use_abilities = TRUE
/datum/component/riding/creature/vatbeast/handle_specials()
. = ..()
set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(0, 15), TEXT_SOUTH = list(0, 15), TEXT_EAST = list(-10, 15), TEXT_WEST = list(10, 15)))
set_vehicle_dir_layer(SOUTH, ABOVE_MOB_LAYER)
set_vehicle_dir_layer(NORTH, OBJ_LAYER)
set_vehicle_dir_layer(EAST, OBJ_LAYER)
set_vehicle_dir_layer(WEST, OBJ_LAYER)
/datum/component/riding/creature/goliath
keytype = /obj/item/key/lasso
/datum/component/riding/creature/goliath/handle_specials()
. = ..()
set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(0, 8), TEXT_SOUTH = list(0, 8), TEXT_EAST = list(-2, 8), TEXT_WEST = list(2, 8)))
set_vehicle_dir_layer(SOUTH, ABOVE_MOB_LAYER)
set_vehicle_dir_layer(NORTH, OBJ_LAYER)
set_vehicle_dir_layer(EAST, OBJ_LAYER)
set_vehicle_dir_layer(WEST, OBJ_LAYER)
/datum/component/riding/creature/glutton/handle_specials()
. = ..()
var/atom/movable/movable_parent = parent
set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(0, 24), TEXT_SOUTH = list(0, 24), TEXT_EAST = list(-16, 24), TEXT_WEST = list(16, 24)))
set_vehicle_dir_layer(SOUTH, ABOVE_MOB_LAYER)
set_vehicle_dir_layer(NORTH, OBJ_LAYER)
set_vehicle_dir_layer(EAST, ABOVE_MOB_LAYER)
set_vehicle_dir_layer(WEST, ABOVE_MOB_LAYER)
set_vehicle_dir_offsets(SOUTH, movable_parent.pixel_x, 0)
set_vehicle_dir_offsets(NORTH, movable_parent.pixel_x, 0)
set_vehicle_dir_offsets(EAST, movable_parent.pixel_x, 0)
set_vehicle_dir_offsets(WEST, movable_parent.pixel_x, 0)