Removes redundant COMPONENT_INCOMPATIBLE crashes
This commit is contained in:
committed by
CitadelStationBot
parent
3044f3ad3a
commit
f0a259d3e5
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
#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}
|
||||
@@ -241,3 +242,247 @@
|
||||
|
||||
/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!")
|
||||
>>>>>>> a1b89c3... Removes redundant COMPONENT_INCOMPATIBLE crashes (#37389)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD
|
||||
/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
|
||||
@@ -324,3 +325,330 @@
|
||||
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("<span class='warning'>[M] falls off of [AM]!</span>")
|
||||
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, "<span class='notice'>You'll need the keys in one of your hands to [drive_verb] [AM].</span>")
|
||||
|
||||
/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("<span class='warning'>[AM] pushes [user] off of them!</span>")
|
||||
|
||||
/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, "<span class='userdanger'>You fall off of [AM]!</span>")
|
||||
Unbuckle(user)
|
||||
return
|
||||
if(iscarbon(user))
|
||||
var/mob/living/carbon/carbonuser = user
|
||||
if(!carbonuser.get_num_arms())
|
||||
Unbuckle(user)
|
||||
to_chat(user, "<span class='userdanger'>You can't grab onto [AM] with no hands!</span>")
|
||||
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("<span class='warning'>[M] is thrown clear of [AM]!</span>")
|
||||
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)
|
||||
. = ..()
|
||||
>>>>>>> a1b89c3... Removes redundant COMPONENT_INCOMPATIBLE crashes (#37389)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -0,0 +1,730 @@
|
||||
#define COLLECT_ONE 0
|
||||
#define COLLECT_EVERYTHING 1
|
||||
#define COLLECT_SAME 2
|
||||
|
||||
#define DROP_NOTHING 0
|
||||
#define DROP_AT_PARENT 1
|
||||
#define DROP_AT_LOCATION 2
|
||||
|
||||
// External storage-related logic:
|
||||
// /mob/proc/ClickOn() in /_onclick/click.dm - clicking items in storages
|
||||
// /mob/living/Move() in /modules/mob/living/living.dm - hiding storage boxes on mob movement
|
||||
|
||||
/datum/component/storage
|
||||
dupe_mode = COMPONENT_DUPE_UNIQUE
|
||||
var/datum/component/storage/concrete/master //If not null, all actions act on master and this is just an access point.
|
||||
|
||||
var/list/can_hold //if this is set, only things in this typecache will fit.
|
||||
var/list/cant_hold //if this is set, anything in this typecache will not be able to fit.
|
||||
|
||||
var/list/mob/is_using //lazy list of mobs looking at the contents of this storage.
|
||||
|
||||
var/locked = FALSE //when locked nothing can see inside or use it.
|
||||
|
||||
var/max_w_class = WEIGHT_CLASS_SMALL //max size of objects that will fit.
|
||||
var/max_combined_w_class = 14 //max combined sizes of objects that will fit.
|
||||
var/max_items = 7 //max number of objects that will fit.
|
||||
|
||||
var/emp_shielded = FALSE
|
||||
|
||||
var/silent = FALSE //whether this makes a message when things are put in.
|
||||
var/click_gather = FALSE //whether this can be clicked on items to pick it up rather than the other way around.
|
||||
var/rustle_sound = TRUE //play rustle sound on interact.
|
||||
var/allow_quick_empty = FALSE //allow empty verb which allows dumping on the floor of everything inside quickly.
|
||||
var/allow_quick_gather = FALSE //allow toggle mob verb which toggles collecting all items from a tile.
|
||||
|
||||
var/collection_mode = COLLECT_EVERYTHING
|
||||
|
||||
var/insert_preposition = "in" //you put things "in" a bag, but "on" a tray.
|
||||
|
||||
var/display_numerical_stacking = FALSE //stack things of the same type and show as a single object with a number.
|
||||
|
||||
var/obj/screen/storage/boxes //storage display object
|
||||
var/obj/screen/close/closer //close button object
|
||||
|
||||
var/allow_big_nesting = FALSE //allow storage objects of the same or greater size.
|
||||
|
||||
var/attack_hand_interact = TRUE //interact on attack hand.
|
||||
var/quickdraw = FALSE //altclick interact
|
||||
|
||||
var/datum/action/item_action/storage_gather_mode/modeswitch_action
|
||||
|
||||
//Screen variables: Do not mess with these vars unless you know what you're doing. They're not defines so storage that isn't in the same location can be supported in the future.
|
||||
var/screen_max_columns = 7 //These two determine maximum screen sizes.
|
||||
var/screen_max_rows = INFINITY
|
||||
var/screen_pixel_x = 16 //These two are pixel values for screen loc of boxes and closer
|
||||
var/screen_pixel_y = 16
|
||||
var/screen_start_x = 4 //These two are where the storage starts being rendered, screen_loc wise.
|
||||
var/screen_start_y = 2
|
||||
//End
|
||||
|
||||
/datum/component/storage/Initialize(datum/component/storage/concrete/master)
|
||||
if(!isatom(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
if(master)
|
||||
change_master(master)
|
||||
boxes = new(null, src)
|
||||
closer = new(null, src)
|
||||
orient2hud()
|
||||
|
||||
RegisterSignal(COMSIG_CONTAINS_STORAGE, .proc/on_check)
|
||||
RegisterSignal(COMSIG_IS_STORAGE_LOCKED, .proc/check_locked)
|
||||
RegisterSignal(COMSIG_TRY_STORAGE_SHOW, .proc/signal_show_attempt)
|
||||
RegisterSignal(COMSIG_TRY_STORAGE_INSERT, .proc/signal_insertion_attempt)
|
||||
RegisterSignal(COMSIG_TRY_STORAGE_CAN_INSERT, .proc/signal_can_insert)
|
||||
RegisterSignal(COMSIG_TRY_STORAGE_TAKE_TYPE, .proc/signal_take_type)
|
||||
RegisterSignal(COMSIG_TRY_STORAGE_FILL_TYPE, .proc/signal_fill_type)
|
||||
RegisterSignal(COMSIG_TRY_STORAGE_SET_LOCKSTATE, .proc/set_locked)
|
||||
RegisterSignal(COMSIG_TRY_STORAGE_TAKE, .proc/signal_take_obj)
|
||||
RegisterSignal(COMSIG_TRY_STORAGE_QUICK_EMPTY, .proc/signal_quick_empty)
|
||||
RegisterSignal(COMSIG_TRY_STORAGE_HIDE_FROM, .proc/signal_hide_attempt)
|
||||
RegisterSignal(COMSIG_TRY_STORAGE_HIDE_ALL, .proc/close_all)
|
||||
RegisterSignal(COMSIG_TRY_STORAGE_RETURN_INVENTORY, .proc/signal_return_inv)
|
||||
|
||||
RegisterSignal(COMSIG_PARENT_ATTACKBY, .proc/attackby)
|
||||
|
||||
RegisterSignal(COMSIG_ATOM_ATTACK_HAND, .proc/on_attack_hand)
|
||||
RegisterSignal(COMSIG_ATOM_ATTACK_PAW, .proc/on_attack_hand)
|
||||
RegisterSignal(COMSIG_ATOM_EMP_ACT, .proc/emp_act)
|
||||
RegisterSignal(COMSIG_ATOM_ATTACK_GHOST, .proc/show_to_ghost)
|
||||
RegisterSignal(COMSIG_ATOM_EXITED, .proc/_removal_reset)
|
||||
|
||||
RegisterSignal(COMSIG_ITEM_PRE_ATTACK, .proc/preattack_intercept)
|
||||
RegisterSignal(COMSIG_ITEM_ATTACK_SELF, .proc/attack_self)
|
||||
RegisterSignal(COMSIG_ITEM_PICKUP, .proc/signal_on_pickup)
|
||||
|
||||
RegisterSignal(COMSIG_MOVABLE_THROW, .proc/close_all)
|
||||
|
||||
RegisterSignal(COMSIG_CLICK_ALT, .proc/on_alt_click)
|
||||
RegisterSignal(COMSIG_MOUSEDROP_ONTO, .proc/mousedrop_onto)
|
||||
RegisterSignal(COMSIG_MOUSEDROPPED_ONTO, .proc/mousedrop_recieve)
|
||||
|
||||
update_actions()
|
||||
|
||||
/datum/component/storage/Destroy()
|
||||
close_all()
|
||||
QDEL_NULL(boxes)
|
||||
QDEL_NULL(closer)
|
||||
LAZYCLEARLIST(is_using)
|
||||
return ..()
|
||||
|
||||
/datum/component/storage/OnTransfer(datum/new_parent)
|
||||
update_actions()
|
||||
|
||||
/datum/component/storage/proc/update_actions()
|
||||
QDEL_NULL(modeswitch_action)
|
||||
if(!isitem(parent) || !allow_quick_gather)
|
||||
return
|
||||
var/obj/item/I = parent
|
||||
modeswitch_action = new(I)
|
||||
if(I.obj_flags & IN_INVENTORY)
|
||||
var/mob/M = I.loc
|
||||
if(!istype(M))
|
||||
return
|
||||
modeswitch_action.Grant(M)
|
||||
|
||||
/datum/component/storage/proc/change_master(datum/component/storage/concrete/new_master)
|
||||
if(!istype(new_master))
|
||||
return FALSE
|
||||
master.on_slave_unlink(src)
|
||||
master = new_master
|
||||
master.on_slave_link(src)
|
||||
return TRUE
|
||||
|
||||
/datum/component/storage/proc/master()
|
||||
if(master == src)
|
||||
return //infinite loops yo.
|
||||
return master
|
||||
|
||||
/datum/component/storage/proc/real_location()
|
||||
var/datum/component/storage/concrete/master = master()
|
||||
return master? master.real_location() : null
|
||||
|
||||
/datum/component/storage/proc/attack_self(mob/M)
|
||||
if(locked)
|
||||
to_chat(M, "<span class='warning'>[parent] seems to be locked!</span>")
|
||||
return FALSE
|
||||
if((M.get_active_held_item() == parent) && allow_quick_empty)
|
||||
quick_empty(M)
|
||||
|
||||
/datum/component/storage/proc/preattack_intercept(obj/O, mob/M, params)
|
||||
if(!isitem(O) || !click_gather || O.SendSignal(COMSIG_CONTAINS_STORAGE))
|
||||
return FALSE
|
||||
. = COMPONENT_NO_ATTACK
|
||||
if(locked)
|
||||
to_chat(M, "<span class='warning'>[parent] seems to be locked!</span>")
|
||||
return FALSE
|
||||
var/obj/item/I = O
|
||||
if(collection_mode == COLLECT_ONE)
|
||||
if(can_be_inserted(I, null, M))
|
||||
handle_item_insertion(I, null, M)
|
||||
return
|
||||
if(!isturf(I.loc))
|
||||
return
|
||||
var/list/things = I.loc.contents.Copy()
|
||||
if(collection_mode == COLLECT_SAME)
|
||||
things = typecache_filter_list(things, typecacheof(I.type))
|
||||
var/len = length(things)
|
||||
if(!len)
|
||||
to_chat(M, "<span class='notice'>You failed to pick up anything with [parent].</span>")
|
||||
return
|
||||
var/datum/progressbar/progress = new(M, len, I.loc)
|
||||
var/list/rejections = list()
|
||||
while(do_after(M, 10, TRUE, parent, FALSE, CALLBACK(src, .proc/handle_mass_pickup, things, I.loc, rejections, progress)))
|
||||
stoplag(1)
|
||||
qdel(progress)
|
||||
to_chat(M, "<span class='notice'>You put everything you could [insert_preposition] [parent].</span>")
|
||||
|
||||
/datum/component/storage/proc/handle_mass_item_insertion(list/things, datum/component/storage/src_object, mob/user, datum/progressbar/progress)
|
||||
var/atom/source_real_location = src_object.real_location()
|
||||
for(var/obj/item/I in things)
|
||||
things -= I
|
||||
if(I.loc != source_real_location)
|
||||
continue
|
||||
if(user.active_storage != src_object)
|
||||
if(I.on_found(user))
|
||||
break
|
||||
if(can_be_inserted(I,FALSE,user))
|
||||
handle_item_insertion(I, TRUE, user)
|
||||
if (TICK_CHECK)
|
||||
progress.update(progress.goal - things.len)
|
||||
return TRUE
|
||||
|
||||
progress.update(progress.goal - things.len)
|
||||
return FALSE
|
||||
|
||||
/datum/component/storage/proc/handle_mass_pickup(list/things, atom/thing_loc, list/rejections, datum/progressbar/progress)
|
||||
var/atom/real_location = real_location()
|
||||
for(var/obj/item/I in things)
|
||||
things -= I
|
||||
if(I.loc != thing_loc)
|
||||
continue
|
||||
if(I.type in rejections) // To limit bag spamming: any given type only complains once
|
||||
continue
|
||||
if(!can_be_inserted(I, stop_messages = TRUE)) // Note can_be_inserted still makes noise when the answer is no
|
||||
if(real_location.contents.len >= max_items)
|
||||
break
|
||||
rejections += I.type // therefore full bags are still a little spammy
|
||||
continue
|
||||
|
||||
handle_item_insertion(I, TRUE) //The TRUE stops the "You put the [parent] into [S]" insertion message from being displayed.
|
||||
|
||||
if (TICK_CHECK)
|
||||
progress.update(progress.goal - things.len)
|
||||
return TRUE
|
||||
|
||||
progress.update(progress.goal - things.len)
|
||||
return FALSE
|
||||
|
||||
/datum/component/storage/proc/quick_empty(mob/M)
|
||||
var/atom/A = parent
|
||||
if((!ishuman(M) && (A.loc != M)) || (M.stat != CONSCIOUS) || M.restrained() || !M.canmove)
|
||||
return
|
||||
if(locked)
|
||||
to_chat(M, "<span class='warning'>[parent] seems to be locked!</span>")
|
||||
return FALSE
|
||||
A.add_fingerprint(M)
|
||||
to_chat(M, "<span class='notice'>You start dumping out [parent].</span>")
|
||||
var/turf/T = get_turf(A)
|
||||
var/list/things = contents()
|
||||
var/datum/progressbar/progress = new(M, length(things), T)
|
||||
while (do_after(M, 10, TRUE, T, FALSE, CALLBACK(src, .proc/mass_remove_from_storage, T, things, progress)))
|
||||
stoplag(1)
|
||||
qdel(progress)
|
||||
|
||||
/datum/component/storage/proc/mass_remove_from_storage(atom/target, list/things, datum/progressbar/progress)
|
||||
var/atom/real_location = real_location()
|
||||
for(var/obj/item/I in things)
|
||||
things -= I
|
||||
if(I.loc != real_location)
|
||||
continue
|
||||
remove_from_storage(I, target)
|
||||
if(TICK_CHECK)
|
||||
progress.update(progress.goal - length(things))
|
||||
return TRUE
|
||||
progress.update(progress.goal - length(things))
|
||||
return FALSE
|
||||
|
||||
/datum/component/storage/proc/do_quick_empty(atom/_target)
|
||||
if(!_target)
|
||||
_target = get_turf(parent)
|
||||
if(usr)
|
||||
hide_from(usr)
|
||||
var/list/contents = contents()
|
||||
var/atom/real_location = real_location()
|
||||
for(var/obj/item/I in contents)
|
||||
if(I.loc != real_location)
|
||||
continue
|
||||
remove_from_storage(I, _target)
|
||||
return TRUE
|
||||
|
||||
/datum/component/storage/proc/set_locked(new_state)
|
||||
locked = new_state
|
||||
if(locked)
|
||||
close_all()
|
||||
|
||||
/datum/component/storage/proc/_process_numerical_display()
|
||||
. = list()
|
||||
var/atom/real_location = real_location()
|
||||
for(var/obj/item/I in real_location.contents)
|
||||
if(QDELETED(I))
|
||||
continue
|
||||
if(!.[I.type])
|
||||
.[I.type] = new /datum/numbered_display(I, 1)
|
||||
else
|
||||
var/datum/numbered_display/ND = .[I.type]
|
||||
ND.number++
|
||||
|
||||
//This proc determines the size of the inventory to be displayed. Please touch it only if you know what you're doing.
|
||||
/datum/component/storage/proc/orient2hud()
|
||||
var/atom/real_location = real_location()
|
||||
var/adjusted_contents = real_location.contents.len
|
||||
|
||||
//Numbered contents display
|
||||
var/list/datum/numbered_display/numbered_contents
|
||||
if(display_numerical_stacking)
|
||||
numbered_contents = _process_numerical_display()
|
||||
adjusted_contents = numbered_contents.len
|
||||
|
||||
var/columns = CLAMP(max_items, 1, screen_max_columns)
|
||||
var/rows = CLAMP(CEILING(adjusted_contents / columns, 1), 1, screen_max_rows)
|
||||
standard_orient_objs(rows, columns, numbered_contents)
|
||||
|
||||
//This proc draws out the inventory and places the items on it. It uses the standard position.
|
||||
/datum/component/storage/proc/standard_orient_objs(rows, cols, list/obj/item/numerical_display_contents)
|
||||
boxes.screen_loc = "[screen_start_x]:[screen_pixel_x],[screen_start_y]:[screen_pixel_y] to [screen_start_x+cols-1]:[screen_pixel_x],[screen_start_y+rows-1]:[screen_pixel_y]"
|
||||
var/cx = screen_start_x
|
||||
var/cy = screen_start_y
|
||||
if(islist(numerical_display_contents))
|
||||
for(var/type in numerical_display_contents)
|
||||
var/datum/numbered_display/ND = numerical_display_contents[type]
|
||||
ND.sample_object.mouse_opacity = MOUSE_OPACITY_OPAQUE
|
||||
ND.sample_object.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]"
|
||||
ND.sample_object.maptext = "<font color='white'>[(ND.number > 1)? "[ND.number]" : ""]</font>"
|
||||
ND.sample_object.layer = ABOVE_HUD_LAYER
|
||||
ND.sample_object.plane = ABOVE_HUD_PLANE
|
||||
cx++
|
||||
if(cx - screen_start_x >= cols)
|
||||
cx = screen_start_x
|
||||
cy++
|
||||
if(cy - screen_start_y >= rows)
|
||||
break
|
||||
else
|
||||
var/atom/real_location = real_location()
|
||||
for(var/obj/O in real_location)
|
||||
if(QDELETED(O))
|
||||
continue
|
||||
O.mouse_opacity = MOUSE_OPACITY_OPAQUE //This is here so storage items that spawn with contents correctly have the "click around item to equip"
|
||||
O.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]"
|
||||
O.maptext = ""
|
||||
O.layer = ABOVE_HUD_LAYER
|
||||
O.plane = ABOVE_HUD_PLANE
|
||||
cx++
|
||||
if(cx - screen_start_x >= cols)
|
||||
cx = screen_start_x
|
||||
cy++
|
||||
if(cy - screen_start_y >= rows)
|
||||
break
|
||||
closer.screen_loc = "[screen_start_x + cols]:[screen_pixel_x],[screen_start_y]:[screen_pixel_y]"
|
||||
|
||||
/datum/component/storage/proc/show_to(mob/M)
|
||||
if(!M.client)
|
||||
return FALSE
|
||||
var/atom/real_location = real_location()
|
||||
if(M.active_storage != src && (M.stat == CONSCIOUS))
|
||||
for(var/obj/item/I in real_location)
|
||||
if(I.on_found(M))
|
||||
return FALSE
|
||||
if(M.active_storage)
|
||||
M.active_storage.hide_from(M)
|
||||
orient2hud()
|
||||
M.client.screen |= boxes
|
||||
M.client.screen |= closer
|
||||
M.client.screen |= real_location.contents
|
||||
M.active_storage = src
|
||||
LAZYOR(is_using, M)
|
||||
return TRUE
|
||||
|
||||
/datum/component/storage/proc/hide_from(mob/M)
|
||||
if(!M.client)
|
||||
return TRUE
|
||||
var/atom/real_location = real_location()
|
||||
M.client.screen -= boxes
|
||||
M.client.screen -= closer
|
||||
M.client.screen -= real_location.contents
|
||||
if(M.active_storage == src)
|
||||
M.active_storage = null
|
||||
LAZYREMOVE(is_using, M)
|
||||
return TRUE
|
||||
|
||||
/datum/component/storage/proc/close(mob/M)
|
||||
hide_from(M)
|
||||
|
||||
/datum/component/storage/proc/close_all()
|
||||
. = FALSE
|
||||
for(var/mob/M in can_see_contents())
|
||||
close(M)
|
||||
. = TRUE //returns TRUE if any mobs actually got a close(M) call
|
||||
|
||||
/datum/component/storage/proc/emp_act(severity)
|
||||
var/atom/A = parent
|
||||
if(!isliving(A.loc) && !emp_shielded)
|
||||
var/datum/component/storage/concrete/master = master()
|
||||
master.emp_act(severity)
|
||||
|
||||
//This proc draws out the inventory and places the items on it. tx and ty are the upper left tile and mx, my are the bottm right.
|
||||
//The numbers are calculated from the bottom-left The bottom-left slot being 1,1.
|
||||
/datum/component/storage/proc/orient_objs(tx, ty, mx, my)
|
||||
var/atom/real_location = real_location()
|
||||
var/cx = tx
|
||||
var/cy = ty
|
||||
boxes.screen_loc = "[tx]:,[ty] to [mx],[my]"
|
||||
for(var/obj/O in real_location)
|
||||
if(QDELETED(O))
|
||||
continue
|
||||
O.screen_loc = "[cx],[cy]"
|
||||
O.layer = ABOVE_HUD_LAYER
|
||||
O.plane = ABOVE_HUD_PLANE
|
||||
cx++
|
||||
if(cx > mx)
|
||||
cx = tx
|
||||
cy--
|
||||
closer.screen_loc = "[mx+1],[my]"
|
||||
|
||||
//Resets something that is being removed from storage.
|
||||
/datum/component/storage/proc/_removal_reset(atom/movable/thing)
|
||||
if(!istype(thing))
|
||||
return FALSE
|
||||
var/datum/component/storage/concrete/master = master()
|
||||
if(!istype(master))
|
||||
return FALSE
|
||||
return master._removal_reset(thing)
|
||||
|
||||
//Call this proc to handle the removal of an item from the storage item. The item will be moved to the new_location target, if that is null it's being deleted
|
||||
/datum/component/storage/proc/remove_from_storage(atom/movable/AM, atom/new_location)
|
||||
if(!istype(AM))
|
||||
return FALSE
|
||||
var/datum/component/storage/concrete/master = master()
|
||||
if(!istype(master))
|
||||
return FALSE
|
||||
return master.remove_from_storage(AM, new_location)
|
||||
|
||||
/datum/component/storage/proc/refresh_mob_views()
|
||||
var/list/seeing = can_see_contents()
|
||||
for(var/i in seeing)
|
||||
show_to(i)
|
||||
return TRUE
|
||||
|
||||
/datum/component/storage/proc/can_see_contents()
|
||||
var/list/cansee = list()
|
||||
for(var/mob/M in is_using)
|
||||
if(M.active_storage == src && M.client)
|
||||
cansee |= M
|
||||
else
|
||||
LAZYREMOVE(is_using, M)
|
||||
return cansee
|
||||
|
||||
//Tries to dump content
|
||||
/datum/component/storage/proc/dump_content_at(atom/dest_object, mob/M)
|
||||
var/atom/A = parent
|
||||
var/atom/dump_destination = dest_object.get_dumping_location()
|
||||
if(A.Adjacent(M) && dump_destination && M.Adjacent(dump_destination))
|
||||
if(locked)
|
||||
to_chat(M, "<span class='warning'>[parent] seems to be locked!</span>")
|
||||
return FALSE
|
||||
if(dump_destination.storage_contents_dump_act(src, M))
|
||||
playsound(A, "rustle", 50, 1, -5)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
//This proc is called when you want to place an item into the storage item.
|
||||
/datum/component/storage/proc/attackby(obj/item/I, mob/M, params)
|
||||
if(istype(I, /obj/item/hand_labeler))
|
||||
var/obj/item/hand_labeler/labeler = I
|
||||
if(labeler.mode)
|
||||
return FALSE
|
||||
. = TRUE //no afterattack
|
||||
if(iscyborg(M))
|
||||
return
|
||||
if(!can_be_inserted(I, FALSE, M))
|
||||
var/atom/real_location = real_location()
|
||||
if(real_location.contents.len >= max_items) //don't use items on the backpack if they don't fit
|
||||
return TRUE
|
||||
return FALSE
|
||||
handle_item_insertion(I, FALSE, M)
|
||||
|
||||
/datum/component/storage/proc/return_inv()
|
||||
. = list()
|
||||
. += contents()
|
||||
for(var/i in contents())
|
||||
var/atom/a = i
|
||||
GET_COMPONENT_FROM(STR, /datum/component/storage, a)
|
||||
if(STR)
|
||||
. += STR.return_inv()
|
||||
|
||||
/datum/component/storage/proc/contents() //ONLY USE IF YOU NEED TO COPY CONTENTS OF REAL LOCATION, COPYING IS NOT AS FAST AS DIRECT ACCESS!
|
||||
var/atom/real_location = real_location()
|
||||
return real_location.contents.Copy()
|
||||
|
||||
/datum/component/storage/proc/signal_return_inv(list/interface)
|
||||
if(!islist(interface))
|
||||
return FALSE
|
||||
interface |= return_inv()
|
||||
return TRUE
|
||||
|
||||
/datum/component/storage/proc/mousedrop_onto(atom/over_object, mob/M)
|
||||
set waitfor = FALSE
|
||||
. = COMPONENT_NO_MOUSEDROP
|
||||
var/atom/A = parent
|
||||
if(ismob(M)) //all the check for item manipulation are in other places, you can safely open any storages as anything and its not buggy, i checked
|
||||
A.add_fingerprint(M)
|
||||
if(!over_object)
|
||||
return FALSE
|
||||
if(ismecha(M.loc)) // stops inventory actions in a mech
|
||||
return FALSE
|
||||
// this must come before the screen objects only block, dunno why it wasn't before
|
||||
if(over_object == M)
|
||||
user_show_to_mob(M)
|
||||
if(!M.incapacitated())
|
||||
if(!istype(over_object, /obj/screen))
|
||||
dump_content_at(over_object, M)
|
||||
return
|
||||
if(A.loc != M)
|
||||
return
|
||||
playsound(A, "rustle", 50, 1, -5)
|
||||
if(istype(over_object, /obj/screen/inventory/hand))
|
||||
var/obj/screen/inventory/hand/H = over_object
|
||||
M.putItemFromInventoryInHandIfPossible(A, H.held_index)
|
||||
return
|
||||
A.add_fingerprint(M)
|
||||
|
||||
/datum/component/storage/proc/user_show_to_mob(mob/M, force = FALSE)
|
||||
var/atom/A = parent
|
||||
if(!istype(M))
|
||||
return FALSE
|
||||
A.add_fingerprint(M)
|
||||
if(locked && !force)
|
||||
to_chat(M, "<span class='warning'>[parent] seems to be locked!</span>")
|
||||
return FALSE
|
||||
if(force || M.CanReach(parent, view_only = TRUE))
|
||||
show_to(M)
|
||||
|
||||
/datum/component/storage/proc/mousedrop_recieve(atom/movable/O, mob/M)
|
||||
if(isitem(O))
|
||||
var/obj/item/I = O
|
||||
if(iscarbon(M) || isdrone(M))
|
||||
var/mob/living/L = M
|
||||
if(!L.incapacitated() && I == L.get_active_held_item())
|
||||
if(!I.SendSignal(COMSIG_CONTAINS_STORAGE) && can_be_inserted(I, FALSE)) //If it has storage it should be trying to dump, not insert.
|
||||
handle_item_insertion(I, FALSE, L)
|
||||
|
||||
//This proc return 1 if the item can be picked up and 0 if it can't.
|
||||
//Set the stop_messages to stop it from printing messages
|
||||
/datum/component/storage/proc/can_be_inserted(obj/item/I, stop_messages = FALSE, mob/M)
|
||||
if(!istype(I) || (I.flags_1 & ABSTRACT_1))
|
||||
return FALSE //Not an item
|
||||
var/atom/real_location = real_location()
|
||||
var/atom/parent = src.parent
|
||||
if(real_location == I.loc)
|
||||
return FALSE //Means the item is already in the storage item
|
||||
if(locked)
|
||||
if(M)
|
||||
parent.add_fingerprint(M)
|
||||
to_chat(M, "<span class='warning'>[parent] seems to be locked!</span>")
|
||||
return FALSE
|
||||
if(real_location.contents.len >= max_items)
|
||||
if(!stop_messages)
|
||||
to_chat(M, "<span class='warning'>[parent] is full, make some space!</span>")
|
||||
return FALSE //Storage item is full
|
||||
if(length(can_hold))
|
||||
if(!is_type_in_typecache(I, can_hold))
|
||||
if(!stop_messages)
|
||||
to_chat(M, "<span class='warning'>[parent] cannot hold [I]!</span>")
|
||||
return FALSE
|
||||
if(is_type_in_typecache(I, cant_hold)) //Check for specific items which this container can't hold.
|
||||
if(!stop_messages)
|
||||
to_chat(M, "<span class='warning'>[parent] cannot hold [I]!</span>")
|
||||
return FALSE
|
||||
if(I.w_class > max_w_class)
|
||||
if(!stop_messages)
|
||||
to_chat(M, "<span class='warning'>[I] is too big for [parent]!</span>")
|
||||
return FALSE
|
||||
var/sum_w_class = I.w_class
|
||||
for(var/obj/item/_I in real_location)
|
||||
sum_w_class += _I.w_class //Adds up the combined w_classes which will be in the storage item if the item is added to it.
|
||||
if(sum_w_class > max_combined_w_class)
|
||||
if(!stop_messages)
|
||||
to_chat(M, "<span class='warning'>[I] won't fit in [parent], make some space!</span>")
|
||||
return FALSE
|
||||
if(isitem(parent))
|
||||
var/obj/item/IP = parent
|
||||
GET_COMPONENT_FROM(STR_I, /datum/component/storage, I)
|
||||
if((I.w_class >= IP.w_class) && STR_I && !allow_big_nesting)
|
||||
if(!stop_messages)
|
||||
to_chat(M, "<span class='warning'>[IP] cannot hold [I] as it's a storage item of the same size!</span>")
|
||||
return FALSE //To prevent the stacking of same sized storage items.
|
||||
if(I.flags_1 & NODROP_1) //SHOULD be handled in unEquip, but better safe than sorry.
|
||||
to_chat(M, "<span class='warning'>\the [I] is stuck to your hand, you can't put it in \the [parent]!</span>")
|
||||
return FALSE
|
||||
var/datum/component/storage/concrete/master = master()
|
||||
if(!istype(master))
|
||||
return FALSE
|
||||
return master.slave_can_insert_object(src, I, stop_messages, M)
|
||||
|
||||
/datum/component/storage/proc/_insert_physical_item(obj/item/I, override = FALSE)
|
||||
return FALSE
|
||||
|
||||
//This proc handles items being inserted. It does not perform any checks of whether an item can or can't be inserted. That's done by can_be_inserted()
|
||||
//The stop_warning parameter will stop the insertion message from being displayed. It is intended for cases where you are inserting multiple items at once,
|
||||
//such as when picking up all the items on a tile with one click.
|
||||
/datum/component/storage/proc/handle_item_insertion(obj/item/I, prevent_warning = FALSE, mob/M, datum/component/storage/remote)
|
||||
var/atom/parent = src.parent
|
||||
var/datum/component/storage/concrete/master = master()
|
||||
if(!istype(master))
|
||||
return FALSE
|
||||
if(silent)
|
||||
prevent_warning = TRUE
|
||||
if(M)
|
||||
parent.add_fingerprint(M)
|
||||
. = master.handle_item_insertion_from_slave(src, I, prevent_warning, M)
|
||||
|
||||
/datum/component/storage/proc/mob_item_insertion_feedback(mob/user, mob/M, obj/item/I, override = FALSE)
|
||||
if(silent && !override)
|
||||
return
|
||||
if(rustle_sound)
|
||||
playsound(parent, "rustle", 50, 1, -5)
|
||||
for(var/mob/viewing in viewers(user, null))
|
||||
if(M == viewing)
|
||||
to_chat(usr, "<span class='notice'>You put [I] [insert_preposition]to [parent].</span>")
|
||||
else if(in_range(M, viewing)) //If someone is standing close enough, they can tell what it is...
|
||||
viewing.show_message("<span class='notice'>[M] puts [I] [insert_preposition]to [parent].</span>", 1)
|
||||
else if(I && I.w_class >= 3) //Otherwise they can only see large or normal items from a distance...
|
||||
viewing.show_message("<span class='notice'>[M] puts [I] [insert_preposition]to [parent].</span>", 1)
|
||||
|
||||
/datum/component/storage/proc/update_icon()
|
||||
if(isobj(parent))
|
||||
var/obj/O = parent
|
||||
O.update_icon()
|
||||
|
||||
/datum/component/storage/proc/signal_insertion_attempt(obj/item/I, mob/M, silent = FALSE, force = FALSE)
|
||||
if(!force && !can_be_inserted(I, TRUE, M))
|
||||
return FALSE
|
||||
return handle_item_insertion(I, silent, M)
|
||||
|
||||
/datum/component/storage/proc/signal_can_insert(obj/item/I, mob/M, silent = FALSE)
|
||||
return can_be_inserted(I, silent, M)
|
||||
|
||||
/datum/component/storage/proc/show_to_ghost(mob/dead/observer/M)
|
||||
return user_show_to_mob(M, TRUE)
|
||||
|
||||
/datum/component/storage/proc/signal_show_attempt(mob/showto, force = FALSE)
|
||||
return user_show_to_mob(showto, force)
|
||||
|
||||
/datum/component/storage/proc/on_check()
|
||||
return TRUE
|
||||
|
||||
/datum/component/storage/proc/check_locked()
|
||||
return locked
|
||||
|
||||
/datum/component/storage/proc/signal_take_type(type, atom/destination, amount = INFINITY, check_adjacent = FALSE, force = FALSE, mob/user, list/inserted)
|
||||
if(!force)
|
||||
if(check_adjacent)
|
||||
if(user)
|
||||
if(!user.CanReach(destination) || !user.CanReach(parent))
|
||||
return FALSE
|
||||
else if(!destination.CanReachStorage(parent))
|
||||
return FALSE
|
||||
var/list/taking = typecache_filter_list(contents(), typecacheof(type))
|
||||
if(length(taking) > amount)
|
||||
taking.Cut(amount)
|
||||
if(inserted) //duplicated code for performance, don't bother checking retval/checking for list every item.
|
||||
for(var/i in taking)
|
||||
if(remove_from_storage(i, destination))
|
||||
inserted |= i
|
||||
else
|
||||
for(var/i in taking)
|
||||
remove_from_storage(i, destination)
|
||||
return TRUE
|
||||
|
||||
/datum/component/storage/proc/remaining_space_items()
|
||||
var/atom/real_location = real_location()
|
||||
return max(0, max_items - real_location.contents.len)
|
||||
|
||||
/datum/component/storage/proc/signal_fill_type(type, amount = 20, force = FALSE)
|
||||
var/atom/real_location = real_location()
|
||||
if(!force)
|
||||
amount = min(remaining_space_items(), amount)
|
||||
for(var/i in 1 to amount)
|
||||
handle_item_insertion(new type(real_location), TRUE)
|
||||
CHECK_TICK
|
||||
return TRUE
|
||||
|
||||
/datum/component/storage/proc/on_attack_hand(mob/user)
|
||||
var/atom/A = parent
|
||||
if(!attack_hand_interact)
|
||||
return
|
||||
if(user.active_storage == src && A.loc == user) //if you're already looking inside the storage item
|
||||
user.active_storage.close(user)
|
||||
close(user)
|
||||
. = COMPONENT_NO_ATTACK_HAND
|
||||
return
|
||||
|
||||
if(rustle_sound)
|
||||
playsound(A, "rustle", 50, 1, -5)
|
||||
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
if(H.l_store == A && !H.get_active_held_item()) //Prevents opening if it's in a pocket.
|
||||
. = COMPONENT_NO_ATTACK_HAND
|
||||
H.put_in_hands(A)
|
||||
H.l_store = null
|
||||
return
|
||||
if(H.r_store == A && !H.get_active_held_item())
|
||||
. = COMPONENT_NO_ATTACK_HAND
|
||||
H.put_in_hands(A)
|
||||
H.r_store = null
|
||||
return
|
||||
|
||||
if(A.loc == user)
|
||||
. = COMPONENT_NO_ATTACK_HAND
|
||||
show_to(user)
|
||||
|
||||
/datum/component/storage/proc/signal_on_pickup(mob/user)
|
||||
var/atom/A = parent
|
||||
update_actions()
|
||||
for(var/mob/M in range(1, A))
|
||||
if(M.active_storage == src)
|
||||
close(M)
|
||||
|
||||
/datum/component/storage/proc/signal_take_obj(atom/movable/AM, new_loc, force = FALSE)
|
||||
if(!(AM in real_location()))
|
||||
return FALSE
|
||||
return remove_from_storage(AM, new_loc)
|
||||
|
||||
/datum/component/storage/proc/signal_quick_empty(atom/loctarget)
|
||||
return do_quick_empty(loctarget)
|
||||
|
||||
/datum/component/storage/proc/signal_hide_attempt(mob/target)
|
||||
return hide_from(target)
|
||||
|
||||
/datum/component/storage/proc/on_alt_click(mob/user)
|
||||
if(!isliving(user) || user.incapacitated() || !quickdraw || !user.CanReach(parent))
|
||||
return
|
||||
var/obj/item/I = locate() in real_location()
|
||||
if(!I)
|
||||
return
|
||||
remove_from_storage(I, get_turf(user))
|
||||
if(!user.put_in_hands(I))
|
||||
to_chat(user, "<span class='notice'>You fumble for [I] and it falls on the floor.</span>")
|
||||
return
|
||||
user.visible_message("<span class='warning'>[user] draws [I] from [parent]!</span>", "<span class='notice'>You draw [I] from [parent].</span>")
|
||||
|
||||
/datum/component/storage/proc/gather_mode_switch(mob/user)
|
||||
collection_mode = (collection_mode+1)%3
|
||||
switch(collection_mode)
|
||||
if(COLLECT_SAME)
|
||||
to_chat(user, "[parent] now picks up all items of a single type at once.")
|
||||
if(COLLECT_EVERYTHING)
|
||||
to_chat(user, "[parent] now picks up all items in a tile at once.")
|
||||
if(COLLECT_ONE)
|
||||
to_chat(user, "[parent] now picks up one item at a time.")
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user