mirror of
https://github.com/Citadel-Station-13/Citadel-Station-13-RP.git
synced 2026-08-21 07:56:35 +01:00
501 lines
18 KiB
Plaintext
501 lines
18 KiB
Plaintext
/**
|
|
* Riding handler components
|
|
*
|
|
* Handles the motion tracking/pixel offsetting/etc of riding
|
|
*
|
|
* This component takes over **after** something is buckled to the atom.
|
|
* Before that, riding_filter handles it.
|
|
*/
|
|
/datum/component/riding_handler
|
|
//? disabled as we don't have dupe handling
|
|
can_transfer = FALSE
|
|
dupe_mode = COMPONENT_DUPE_UNIQUE
|
|
registered_type = /datum/component/riding_handler
|
|
//! main
|
|
/// expected typepath of what we're handling for
|
|
var/expected_typepath = /atom/movable
|
|
|
|
//! check flags
|
|
/// rider check flags : kicks people off if they don't meet these requirements.
|
|
var/rider_check_flags = NONE
|
|
/// ridden check flags : kicks people off if the parent atom doesn't meet these requirements.
|
|
var/ridden_check_flags = NONE
|
|
/// handler flags : determines some of our behavior
|
|
var/riding_handler_flags = CF_RIDING_HANDLER_ALLOW_BORDER
|
|
|
|
//! offsets - highly optimized, eat my ass if you can't handle it, we need high performance for /Move()s.
|
|
/**
|
|
* holds vehicle offsets
|
|
*
|
|
* list(x, y) OR list(list(x, y), list(x, y), list(x, y), list(x, y)) for NESW
|
|
*/
|
|
var/list/vehicle_offsets = list(0, 0)
|
|
/**
|
|
* holds rider offsets
|
|
* first: x
|
|
* second: y
|
|
* third: layer
|
|
* fourth: dir
|
|
*
|
|
* see [code/__DEFINES/dcs/components/riding.dm]
|
|
*/
|
|
var/list/rider_offsets = list(0, 0, 0, null)
|
|
/// see [code/__DEFINES/dcs/components/riding.dm]
|
|
var/rider_offset_format = CF_RIDING_OFFSETS_SIMPLE
|
|
|
|
//! component-controlled movemnet
|
|
/// default allowed turf handling - set to off if you override the var!!
|
|
var/default_turf_checks = TRUE
|
|
/// allowed turf types - turned into typecache.
|
|
var/list/allowed_turf_types
|
|
/// forbidden turf types - turned into typecache.
|
|
var/list/forbid_turf_types
|
|
/// tick delay between movements
|
|
var/vehicle_move_delay = 2
|
|
/// key type needed, if it is needed
|
|
var/keytype
|
|
/// verb for controlling this
|
|
var/drive_verb = "drive"
|
|
|
|
//! operational - general
|
|
/// last dir. used to avoid redoing expensive setdir stuff
|
|
var/tmp/_last_dir
|
|
|
|
//! operational - driving
|
|
/// typecache of allowed turfs
|
|
VAR_PRIVATE/tmp/list/allowed_turf_typecache
|
|
/// typecache of forbidden turfs
|
|
VAR_PRIVATE/tmp/list/forbid_turf_typecache
|
|
/// last time we moved via driving
|
|
var/last_move_time = 0
|
|
/// next time we can move via driving
|
|
var/next_move_time
|
|
/// was the last move diagonal?
|
|
var/last_move_diagonal = FALSE
|
|
/// last turf we were on
|
|
var/turf/last_turf
|
|
|
|
/datum/component/riding_handler/Initialize()
|
|
. = ..()
|
|
if(. == COMPONENT_INCOMPATIBLE)
|
|
return
|
|
if(!istype(parent, expected_typepath))
|
|
return COMPONENT_INCOMPATIBLE
|
|
build_turf_typecaches()
|
|
|
|
/datum/component/riding_handler/RegisterWithParent()
|
|
. = ..()
|
|
RegisterSignal(parent, COMSIG_MOVABLE_MOB_BUCKLED, PROC_REF(signal_hook_mob_buckled))
|
|
RegisterSignal(parent, COMSIG_MOVABLE_MOB_UNBUCKLED, PROC_REF(signal_hook_mob_unbuckled))
|
|
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(signal_hook_handle_move))
|
|
RegisterSignal(parent, COMSIG_ATOM_DIR_CHANGE, PROC_REF(signal_hook_handle_turn))
|
|
RegisterSignal(parent, COMSIG_ATOM_RELAYMOVE_FROM_BUCKLED, PROC_REF(signal_hook_handle_relaymove))
|
|
RegisterSignal(parent, COMSIG_MOVABLE_PRE_BUCKLE_MOB, PROC_REF(signal_hook_pre_buckle_mob))
|
|
RegisterSignal(parent, COMSIG_MOVABLE_PIXEL_OFFSET_CHANGED, PROC_REF(signal_hook_pixel_offset_changed))
|
|
|
|
/datum/component/riding_handler/UnregisterFromParent()
|
|
. = ..()
|
|
UnregisterSignal(parent, list(
|
|
COMSIG_MOVABLE_MOB_BUCKLED,
|
|
COMSIG_MOVABLE_MOB_UNBUCKLED,
|
|
COMSIG_MOVABLE_MOVED,
|
|
COMSIG_ATOM_DIR_CHANGE,
|
|
COMSIG_ATOM_RELAYMOVE_FROM_BUCKLED,
|
|
COMSIG_MOVABLE_PRE_BUCKLE_MOB
|
|
))
|
|
|
|
/datum/component/riding_handler/proc/signal_hook_mob_buckled(atom/movable/source, mob/M, buckle_flags, mob/user, semantic)
|
|
SIGNAL_HANDLER
|
|
on_rider_buckled(M, semantic)
|
|
|
|
/datum/component/riding_handler/proc/signal_hook_mob_unbuckled(atom/movable/source, mob/M, buckle_flags, mob/user, semantic)
|
|
SIGNAL_HANDLER
|
|
on_rider_unbuckled(M, semantic)
|
|
if(!source.has_buckled_mobs() && (riding_handler_flags & CF_RIDING_HANDLER_EPHEMERAL))
|
|
qdel(src)
|
|
|
|
/datum/component/riding_handler/proc/signal_hook_handle_move(atom/movable/source, atom/old_loc, dir)
|
|
SIGNAL_HANDLER
|
|
update_riders_on_move(old_loc, dir)
|
|
last_turf = isturf(old_loc)? old_loc : null
|
|
|
|
/datum/component/riding_handler/proc/signal_hook_handle_turn(atom/movable/source, old_dir, new_dir)
|
|
SIGNAL_HANDLER
|
|
update_vehicle_on_turn(new_dir)
|
|
full_update_riders(new_dir)
|
|
|
|
/datum/component/riding_handler/proc/signal_hook_pre_buckle_mob(atom/movable/source, mob/M, flags, mob/user, semantic)
|
|
SIGNAL_HANDLER_DOES_SLEEP
|
|
if(!check_rider(M, semantic, TRUE, user = user))
|
|
return SIGNAL_RETURN_BLOCK_BUCKLE_OPERATION
|
|
|
|
/datum/component/riding_handler/proc/signal_hook_pixel_offset_changed(atom/movable/source)
|
|
full_update_riders(null, TRUE)
|
|
|
|
/datum/component/riding_handler/proc/update_vehicle_on_turn(dir)
|
|
if(!vehicle_offsets)
|
|
return
|
|
var/atom/movable/AM = parent
|
|
var/opx = AM.get_managed_pixel_x()
|
|
var/opy = AM.get_managed_pixel_y()
|
|
if(islist(vehicle_offsets[1]))
|
|
// NESW format
|
|
switch(dir)
|
|
if(NORTH)
|
|
AM.pixel_x = vehicle_offsets[1][1] + opx
|
|
AM.pixel_y = vehicle_offsets[1][2] + opy
|
|
if(EAST)
|
|
AM.pixel_x = vehicle_offsets[2][1] + opx
|
|
AM.pixel_y = vehicle_offsets[2][2] + opy
|
|
if(SOUTH)
|
|
AM.pixel_x = vehicle_offsets[3][1] + opx
|
|
AM.pixel_y = vehicle_offsets[3][2] + opy
|
|
if(WEST)
|
|
AM.pixel_x = vehicle_offsets[4][1] + opx
|
|
AM.pixel_y = vehicle_offsets[4][2] + opy
|
|
else
|
|
AM.pixel_x = vehicle_offsets[1] + opx
|
|
AM.pixel_y = vehicle_offsets[2] + opy
|
|
|
|
/datum/component/riding_handler/proc/on_rider_buckled(mob/rider, semantic)
|
|
full_update_riders(force = TRUE)
|
|
if(isliving(parent))
|
|
var/mob/living/L = parent
|
|
if(istype(L.ai_holder, /datum/ai_holder/polaris))
|
|
var/datum/ai_holder/polaris/ai_holder = L.ai_holder
|
|
ai_holder?.pause_automated_movement()
|
|
|
|
/datum/component/riding_handler/proc/on_rider_unbuckled(mob/rider, semantic)
|
|
reset_rider(rider, semantic)
|
|
full_update_riders(force = TRUE)
|
|
if(isliving(parent))
|
|
var/mob/living/L = parent
|
|
if(istype(L.ai_holder, /datum/ai_holder/polaris))
|
|
var/datum/ai_holder/polaris/ai_holder = L.ai_holder
|
|
ai_holder?.resume_automated_movement()
|
|
|
|
/datum/component/riding_handler/proc/reset_rider(mob/rider, semantic)
|
|
rider.reset_plane_and_layer()
|
|
rider.reset_pixel_shifting()
|
|
rider.reset_pixel_offsets()
|
|
|
|
/datum/component/riding_handler/proc/update_riders_on_turn(dir)
|
|
full_update_riders()
|
|
|
|
/datum/component/riding_handler/proc/full_update_riders(dir, force)
|
|
var/atom/movable/AM = parent
|
|
if(!dir)
|
|
dir = AM.dir
|
|
if(_last_dir == dir && !force)
|
|
return
|
|
_last_dir = dir
|
|
// the ridden mob is assumed to be centered in their icon dimensions!
|
|
var/self_center_px = AM.get_centering_pixel_x_offset(dir)
|
|
var/self_center_py = AM.get_centering_pixel_y_offset(dir)
|
|
var/resizeX = 1
|
|
var/resizeY = 1
|
|
if(isliving(AM))
|
|
var/mob/living/mounted = AM
|
|
resizeX *= mounted.size_multiplier * mounted.icon_scale_x
|
|
resizeY *= mounted.size_multiplier * mounted.icon_scale_y
|
|
self_center_px = 2*mounted.shift_pixel_x - self_center_px //Jank to negate the offset, while following the shift
|
|
self_center_py = 2*mounted.shift_pixel_y - self_center_py
|
|
var/list/offsets
|
|
var/i
|
|
var/mob/M
|
|
var/semantic
|
|
var/dir_override
|
|
// determine offset format
|
|
switch(rider_offset_format)
|
|
if(CF_RIDING_OFFSETS_SIMPLE)
|
|
for(i in 1 to length(AM.buckled_mobs))
|
|
M = AM.buckled_mobs[i]
|
|
semantic = AM.buckled_mobs[M]
|
|
offsets = rider_offsets(M, i, semantic, rider_offsets, dir)
|
|
dir_override = offsets[4] || dir
|
|
if(dir_override != M.dir)
|
|
M.setDir(offsets[4])
|
|
M.reset_pixel_shifting()
|
|
M.set_base_layer(offsets[3] + AM.layer)
|
|
// riders are assumed centered to their icon dim x/y's
|
|
// we still take into account managed pixel x/y for whatever reason, mostly because
|
|
// we assume things like taur / up/down shifting is in there
|
|
// maybe we should look into this
|
|
M.pixel_x = self_center_px + M.get_managed_pixel_x() + M.get_centering_pixel_x_offset() + resizeX * offsets[1]
|
|
M.pixel_y = self_center_py + M.get_managed_pixel_y() + M.get_centering_pixel_y_offset() + resizeY * offsets[2]
|
|
if(CF_RIDING_OFFSETS_DIRECTIONAL)
|
|
var/list/relevant
|
|
switch(dir)
|
|
if(NORTH)
|
|
relevant = rider_offsets[1]
|
|
if(SOUTH)
|
|
relevant = rider_offsets[3]
|
|
if(EAST)
|
|
relevant = rider_offsets[2]
|
|
if(WEST)
|
|
relevant = rider_offsets[4]
|
|
for(i in 1 to length(AM.buckled_mobs))
|
|
M = AM.buckled_mobs[i]
|
|
semantic = AM.buckled_mobs[M]
|
|
offsets = rider_offsets(M, i, semantic, relevant, dir)
|
|
dir_override = offsets[4] || dir
|
|
if(dir_override != M.dir)
|
|
M.setDir(offsets[4])
|
|
M.reset_pixel_shifting()
|
|
M.set_base_layer(offsets[3] + AM.layer)
|
|
// riders are assumed centered to their icon dim x/y's
|
|
// we still take into account managed pixel x/y for whatever reason, mostly because
|
|
// we assume things like taur / up/down shifting is in there
|
|
// maybe we should look into this
|
|
M.pixel_x = self_center_px + M.get_managed_pixel_x() + M.get_centering_pixel_x_offset() + resizeX * offsets[1]
|
|
M.pixel_y = self_center_py + M.get_managed_pixel_y() + M.get_centering_pixel_y_offset() + resizeY * offsets[2]
|
|
if(CF_RIDING_OFFSETS_ENUMERATED)
|
|
var/list/relevant
|
|
var/rider_offsets_len = length(rider_offsets)
|
|
for(i in 1 to length(AM.buckled_mobs))
|
|
relevant = rider_offsets[min(rider_offsets_len, i)]
|
|
switch(dir)
|
|
if(NORTH)
|
|
relevant = relevant[1]
|
|
if(SOUTH)
|
|
relevant = relevant[3]
|
|
if(EAST)
|
|
relevant = relevant[2]
|
|
if(WEST)
|
|
relevant = relevant[4]
|
|
M = AM.buckled_mobs[i]
|
|
semantic = AM.buckled_mobs[M]
|
|
offsets = rider_offsets(M, i, semantic, relevant, dir)
|
|
dir_override = offsets[4] || dir
|
|
if(dir_override != M.dir)
|
|
M.setDir(offsets[4])
|
|
M.reset_pixel_shifting()
|
|
M.set_base_layer(offsets[3] + AM.layer)
|
|
// riders are assumed centered to their icon dim x/y's
|
|
// we still take into account managed pixel x/y for whatever reason, mostly because
|
|
// we assume things like taur / up/down shifting is in there
|
|
// maybe we should look into this
|
|
M.pixel_x = self_center_px + M.get_managed_pixel_x() + M.get_centering_pixel_x_offset() + resizeX * offsets[1]
|
|
M.pixel_y = self_center_py + M.get_managed_pixel_y() + M.get_centering_pixel_y_offset() + resizeY * offsets[2]
|
|
|
|
/**
|
|
* returns transformed rider offset list
|
|
*
|
|
* DO NOT CHANGE DEFAULT FOR THE LOVE OF GOD, COPY IT IF YOU ARE CHANGING IT!!
|
|
* You just need to call the parent to get it, even.
|
|
*/
|
|
/datum/component/riding_handler/proc/rider_offsets(mob/rider, pos, semantic, list/default, dir)
|
|
return default.Copy()
|
|
|
|
/datum/component/riding_handler/proc/signal_hook_handle_relaymove(datum/source, mob/M, dir)
|
|
attempt_drive(M, dir)
|
|
return COMPONENT_RELAYMOVE_HANDLED
|
|
|
|
/**
|
|
* called to check if a mob has keys to us
|
|
*/
|
|
/datum/component/riding_handler/proc/keycheck(mob/M)
|
|
return !keytype || M.get_held_item_of_type(keytype)
|
|
|
|
/**
|
|
* handles building our typecaches
|
|
*/
|
|
/datum/component/riding_handler/proc/build_turf_typecaches()
|
|
allowed_turf_types = typelist(NAMEOF(src, allowed_turf_types), allowed_turf_types)
|
|
forbid_turf_types = typelist(NAMEOF(src, forbid_turf_types), forbid_turf_types)
|
|
if(!default_turf_checks)
|
|
return
|
|
if(LAZYLEN(allowed_turf_types))
|
|
if(has_typelist(NAMEOF(src, allowed_turf_typecache)))
|
|
allowed_turf_typecache = get_typelist(NAMEOF(src, allowed_turf_typecache))
|
|
else
|
|
allowed_turf_typecache = typelist(NAMEOF(src, allowed_turf_typecache), typecacheof(allowed_turf_types))
|
|
if(LAZYLEN(forbid_turf_types))
|
|
if(has_typelist(NAMEOF(src, forbid_turf_typecache)))
|
|
forbid_turf_typecache = get_typelist(NAMEOF(src, forbid_turf_typecache))
|
|
else
|
|
forbid_turf_typecache = typelist(NAMEOF(src, forbid_turf_typecache), typecacheof(forbid_turf_types))
|
|
|
|
/**
|
|
* checks if we can move onto a turf
|
|
*/
|
|
/datum/component/riding_handler/proc/turfcheck(turf/next)
|
|
if(!default_turf_checks)
|
|
stack_trace("default turf checks was off even though we use them")
|
|
default_turf_checks = TRUE
|
|
return (!allowed_turf_typecache || allowed_turf_typecache[next.type]) && (!forbid_turf_typecache || !forbid_turf_typecache[next.type])
|
|
|
|
/**
|
|
* handles checking if we can go on a turf
|
|
* don't override this, override turfcheck().
|
|
*/
|
|
/datum/component/riding_handler/proc/_check_turf(turf/next)
|
|
SHOULD_NOT_OVERRIDE(TRUE)
|
|
if(riding_handler_flags & CF_RIDING_HANDLER_ALLOW_BORDER)
|
|
// allow one off
|
|
if(riding_handler_flags & CF_RIDING_HANDLER_FORBID_BORDER_CROSS)
|
|
// allow if last was fine, or if next and current are fine
|
|
var/current_fine = turfcheck(get_turf(parent))
|
|
// this WILL let us skip over diagonal boundaries, so you have to design around that!
|
|
return current_fine ? (turfcheck(next) || turfcheck(last_turf)) : (turfcheck(next) && (get_dist(next, last_turf) <= 1))
|
|
else
|
|
// allow either if next or current is fine, which lets us cross 1-wide borders
|
|
return turfcheck(next) || turfcheck(get_turf(parent))
|
|
else
|
|
// don't
|
|
return turfcheck(next)
|
|
|
|
/**
|
|
* called when a mob wants to drive us
|
|
*/
|
|
/datum/component/riding_handler/proc/attempt_drive(mob/M, dir)
|
|
if(!(riding_handler_flags & CF_RIDING_HANDLER_IS_CONTROLLABLE))
|
|
return FALSE
|
|
var/atom/movable/AM = parent
|
|
if(!AM.loc)
|
|
return FALSE
|
|
// cheap checks first
|
|
if(world.time < next_move_time)
|
|
return
|
|
// then expensive
|
|
if(!driver_check(M))
|
|
return
|
|
if(!keycheck(M))
|
|
if(CHATSPAM_THROTTLE_DEFAULT)
|
|
to_chat(M, SPAN_WARNING("You must have one of the keys in your hand to drive [AM]!"))
|
|
return FALSE
|
|
var/turf/next = get_step(AM, dir)
|
|
if(!_check_turf(next))
|
|
if(CHATSPAM_THROTTLE_DEFAULT)
|
|
to_chat(M, SPAN_WARNING("[parent] cannot drive onto [next]!"))
|
|
return FALSE
|
|
if(!process_spacemove(dir))
|
|
return FALSE
|
|
// move
|
|
last_move_time = world.time
|
|
next_move_time = world.time + (last_move_diagonal? SQRT_2 : 1) * vehicle_move_delay
|
|
step(AM, dir)
|
|
last_move_diagonal = (AM.loc == next) && (ISDIAGONALDIR(dir))
|
|
return TRUE
|
|
|
|
/**
|
|
* arbitrary driver check for a mob
|
|
*/
|
|
/datum/component/riding_handler/proc/driver_check(mob/M)
|
|
return TRUE
|
|
|
|
/**
|
|
* handles checks/updates when we move
|
|
*/
|
|
/datum/component/riding_handler/proc/update_riders_on_move(atom/old_loc, dir)
|
|
var/atom/movable/AM = parent
|
|
// first check ridden mob
|
|
if(!check_ridden(parent, TRUE))
|
|
// kick everyone off
|
|
for(var/mob/M as anything in AM.buckled_mobs)
|
|
force_dismount(M)
|
|
return
|
|
for(var/mob/M as anything in AM.buckled_mobs)
|
|
if(!check_rider(M, AM.buckled_mobs[M], TRUE))
|
|
force_dismount(M)
|
|
continue // don't do rest of logic
|
|
|
|
/**
|
|
* checks if a person can stay on us. if not, they'll be kicked off by ride_check()
|
|
*/
|
|
/datum/component/riding_handler/proc/check_rider(mob/M, semantic, notify, mob/user)
|
|
return check_entity(M, rider_check_flags, semantic, notify, user)
|
|
|
|
/**
|
|
* checks if the vehicle is usable right now.
|
|
* if not, kicks everyone off.
|
|
*/
|
|
/datum/component/riding_handler/proc/check_ridden(atom/movable/AM, notify, mob/user)
|
|
return check_entity(AM, ridden_check_flags, BUCKLE_SEMANTIC_WE_ARE_THE_VEHICLE, notify, user)
|
|
|
|
/**
|
|
* checks an atom of riding flags
|
|
*/
|
|
/datum/component/riding_handler/proc/check_entity(atom/movable/AM, flags, semantic, notify, mob/user)
|
|
var/mob/M = ismob(AM)? AM : null
|
|
if(!user)
|
|
user = M
|
|
var/we_are_the_vehicle = semantic == BUCKLE_SEMANTIC_WE_ARE_THE_VEHICLE
|
|
if(M && flags & CF_RIDING_CHECK_ARMS)
|
|
// unimplemented
|
|
pass()
|
|
if(M && (flags & CF_RIDING_CHECK_LEGS))
|
|
// unimplemented
|
|
pass()
|
|
if(M && (flags & CF_RIDING_CHECK_RESTRAINED) && M.restrained())
|
|
if(notify && user)
|
|
if(we_are_the_vehicle)
|
|
to_chat(user, SPAN_WARNING("You cannot carry people while restrained!"))
|
|
else if(user == AM)
|
|
to_chat(user, SPAN_WARNING("You cannot ride on [parent] while restrained!"))
|
|
else
|
|
to_chat(user, SPAN_WARNING("[AM] cannot ride on [parent] whlie restrained!"))
|
|
return FALSE
|
|
if(M && (flags & CF_RIDING_CHECK_UNCONSCIOUS) && !IS_CONSCIOUS(M))
|
|
if(notify && user)
|
|
if(we_are_the_vehicle)
|
|
to_chat(user, SPAN_WARNING("You cannot carry people while unconscious!"))
|
|
else if(user == AM)
|
|
to_chat(user, SPAN_WARNING("You cannot ride on [parent] while unconscious!"))
|
|
else
|
|
to_chat(user, SPAN_WARNING("[AM] cannot ride on [parent] whlie unconscious!"))
|
|
return FALSE
|
|
if(M && (flags & CF_RIDING_CHECK_RESTRAINED) && M.restrained())
|
|
if(notify && user)
|
|
if(we_are_the_vehicle)
|
|
to_chat(user, SPAN_WARNING("You cannot carry people while restrained!"))
|
|
else if(user == AM)
|
|
to_chat(user, SPAN_WARNING("You cannot ride on [parent] while restrained!"))
|
|
else
|
|
to_chat(user, SPAN_WARNING("[AM] cannot ride on [parent] whlie restrained!"))
|
|
return FALSE
|
|
if(M && (flags & CF_RIDING_CHECK_INCAPACITATED) && M.incapacitated())
|
|
if(notify && user)
|
|
if(we_are_the_vehicle)
|
|
to_chat(user, SPAN_WARNING("You cannot carry people while incapacitated!"))
|
|
else if(user == AM)
|
|
to_chat(user, SPAN_WARNING("You cannot ride on [parent] while incapacitated!"))
|
|
else
|
|
to_chat(user, SPAN_WARNING("[AM] cannot ride on [parent] whlie incapacitated!"))
|
|
return FALSE
|
|
if(M && (flags & CF_RIDING_CHECK_LYING) && M.lying)
|
|
if(notify && user)
|
|
if(we_are_the_vehicle)
|
|
to_chat(user, SPAN_WARNING("You cannot carry people while laying down!"))
|
|
else if(user == AM)
|
|
to_chat(user, SPAN_WARNING("You cannot ride on [parent] while laying down!"))
|
|
else
|
|
to_chat(user, SPAN_WARNING("[AM] cannot ride on [parent] whlie laying down!"))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/**
|
|
* checks if someone can stay on. if they can't, kick them off.
|
|
*/
|
|
/datum/component/riding_handler/proc/ride_check(mob/M)
|
|
var/atom/movable/AM = parent
|
|
if(!check_rider(M, AM.buckled_mobs[M]))
|
|
force_dismount(M, AM.buckled_mobs[M])
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/**
|
|
* kicks off a rider
|
|
*/
|
|
/datum/component/riding_handler/proc/force_dismount(mob/M, semantic)
|
|
var/atom/movable/AM = parent
|
|
AM.visible_message(
|
|
SPAN_WARNING("[M] falls off of [AM]!"),
|
|
SPAN_WARNING("You fall off of [AM]!")
|
|
)
|
|
AM.unbuckle_mob(M, BUCKLE_OP_FORCE)
|
|
|
|
/datum/component/riding_handler/proc/process_spacemove()
|
|
var/atom/movable/AM = parent
|
|
return (riding_handler_flags & CF_RIDING_HANDLER_FORCED_SPACEMOVE) || AM.Process_Spacemove()
|