diff --git a/code/datums/components/README.md b/code/datums/components/README.md
index 026b387e27..110b3626a3 100644
--- a/code/datums/components/README.md
+++ b/code/datums/components/README.md
@@ -26,7 +26,7 @@ Stands have a lot of procs which mimic mob procs. Rather than inserting hooks fo
### Defines
-1. `COMPONENT_INCOMPATIBLE` Return this from `/datum/component/Initialize` or `datum/component/OnTransfer` to have the component be deleted if it's applied to an incorrect type. `parent` must not be modified if this is to be returned.
+1. `COMPONENT_INCOMPATIBLE` Return this from `/datum/component/Initialize` or `datum/component/OnTransfer` to have the component be deleted if it's applied to an incorrect type. `parent` must not be modified if this is to be returned. This will be noted in the runtime logs
### Vars
diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm
index 423dcf82df..4076918ecf 100644
--- a/code/datums/components/_component.dm
+++ b/code/datums/components/_component.dm
@@ -10,7 +10,7 @@
var/list/arguments = args.Copy(2)
if(Initialize(arglist(arguments)) == COMPONENT_INCOMPATIBLE)
qdel(src, TRUE, TRUE)
- CRASH("Incompatible [type] assigned to a [P]!")
+ CRASH("Incompatible [type] assigned to a [P.type]!")
_JoinParent(P)
@@ -233,7 +233,9 @@
//if we're taking to the same thing no need for anything
return
if(C.OnTransfer(src) == COMPONENT_INCOMPATIBLE)
+ var/c_type = C.type
qdel(C)
+ CRASH("Incompatible [c_type] transfer attempt to a [type]!")
return
C._RemoveFromParent()
helicopter.SendSignal(COMSIG_COMPONENT_REMOVING, C)
diff --git a/code/datums/components/beauty.dm b/code/datums/components/beauty.dm
index 4dbfca4014..f6031046bd 100644
--- a/code/datums/components/beauty.dm
+++ b/code/datums/components/beauty.dm
@@ -3,8 +3,7 @@
/datum/component/beauty/Initialize(beautyamount)
if(!ismovableatom(parent))
- . = COMPONENT_INCOMPATIBLE
- CRASH("Someone put a beauty component on a non-atom/movable, not everything can be pretty.")
+ return COMPONENT_INCOMPATIBLE
beauty = beautyamount
RegisterSignal(COMSIG_ENTER_AREA, .proc/enter_area)
RegisterSignal(COMSIG_EXIT_AREA, .proc/exit_area)
diff --git a/code/datums/components/cleaning.dm b/code/datums/components/cleaning.dm
index 9566b5faab..6f2f08c617 100644
--- a/code/datums/components/cleaning.dm
+++ b/code/datums/components/cleaning.dm
@@ -3,8 +3,7 @@
/datum/component/cleaning/Initialize()
if(!ismovableatom(parent))
- . = COMPONENT_INCOMPATIBLE
- CRASH("[type] added to a [parent.type]")
+ return COMPONENT_INCOMPATIBLE
RegisterSignal(list(COMSIG_MOVABLE_MOVED), .proc/Clean)
/datum/component/cleaning/proc/Clean()
diff --git a/code/datums/components/construction.dm b/code/datums/components/construction.dm
index 74b6f54a6c..c9cf47e221 100644
--- a/code/datums/components/construction.dm
+++ b/code/datums/components/construction.dm
@@ -13,8 +13,7 @@
/datum/component/construction/Initialize()
if(!isatom(parent))
- . = COMPONENT_INCOMPATIBLE
- CRASH("A construction component was applied incorrectly to non-atom: [parent.type].")
+ return COMPONENT_INCOMPATIBLE
RegisterSignal(COMSIG_PARENT_EXAMINE, .proc/examine)
RegisterSignal(COMSIG_PARENT_ATTACKBY,.proc/action)
diff --git a/code/datums/components/decal.dm b/code/datums/components/decal.dm
index 6b7f846e83..0d8613bd86 100644
--- a/code/datums/components/decal.dm
+++ b/code/datums/components/decal.dm
@@ -7,8 +7,7 @@
/datum/component/decal/Initialize(_icon, _icon_state, _dir, _cleanable=CLEAN_GOD, _color, _layer=TURF_LAYER, _description)
if(!isatom(parent) || !generate_appearance(_icon, _icon_state, _dir, _layer, _color))
- . = COMPONENT_INCOMPATIBLE
- CRASH("A turf decal was applied incorrectly to [parent.type]: icon:[_icon ? _icon : "none"] icon_state:[_icon_state ? _icon_state : "none"]")
+ return COMPONENT_INCOMPATIBLE
description = _description
cleanable = _cleanable
diff --git a/code/datums/components/decals/blood.dm b/code/datums/components/decals/blood.dm
index 5a14aecd52..e69f94358c 100644
--- a/code/datums/components/decals/blood.dm
+++ b/code/datums/components/decals/blood.dm
@@ -3,8 +3,7 @@
/datum/component/decal/blood/Initialize(_icon, _icon_state, _dir, _cleanable=CLEAN_STRENGTH_BLOOD, _color, _layer=ABOVE_OBJ_LAYER)
if(!isitem(parent))
- . = COMPONENT_INCOMPATIBLE
- CRASH("Warning: Blood decal attempted to be added to non-item of type [parent.type]")
+ return COMPONENT_INCOMPATIBLE
. = ..()
RegisterSignal(COMSIG_ATOM_GET_EXAMINE_NAME, .proc/get_examine_name)
diff --git a/code/datums/components/forensics.dm b/code/datums/components/forensics.dm
index 417525ab08..33456e6e98 100644
--- a/code/datums/components/forensics.dm
+++ b/code/datums/components/forensics.dm
@@ -15,8 +15,7 @@
/datum/component/forensics/Initialize(new_fingerprints, new_hiddenprints, new_blood_DNA, new_fibers)
if(!isatom(parent))
- . = COMPONENT_INCOMPATIBLE
- CRASH("Forensics datum applied incorrectly to non-atom of type [parent.type]!")
+ return COMPONENT_INCOMPATIBLE
fingerprints = new_fingerprints
hiddenprints = new_hiddenprints
blood_DNA = new_blood_DNA
diff --git a/code/datums/components/jousting.dm b/code/datums/components/jousting.dm
index 8348e59fe2..679d37738b 100644
--- a/code/datums/components/jousting.dm
+++ b/code/datums/components/jousting.dm
@@ -18,8 +18,7 @@
/datum/component/jousting/Initialize()
if(!isitem(parent))
- . = COMPONENT_INCOMPATIBLE
- CRASH("Warning: Jousting component incorrectly applied to invalid parent type [parent.type]")
+ return COMPONENT_INCOMPATIBLE
RegisterSignal(COMSIG_ITEM_EQUIPPED, .proc/on_equip)
RegisterSignal(COMSIG_ITEM_DROPPED, .proc/on_drop)
RegisterSignal(COMSIG_ITEM_ATTACK, .proc/on_attack)
diff --git a/code/datums/components/knockoff.dm b/code/datums/components/knockoff.dm
index 35d1e5423e..60b86f01d2 100644
--- a/code/datums/components/knockoff.dm
+++ b/code/datums/components/knockoff.dm
@@ -7,8 +7,7 @@
/datum/component/knockoff/Initialize(knockoff_chance,zone_override,slots_knockoffable)
if(!isitem(parent))
- . = COMPONENT_INCOMPATIBLE
- CRASH("Knockoff component misuse")
+ return COMPONENT_INCOMPATIBLE
RegisterSignal(COMSIG_ITEM_EQUIPPED,.proc/OnEquipped)
RegisterSignal(COMSIG_ITEM_DROPPED,.proc/OnDropped)
diff --git a/code/datums/components/lockon_aiming.dm b/code/datums/components/lockon_aiming.dm
index fe707a9d32..02dfb8eaa0 100644
--- a/code/datums/components/lockon_aiming.dm
+++ b/code/datums/components/lockon_aiming.dm
@@ -1,243 +1,242 @@
-#define LOCKON_AIMING_MAX_CURSOR_RADIUS 7
-#define LOCKON_IGNORE_RESULT "ignore_my_result"
-#define LOCKON_RANGING_BREAK_CHECK if(current_ranging_id != this_id){return LOCKON_IGNORE_RESULT}
-
-/datum/component/lockon_aiming
- dupe_mode = COMPONENT_DUPE_ALLOWED
- var/lock_icon = 'icons/mob/blob.dmi'
- var/lock_icon_state = "marker"
- var/mutable_appearance/lock_appearance
- var/list/image/lock_images
- var/list/target_typecache
- var/list/immune_weakrefs //list(weakref = TRUE)
- var/mob_stat_check = TRUE //if a potential target is a mob make sure it's conscious!
- var/lock_amount = 1
- var/lock_cursor_range = 5
- var/list/locked_weakrefs
- var/update_disabled = FALSE
- var/current_ranging_id = 0
- var/list/last_location
- var/datum/callback/on_lock
- var/datum/callback/can_target_callback
-
-/datum/component/lockon_aiming/Initialize(range, list/typecache, amount, list/immune, datum/callback/when_locked, icon, icon_state, datum/callback/target_callback)
- if(!ismob(parent))
- . = COMPONENT_INCOMPATIBLE
- CRASH("Lockon aiming component attempted to be added to a non mob!")
- if(target_callback)
- can_target_callback = target_callback
- else
- can_target_callback = CALLBACK(src, .proc/can_target)
- if(range)
- lock_cursor_range = range
- if(typecache)
- target_typecache = typecache
- if(amount)
- lock_amount = amount
- immune_weakrefs = list(WEAKREF(parent) = TRUE) //Manually take this out if you want..
- if(immune)
- for(var/i in immune)
- if(isweakref(i))
- immune_weakrefs[i] = TRUE
- else if(isatom(i))
- immune_weakrefs[WEAKREF(i)] = TRUE
- if(when_locked)
- on_lock = when_locked
- if(icon)
- lock_icon = icon
- if(icon_state)
- lock_icon_state = icon_state
- generate_lock_visuals()
- var/mob/M = parent
- LAZYOR(M.mousemove_intercept_objects, src)
- START_PROCESSING(SSfastprocess, src)
-
-/datum/component/lockon_aiming/Destroy()
- var/mob/M = parent
- clear_visuals()
- LAZYREMOVE(M.mousemove_intercept_objects, src)
- STOP_PROCESSING(SSfastprocess, src)
- return ..()
-
-/datum/component/lockon_aiming/proc/show_visuals()
- LAZYINITLIST(lock_images)
- var/mob/M = parent
- if(!M.client)
- return
- for(var/i in locked_weakrefs)
- var/datum/weakref/R = i
- var/atom/A = R.resolve()
- if(!A)
- continue //It'll be cleared by processing.
- var/image/I = new
- I.appearance = lock_appearance
- I.loc = A
- M.client.images |= I
- lock_images |= I
-
-/datum/component/lockon_aiming/proc/clear_visuals()
- var/mob/M = parent
- if(!M.client)
- return
- if(!lock_images)
- return
- for(var/i in lock_images)
- M.client.images -= i
- qdel(i)
- lock_images.Cut()
-
-/datum/component/lockon_aiming/proc/refresh_visuals()
- clear_visuals()
- show_visuals()
-
-/datum/component/lockon_aiming/proc/generate_lock_visuals()
- lock_appearance = mutable_appearance(icon = lock_icon, icon_state = lock_icon_state, layer = FLOAT_LAYER)
-
-/datum/component/lockon_aiming/proc/unlock_all(refresh_vis = TRUE)
- LAZYCLEARLIST(locked_weakrefs)
- if(refresh_vis)
- refresh_visuals()
-
-/datum/component/lockon_aiming/proc/unlock(atom/A, refresh_vis = TRUE)
- if(!A.weak_reference)
- return
- LAZYREMOVE(locked_weakrefs, A.weak_reference)
- if(refresh_vis)
- refresh_visuals()
-
-/datum/component/lockon_aiming/proc/lock(atom/A, refresh_vis = TRUE)
- LAZYOR(locked_weakrefs, WEAKREF(A))
- if(refresh_vis)
- refresh_visuals()
-
-/datum/component/lockon_aiming/proc/add_immune_atom(atom/A)
- var/datum/weakref/R = WEAKREF(A)
- if(immune_weakrefs && (immune_weakrefs[R]))
- return
- LAZYSET(immune_weakrefs, R, TRUE)
-
-/datum/component/lockon_aiming/proc/remove_immune_atom(atom/A)
- if(!A.weak_reference || !immune_weakrefs) //if A doesn't have a weakref how did it get on the immunity list?
- return
- LAZYREMOVE(immune_weakrefs, A.weak_reference)
-
-/datum/component/lockon_aiming/onMouseMove(object,location,control,params)
- var/mob/M = parent
- if(!istype(M) || !M.client)
- return
- var/datum/position/P = mouse_absolute_datum_map_position_from_client(M.client)
- if(!P)
- return
- var/turf/T = P.return_turf()
- LAZYINITLIST(last_location)
- if(length(last_location) == 3 && last_location[1] == T.x && last_location[2] == T.y && last_location[3] == T.z)
- return //Same turf, don't bother.
- if(last_location)
- last_location.Cut()
- else
- last_location = list()
- last_location.len = 3
- last_location[1] = T.x
- last_location[2] = T.y
- last_location[3] = T.z
- autolock()
-
-/datum/component/lockon_aiming/process()
- if(update_disabled)
- return
- if(!last_location)
- return
- var/changed = FALSE
- for(var/i in locked_weakrefs)
- var/datum/weakref/R = i
- if(istype(R))
- var/atom/thing = R.resolve()
- if(!istype(thing) || (get_dist(thing, locate(last_location[1], last_location[2], last_location[3])) > lock_cursor_range))
- unlock(R)
- changed = TRUE
- else
- unlock(R)
- changed = TRUE
- if(changed)
- autolock()
-
-/datum/component/lockon_aiming/proc/autolock()
- var/mob/M = parent
- if(!M.client)
- return FALSE
- var/datum/position/current = mouse_absolute_datum_map_position_from_client(M.client)
- var/turf/target = current.return_turf()
- var/list/atom/targets = get_nearest(target, target_typecache, lock_amount, lock_cursor_range)
- if(targets == LOCKON_IGNORE_RESULT)
- return
- unlock_all(FALSE)
- for(var/i in targets)
- if(immune_weakrefs[WEAKREF(i)])
- continue
- lock(i, FALSE)
- refresh_visuals()
- on_lock.Invoke(locked_weakrefs)
-
-/datum/component/lockon_aiming/proc/can_target(atom/A)
- var/mob/M = A
- return is_type_in_typecache(A, target_typecache) && !(ismob(A) && mob_stat_check && M.stat != CONSCIOUS) && !immune_weakrefs[WEAKREF(A)]
-
-/datum/component/lockon_aiming/proc/get_nearest(turf/T, list/typecache, amount, range)
- current_ranging_id++
- var/this_id = current_ranging_id
- var/list/L = list()
- var/turf/center = get_turf(T)
- if(amount < 1 || range < 0 || !istype(center) || !islist(typecache))
- return
- if(range == 0)
- return typecache_filter_list(T.contents + T, typecache)
- var/x = 0
- var/y = 0
- var/cd = 0
- while(cd <= range)
- x = center.x - cd + 1
- y = center.y + cd
- LOCKON_RANGING_BREAK_CHECK
- for(x in x to center.x + cd)
- T = locate(x, y, center.z)
- if(T)
- L |= special_list_filter(T.contents, can_target_callback)
- if(L.len >= amount)
- L.Cut(amount+1)
- return L
- LOCKON_RANGING_BREAK_CHECK
- y = center.y + cd - 1
- x = center.x + cd
- for(y in center.y - cd to y)
- T = locate(x, y, center.z)
- if(T)
- L |= special_list_filter(T.contents, can_target_callback)
- if(L.len >= amount)
- L.Cut(amount+1)
- return L
- LOCKON_RANGING_BREAK_CHECK
- y = center.y - cd
- x = center.x + cd - 1
- for(x in center.x - cd to x)
- T = locate(x, y, center.z)
- if(T)
- L |= special_list_filter(T.contents, can_target_callback)
- if(L.len >= amount)
- L.Cut(amount+1)
- return L
- LOCKON_RANGING_BREAK_CHECK
- y = center.y - cd + 1
- x = center.x - cd
- for(y in y to center.y + cd)
- T = locate(x, y, center.z)
- if(T)
- L |= special_list_filter(T.contents, can_target_callback)
- if(L.len >= amount)
- L.Cut(amount+1)
- return L
- LOCKON_RANGING_BREAK_CHECK
- cd++
- CHECK_TICK
-
-/datum/component/lockon_aiming/OnTransfer(datum/new_parent)
- CRASH("Warning: Lockon aiming component transfer attempted, but transfer behavior is not implemented!")
+#define LOCKON_AIMING_MAX_CURSOR_RADIUS 7
+#define LOCKON_IGNORE_RESULT "ignore_my_result"
+#define LOCKON_RANGING_BREAK_CHECK if(current_ranging_id != this_id){return LOCKON_IGNORE_RESULT}
+
+/datum/component/lockon_aiming
+ dupe_mode = COMPONENT_DUPE_ALLOWED
+ var/lock_icon = 'icons/mob/blob.dmi'
+ var/lock_icon_state = "marker"
+ var/mutable_appearance/lock_appearance
+ var/list/image/lock_images
+ var/list/target_typecache
+ var/list/immune_weakrefs //list(weakref = TRUE)
+ var/mob_stat_check = TRUE //if a potential target is a mob make sure it's conscious!
+ var/lock_amount = 1
+ var/lock_cursor_range = 5
+ var/list/locked_weakrefs
+ var/update_disabled = FALSE
+ var/current_ranging_id = 0
+ var/list/last_location
+ var/datum/callback/on_lock
+ var/datum/callback/can_target_callback
+
+/datum/component/lockon_aiming/Initialize(range, list/typecache, amount, list/immune, datum/callback/when_locked, icon, icon_state, datum/callback/target_callback)
+ if(!ismob(parent))
+ return COMPONENT_INCOMPATIBLE
+ if(target_callback)
+ can_target_callback = target_callback
+ else
+ can_target_callback = CALLBACK(src, .proc/can_target)
+ if(range)
+ lock_cursor_range = range
+ if(typecache)
+ target_typecache = typecache
+ if(amount)
+ lock_amount = amount
+ immune_weakrefs = list(WEAKREF(parent) = TRUE) //Manually take this out if you want..
+ if(immune)
+ for(var/i in immune)
+ if(isweakref(i))
+ immune_weakrefs[i] = TRUE
+ else if(isatom(i))
+ immune_weakrefs[WEAKREF(i)] = TRUE
+ if(when_locked)
+ on_lock = when_locked
+ if(icon)
+ lock_icon = icon
+ if(icon_state)
+ lock_icon_state = icon_state
+ generate_lock_visuals()
+ var/mob/M = parent
+ LAZYOR(M.mousemove_intercept_objects, src)
+ START_PROCESSING(SSfastprocess, src)
+
+/datum/component/lockon_aiming/Destroy()
+ var/mob/M = parent
+ clear_visuals()
+ LAZYREMOVE(M.mousemove_intercept_objects, src)
+ STOP_PROCESSING(SSfastprocess, src)
+ return ..()
+
+/datum/component/lockon_aiming/proc/show_visuals()
+ LAZYINITLIST(lock_images)
+ var/mob/M = parent
+ if(!M.client)
+ return
+ for(var/i in locked_weakrefs)
+ var/datum/weakref/R = i
+ var/atom/A = R.resolve()
+ if(!A)
+ continue //It'll be cleared by processing.
+ var/image/I = new
+ I.appearance = lock_appearance
+ I.loc = A
+ M.client.images |= I
+ lock_images |= I
+
+/datum/component/lockon_aiming/proc/clear_visuals()
+ var/mob/M = parent
+ if(!M.client)
+ return
+ if(!lock_images)
+ return
+ for(var/i in lock_images)
+ M.client.images -= i
+ qdel(i)
+ lock_images.Cut()
+
+/datum/component/lockon_aiming/proc/refresh_visuals()
+ clear_visuals()
+ show_visuals()
+
+/datum/component/lockon_aiming/proc/generate_lock_visuals()
+ lock_appearance = mutable_appearance(icon = lock_icon, icon_state = lock_icon_state, layer = FLOAT_LAYER)
+
+/datum/component/lockon_aiming/proc/unlock_all(refresh_vis = TRUE)
+ LAZYCLEARLIST(locked_weakrefs)
+ if(refresh_vis)
+ refresh_visuals()
+
+/datum/component/lockon_aiming/proc/unlock(atom/A, refresh_vis = TRUE)
+ if(!A.weak_reference)
+ return
+ LAZYREMOVE(locked_weakrefs, A.weak_reference)
+ if(refresh_vis)
+ refresh_visuals()
+
+/datum/component/lockon_aiming/proc/lock(atom/A, refresh_vis = TRUE)
+ LAZYOR(locked_weakrefs, WEAKREF(A))
+ if(refresh_vis)
+ refresh_visuals()
+
+/datum/component/lockon_aiming/proc/add_immune_atom(atom/A)
+ var/datum/weakref/R = WEAKREF(A)
+ if(immune_weakrefs && (immune_weakrefs[R]))
+ return
+ LAZYSET(immune_weakrefs, R, TRUE)
+
+/datum/component/lockon_aiming/proc/remove_immune_atom(atom/A)
+ if(!A.weak_reference || !immune_weakrefs) //if A doesn't have a weakref how did it get on the immunity list?
+ return
+ LAZYREMOVE(immune_weakrefs, A.weak_reference)
+
+/datum/component/lockon_aiming/onMouseMove(object,location,control,params)
+ var/mob/M = parent
+ if(!istype(M) || !M.client)
+ return
+ var/datum/position/P = mouse_absolute_datum_map_position_from_client(M.client)
+ if(!P)
+ return
+ var/turf/T = P.return_turf()
+ LAZYINITLIST(last_location)
+ if(length(last_location) == 3 && last_location[1] == T.x && last_location[2] == T.y && last_location[3] == T.z)
+ return //Same turf, don't bother.
+ if(last_location)
+ last_location.Cut()
+ else
+ last_location = list()
+ last_location.len = 3
+ last_location[1] = T.x
+ last_location[2] = T.y
+ last_location[3] = T.z
+ autolock()
+
+/datum/component/lockon_aiming/process()
+ if(update_disabled)
+ return
+ if(!last_location)
+ return
+ var/changed = FALSE
+ for(var/i in locked_weakrefs)
+ var/datum/weakref/R = i
+ if(istype(R))
+ var/atom/thing = R.resolve()
+ if(!istype(thing) || (get_dist(thing, locate(last_location[1], last_location[2], last_location[3])) > lock_cursor_range))
+ unlock(R)
+ changed = TRUE
+ else
+ unlock(R)
+ changed = TRUE
+ if(changed)
+ autolock()
+
+/datum/component/lockon_aiming/proc/autolock()
+ var/mob/M = parent
+ if(!M.client)
+ return FALSE
+ var/datum/position/current = mouse_absolute_datum_map_position_from_client(M.client)
+ var/turf/target = current.return_turf()
+ var/list/atom/targets = get_nearest(target, target_typecache, lock_amount, lock_cursor_range)
+ if(targets == LOCKON_IGNORE_RESULT)
+ return
+ unlock_all(FALSE)
+ for(var/i in targets)
+ if(immune_weakrefs[WEAKREF(i)])
+ continue
+ lock(i, FALSE)
+ refresh_visuals()
+ on_lock.Invoke(locked_weakrefs)
+
+/datum/component/lockon_aiming/proc/can_target(atom/A)
+ var/mob/M = A
+ return is_type_in_typecache(A, target_typecache) && !(ismob(A) && mob_stat_check && M.stat != CONSCIOUS) && !immune_weakrefs[WEAKREF(A)]
+
+/datum/component/lockon_aiming/proc/get_nearest(turf/T, list/typecache, amount, range)
+ current_ranging_id++
+ var/this_id = current_ranging_id
+ var/list/L = list()
+ var/turf/center = get_turf(T)
+ if(amount < 1 || range < 0 || !istype(center) || !islist(typecache))
+ return
+ if(range == 0)
+ return typecache_filter_list(T.contents + T, typecache)
+ var/x = 0
+ var/y = 0
+ var/cd = 0
+ while(cd <= range)
+ x = center.x - cd + 1
+ y = center.y + cd
+ LOCKON_RANGING_BREAK_CHECK
+ for(x in x to center.x + cd)
+ T = locate(x, y, center.z)
+ if(T)
+ L |= special_list_filter(T.contents, can_target_callback)
+ if(L.len >= amount)
+ L.Cut(amount+1)
+ return L
+ LOCKON_RANGING_BREAK_CHECK
+ y = center.y + cd - 1
+ x = center.x + cd
+ for(y in center.y - cd to y)
+ T = locate(x, y, center.z)
+ if(T)
+ L |= special_list_filter(T.contents, can_target_callback)
+ if(L.len >= amount)
+ L.Cut(amount+1)
+ return L
+ LOCKON_RANGING_BREAK_CHECK
+ y = center.y - cd
+ x = center.x + cd - 1
+ for(x in center.x - cd to x)
+ T = locate(x, y, center.z)
+ if(T)
+ L |= special_list_filter(T.contents, can_target_callback)
+ if(L.len >= amount)
+ L.Cut(amount+1)
+ return L
+ LOCKON_RANGING_BREAK_CHECK
+ y = center.y - cd + 1
+ x = center.x - cd
+ for(y in y to center.y + cd)
+ T = locate(x, y, center.z)
+ if(T)
+ L |= special_list_filter(T.contents, can_target_callback)
+ if(L.len >= amount)
+ L.Cut(amount+1)
+ return L
+ LOCKON_RANGING_BREAK_CHECK
+ cd++
+ CHECK_TICK
+
+/datum/component/lockon_aiming/OnTransfer(datum/new_parent)
+ CRASH("Warning: Lockon aiming component transfer attempted, but transfer behavior is not implemented!")
diff --git a/code/datums/components/mood.dm b/code/datums/components/mood.dm
index 95f8f65bb6..8714b41f05 100644
--- a/code/datums/components/mood.dm
+++ b/code/datums/components/mood.dm
@@ -10,8 +10,7 @@
/datum/component/mood/Initialize()
if(!isliving(parent))
- . = COMPONENT_INCOMPATIBLE
- CRASH("Some good for nothing loser put a mood component on something that isn't even a living mob.")
+ return COMPONENT_INCOMPATIBLE
START_PROCESSING(SSmood, src)
owner = parent
soundloop = new(list(owner), FALSE, TRUE)
diff --git a/code/datums/components/riding.dm b/code/datums/components/riding.dm
index e21db3e094..3701be2ae9 100644
--- a/code/datums/components/riding.dm
+++ b/code/datums/components/riding.dm
@@ -1,326 +1,325 @@
-/datum/component/riding
- var/next_vehicle_move = 0 //used for move delays
- var/vehicle_move_delay = 2 //tick delay between movements, lower = faster, higher = slower
- var/keytype
-
- var/slowed = FALSE
- var/slowvalue = 1
-
- var/list/riding_offsets = list() //position_of_user = list(dir = list(px, py)), or RIDING_OFFSET_ALL for a generic one.
- var/list/directional_vehicle_layers = list() //["[DIRECTION]"] = layer. Don't set it for a direction for default, set a direction to null for no change.
- var/list/directional_vehicle_offsets = list() //same as above but instead of layer you have a list(px, py)
- var/list/allowed_turf_typecache
- var/list/forbid_turf_typecache //allow typecache for only certain turfs, forbid to allow all but those. allow only certain turfs will take precedence.
- var/allow_one_away_from_valid_turf = TRUE //allow moving one tile away from a valid turf but not more.
- var/override_allow_spacemove = FALSE
- var/drive_verb = "drive"
- var/ride_check_rider_incapacitated = FALSE
- var/ride_check_rider_restrained = FALSE
- var/ride_check_ridden_incapacitated = FALSE
-
-/datum/component/riding/Initialize()
- if(!ismovableatom(parent))
- . = COMPONENT_INCOMPATIBLE
- CRASH("RIDING COMPONENT ASSIGNED TO NON ATOM MOVABLE!")
- RegisterSignal(COMSIG_MOVABLE_BUCKLE, .proc/vehicle_mob_buckle)
- RegisterSignal(COMSIG_MOVABLE_UNBUCKLE, .proc/vehicle_mob_unbuckle)
- RegisterSignal(COMSIG_MOVABLE_MOVED, .proc/vehicle_moved)
-
-/datum/component/riding/proc/vehicle_mob_unbuckle(mob/living/M, force = FALSE)
- restore_position(M)
- unequip_buckle_inhands(M)
-
-/datum/component/riding/proc/vehicle_mob_buckle(mob/living/M, force = FALSE)
- handle_vehicle_offsets()
-
-/datum/component/riding/proc/handle_vehicle_layer()
- var/atom/movable/AM = parent
- var/static/list/defaults = list(TEXT_NORTH = OBJ_LAYER, TEXT_SOUTH = ABOVE_MOB_LAYER, TEXT_EAST = ABOVE_MOB_LAYER, TEXT_WEST = ABOVE_MOB_LAYER)
- . = defaults["[AM.dir]"]
- if(directional_vehicle_layers["[AM.dir]"])
- . = directional_vehicle_layers["[AM.dir]"]
- if(isnull(.)) //you can set it to null to not change it.
- . = AM.layer
- AM.layer = .
-
-/datum/component/riding/proc/set_vehicle_dir_layer(dir, layer)
- directional_vehicle_layers["[dir]"] = layer
-
-/datum/component/riding/proc/vehicle_moved()
- var/atom/movable/AM = parent
- for(var/i in AM.buckled_mobs)
- ride_check(i)
- handle_vehicle_offsets()
- handle_vehicle_layer()
-
-/datum/component/riding/proc/ride_check(mob/living/M)
- var/atom/movable/AM = parent
- var/mob/AMM = AM
- if((ride_check_rider_restrained && M.restrained(TRUE)) || (ride_check_rider_incapacitated && M.incapacitated(FALSE, TRUE)) || (ride_check_ridden_incapacitated && istype(AMM) && AMM.incapacitated(FALSE, TRUE)))
- AM.visible_message("[M] falls off of [AM]!")
- AM.unbuckle_mob(M)
- return TRUE
-
-/datum/component/riding/proc/force_dismount(mob/living/M)
- var/atom/movable/AM = parent
- AM.unbuckle_mob(M)
-
-/datum/component/riding/proc/handle_vehicle_offsets()
- var/atom/movable/AM = parent
- var/AM_dir = "[AM.dir]"
- var/passindex = 0
- if(AM.has_buckled_mobs())
- for(var/m in AM.buckled_mobs)
- passindex++
- var/mob/living/buckled_mob = m
- var/list/offsets = get_offsets(passindex)
- var/rider_dir = get_rider_dir(passindex)
- buckled_mob.setDir(rider_dir)
- dir_loop:
- for(var/offsetdir in offsets)
- if(offsetdir == AM_dir)
- var/list/diroffsets = offsets[offsetdir]
- buckled_mob.pixel_x = diroffsets[1]
- if(diroffsets.len >= 2)
- buckled_mob.pixel_y = diroffsets[2]
- if(diroffsets.len == 3)
- buckled_mob.layer = diroffsets[3]
- break dir_loop
- var/list/static/default_vehicle_pixel_offsets = list(TEXT_NORTH = list(0, 0), TEXT_SOUTH = list(0, 0), TEXT_EAST = list(0, 0), TEXT_WEST = list(0, 0))
- var/px = default_vehicle_pixel_offsets[AM_dir]
- var/py = default_vehicle_pixel_offsets[AM_dir]
- if(directional_vehicle_offsets[AM_dir])
- if(isnull(directional_vehicle_offsets[AM_dir]))
- px = AM.pixel_x
- py = AM.pixel_y
- else
- px = directional_vehicle_offsets[AM_dir][1]
- py = directional_vehicle_offsets[AM_dir][2]
- AM.pixel_x = px
- AM.pixel_y = py
-
-/datum/component/riding/proc/set_vehicle_dir_offsets(dir, x, y)
- directional_vehicle_offsets["[dir]"] = list(x, y)
-
-//Override this to set your vehicle's various pixel offsets
-/datum/component/riding/proc/get_offsets(pass_index) // list(dir = x, y, layer)
- . = list(TEXT_NORTH = list(0, 0), TEXT_SOUTH = list(0, 0), TEXT_EAST = list(0, 0), TEXT_WEST = list(0, 0))
- if(riding_offsets["[pass_index]"])
- . = riding_offsets["[pass_index]"]
- else if(riding_offsets["[RIDING_OFFSET_ALL]"])
- . = riding_offsets["[RIDING_OFFSET_ALL]"]
-
-/datum/component/riding/proc/set_riding_offsets(index, list/offsets)
- if(!islist(offsets))
- return FALSE
- riding_offsets["[index]"] = offsets
-
-//Override this to set the passengers/riders dir based on which passenger they are.
-//ie: rider facing the vehicle's dir, but passenger 2 facing backwards, etc.
-/datum/component/riding/proc/get_rider_dir(pass_index)
- var/atom/movable/AM = parent
- return AM.dir
-
-//KEYS
-/datum/component/riding/proc/keycheck(mob/user)
- return !keytype || user.is_holding_item_of_type(keytype)
-
-//BUCKLE HOOKS
-/datum/component/riding/proc/restore_position(mob/living/buckled_mob)
- if(buckled_mob)
- buckled_mob.pixel_x = 0
- buckled_mob.pixel_y = 0
- if(buckled_mob.client)
- buckled_mob.client.change_view(CONFIG_GET(string/default_view))
-
-//MOVEMENT
-/datum/component/riding/proc/turf_check(turf/next, turf/current)
- if(allowed_turf_typecache && !allowed_turf_typecache[next.type])
- return (allow_one_away_from_valid_turf && allowed_turf_typecache[current.type])
- else if(forbid_turf_typecache && forbid_turf_typecache[next.type])
- return (allow_one_away_from_valid_turf && !forbid_turf_typecache[current.type])
- return TRUE
-
-/datum/component/riding/proc/handle_ride(mob/user, direction)
- var/atom/movable/AM = parent
- if(user.incapacitated())
- Unbuckle(user)
- return
-
- if(world.time < next_vehicle_move)
- return
- next_vehicle_move = world.time + vehicle_move_delay
-
- if(keycheck(user))
- var/turf/next = get_step(AM, direction)
- var/turf/current = get_turf(AM)
- if(!istype(next) || !istype(current))
- return //not happening.
- if(!turf_check(next, current))
- to_chat(user, "Your \the [AM] can not go onto [next]!")
- return
- if(!Process_Spacemove(direction) || !isturf(AM.loc))
- return
- step(AM, direction)
-
- handle_vehicle_layer()
- handle_vehicle_offsets()
- else
- to_chat(user, "You'll need the keys in one of your hands to [drive_verb] [AM].")
-
-/datum/component/riding/proc/Unbuckle(atom/movable/M)
- addtimer(CALLBACK(parent, /atom/movable/.proc/unbuckle_mob, M), 0, TIMER_UNIQUE)
-
-/datum/component/riding/proc/Process_Spacemove(direction)
- var/atom/movable/AM = parent
- return override_allow_spacemove || AM.has_gravity()
-
-/datum/component/riding/proc/account_limbs(mob/living/M)
- if(M.get_num_legs() < 2 && !slowed)
- vehicle_move_delay = vehicle_move_delay + slowvalue
- slowed = TRUE
- else if(slowed)
- vehicle_move_delay = vehicle_move_delay - slowvalue
- slowed = FALSE
-
-///////Yes, I said humans. No, this won't end well...//////////
-/datum/component/riding/human
-
-/datum/component/riding/human/Initialize()
- . = ..()
- RegisterSignal(COMSIG_HUMAN_MELEE_UNARMED_ATTACK, .proc/on_host_unarmed_melee)
-
-/datum/component/riding/human/proc/on_host_unarmed_melee(atom/target)
- var/mob/living/carbon/human/AM = parent
- if(AM.a_intent == INTENT_DISARM && (target in AM.buckled_mobs))
- force_dismount(target)
-
-/datum/component/riding/human/handle_vehicle_layer()
- var/atom/movable/AM = parent
- if(AM.buckled_mobs && AM.buckled_mobs.len)
- if(AM.dir == SOUTH)
- AM.layer = ABOVE_MOB_LAYER
- else
- AM.layer = OBJ_LAYER
- else
- AM.layer = MOB_LAYER
-
-/datum/component/riding/human/force_dismount(mob/living/user)
- var/atom/movable/AM = parent
- AM.unbuckle_mob(user)
- user.Knockdown(60)
- user.visible_message("[AM] pushes [user] off of them!")
-
-/datum/component/riding/cyborg
-
-/datum/component/riding/cyborg/ride_check(mob/user)
- var/atom/movable/AM = parent
- if(user.incapacitated())
- var/kick = TRUE
- if(iscyborg(AM))
- var/mob/living/silicon/robot/R = AM
- if(R.module && R.module.ride_allow_incapacitated)
- kick = FALSE
- if(kick)
- to_chat(user, "You fall off of [AM]!")
- Unbuckle(user)
- return
- if(iscarbon(user))
- var/mob/living/carbon/carbonuser = user
- if(!carbonuser.get_num_arms())
- Unbuckle(user)
- to_chat(user, "You can't grab onto [AM] with no hands!")
- return
-
-/datum/component/riding/cyborg/handle_vehicle_layer()
- var/atom/movable/AM = parent
- if(AM.buckled_mobs && AM.buckled_mobs.len)
- if(AM.dir == SOUTH)
- AM.layer = ABOVE_MOB_LAYER
- else
- AM.layer = OBJ_LAYER
- else
- AM.layer = MOB_LAYER
-
-/datum/component/riding/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/cyborg/handle_vehicle_offsets()
- var/atom/movable/AM = parent
- if(AM.has_buckled_mobs())
- for(var/mob/living/M in AM.buckled_mobs)
- M.setDir(AM.dir)
- if(iscyborg(AM))
- var/mob/living/silicon/robot/R = AM
- if(istype(R.module))
- M.pixel_x = R.module.ride_offset_x[dir2text(AM.dir)]
- M.pixel_y = R.module.ride_offset_y[dir2text(AM.dir)]
- else
- ..()
-
-/datum/component/riding/cyborg/force_dismount(mob/living/M)
- var/atom/movable/AM = parent
- AM.unbuckle_mob(M)
- var/turf/target = get_edge_target_turf(AM, AM.dir)
- var/turf/targetm = get_step(get_turf(AM), AM.dir)
- M.Move(targetm)
- M.visible_message("[M] is thrown clear of [AM]!")
- M.throw_at(target, 14, 5, AM)
- M.Knockdown(60)
-
-/datum/component/riding/proc/equip_buckle_inhands(mob/living/carbon/human/user, amount_required = 1)
- var/atom/movable/AM = parent
- var/amount_equipped = 0
- for(var/amount_needed = amount_required, amount_needed > 0, amount_needed--)
- var/obj/item/riding_offhand/inhand = new /obj/item/riding_offhand(user)
- inhand.rider = user
- inhand.parent = AM
- if(user.put_in_hands(inhand, TRUE))
- amount_equipped++
- else
- break
- if(amount_equipped >= amount_required)
- return TRUE
- else
- unequip_buckle_inhands(user)
- return FALSE
-
-/datum/component/riding/proc/unequip_buckle_inhands(mob/living/carbon/user)
- var/atom/movable/AM = parent
- for(var/obj/item/riding_offhand/O in user.contents)
- if(O.parent != AM)
- CRASH("RIDING OFFHAND ON WRONG MOB")
- continue
- if(O.selfdeleting)
- continue
- else
- qdel(O)
- return TRUE
-
-/obj/item/riding_offhand
- name = "offhand"
- icon = 'icons/obj/items_and_weapons.dmi'
- icon_state = "offhand"
- w_class = WEIGHT_CLASS_HUGE
- flags_1 = ABSTRACT_1 | DROPDEL_1 | NOBLUDGEON_1
- resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
- var/mob/living/carbon/rider
- var/mob/living/parent
- var/selfdeleting = FALSE
-
-/obj/item/riding_offhand/dropped()
- selfdeleting = TRUE
- . = ..()
-
-/obj/item/riding_offhand/equipped()
- if(loc != rider)
- selfdeleting = TRUE
- qdel(src)
- . = ..()
-
-/obj/item/riding_offhand/Destroy()
- var/atom/movable/AM = parent
- if(selfdeleting)
- if(rider in AM.buckled_mobs)
- AM.unbuckle_mob(rider)
- . = ..()
+/datum/component/riding
+ var/next_vehicle_move = 0 //used for move delays
+ var/vehicle_move_delay = 2 //tick delay between movements, lower = faster, higher = slower
+ var/keytype
+
+ var/slowed = FALSE
+ var/slowvalue = 1
+
+ var/list/riding_offsets = list() //position_of_user = list(dir = list(px, py)), or RIDING_OFFSET_ALL for a generic one.
+ var/list/directional_vehicle_layers = list() //["[DIRECTION]"] = layer. Don't set it for a direction for default, set a direction to null for no change.
+ var/list/directional_vehicle_offsets = list() //same as above but instead of layer you have a list(px, py)
+ var/list/allowed_turf_typecache
+ var/list/forbid_turf_typecache //allow typecache for only certain turfs, forbid to allow all but those. allow only certain turfs will take precedence.
+ var/allow_one_away_from_valid_turf = TRUE //allow moving one tile away from a valid turf but not more.
+ var/override_allow_spacemove = FALSE
+ var/drive_verb = "drive"
+ var/ride_check_rider_incapacitated = FALSE
+ var/ride_check_rider_restrained = FALSE
+ var/ride_check_ridden_incapacitated = FALSE
+
+/datum/component/riding/Initialize()
+ if(!ismovableatom(parent))
+ return COMPONENT_INCOMPATIBLE
+ RegisterSignal(COMSIG_MOVABLE_BUCKLE, .proc/vehicle_mob_buckle)
+ RegisterSignal(COMSIG_MOVABLE_UNBUCKLE, .proc/vehicle_mob_unbuckle)
+ RegisterSignal(COMSIG_MOVABLE_MOVED, .proc/vehicle_moved)
+
+/datum/component/riding/proc/vehicle_mob_unbuckle(mob/living/M, force = FALSE)
+ restore_position(M)
+ unequip_buckle_inhands(M)
+
+/datum/component/riding/proc/vehicle_mob_buckle(mob/living/M, force = FALSE)
+ handle_vehicle_offsets()
+
+/datum/component/riding/proc/handle_vehicle_layer()
+ var/atom/movable/AM = parent
+ var/static/list/defaults = list(TEXT_NORTH = OBJ_LAYER, TEXT_SOUTH = ABOVE_MOB_LAYER, TEXT_EAST = ABOVE_MOB_LAYER, TEXT_WEST = ABOVE_MOB_LAYER)
+ . = defaults["[AM.dir]"]
+ if(directional_vehicle_layers["[AM.dir]"])
+ . = directional_vehicle_layers["[AM.dir]"]
+ if(isnull(.)) //you can set it to null to not change it.
+ . = AM.layer
+ AM.layer = .
+
+/datum/component/riding/proc/set_vehicle_dir_layer(dir, layer)
+ directional_vehicle_layers["[dir]"] = layer
+
+/datum/component/riding/proc/vehicle_moved()
+ var/atom/movable/AM = parent
+ for(var/i in AM.buckled_mobs)
+ ride_check(i)
+ handle_vehicle_offsets()
+ handle_vehicle_layer()
+
+/datum/component/riding/proc/ride_check(mob/living/M)
+ var/atom/movable/AM = parent
+ var/mob/AMM = AM
+ if((ride_check_rider_restrained && M.restrained(TRUE)) || (ride_check_rider_incapacitated && M.incapacitated(FALSE, TRUE)) || (ride_check_ridden_incapacitated && istype(AMM) && AMM.incapacitated(FALSE, TRUE)))
+ AM.visible_message("[M] falls off of [AM]!")
+ AM.unbuckle_mob(M)
+ return TRUE
+
+/datum/component/riding/proc/force_dismount(mob/living/M)
+ var/atom/movable/AM = parent
+ AM.unbuckle_mob(M)
+
+/datum/component/riding/proc/handle_vehicle_offsets()
+ var/atom/movable/AM = parent
+ var/AM_dir = "[AM.dir]"
+ var/passindex = 0
+ if(AM.has_buckled_mobs())
+ for(var/m in AM.buckled_mobs)
+ passindex++
+ var/mob/living/buckled_mob = m
+ var/list/offsets = get_offsets(passindex)
+ var/rider_dir = get_rider_dir(passindex)
+ buckled_mob.setDir(rider_dir)
+ dir_loop:
+ for(var/offsetdir in offsets)
+ if(offsetdir == AM_dir)
+ var/list/diroffsets = offsets[offsetdir]
+ buckled_mob.pixel_x = diroffsets[1]
+ if(diroffsets.len >= 2)
+ buckled_mob.pixel_y = diroffsets[2]
+ if(diroffsets.len == 3)
+ buckled_mob.layer = diroffsets[3]
+ break dir_loop
+ var/list/static/default_vehicle_pixel_offsets = list(TEXT_NORTH = list(0, 0), TEXT_SOUTH = list(0, 0), TEXT_EAST = list(0, 0), TEXT_WEST = list(0, 0))
+ var/px = default_vehicle_pixel_offsets[AM_dir]
+ var/py = default_vehicle_pixel_offsets[AM_dir]
+ if(directional_vehicle_offsets[AM_dir])
+ if(isnull(directional_vehicle_offsets[AM_dir]))
+ px = AM.pixel_x
+ py = AM.pixel_y
+ else
+ px = directional_vehicle_offsets[AM_dir][1]
+ py = directional_vehicle_offsets[AM_dir][2]
+ AM.pixel_x = px
+ AM.pixel_y = py
+
+/datum/component/riding/proc/set_vehicle_dir_offsets(dir, x, y)
+ directional_vehicle_offsets["[dir]"] = list(x, y)
+
+//Override this to set your vehicle's various pixel offsets
+/datum/component/riding/proc/get_offsets(pass_index) // list(dir = x, y, layer)
+ . = list(TEXT_NORTH = list(0, 0), TEXT_SOUTH = list(0, 0), TEXT_EAST = list(0, 0), TEXT_WEST = list(0, 0))
+ if(riding_offsets["[pass_index]"])
+ . = riding_offsets["[pass_index]"]
+ else if(riding_offsets["[RIDING_OFFSET_ALL]"])
+ . = riding_offsets["[RIDING_OFFSET_ALL]"]
+
+/datum/component/riding/proc/set_riding_offsets(index, list/offsets)
+ if(!islist(offsets))
+ return FALSE
+ riding_offsets["[index]"] = offsets
+
+//Override this to set the passengers/riders dir based on which passenger they are.
+//ie: rider facing the vehicle's dir, but passenger 2 facing backwards, etc.
+/datum/component/riding/proc/get_rider_dir(pass_index)
+ var/atom/movable/AM = parent
+ return AM.dir
+
+//KEYS
+/datum/component/riding/proc/keycheck(mob/user)
+ return !keytype || user.is_holding_item_of_type(keytype)
+
+//BUCKLE HOOKS
+/datum/component/riding/proc/restore_position(mob/living/buckled_mob)
+ if(buckled_mob)
+ buckled_mob.pixel_x = 0
+ buckled_mob.pixel_y = 0
+ if(buckled_mob.client)
+ buckled_mob.client.change_view(CONFIG_GET(string/default_view))
+
+//MOVEMENT
+/datum/component/riding/proc/turf_check(turf/next, turf/current)
+ if(allowed_turf_typecache && !allowed_turf_typecache[next.type])
+ return (allow_one_away_from_valid_turf && allowed_turf_typecache[current.type])
+ else if(forbid_turf_typecache && forbid_turf_typecache[next.type])
+ return (allow_one_away_from_valid_turf && !forbid_turf_typecache[current.type])
+ return TRUE
+
+/datum/component/riding/proc/handle_ride(mob/user, direction)
+ var/atom/movable/AM = parent
+ if(user.incapacitated())
+ Unbuckle(user)
+ return
+
+ if(world.time < next_vehicle_move)
+ return
+ next_vehicle_move = world.time + vehicle_move_delay
+
+ if(keycheck(user))
+ var/turf/next = get_step(AM, direction)
+ var/turf/current = get_turf(AM)
+ if(!istype(next) || !istype(current))
+ return //not happening.
+ if(!turf_check(next, current))
+ to_chat(user, "Your \the [AM] can not go onto [next]!")
+ return
+ if(!Process_Spacemove(direction) || !isturf(AM.loc))
+ return
+ step(AM, direction)
+
+ handle_vehicle_layer()
+ handle_vehicle_offsets()
+ else
+ to_chat(user, "You'll need the keys in one of your hands to [drive_verb] [AM].")
+
+/datum/component/riding/proc/Unbuckle(atom/movable/M)
+ addtimer(CALLBACK(parent, /atom/movable/.proc/unbuckle_mob, M), 0, TIMER_UNIQUE)
+
+/datum/component/riding/proc/Process_Spacemove(direction)
+ var/atom/movable/AM = parent
+ return override_allow_spacemove || AM.has_gravity()
+
+/datum/component/riding/proc/account_limbs(mob/living/M)
+ if(M.get_num_legs() < 2 && !slowed)
+ vehicle_move_delay = vehicle_move_delay + slowvalue
+ slowed = TRUE
+ else if(slowed)
+ vehicle_move_delay = vehicle_move_delay - slowvalue
+ slowed = FALSE
+
+///////Yes, I said humans. No, this won't end well...//////////
+/datum/component/riding/human
+
+/datum/component/riding/human/Initialize()
+ . = ..()
+ RegisterSignal(COMSIG_HUMAN_MELEE_UNARMED_ATTACK, .proc/on_host_unarmed_melee)
+
+/datum/component/riding/human/proc/on_host_unarmed_melee(atom/target)
+ var/mob/living/carbon/human/AM = parent
+ if(AM.a_intent == INTENT_DISARM && (target in AM.buckled_mobs))
+ force_dismount(target)
+
+/datum/component/riding/human/handle_vehicle_layer()
+ var/atom/movable/AM = parent
+ if(AM.buckled_mobs && AM.buckled_mobs.len)
+ if(AM.dir == SOUTH)
+ AM.layer = ABOVE_MOB_LAYER
+ else
+ AM.layer = OBJ_LAYER
+ else
+ AM.layer = MOB_LAYER
+
+/datum/component/riding/human/force_dismount(mob/living/user)
+ var/atom/movable/AM = parent
+ AM.unbuckle_mob(user)
+ user.Knockdown(60)
+ user.visible_message("[AM] pushes [user] off of them!")
+
+/datum/component/riding/cyborg
+
+/datum/component/riding/cyborg/ride_check(mob/user)
+ var/atom/movable/AM = parent
+ if(user.incapacitated())
+ var/kick = TRUE
+ if(iscyborg(AM))
+ var/mob/living/silicon/robot/R = AM
+ if(R.module && R.module.ride_allow_incapacitated)
+ kick = FALSE
+ if(kick)
+ to_chat(user, "You fall off of [AM]!")
+ Unbuckle(user)
+ return
+ if(iscarbon(user))
+ var/mob/living/carbon/carbonuser = user
+ if(!carbonuser.get_num_arms())
+ Unbuckle(user)
+ to_chat(user, "You can't grab onto [AM] with no hands!")
+ return
+
+/datum/component/riding/cyborg/handle_vehicle_layer()
+ var/atom/movable/AM = parent
+ if(AM.buckled_mobs && AM.buckled_mobs.len)
+ if(AM.dir == SOUTH)
+ AM.layer = ABOVE_MOB_LAYER
+ else
+ AM.layer = OBJ_LAYER
+ else
+ AM.layer = MOB_LAYER
+
+/datum/component/riding/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/cyborg/handle_vehicle_offsets()
+ var/atom/movable/AM = parent
+ if(AM.has_buckled_mobs())
+ for(var/mob/living/M in AM.buckled_mobs)
+ M.setDir(AM.dir)
+ if(iscyborg(AM))
+ var/mob/living/silicon/robot/R = AM
+ if(istype(R.module))
+ M.pixel_x = R.module.ride_offset_x[dir2text(AM.dir)]
+ M.pixel_y = R.module.ride_offset_y[dir2text(AM.dir)]
+ else
+ ..()
+
+/datum/component/riding/cyborg/force_dismount(mob/living/M)
+ var/atom/movable/AM = parent
+ AM.unbuckle_mob(M)
+ var/turf/target = get_edge_target_turf(AM, AM.dir)
+ var/turf/targetm = get_step(get_turf(AM), AM.dir)
+ M.Move(targetm)
+ M.visible_message("[M] is thrown clear of [AM]!")
+ M.throw_at(target, 14, 5, AM)
+ M.Knockdown(60)
+
+/datum/component/riding/proc/equip_buckle_inhands(mob/living/carbon/human/user, amount_required = 1)
+ var/atom/movable/AM = parent
+ var/amount_equipped = 0
+ for(var/amount_needed = amount_required, amount_needed > 0, amount_needed--)
+ var/obj/item/riding_offhand/inhand = new /obj/item/riding_offhand(user)
+ inhand.rider = user
+ inhand.parent = AM
+ if(user.put_in_hands(inhand, TRUE))
+ amount_equipped++
+ else
+ break
+ if(amount_equipped >= amount_required)
+ return TRUE
+ else
+ unequip_buckle_inhands(user)
+ return FALSE
+
+/datum/component/riding/proc/unequip_buckle_inhands(mob/living/carbon/user)
+ var/atom/movable/AM = parent
+ for(var/obj/item/riding_offhand/O in user.contents)
+ if(O.parent != AM)
+ CRASH("RIDING OFFHAND ON WRONG MOB")
+ continue
+ if(O.selfdeleting)
+ continue
+ else
+ qdel(O)
+ return TRUE
+
+/obj/item/riding_offhand
+ name = "offhand"
+ icon = 'icons/obj/items_and_weapons.dmi'
+ icon_state = "offhand"
+ w_class = WEIGHT_CLASS_HUGE
+ flags_1 = ABSTRACT_1 | DROPDEL_1 | NOBLUDGEON_1
+ resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
+ var/mob/living/carbon/rider
+ var/mob/living/parent
+ var/selfdeleting = FALSE
+
+/obj/item/riding_offhand/dropped()
+ selfdeleting = TRUE
+ . = ..()
+
+/obj/item/riding_offhand/equipped()
+ if(loc != rider)
+ selfdeleting = TRUE
+ qdel(src)
+ . = ..()
+
+/obj/item/riding_offhand/Destroy()
+ var/atom/movable/AM = parent
+ if(selfdeleting)
+ if(rider in AM.buckled_mobs)
+ AM.unbuckle_mob(rider)
+ . = ..()
diff --git a/code/datums/components/signal_redirect.dm b/code/datums/components/signal_redirect.dm
index 38372dd356..764a44e4a9 100644
--- a/code/datums/components/signal_redirect.dm
+++ b/code/datums/components/signal_redirect.dm
@@ -4,6 +4,5 @@
/datum/component/redirect/Initialize(list/signals, datum/callback/_callback)
//It's not our job to verify the right signals are registered here, just do it.
if(!LAZYLEN(signals) || !istype(_callback))
- . = COMPONENT_INCOMPATIBLE
- CRASH("A redirection component was initialized with incorrect args.")
+ return COMPONENT_INCOMPATIBLE
RegisterSignal(signals, _callback)
diff --git a/code/datums/components/thermite.dm b/code/datums/components/thermite.dm
index 0cc95e57b3..809abbfdcc 100644
--- a/code/datums/components/thermite.dm
+++ b/code/datums/components/thermite.dm
@@ -21,10 +21,7 @@
)
/datum/component/thermite/Initialize(_amount)
- if(!istype(parent, /turf))
- . = COMPONENT_INCOMPATIBLE
- CRASH("A thermite component has been applied to an incorrect object. parent: [parent]")
- if(blacklist[parent.type])
+ if(!istype(parent, /turf) || blacklist[parent.type])
return COMPONENT_INCOMPATIBLE
if(immunelist[parent.type])
_amount*=0 //Yeah the overlay can still go on it and be cleaned but you arent burning down a diamond wall
diff --git a/code/datums/components/wet_floor.dm b/code/datums/components/wet_floor.dm
index 7faeb2dfb2..78b31802c9 100644
--- a/code/datums/components/wet_floor.dm
+++ b/code/datums/components/wet_floor.dm
@@ -24,8 +24,7 @@
/datum/component/wet_floor/Initialize(strength, duration_minimum, duration_add, duration_maximum, _permanent = FALSE)
if(!isopenturf(parent))
- . = COMPONENT_INCOMPATIBLE
- CRASH("Wet floor component attempted to be applied to a non open turf!")
+ return COMPONENT_INCOMPATIBLE
add_wet(strength, duration_minimum, duration_add, duration_maximum)
RegisterSignal(COMSIG_TURF_IS_WET, .proc/is_wet)
RegisterSignal(COMSIG_TURF_MAKE_DRY, .proc/dry)
@@ -138,8 +137,7 @@
/datum/component/wet_floor/OnTransfer(datum/to_datum)
if(!isopenturf(to_datum))
- . = COMPONENT_INCOMPATIBLE
- CRASH("Wet floor component attempted to be transferred to a non open turf!")
+ return COMPONENT_INCOMPATIBLE
var/turf/O = parent
O.cut_overlay(current_overlay)
var/turf/T = to_datum