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