diff --git a/code/controllers/subsystem/vis_overlays.dm b/code/controllers/subsystem/vis_overlays.dm
index 07c786b1..fe433c3e 100644
--- a/code/controllers/subsystem/vis_overlays.dm
+++ b/code/controllers/subsystem/vis_overlays.dm
@@ -7,12 +7,10 @@ SUBSYSTEM_DEF(vis_overlays)
var/list/vis_overlay_cache
var/list/unique_vis_overlays
var/list/currentrun
- var/datum/callback/rotate_cb
/datum/controller/subsystem/vis_overlays/Initialize()
vis_overlay_cache = list()
unique_vis_overlays = list()
- rotate_cb = CALLBACK(src, .proc/rotate_vis_overlay)
return ..()
/datum/controller/subsystem/vis_overlays/fire(resumed = FALSE)
@@ -57,7 +55,7 @@ SUBSYSTEM_DEF(vis_overlays)
if(!thing.managed_vis_overlays)
thing.managed_vis_overlays = list(overlay)
- RegisterSignal(thing, COMSIG_ATOM_DIR_CHANGE, rotate_cb)
+ RegisterSignal(thing, COMSIG_ATOM_DIR_CHANGE, .proc/rotate_vis_overlay)
else
thing.managed_vis_overlays += overlay
diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm
index ab0f7584..6c74f973 100644
--- a/code/datums/components/_component.dm
+++ b/code/datums/components/_component.dm
@@ -1,318 +1,315 @@
-/datum/component
- var/dupe_mode = COMPONENT_DUPE_HIGHLANDER
- var/dupe_type
- var/datum/parent
- //only set to true if you are able to properly transfer this component
- //At a minimum RegisterWithParent and UnregisterFromParent should be used
- //Make sure you also implement PostTransfer for any post transfer handling
- var/can_transfer = FALSE
-
-/datum/component/New(datum/P, ...)
- parent = P
- var/list/arguments = args.Copy(2)
- if(Initialize(arglist(arguments)) == COMPONENT_INCOMPATIBLE)
- qdel(src, TRUE, TRUE)
- CRASH("Incompatible [type] assigned to a [P.type]! args: [json_encode(arguments)]")
-
- _JoinParent(P)
-
-/datum/component/proc/_JoinParent()
- var/datum/P = parent
- //lazy init the parent's dc list
- var/list/dc = P.datum_components
- if(!dc)
- P.datum_components = dc = list()
-
- //set up the typecache
- var/our_type = type
- for(var/I in _GetInverseTypeList(our_type))
- var/test = dc[I]
- if(test) //already another component of this type here
- var/list/components_of_type
- if(!length(test))
- components_of_type = list(test)
- dc[I] = components_of_type
- else
- components_of_type = test
- if(I == our_type) //exact match, take priority
- var/inserted = FALSE
- for(var/J in 1 to components_of_type.len)
- var/datum/component/C = components_of_type[J]
- if(C.type != our_type) //but not over other exact matches
- components_of_type.Insert(J, I)
- inserted = TRUE
- break
- if(!inserted)
- components_of_type += src
- else //indirect match, back of the line with ya
- components_of_type += src
- else //only component of this type, no list
- dc[I] = src
-
- RegisterWithParent()
-
-// If you want/expect to be moving the component around between parents, use this to register on the parent for signals
-/datum/component/proc/RegisterWithParent()
- return
-
-/datum/component/proc/Initialize(...)
- return
-
-/datum/component/Destroy(force=FALSE, silent=FALSE)
- if(!force && parent)
- _RemoveFromParent()
- if(!silent)
- SEND_SIGNAL(parent, COMSIG_COMPONENT_REMOVING, src)
- parent = null
- return ..()
-
-/datum/component/proc/_RemoveFromParent()
- var/datum/P = parent
- var/list/dc = P.datum_components
- for(var/I in _GetInverseTypeList())
- var/list/components_of_type = dc[I]
- if(length(components_of_type)) //
- var/list/subtracted = components_of_type - src
- if(subtracted.len == 1) //only 1 guy left
- dc[I] = subtracted[1] //make him special
- else
- dc[I] = subtracted
- else //just us
- dc -= I
- if(!dc.len)
- P.datum_components = null
-
- UnregisterFromParent()
-
-/datum/component/proc/UnregisterFromParent()
- return
-
-/datum/proc/RegisterSignal(datum/target, sig_type_or_types, proc_or_callback, override = FALSE)
- if(QDELETED(src) || QDELETED(target))
- return
-
- var/list/procs = signal_procs
- if(!procs)
- signal_procs = procs = list()
- if(!procs[target])
- procs[target] = list()
- var/list/lookup = target.comp_lookup
- if(!lookup)
- target.comp_lookup = lookup = list()
-
- if(!istype(proc_or_callback, /datum/callback)) //if it wasnt a callback before, it is now
- proc_or_callback = CALLBACK(src, proc_or_callback)
-
- var/list/sig_types = islist(sig_type_or_types) ? sig_type_or_types : list(sig_type_or_types)
- for(var/sig_type in sig_types)
- if(!override && procs[target][sig_type])
- stack_trace("[sig_type] overridden. Use override = TRUE to suppress this warning")
-
- procs[target][sig_type] = proc_or_callback
-
- if(!lookup[sig_type]) // Nothing has registered here yet
- lookup[sig_type] = src
- else if(lookup[sig_type] == src) // We already registered here
- continue
- else if(!length(lookup[sig_type])) // One other thing registered here
- lookup[sig_type] = list(lookup[sig_type]=TRUE)
- lookup[sig_type][src] = TRUE
- else // Many other things have registered here
- lookup[sig_type][src] = TRUE
-
- signal_enabled = TRUE
-
-/datum/proc/UnregisterSignal(datum/target, sig_type_or_types)
- if(!target)
- CRASH("[src] tried to unregister a signal without having a target.")
- var/list/lookup = target.comp_lookup
- if(!signal_procs || !signal_procs[target] || !lookup)
- return
- if(!islist(sig_type_or_types))
- sig_type_or_types = list(sig_type_or_types)
- for(var/sig in sig_type_or_types)
- switch(length(lookup[sig]))
- if(2)
- lookup[sig] = (lookup[sig]-src)[1]
- if(1)
- stack_trace("[target] ([target.type]) somehow has single length list inside comp_lookup")
- if(src in lookup[sig])
- lookup -= sig
- if(!length(lookup))
- target.comp_lookup = null
- break
- if(0)
- lookup -= sig
- if(!length(lookup))
- target.comp_lookup = null
- break
- else
- lookup[sig] -= src
-
- signal_procs[target] -= sig_type_or_types
- if(!signal_procs[target].len)
- signal_procs -= target
-
-/datum/component/proc/InheritComponent(datum/component/C, i_am_original)
- return
-
-/datum/component/proc/PreTransfer()
- return
-
-/datum/component/proc/PostTransfer()
- return COMPONENT_INCOMPATIBLE //Do not support transfer by default as you must properly support it
-
-/datum/component/proc/_GetInverseTypeList(our_type = type)
- //we can do this one simple trick
- var/current_type = parent_type
- . = list(our_type, current_type)
- //and since most components are root level + 1, this won't even have to run
- while (current_type != /datum/component)
- current_type = type2parent(current_type)
- . += current_type
-
-/datum/proc/_SendSignal(sigtype, list/arguments)
- var/target = comp_lookup[sigtype]
- if(!length(target))
- var/datum/C = target
- if(!C.signal_enabled)
- return NONE
- var/datum/callback/CB = C.signal_procs[src][sigtype]
- return CB.InvokeAsync(arglist(arguments))
- . = NONE
- for(var/I in target)
- var/datum/C = I
- if(!C.signal_enabled)
- continue
- var/datum/callback/CB = C.signal_procs[src][sigtype]
- . |= CB.InvokeAsync(arglist(arguments))
-
-/datum/proc/GetComponent(c_type)
- var/list/dc = datum_components
- if(!dc)
- return null
- . = dc[c_type]
- if(length(.))
- return .[1]
-
-/datum/proc/GetExactComponent(c_type)
- var/list/dc = datum_components
- if(!dc)
- return null
- var/datum/component/C = dc[c_type]
- if(C)
- if(length(C))
- C = C[1]
- if(C.type == c_type)
- return C
- return null
-
-/datum/proc/GetComponents(c_type)
- var/list/dc = datum_components
- if(!dc)
- return null
- . = dc[c_type]
- if(!length(.))
- return list(.)
-
-/datum/proc/AddComponent(new_type, ...)
- var/datum/component/nt = new_type
- var/dm = initial(nt.dupe_mode)
- var/dt = initial(nt.dupe_type)
-
- var/datum/component/old_comp
- var/datum/component/new_comp
-
- if(ispath(nt))
- if(nt == /datum/component)
- CRASH("[nt] attempted instantiation!")
- if(!isnum(dm))
- CRASH("[nt]: Invalid dupe_mode ([dm])!")
- if(dt && !ispath(dt))
- CRASH("[nt]: Invalid dupe_type ([dt])!")
- else
- new_comp = nt
- nt = new_comp.type
-
- args[1] = src
-
- if(dm != COMPONENT_DUPE_ALLOWED)
- if(!dt)
- old_comp = GetExactComponent(nt)
- else
- old_comp = GetComponent(dt)
- if(old_comp)
- switch(dm)
- if(COMPONENT_DUPE_UNIQUE)
- if(!new_comp)
- new_comp = new nt(arglist(args))
- if(!QDELETED(new_comp))
- old_comp.InheritComponent(new_comp, TRUE)
- QDEL_NULL(new_comp)
- if(COMPONENT_DUPE_HIGHLANDER)
- if(!new_comp)
- new_comp = new nt(arglist(args))
- if(!QDELETED(new_comp))
- new_comp.InheritComponent(old_comp, FALSE)
- QDEL_NULL(old_comp)
- if(COMPONENT_DUPE_UNIQUE_PASSARGS)
- if(!new_comp)
- var/list/arguments = args.Copy(2)
- old_comp.InheritComponent(null, TRUE, arguments)
- else
- old_comp.InheritComponent(new_comp, TRUE)
- else if(!new_comp)
- new_comp = new nt(arglist(args)) // There's a valid dupe mode but there's no old component, act like normal
- else if(!new_comp)
- new_comp = new nt(arglist(args)) // Dupes are allowed, act like normal
-
- if(!old_comp && !QDELETED(new_comp)) // Nothing related to duplicate components happened and the new component is healthy
- SEND_SIGNAL(src, COMSIG_COMPONENT_ADDED, new_comp)
- return new_comp
- return old_comp
-
-/datum/proc/LoadComponent(component_type, ...)
- . = GetComponent(component_type)
- if(!.)
- return AddComponent(arglist(args))
-
-/datum/component/proc/RemoveComponent()
- if(!parent)
- return
- var/datum/old_parent = parent
- PreTransfer()
- _RemoveFromParent()
- parent = null
- SEND_SIGNAL(old_parent, COMSIG_COMPONENT_REMOVING, src)
-
-/datum/proc/TakeComponent(datum/component/target)
- if(!target || target.parent == src)
- return
- if(target.parent)
- target.RemoveComponent()
- target.parent = src
- var/result = target.PostTransfer()
- switch(result)
- if(COMPONENT_INCOMPATIBLE)
- var/c_type = target.type
- qdel(target)
- CRASH("Incompatible [c_type] transfer attempt to a [type]!")
- if(target == AddComponent(target))
- target._JoinParent()
-
-/datum/proc/TransferComponents(datum/target)
- var/list/dc = datum_components
- if(!dc)
- return
- var/comps = dc[/datum/component]
- if(islist(comps))
- for(var/datum/component/I in comps)
- if(I.can_transfer)
- target.TakeComponent(I)
- else
- var/datum/component/C = comps
- if(C.can_transfer)
- target.TakeComponent(comps)
-
-/datum/component/ui_host()
- return parent
+/datum/component
+ var/dupe_mode = COMPONENT_DUPE_HIGHLANDER
+ var/dupe_type
+ var/datum/parent
+ //only set to true if you are able to properly transfer this component
+ //At a minimum RegisterWithParent and UnregisterFromParent should be used
+ //Make sure you also implement PostTransfer for any post transfer handling
+ var/can_transfer = FALSE
+
+/datum/component/New(datum/P, ...)
+ parent = P
+ var/list/arguments = args.Copy(2)
+ if(Initialize(arglist(arguments)) == COMPONENT_INCOMPATIBLE)
+ qdel(src, TRUE, TRUE)
+ CRASH("Incompatible [type] assigned to a [P.type]! args: [json_encode(arguments)]")
+
+ _JoinParent(P)
+
+/datum/component/proc/_JoinParent()
+ var/datum/P = parent
+ //lazy init the parent's dc list
+ var/list/dc = P.datum_components
+ if(!dc)
+ P.datum_components = dc = list()
+
+ //set up the typecache
+ var/our_type = type
+ for(var/I in _GetInverseTypeList(our_type))
+ var/test = dc[I]
+ if(test) //already another component of this type here
+ var/list/components_of_type
+ if(!length(test))
+ components_of_type = list(test)
+ dc[I] = components_of_type
+ else
+ components_of_type = test
+ if(I == our_type) //exact match, take priority
+ var/inserted = FALSE
+ for(var/J in 1 to components_of_type.len)
+ var/datum/component/C = components_of_type[J]
+ if(C.type != our_type) //but not over other exact matches
+ components_of_type.Insert(J, I)
+ inserted = TRUE
+ break
+ if(!inserted)
+ components_of_type += src
+ else //indirect match, back of the line with ya
+ components_of_type += src
+ else //only component of this type, no list
+ dc[I] = src
+
+ RegisterWithParent()
+
+// If you want/expect to be moving the component around between parents, use this to register on the parent for signals
+/datum/component/proc/RegisterWithParent()
+ return
+
+/datum/component/proc/Initialize(...)
+ return
+
+/datum/component/Destroy(force=FALSE, silent=FALSE)
+ if(!force && parent)
+ _RemoveFromParent()
+ if(!silent)
+ SEND_SIGNAL(parent, COMSIG_COMPONENT_REMOVING, src)
+ parent = null
+ return ..()
+
+/datum/component/proc/_RemoveFromParent()
+ var/datum/P = parent
+ var/list/dc = P.datum_components
+ for(var/I in _GetInverseTypeList())
+ var/list/components_of_type = dc[I]
+ if(length(components_of_type)) //
+ var/list/subtracted = components_of_type - src
+ if(subtracted.len == 1) //only 1 guy left
+ dc[I] = subtracted[1] //make him special
+ else
+ dc[I] = subtracted
+ else //just us
+ dc -= I
+ if(!dc.len)
+ P.datum_components = null
+
+ UnregisterFromParent()
+
+/datum/component/proc/UnregisterFromParent()
+ return
+
+/datum/proc/RegisterSignal(datum/target, sig_type_or_types, proctype, override = FALSE)
+ if(QDELETED(src) || QDELETED(target))
+ return
+
+ var/list/procs = signal_procs
+ if(!procs)
+ signal_procs = procs = list()
+ if(!procs[target])
+ procs[target] = list()
+ var/list/lookup = target.comp_lookup
+ if(!lookup)
+ target.comp_lookup = lookup = list()
+
+ var/list/sig_types = islist(sig_type_or_types) ? sig_type_or_types : list(sig_type_or_types)
+ for(var/sig_type in sig_types)
+ if(!override && procs[target][sig_type])
+ stack_trace("[sig_type] overridden. Use override = TRUE to suppress this warning")
+
+ procs[target][sig_type] = proctype
+
+ if(!lookup[sig_type]) // Nothing has registered here yet
+ lookup[sig_type] = src
+ else if(lookup[sig_type] == src) // We already registered here
+ continue
+ else if(!length(lookup[sig_type])) // One other thing registered here
+ lookup[sig_type] = list(lookup[sig_type]=TRUE)
+ lookup[sig_type][src] = TRUE
+ else // Many other things have registered here
+ lookup[sig_type][src] = TRUE
+
+ signal_enabled = TRUE
+
+/datum/proc/UnregisterSignal(datum/target, sig_type_or_types)
+ if(!target)
+ CRASH("[src] tried to unregister a signal without having a target.")
+ var/list/lookup = target.comp_lookup
+ if(!signal_procs || !signal_procs[target] || !lookup)
+ return
+ if(!islist(sig_type_or_types))
+ sig_type_or_types = list(sig_type_or_types)
+ for(var/sig in sig_type_or_types)
+ switch(length(lookup[sig]))
+ if(2)
+ lookup[sig] = (lookup[sig]-src)[1]
+ if(1)
+ stack_trace("[target] ([target.type]) somehow has single length list inside comp_lookup")
+ if(src in lookup[sig])
+ lookup -= sig
+ if(!length(lookup))
+ target.comp_lookup = null
+ break
+ if(0)
+ lookup -= sig
+ if(!length(lookup))
+ target.comp_lookup = null
+ break
+ else
+ lookup[sig] -= src
+
+ signal_procs[target] -= sig_type_or_types
+ if(!signal_procs[target].len)
+ signal_procs -= target
+
+/datum/component/proc/InheritComponent(datum/component/C, i_am_original)
+ return
+
+/datum/component/proc/PreTransfer()
+ return
+
+/datum/component/proc/PostTransfer()
+ return COMPONENT_INCOMPATIBLE //Do not support transfer by default as you must properly support it
+
+/datum/component/proc/_GetInverseTypeList(our_type = type)
+ //we can do this one simple trick
+ var/current_type = parent_type
+ . = list(our_type, current_type)
+ //and since most components are root level + 1, this won't even have to run
+ while (current_type != /datum/component)
+ current_type = type2parent(current_type)
+ . += current_type
+
+/datum/proc/_SendSignal(sigtype, list/arguments)
+ var/target = comp_lookup[sigtype]
+ if(!length(target))
+ var/datum/C = target
+ if(!C.signal_enabled)
+ return NONE
+ var/proctype = C.signal_procs[src][sigtype]
+ return NONE | CallAsync(C, proctype, arguments)
+ . = NONE
+ for(var/I in target)
+ var/datum/C = I
+ if(!C.signal_enabled)
+ continue
+ var/proctype = C.signal_procs[src][sigtype]
+ . |= CallAsync(C, proctype, arguments)
+
+// The type arg is casted so initial works, you shouldn't be passing a real instance into this
+/datum/proc/GetComponent(datum/component/c_type)
+ if(initial(c_type.dupe_mode) == COMPONENT_DUPE_ALLOWED)
+ stack_trace("GetComponent was called to get a component of which multiple copies could be on an object. This can easily break and should be changed. Type: \[[c_type]\]")
+ var/list/dc = datum_components
+ if(!dc)
+ return null
+ . = dc[c_type]
+ if(length(.))
+ return .[1]
+
+/datum/proc/GetExactComponent(c_type)
+ var/list/dc = datum_components
+ if(!dc)
+ return null
+ var/datum/component/C = dc[c_type]
+ if(C)
+ if(length(C))
+ C = C[1]
+ if(C.type == c_type)
+ return C
+ return null
+
+/datum/proc/GetComponents(c_type)
+ var/list/dc = datum_components
+ if(!dc)
+ return null
+ . = dc[c_type]
+ if(!length(.))
+ return list(.)
+
+/datum/proc/AddComponent(new_type, ...)
+ var/datum/component/nt = new_type
+ var/dm = initial(nt.dupe_mode)
+ var/dt = initial(nt.dupe_type)
+
+ var/datum/component/old_comp
+ var/datum/component/new_comp
+
+ if(ispath(nt))
+ if(nt == /datum/component)
+ CRASH("[nt] attempted instantiation!")
+ else
+ new_comp = nt
+ nt = new_comp.type
+
+ args[1] = src
+
+ if(dm != COMPONENT_DUPE_ALLOWED)
+ if(!dt)
+ old_comp = GetExactComponent(nt)
+ else
+ old_comp = GetComponent(dt)
+ if(old_comp)
+ switch(dm)
+ if(COMPONENT_DUPE_UNIQUE)
+ if(!new_comp)
+ new_comp = new nt(arglist(args))
+ if(!QDELETED(new_comp))
+ old_comp.InheritComponent(new_comp, TRUE)
+ QDEL_NULL(new_comp)
+ if(COMPONENT_DUPE_HIGHLANDER)
+ if(!new_comp)
+ new_comp = new nt(arglist(args))
+ if(!QDELETED(new_comp))
+ new_comp.InheritComponent(old_comp, FALSE)
+ QDEL_NULL(old_comp)
+ if(COMPONENT_DUPE_UNIQUE_PASSARGS)
+ if(!new_comp)
+ var/list/arguments = args.Copy(2)
+ old_comp.InheritComponent(null, TRUE, arguments)
+ else
+ old_comp.InheritComponent(new_comp, TRUE)
+ else if(!new_comp)
+ new_comp = new nt(arglist(args)) // There's a valid dupe mode but there's no old component, act like normal
+ else if(!new_comp)
+ new_comp = new nt(arglist(args)) // Dupes are allowed, act like normal
+
+ if(!old_comp && !QDELETED(new_comp)) // Nothing related to duplicate components happened and the new component is healthy
+ SEND_SIGNAL(src, COMSIG_COMPONENT_ADDED, new_comp)
+ return new_comp
+ return old_comp
+
+/datum/proc/LoadComponent(component_type, ...)
+ . = GetComponent(component_type)
+ if(!.)
+ return AddComponent(arglist(args))
+
+/datum/component/proc/RemoveComponent()
+ if(!parent)
+ return
+ var/datum/old_parent = parent
+ PreTransfer()
+ _RemoveFromParent()
+ parent = null
+ SEND_SIGNAL(old_parent, COMSIG_COMPONENT_REMOVING, src)
+
+/datum/proc/TakeComponent(datum/component/target)
+ if(!target || target.parent == src)
+ return
+ if(target.parent)
+ target.RemoveComponent()
+ target.parent = src
+ var/result = target.PostTransfer()
+ switch(result)
+ if(COMPONENT_INCOMPATIBLE)
+ var/c_type = target.type
+ qdel(target)
+ CRASH("Incompatible [c_type] transfer attempt to a [type]!")
+
+ if(target == AddComponent(target))
+ target._JoinParent()
+
+/datum/proc/TransferComponents(datum/target)
+ var/list/dc = datum_components
+ if(!dc)
+ return
+ var/comps = dc[/datum/component]
+ if(islist(comps))
+ for(var/datum/component/I in comps)
+ if(I.can_transfer)
+ target.TakeComponent(I)
+ else
+ var/datum/component/C = comps
+ if(C.can_transfer)
+ target.TakeComponent(comps)
+
+/datum/component/ui_host()
+ return parent
diff --git a/code/datums/components/earprotection.dm b/code/datums/components/earprotection.dm
index 20849437..9256c431 100644
--- a/code/datums/components/earprotection.dm
+++ b/code/datums/components/earprotection.dm
@@ -1,11 +1,11 @@
/datum/component/wearertargeting/earprotection
signals = list(COMSIG_CARBON_SOUNDBANG)
mobtype = /mob/living/carbon
+ proctype = .proc/reducebang
/datum/component/wearertargeting/earprotection/Initialize(_valid_slots)
. = ..()
valid_slots = _valid_slots
- callback = CALLBACK(src, .proc/reducebang)
/datum/component/wearertargeting/earprotection/proc/reducebang(datum/source, list/reflist)
reflist[1]--
diff --git a/code/datums/components/mood.dm b/code/datums/components/mood.dm
index 43d13380..7297e8b7 100644
--- a/code/datums/components/mood.dm
+++ b/code/datums/components/mood.dm
@@ -261,8 +261,6 @@
if(the_event.timeout)
addtimer(CALLBACK(src, .proc/clear_event, null, category), the_event.timeout, TIMER_UNIQUE|TIMER_OVERRIDE)
- return the_event
-
/datum/component/mood/proc/clear_event(datum/source, category)
var/datum/mood_event/event = mood_events[category]
if(!event)
diff --git a/code/datums/components/orbiter.dm b/code/datums/components/orbiter.dm
index a9d2c73b..b23fe362 100644
--- a/code/datums/components/orbiter.dm
+++ b/code/datums/components/orbiter.dm
@@ -1,8 +1,7 @@
/datum/component/orbiter
+ can_transfer = TRUE
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
var/list/orbiters
- var/datum/callback/orbiter_spy
- var/datum/callback/orbited_spy
//radius: range to orbit at, radius of the circle formed by orbiting (in pixels)
//clockwise: whether you orbit clockwise or anti clockwise
@@ -14,8 +13,6 @@
return COMPONENT_INCOMPATIBLE
orbiters = list()
- orbiter_spy = CALLBACK(src, .proc/orbiter_move_react)
- orbited_spy = CALLBACK(src, .proc/move_react)
var/atom/master = parent
master.orbiters = src
@@ -25,7 +22,7 @@
/datum/component/orbiter/RegisterWithParent()
var/atom/target = parent
while(ismovableatom(target))
- RegisterSignal(target, COMSIG_MOVABLE_MOVED, orbited_spy)
+ RegisterSignal(target, COMSIG_MOVABLE_MOVED, .proc/move_react)
target = target.loc
RegisterSignal(parent, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, .proc/orbiter_glide_size_update)
@@ -44,8 +41,6 @@
for(var/i in orbiters)
end_orbit(i)
orbiters = null
- QDEL_NULL(orbiter_spy)
- QDEL_NULL(orbited_spy)
return ..()
/datum/component/orbiter/InheritComponent(datum/component/orbiter/newcomp, original, list/arguments)
@@ -69,7 +64,7 @@
orbiter.orbiting.end_orbit(orbiter)
orbiters[orbiter] = TRUE
orbiter.orbiting = src
- RegisterSignal(orbiter, COMSIG_MOVABLE_MOVED, orbiter_spy)
+ RegisterSignal(orbiter, COMSIG_MOVABLE_MOVED, .proc/orbiter_move_react)
var/matrix/initial_transform = matrix(orbiter.transform)
orbiters[orbiter] = initial_transform
@@ -137,7 +132,7 @@
if(orbited?.loc && orbited.loc != newturf) // We want to know when anything holding us moves too
var/atom/target = orbited.loc
while(ismovableatom(target))
- RegisterSignal(target, COMSIG_MOVABLE_MOVED, orbited_spy, TRUE)
+ RegisterSignal(target, COMSIG_MOVABLE_MOVED, .proc/move_react, TRUE)
target = target.loc
var/atom/curloc = master.loc
diff --git a/code/datums/components/phantomthief.dm b/code/datums/components/phantomthief.dm
new file mode 100644
index 00000000..ff1c4893
--- /dev/null
+++ b/code/datums/components/phantomthief.dm
@@ -0,0 +1,39 @@
+//This component applies a customizable drop_shadow filter to its wearer when they toggle combat mode on or off. This can stack.
+
+/datum/component/wearertargeting/phantomthief
+ dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
+ signals = list(COMSIG_COMBAT_TOGGLED)
+ proctype = .proc/handlefilterstuff
+ var/filter_x
+ var/filter_y
+ var/filter_size
+ var/filter_border
+ var/filter_color
+
+/datum/component/wearertargeting/phantomthief/Initialize(_x = -2, _y = 0, _size = 0, _border = 0, _color = "#E62111", list/_valid_slots = list(SLOT_GLASSES))
+ . = ..()
+ if(. == COMPONENT_INCOMPATIBLE)
+ return
+ filter_x = _x
+ filter_y = _y
+ filter_size = _size
+ filter_border = _border
+ filter_color = _color
+ valid_slots = _valid_slots
+
+/datum/component/wearertargeting/phantomthief/proc/handlefilterstuff(datum/source, mob/user, combatmodestate)
+ if(istype(user))
+ var/thefilter = filter(type = "drop_shadow", x = filter_x, y = filter_y, size = filter_size, border = filter_border, color = filter_color)
+ if(!combatmodestate)
+ user.filters -= thefilter
+ else
+ user.filters += thefilter
+
+/datum/component/wearertargeting/phantomthief/proc/stripdesiredfilter(mob/user)
+ if(istype(user))
+ var/thefilter = filter(type = "drop_shadow", x = filter_x, y = filter_y, size = filter_size, border = filter_border, color = filter_color)
+ user.filters -= thefilter
+
+/datum/component/wearertargeting/phantomthief/on_drop(datum/source, mob/user)
+ . = ..()
+ stripdesiredfilter(user)
diff --git a/code/datums/components/signal_redirect.dm b/code/datums/components/signal_redirect.dm
deleted file mode 100644
index db98d566..00000000
--- a/code/datums/components/signal_redirect.dm
+++ /dev/null
@@ -1,33 +0,0 @@
-// This should only be used by non components trying to listen to a signal
-// If you use this inside a component I will replace your eyes with lemons ~ninjanomnom
-
-/datum/component/redirect
- dupe_mode = COMPONENT_DUPE_ALLOWED
- var/list/signals
- var/datum/callback/turfchangeCB
-
-/datum/component/redirect/Initialize(list/_signals, flags=NONE)
- //It's not our job to verify the right signals are registered here, just do it.
- if(!LAZYLEN(_signals))
- return COMPONENT_INCOMPATIBLE
- if(flags & REDIRECT_TRANSFER_WITH_TURF && isturf(parent))
- // If they also want to listen to the turf change then we need to set it up so both callbacks run
- if(_signals[COMSIG_TURF_CHANGE])
- turfchangeCB = _signals[COMSIG_TURF_CHANGE]
- if(!istype(turfchangeCB))
- . = COMPONENT_INCOMPATIBLE
- CRASH("Redirect components must be given instanced callbacks, not proc paths.")
- _signals[COMSIG_TURF_CHANGE] = CALLBACK(src, .proc/turf_change)
-
- signals = _signals
-
-/datum/component/redirect/RegisterWithParent()
- for(var/signal in signals)
- RegisterSignal(parent, signal, signals[signal])
-
-/datum/component/redirect/UnregisterFromParent()
- UnregisterSignal(parent, signals)
-
-/datum/component/redirect/proc/turf_change(datum/source, path, new_baseturfs, flags, list/transfers)
- transfers += src
- return turfchangeCB?.InvokeAsync(arglist(args))
diff --git a/code/datums/components/wearertargeting.dm b/code/datums/components/wearertargeting.dm
index 0aa08311..feaa88f9 100644
--- a/code/datums/components/wearertargeting.dm
+++ b/code/datums/components/wearertargeting.dm
@@ -3,7 +3,7 @@
/datum/component/wearertargeting
var/list/valid_slots = list()
var/list/signals = list()
- var/datum/callback/callback = CALLBACK(GLOBAL_PROC, .proc/pass)
+ var/proctype = .proc/pass
var/mobtype = /mob/living
/datum/component/wearertargeting/Initialize()
@@ -14,13 +14,9 @@
/datum/component/wearertargeting/proc/on_equip(datum/source, mob/equipper, slot)
if((slot in valid_slots) && istype(equipper, mobtype))
- RegisterSignal(equipper, signals, callback, TRUE)
+ RegisterSignal(equipper, signals, proctype, TRUE)
else
UnregisterSignal(equipper, signals)
/datum/component/wearertargeting/proc/on_drop(datum/source, mob/user)
- UnregisterSignal(user, signals)
-
-/datum/component/wearertargeting/Destroy()
- QDEL_NULL(callback) //is likely to ourselves.
- return ..()
\ No newline at end of file
+ UnregisterSignal(user, signals)
\ No newline at end of file
diff --git a/code/datums/components/wet_floor.dm b/code/datums/components/wet_floor.dm
index 526d482e..84b03203 100644
--- a/code/datums/components/wet_floor.dm
+++ b/code/datums/components/wet_floor.dm
@@ -1,206 +1,206 @@
-/datum/component/wet_floor
- dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
- can_transfer = TRUE
- var/highest_strength = TURF_DRY
- var/lube_flags = NONE //why do we have this?
- var/list/time_left_list //In deciseconds.
- var/static/mutable_appearance/permafrost_overlay = mutable_appearance('icons/effects/water.dmi', "ice_floor")
- var/static/mutable_appearance/ice_overlay = mutable_appearance('icons/turf/overlays.dmi', "snowfloor")
- var/static/mutable_appearance/water_overlay = mutable_appearance('icons/effects/water.dmi', "wet_floor_static")
- var/static/mutable_appearance/generic_turf_overlay = mutable_appearance('icons/effects/water.dmi', "wet_static")
- var/current_overlay
- var/permanent = FALSE
- var/last_process = 0
-
-/datum/component/wet_floor/InheritComponent(datum/newcomp, orig, argslist)
- if(!newcomp) //We are getting passed the arguments of a would-be new component, but not a new component
- add_wet(arglist(argslist))
- else //We are being passed in a full blown component
- var/datum/component/wet_floor/WF = newcomp //Lets make an assumption
- if(WF.gc()) //See if it's even valid, still. Also does LAZYLEN and stuff for us.
- CRASH("Wet floor component tried to inherit another, but the other was able to garbage collect while being inherited! What a waste of time!")
- return
- for(var/i in WF.time_left_list)
- add_wet(text2num(i), WF.time_left_list[i])
-
-/datum/component/wet_floor/Initialize(strength, duration_minimum, duration_add, duration_maximum, _permanent = FALSE)
- if(!isopenturf(parent))
- return COMPONENT_INCOMPATIBLE
- add_wet(strength, duration_minimum, duration_add, duration_maximum)
- permanent = _permanent
- if(!permanent)
- START_PROCESSING(SSwet_floors, src)
- addtimer(CALLBACK(src, .proc/gc, TRUE), 1) //GC after initialization.
- last_process = world.time
-
-/datum/component/wet_floor/RegisterWithParent()
- RegisterSignal(parent, COMSIG_TURF_IS_WET, .proc/is_wet)
- RegisterSignal(parent, COMSIG_TURF_MAKE_DRY, .proc/dry)
-
-/datum/component/wet_floor/UnregisterFromParent()
- UnregisterSignal(parent, list(COMSIG_TURF_IS_WET, COMSIG_TURF_MAKE_DRY))
-
-/datum/component/wet_floor/Destroy()
- STOP_PROCESSING(SSwet_floors, src)
- var/turf/T = parent
- qdel(T.GetComponent(/datum/component/slippery))
- if(istype(T)) //If this is false there is so many things wrong with it.
- T.cut_overlay(current_overlay)
- else
- stack_trace("Warning: Wet floor component wasn't on a turf when being destroyed! This is really bad!")
- return ..()
-
-/datum/component/wet_floor/proc/update_overlay()
- var/intended
- if(!istype(parent, /turf/open/floor))
- intended = generic_turf_overlay
- else
- switch(highest_strength)
- if(TURF_WET_PERMAFROST)
- intended = permafrost_overlay
- if(TURF_WET_ICE)
- intended = ice_overlay
- else
- intended = water_overlay
- if(current_overlay != intended)
- var/turf/T = parent
- T.cut_overlay(current_overlay)
- T.add_overlay(intended)
- current_overlay = intended
-
-/datum/component/wet_floor/proc/AfterSlip(mob/living/L)
- if(highest_strength == TURF_WET_LUBE)
- L.confused = max(L.confused, 8)
-
-/datum/component/wet_floor/proc/update_flags()
- var/intensity
- lube_flags = NONE
- switch(highest_strength)
- if(TURF_WET_WATER)
- intensity = 60
- lube_flags = NO_SLIP_WHEN_WALKING
- if(TURF_WET_LUBE)
- intensity = 80
- lube_flags = SLIDE | GALOSHES_DONT_HELP
- if(TURF_WET_ICE)
- intensity = 120
- lube_flags = SLIDE | GALOSHES_DONT_HELP
- if(TURF_WET_PERMAFROST)
- intensity = 120
- lube_flags = SLIDE_ICE | GALOSHES_DONT_HELP
- else
- qdel(parent.GetComponent(/datum/component/slippery))
- return
-
- var/datum/component/slippery/S = parent.LoadComponent(/datum/component/slippery, NONE, CALLBACK(src, .proc/AfterSlip))
- S.intensity = intensity
- S.lube_flags = lube_flags
-
-/datum/component/wet_floor/proc/dry(datum/source, strength = TURF_WET_WATER, immediate = FALSE, duration_decrease = INFINITY)
- for(var/i in time_left_list)
- if(text2num(i) <= strength)
- time_left_list[i] = max(0, time_left_list[i] - duration_decrease)
- if(immediate)
- check()
-
-/datum/component/wet_floor/proc/max_time_left()
- . = 0
- for(var/i in time_left_list)
- . = max(., time_left_list[i])
-
-/datum/component/wet_floor/process()
- var/turf/open/T = parent
- var/diff = world.time - last_process
- var/decrease = 0
- var/t = T.GetTemperature()
- switch(t)
- if(-INFINITY to T0C)
- add_wet(TURF_WET_ICE, max_time_left()) //Water freezes into ice!
- if(T0C to T0C + 100)
- decrease = ((T.air.temperature - T0C) / SSwet_floors.temperature_coeff) * (diff / SSwet_floors.time_ratio)
- if(T0C + 100 to INFINITY)
- decrease = INFINITY
- decrease = max(0, decrease)
- if((is_wet() & TURF_WET_ICE) && t > T0C) //Ice melts into water!
- for(var/obj/O in T.contents)
- if(O.obj_flags & FROZEN)
- O.make_unfrozen()
- add_wet(TURF_WET_WATER, max_time_left())
- dry(null, TURF_WET_ICE)
- dry(null, ALL, FALSE, decrease)
- check()
- last_process = world.time
-
-/datum/component/wet_floor/proc/update_strength()
- highest_strength = 0 //Not bitflag.
- for(var/i in time_left_list)
- highest_strength = max(highest_strength, text2num(i))
-
-/datum/component/wet_floor/proc/is_wet()
- . = 0
- for(var/i in time_left_list)
- . |= text2num(i)
-
-/datum/component/wet_floor/PreTransfer()
- var/turf/O = parent
- O.cut_overlay(current_overlay)
- //That turf is no longer slippery, we're out of here
- //Slippery components don't transfer due to callbacks
- qdel(O.GetComponent(/datum/component/slippery))
-
-/datum/component/wet_floor/PostTransfer()
- if(!isopenturf(parent))
- return COMPONENT_INCOMPATIBLE
- var/turf/T = parent
- T.add_overlay(current_overlay)
- //Make sure to add/update any slippery component on the new turf (update_flags calls LoadComponent)
- update_flags()
-
- //NB it's possible we get deleted after this, due to inherit
-
-/datum/component/wet_floor/proc/add_wet(type, duration_minimum = 0, duration_add = 0, duration_maximum = MAXIMUM_WET_TIME, _permanent = FALSE)
- var/static/list/allowed_types = list(TURF_WET_WATER, TURF_WET_LUBE, TURF_WET_ICE, TURF_WET_PERMAFROST)
- if(duration_minimum <= 0 || !type)
- return FALSE
- if(type in allowed_types)
- return _do_add_wet(type, duration_minimum, duration_add, duration_maximum)
- else
- . = NONE
- for(var/i in allowed_types)
- if(!(type & i))
- continue
- . |= _do_add_wet(i, duration_minimum, duration_add, duration_maximum)
- if(_permanent)
- permanent = TRUE
- STOP_PROCESSING(SSwet_floors, src)
-
-/datum/component/wet_floor/proc/_do_add_wet(type, duration_minimum, duration_add, duration_maximum)
- var/time = 0
- if(LAZYACCESS(time_left_list, "[type]"))
- time = CLAMP(LAZYACCESS(time_left_list, "[type]") + duration_add, duration_minimum, duration_maximum)
- else
- time = min(duration_minimum, duration_maximum)
- LAZYSET(time_left_list, "[type]", time)
- check(TRUE)
- return TRUE
-
-/datum/component/wet_floor/proc/gc(on_init = FALSE)
- if(!LAZYLEN(time_left_list))
- if(on_init)
- var/turf/T = parent
- stack_trace("Warning: Wet floor component gc'd right after initialization! What a waste of time and CPU! Type = [T? T.type : "ERROR - NO PARENT"], Location = [istype(T)? AREACOORD(T) : "ERROR - INVALID PARENT"].")
- qdel(src)
- return TRUE
- return FALSE
-
-/datum/component/wet_floor/proc/check(force_update = FALSE)
- var/changed = FALSE
- for(var/i in time_left_list)
- if(time_left_list[i] <= 0)
- time_left_list -= i
- changed = TRUE
- if(changed || force_update)
- update_strength()
- update_overlay()
- update_flags()
- gc()
+/datum/component/wet_floor
+ dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
+ can_transfer = TRUE
+ var/highest_strength = TURF_DRY
+ var/lube_flags = NONE //why do we have this?
+ var/list/time_left_list //In deciseconds.
+ var/static/mutable_appearance/permafrost_overlay = mutable_appearance('icons/effects/water.dmi', "ice_floor")
+ var/static/mutable_appearance/ice_overlay = mutable_appearance('icons/turf/overlays.dmi', "snowfloor")
+ var/static/mutable_appearance/water_overlay = mutable_appearance('icons/effects/water.dmi', "wet_floor_static")
+ var/static/mutable_appearance/generic_turf_overlay = mutable_appearance('icons/effects/water.dmi', "wet_static")
+ var/current_overlay
+ var/permanent = FALSE
+ var/last_process = 0
+
+/datum/component/wet_floor/InheritComponent(datum/newcomp, orig, argslist)
+ if(!newcomp) //We are getting passed the arguments of a would-be new component, but not a new component
+ add_wet(arglist(argslist))
+ else //We are being passed in a full blown component
+ var/datum/component/wet_floor/WF = newcomp //Lets make an assumption
+ if(WF.gc()) //See if it's even valid, still. Also does LAZYLEN and stuff for us.
+ CRASH("Wet floor component tried to inherit another, but the other was able to garbage collect while being inherited! What a waste of time!")
+ return
+ for(var/i in WF.time_left_list)
+ add_wet(text2num(i), WF.time_left_list[i])
+
+/datum/component/wet_floor/Initialize(strength, duration_minimum, duration_add, duration_maximum, _permanent = FALSE)
+ if(!isopenturf(parent))
+ return COMPONENT_INCOMPATIBLE
+ add_wet(strength, duration_minimum, duration_add, duration_maximum)
+ permanent = _permanent
+ if(!permanent)
+ START_PROCESSING(SSwet_floors, src)
+ addtimer(CALLBACK(src, .proc/gc, TRUE), 1) //GC after initialization.
+ last_process = world.time
+
+/datum/component/wet_floor/RegisterWithParent()
+ RegisterSignal(parent, COMSIG_TURF_IS_WET, .proc/is_wet)
+ RegisterSignal(parent, COMSIG_TURF_MAKE_DRY, .proc/dry)
+
+/datum/component/wet_floor/UnregisterFromParent()
+ UnregisterSignal(parent, list(COMSIG_TURF_IS_WET, COMSIG_TURF_MAKE_DRY))
+
+/datum/component/wet_floor/Destroy()
+ STOP_PROCESSING(SSwet_floors, src)
+ var/turf/T = parent
+ qdel(T.GetComponent(/datum/component/slippery))
+ if(istype(T)) //If this is false there is so many things wrong with it.
+ T.cut_overlay(current_overlay)
+ else
+ stack_trace("Warning: Wet floor component wasn't on a turf when being destroyed! This is really bad!")
+ return ..()
+
+/datum/component/wet_floor/proc/update_overlay()
+ var/intended
+ if(!istype(parent, /turf/open/floor))
+ intended = generic_turf_overlay
+ else
+ switch(highest_strength)
+ if(TURF_WET_PERMAFROST)
+ intended = permafrost_overlay
+ if(TURF_WET_ICE)
+ intended = ice_overlay
+ else
+ intended = water_overlay
+ if(current_overlay != intended)
+ var/turf/T = parent
+ T.cut_overlay(current_overlay)
+ T.add_overlay(intended)
+ current_overlay = intended
+
+/datum/component/wet_floor/proc/AfterSlip(mob/living/L)
+ if(highest_strength == TURF_WET_LUBE)
+ L.confused = max(L.confused, 8)
+
+/datum/component/wet_floor/proc/update_flags()
+ var/intensity
+ lube_flags = NONE
+ switch(highest_strength)
+ if(TURF_WET_WATER)
+ intensity = 60
+ lube_flags = NO_SLIP_WHEN_WALKING
+ if(TURF_WET_LUBE)
+ intensity = 80
+ lube_flags = SLIDE | GALOSHES_DONT_HELP
+ if(TURF_WET_ICE)
+ intensity = 120
+ lube_flags = SLIDE | GALOSHES_DONT_HELP
+ if(TURF_WET_PERMAFROST)
+ intensity = 120
+ lube_flags = SLIDE_ICE | GALOSHES_DONT_HELP
+ else
+ qdel(parent.GetComponent(/datum/component/slippery))
+ return
+
+ var/datum/component/slippery/S = parent.LoadComponent(/datum/component/slippery, NONE, CALLBACK(src, .proc/AfterSlip))
+ S.intensity = intensity
+ S.lube_flags = lube_flags
+
+/datum/component/wet_floor/proc/dry(datum/source, strength = TURF_WET_WATER, immediate = FALSE, duration_decrease = INFINITY)
+ for(var/i in time_left_list)
+ if(text2num(i) <= strength)
+ time_left_list[i] = max(0, time_left_list[i] - duration_decrease)
+ if(immediate)
+ check()
+
+/datum/component/wet_floor/proc/max_time_left()
+ . = 0
+ for(var/i in time_left_list)
+ . = max(., time_left_list[i])
+
+/datum/component/wet_floor/process()
+ var/turf/open/T = parent
+ var/diff = world.time - last_process
+ var/decrease = 0
+ var/t = T.GetTemperature()
+ switch(t)
+ if(-INFINITY to T0C)
+ add_wet(TURF_WET_ICE, max_time_left()) //Water freezes into ice!
+ if(T0C to T0C + 100)
+ decrease = ((T.air.temperature - T0C) / SSwet_floors.temperature_coeff) * (diff / SSwet_floors.time_ratio)
+ if(T0C + 100 to INFINITY)
+ decrease = INFINITY
+ decrease = max(0, decrease)
+ if((is_wet() & TURF_WET_ICE) && t > T0C) //Ice melts into water!
+ for(var/obj/O in T.contents)
+ if(O.obj_flags & FROZEN)
+ O.make_unfrozen()
+ add_wet(TURF_WET_WATER, max_time_left())
+ dry(null, TURF_WET_ICE)
+ dry(null, ALL, FALSE, decrease)
+ check()
+ last_process = world.time
+
+/datum/component/wet_floor/proc/update_strength()
+ highest_strength = 0 //Not bitflag.
+ for(var/i in time_left_list)
+ highest_strength = max(highest_strength, text2num(i))
+
+/datum/component/wet_floor/proc/is_wet()
+ . = 0
+ for(var/i in time_left_list)
+ . |= text2num(i)
+
+/datum/component/wet_floor/PreTransfer()
+ var/turf/O = parent
+ O.cut_overlay(current_overlay)
+ //That turf is no longer slippery, we're out of here
+ //Slippery components don't transfer due to callbacks
+ qdel(O.GetComponent(/datum/component/slippery))
+
+/datum/component/wet_floor/PostTransfer()
+ if(!isopenturf(parent))
+ return COMPONENT_INCOMPATIBLE
+ var/turf/T = parent
+ T.add_overlay(current_overlay)
+ //Make sure to add/update any slippery component on the new turf (update_flags calls LoadComponent)
+ update_flags()
+
+ //NB it's possible we get deleted after this, due to inherit
+
+/datum/component/wet_floor/proc/add_wet(type, duration_minimum = 0, duration_add = 0, duration_maximum = MAXIMUM_WET_TIME, _permanent = FALSE)
+ var/static/list/allowed_types = list(TURF_WET_WATER, TURF_WET_LUBE, TURF_WET_ICE, TURF_WET_PERMAFROST)
+ if(duration_minimum <= 0 || !type)
+ return FALSE
+ if(type in allowed_types)
+ return _do_add_wet(type, duration_minimum, duration_add, duration_maximum)
+ else
+ . = NONE
+ for(var/i in allowed_types)
+ if(!(type & i))
+ continue
+ . |= _do_add_wet(i, duration_minimum, duration_add, duration_maximum)
+ if(_permanent)
+ permanent = TRUE
+ STOP_PROCESSING(SSwet_floors, src)
+
+/datum/component/wet_floor/proc/_do_add_wet(type, duration_minimum, duration_add, duration_maximum)
+ var/time = 0
+ if(LAZYACCESS(time_left_list, "[type]"))
+ time = CLAMP(LAZYACCESS(time_left_list, "[type]") + duration_add, duration_minimum, duration_maximum)
+ else
+ time = min(duration_minimum, duration_maximum)
+ LAZYSET(time_left_list, "[type]", time)
+ check(TRUE)
+ return TRUE
+
+/datum/component/wet_floor/proc/gc(on_init = FALSE)
+ if(!LAZYLEN(time_left_list))
+ if(on_init)
+ var/turf/T = parent
+ stack_trace("Warning: Wet floor component gc'd right after initialization! What a waste of time and CPU! Type = [T? T.type : "ERROR - NO PARENT"], Location = [istype(T)? AREACOORD(T) : "ERROR - INVALID PARENT"].")
+ qdel(src)
+ return TRUE
+ return FALSE
+
+/datum/component/wet_floor/proc/check(force_update = FALSE)
+ var/changed = FALSE
+ for(var/i in time_left_list)
+ if(time_left_list[i] <= 0)
+ time_left_list -= i
+ changed = TRUE
+ if(changed || force_update)
+ update_strength()
+ update_overlay()
+ update_flags()
+ gc()
diff --git a/code/datums/status_effects/gas.dm b/code/datums/status_effects/gas.dm
index dfe0a1d9..00417993 100644
--- a/code/datums/status_effects/gas.dm
+++ b/code/datums/status_effects/gas.dm
@@ -5,7 +5,6 @@
alert_type = /obj/screen/alert/status_effect/freon
var/icon/cube
var/can_melt = TRUE
- var/datum/weakref/redirect_component
/obj/screen/alert/status_effect/freon
name = "Frozen Solid"
@@ -13,7 +12,7 @@
icon_state = "frozen"
/datum/status_effect/freon/on_apply()
- redirect_component = WEAKREF(owner.AddComponent(/datum/component/redirect, list(COMSIG_LIVING_RESIST = CALLBACK(src, .proc/owner_resist))))
+ RegisterSignal(owner, COMSIG_LIVING_RESIST, .proc/owner_resist)
if(!owner.stat)
to_chat(owner, "You become frozen in a cube!")
cube = icon('icons/effects/freeze.dmi', "ice_cube")
@@ -40,8 +39,7 @@
owner.cut_overlay(cube)
owner.adjust_bodytemperature(100)
owner.update_canmove()
- qdel(redirect_component.resolve())
- redirect_component = null
+ UnregisterSignal(owner, COMSIG_LIVING_RESIST)
/datum/status_effect/freon/watcher
duration = 8
diff --git a/code/game/objects/effects/proximity.dm b/code/game/objects/effects/proximity.dm
index 23977746..34d6497d 100644
--- a/code/game/objects/effects/proximity.dm
+++ b/code/game/objects/effects/proximity.dm
@@ -1,112 +1,112 @@
-/datum/proximity_monitor
- var/atom/host //the atom we are tracking
- var/atom/hasprox_receiver //the atom that will receive HasProximity calls.
- var/atom/last_host_loc
- var/list/checkers //list of /obj/effect/abstract/proximity_checkers
- var/current_range
- var/ignore_if_not_on_turf //don't check turfs in range if the host's loc isn't a turf
- var/datum/component/movement_tracker
-
-/datum/proximity_monitor/New(atom/_host, range, _ignore_if_not_on_turf = TRUE)
- checkers = list()
- last_host_loc = _host.loc
- ignore_if_not_on_turf = _ignore_if_not_on_turf
- current_range = range
- SetHost(_host)
-
-/datum/proximity_monitor/proc/SetHost(atom/H,atom/R)
- if(R)
- hasprox_receiver = R
- else if(hasprox_receiver == host) //Default case
- hasprox_receiver = H
- host = H
- last_host_loc = host.loc
- if(movement_tracker)
- QDEL_NULL(movement_tracker)
- movement_tracker = host.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/HandleMove)))
- SetRange(current_range,TRUE)
-
-/datum/proximity_monitor/Destroy()
- host = null
- last_host_loc = null
- hasprox_receiver = null
- QDEL_LIST(checkers)
- QDEL_NULL(movement_tracker)
- return ..()
-
-/datum/proximity_monitor/proc/HandleMove()
- var/atom/_host = host
- var/atom/new_host_loc = _host.loc
- if(last_host_loc != new_host_loc)
- last_host_loc = new_host_loc //hopefully this won't cause GC issues with containers
- var/curr_range = current_range
- SetRange(curr_range, TRUE)
- if(curr_range)
- testing("HasProx: [host] -> [host]")
- hasprox_receiver.HasProximity(host) //if we are processing, we're guaranteed to be a movable
-
-/datum/proximity_monitor/proc/SetRange(range, force_rebuild = FALSE)
- if(!force_rebuild && range == current_range)
- return FALSE
- . = TRUE
-
- current_range = range
-
- var/list/checkers_local = checkers
- var/old_checkers_len = checkers_local.len
-
- var/atom/_host = host
-
- var/atom/loc_to_use = ignore_if_not_on_turf ? _host.loc : get_turf(_host)
- if(!isturf(loc_to_use)) //only check the host's loc
- if(range)
- var/obj/effect/abstract/proximity_checker/pc
- if(old_checkers_len)
- pc = checkers_local[old_checkers_len]
- --checkers_local.len
- QDEL_LIST(checkers_local)
- else
- pc = new(loc_to_use, src)
-
- checkers_local += pc //only check the host's loc
- return
-
- var/list/turfs = RANGE_TURFS(range, loc_to_use)
- var/turfs_len = turfs.len
- var/old_checkers_used = min(turfs_len, old_checkers_len)
-
- //reuse what we can
- for(var/I in 1 to old_checkers_len)
- if(I <= old_checkers_used)
- var/obj/effect/abstract/proximity_checker/pc = checkers_local[I]
- pc.forceMove(turfs[I])
- else
- qdel(checkers_local[I]) //delete the leftovers
-
- if(old_checkers_len < turfs_len)
- //create what we lack
- for(var/I in (old_checkers_used + 1) to turfs_len)
- checkers_local += new /obj/effect/abstract/proximity_checker(turfs[I], src)
- else
- checkers_local.Cut(old_checkers_used + 1, old_checkers_len)
-
-/obj/effect/abstract/proximity_checker
- invisibility = INVISIBILITY_ABSTRACT
- anchored = TRUE
- var/datum/proximity_monitor/monitor
-
-/obj/effect/abstract/proximity_checker/Initialize(mapload, datum/proximity_monitor/_monitor)
- . = ..()
- if(_monitor)
- monitor = _monitor
- else
- stack_trace("proximity_checker created without host")
- return INITIALIZE_HINT_QDEL
-
-/obj/effect/abstract/proximity_checker/Destroy()
- monitor = null
- return ..()
-
-/obj/effect/abstract/proximity_checker/Crossed(atom/movable/AM)
- set waitfor = FALSE
- monitor?.hasprox_receiver.HasProximity(AM)
+/datum/proximity_monitor
+ var/atom/host //the atom we are tracking
+ var/atom/hasprox_receiver //the atom that will receive HasProximity calls.
+ var/atom/last_host_loc
+ var/list/checkers //list of /obj/effect/abstract/proximity_checkers
+ var/current_range
+ var/ignore_if_not_on_turf //don't check turfs in range if the host's loc isn't a turf
+
+/datum/proximity_monitor/New(atom/_host, range, _ignore_if_not_on_turf = TRUE)
+ checkers = list()
+ last_host_loc = _host.loc
+ ignore_if_not_on_turf = _ignore_if_not_on_turf
+ current_range = range
+ SetHost(_host)
+
+/datum/proximity_monitor/proc/SetHost(atom/H,atom/R)
+ if(H == host)
+ return
+ if(host)
+ UnregisterSignal(host, COMSIG_MOVABLE_MOVED)
+ if(R)
+ hasprox_receiver = R
+ else if(hasprox_receiver == host) //Default case
+ hasprox_receiver = H
+ host = H
+ RegisterSignal(host, COMSIG_MOVABLE_MOVED, .proc/HandleMove)
+ last_host_loc = host.loc
+ SetRange(current_range,TRUE)
+
+/datum/proximity_monitor/Destroy()
+ host = null
+ last_host_loc = null
+ hasprox_receiver = null
+ QDEL_LIST(checkers)
+ return ..()
+
+/datum/proximity_monitor/proc/HandleMove()
+ var/atom/_host = host
+ var/atom/new_host_loc = _host.loc
+ if(last_host_loc != new_host_loc)
+ last_host_loc = new_host_loc //hopefully this won't cause GC issues with containers
+ var/curr_range = current_range
+ SetRange(curr_range, TRUE)
+ if(curr_range)
+ testing("HasProx: [host] -> [host]")
+ hasprox_receiver.HasProximity(host) //if we are processing, we're guaranteed to be a movable
+
+/datum/proximity_monitor/proc/SetRange(range, force_rebuild = FALSE)
+ if(!force_rebuild && range == current_range)
+ return FALSE
+ . = TRUE
+
+ current_range = range
+
+ var/list/checkers_local = checkers
+ var/old_checkers_len = checkers_local.len
+
+ var/atom/_host = host
+
+ var/atom/loc_to_use = ignore_if_not_on_turf ? _host.loc : get_turf(_host)
+ if(!isturf(loc_to_use)) //only check the host's loc
+ if(range)
+ var/obj/effect/abstract/proximity_checker/pc
+ if(old_checkers_len)
+ pc = checkers_local[old_checkers_len]
+ --checkers_local.len
+ QDEL_LIST(checkers_local)
+ else
+ pc = new(loc_to_use, src)
+
+ checkers_local += pc //only check the host's loc
+ return
+
+ var/list/turfs = RANGE_TURFS(range, loc_to_use)
+ var/turfs_len = turfs.len
+ var/old_checkers_used = min(turfs_len, old_checkers_len)
+
+ //reuse what we can
+ for(var/I in 1 to old_checkers_len)
+ if(I <= old_checkers_used)
+ var/obj/effect/abstract/proximity_checker/pc = checkers_local[I]
+ pc.forceMove(turfs[I])
+ else
+ qdel(checkers_local[I]) //delete the leftovers
+
+ if(old_checkers_len < turfs_len)
+ //create what we lack
+ for(var/I in (old_checkers_used + 1) to turfs_len)
+ checkers_local += new /obj/effect/abstract/proximity_checker(turfs[I], src)
+ else
+ checkers_local.Cut(old_checkers_used + 1, old_checkers_len)
+
+/obj/effect/abstract/proximity_checker
+ invisibility = INVISIBILITY_ABSTRACT
+ anchored = TRUE
+ var/datum/proximity_monitor/monitor
+
+/obj/effect/abstract/proximity_checker/Initialize(mapload, datum/proximity_monitor/_monitor)
+ . = ..()
+ if(_monitor)
+ monitor = _monitor
+ else
+ stack_trace("proximity_checker created without host")
+ return INITIALIZE_HINT_QDEL
+
+/obj/effect/abstract/proximity_checker/Destroy()
+ monitor = null
+ return ..()
+
+/obj/effect/abstract/proximity_checker/Crossed(atom/movable/AM)
+ set waitfor = FALSE
+ monitor?.hasprox_receiver.HasProximity(AM)
diff --git a/code/game/objects/items/RCL.dm b/code/game/objects/items/RCL.dm
index 16104f0b..0eb97a8d 100644
--- a/code/game/objects/items/RCL.dm
+++ b/code/game/objects/items/RCL.dm
@@ -20,8 +20,8 @@
var/ghetto = FALSE
lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi'
- var/datum/component/mobhook
var/datum/radial_menu/persistent/wiring_gui_menu
+ var/mob/listeningTo
/obj/item/twohanded/rcl/attackby(obj/item/W, mob/user)
if(istype(W, /obj/item/stack/cable_coil))
@@ -86,7 +86,7 @@
/obj/item/twohanded/rcl/Destroy()
QDEL_NULL(loaded)
last = null
- QDEL_NULL(mobhook)
+ listeningTo = null
QDEL_NULL(wiring_gui_menu)
return ..()
@@ -141,9 +141,8 @@
/obj/item/twohanded/rcl/dropped(mob/wearer)
..()
- if(mobhook)
- active = FALSE
- QDEL_NULL(mobhook)
+ UnregisterSignal(wearer, COMSIG_MOVABLE_MOVED)
+ listeningTo = null
last = null
/obj/item/twohanded/rcl/attack_self(mob/user)
@@ -158,13 +157,12 @@
break
obj/item/twohanded/rcl/proc/getMobhook(mob/to_hook)
- if(to_hook)
- if(mobhook && mobhook.parent != to_hook)
- QDEL_NULL(mobhook)
- if (!mobhook)
- mobhook = to_hook.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/trigger)))
- else
- QDEL_NULL(mobhook)
+ if(listeningTo == to_hook)
+ return
+ if(listeningTo)
+ UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
+ RegisterSignal(to_hook, COMSIG_MOVABLE_MOVED, .proc/trigger)
+ listeningTo = to_hook
/obj/item/twohanded/rcl/proc/trigger(mob/user)
if(active)
diff --git a/code/game/objects/items/defib.dm b/code/game/objects/items/defib.dm
index e3752ab4..f3efb52d 100644
--- a/code/game/objects/items/defib.dm
+++ b/code/game/objects/items/defib.dm
@@ -39,7 +39,6 @@
/obj/item/defibrillator/loaded/Initialize() //starts with hicap
. = ..()
- paddles = make_paddles()
cell = new(src)
update_icon()
return
@@ -204,8 +203,8 @@
var/M = get(paddles, /mob)
remove_paddles(M)
QDEL_NULL(paddles)
- . = ..()
- update_icon()
+ QDEL_NULL(cell)
+ return ..()
/obj/item/defibrillator/proc/deductcharge(chrgdeductamt)
if(cell)
@@ -246,7 +245,6 @@
/obj/item/defibrillator/compact/loaded/Initialize()
. = ..()
- paddles = make_paddles()
cell = new(src)
update_icon()
@@ -258,7 +256,6 @@
/obj/item/defibrillator/compact/combat/loaded/Initialize()
. = ..()
- paddles = make_paddles()
cell = new /obj/item/stock_parts/cell/infinite(src)
update_icon()
@@ -293,31 +290,28 @@
var/grab_ghost = FALSE
var/tlimit = DEFIB_TIME_LIMIT * 10
- var/datum/component/mobhook
+ var/mob/listeningTo
/obj/item/twohanded/shockpaddles/equipped(mob/user, slot)
. = ..()
- if(req_defib)
- if (mobhook && mobhook.parent != user)
- QDEL_NULL(mobhook)
- if (!mobhook)
- mobhook = user.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/check_range)))
+ if(!req_defib)
+ return
+ RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/check_range)
/obj/item/twohanded/shockpaddles/Moved()
. = ..()
check_range()
/obj/item/twohanded/shockpaddles/proc/check_range()
- if(!req_defib)
+ if(!req_defib || !defib)
return
if(!in_range(src,defib))
var/mob/living/L = loc
if(istype(L))
to_chat(L, "[defib]'s paddles overextend and come out of your hands!")
- L.temporarilyRemoveItemFromInventory(src,TRUE)
else
visible_message("[src] snap back into [defib].")
- snap_back()
+ snap_back()
/obj/item/twohanded/shockpaddles/proc/recharge(var/time)
if(req_defib || !time)
@@ -358,14 +352,14 @@
/obj/item/twohanded/shockpaddles/dropped(mob/user)
if(!req_defib)
return ..()
- if (mobhook)
- QDEL_NULL(mobhook)
if(user)
+ UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
var/obj/item/twohanded/offhand/O = user.get_inactive_held_item()
if(istype(O))
O.unwield()
- to_chat(user, "The paddles snap back into the main unit.")
- snap_back()
+ if(user != loc)
+ to_chat(user, "The paddles snap back into the main unit.")
+ snap_back()
return unwield(user)
/obj/item/twohanded/shockpaddles/proc/snap_back()
diff --git a/code/game/objects/items/devices/geiger_counter.dm b/code/game/objects/items/devices/geiger_counter.dm
index 0dbede18..8d386842 100644
--- a/code/game/objects/items/devices/geiger_counter.dm
+++ b/code/game/objects/items/devices/geiger_counter.dm
@@ -205,21 +205,25 @@
obj_flags |= EMAGGED
/obj/item/geiger_counter/cyborg
- var/datum/component/mobhook
+ var/mob/listeningTo
/obj/item/geiger_counter/cyborg/equipped(mob/user)
. = ..()
- if (mobhook && mobhook.parent != user)
- QDEL_NULL(mobhook)
- if (!mobhook)
- mobhook = user.AddComponent(/datum/component/redirect, list(COMSIG_ATOM_RAD_ACT = CALLBACK(src, .proc/redirect_rad_act)))
+ if(listeningTo == user)
+ return
+ if(listeningTo)
+ UnregisterSignal(listeningTo, COMSIG_ATOM_RAD_ACT)
+ RegisterSignal(user, COMSIG_ATOM_RAD_ACT, .proc/redirect_rad_act)
+ listeningTo = user
/obj/item/geiger_counter/cyborg/proc/redirect_rad_act(datum/source, amount)
rad_act(amount)
/obj/item/geiger_counter/cyborg/dropped()
. = ..()
- QDEL_NULL(mobhook)
+ if(listeningTo)
+ UnregisterSignal(listeningTo, COMSIG_ATOM_RAD_ACT)
+ listeningTo = null
#undef RAD_LEVEL_NORMAL
#undef RAD_LEVEL_MODERATE
diff --git a/code/game/objects/items/his_grace.dm b/code/game/objects/items/his_grace.dm
index b5c7af4b..ce5810f5 100644
--- a/code/game/objects/items/his_grace.dm
+++ b/code/game/objects/items/his_grace.dm
@@ -29,7 +29,7 @@
. = ..()
START_PROCESSING(SSprocessing, src)
GLOB.poi_list += src
- AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_POST_THROW = CALLBACK(src, .proc/move_gracefully)))
+ RegisterSignal(src, COMSIG_MOVABLE_POST_THROW, .proc/move_gracefully)
/obj/item/his_grace/Destroy()
STOP_PROCESSING(SSprocessing, src)
diff --git a/code/game/objects/items/storage/bags.dm b/code/game/objects/items/storage/bags.dm
index 14a8d793..809e4bdb 100644
--- a/code/game/objects/items/storage/bags.dm
+++ b/code/game/objects/items/storage/bags.dm
@@ -1,439 +1,442 @@
-/*
- * These absorb the functionality of the plant bag, ore satchel, etc
- * They use the use_to_pickup, quick_gather, and quick_empty functions
- * that were already defined in weapon/storage, but which had been
- * re-implemented in other classes.
- *
- * Contains:
- * Trash Bag
- * Mining Satchel
- * Plant Bag
- * Sheet Snatcher
- * Book Bag
- * Biowaste Bag
- *
- * -Sayu
- */
-
-// Generic non-item
-/obj/item/storage/bag
- slot_flags = ITEM_SLOT_BELT
-
-/obj/item/storage/bag/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.allow_quick_gather = TRUE
- STR.allow_quick_empty = TRUE
- STR.display_numerical_stacking = TRUE
- STR.click_gather = TRUE
-
-// -----------------------------
-// Trash bag
-// -----------------------------
-/obj/item/storage/bag/trash
- name = "trash bag"
- desc = "It's the heavy-duty black polymer kind. Time to take out the trash!"
- icon = 'icons/obj/janitor.dmi'
- icon_state = "trashbag"
- item_state = "trashbag"
- lefthand_file = 'icons/mob/inhands/equipment/custodial_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/equipment/custodial_righthand.dmi'
-
- w_class = WEIGHT_CLASS_BULKY
- var/insertable = TRUE
-
-/obj/item/storage/bag/trash/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_SMALL
- STR.max_combined_w_class = 30
- STR.max_items = 30
- STR.cant_hold = typecacheof(list(/obj/item/disk/nuclear))
-
-/obj/item/storage/bag/trash/suicide_act(mob/user)
- user.visible_message("[user] puts [src] over [user.p_their()] head and starts chomping at the insides! Disgusting!")
- playsound(loc, 'sound/items/eatfood.ogg', 50, 1, -1)
- return (TOXLOSS)
-
-/obj/item/storage/bag/trash/update_icon()
- if(contents.len == 0)
- icon_state = "[initial(icon_state)]"
- else if(contents.len < 12)
- icon_state = "[initial(icon_state)]1"
- else if(contents.len < 21)
- icon_state = "[initial(icon_state)]2"
- else icon_state = "[initial(icon_state)]3"
-
-/obj/item/storage/bag/trash/cyborg
- insertable = FALSE
-
-/obj/item/storage/bag/trash/proc/janicart_insert(mob/user, obj/structure/janitorialcart/J)
- if(insertable)
- J.put_in_cart(src, user)
- J.mybag=src
- J.update_icon()
- else
- to_chat(user, "You are unable to fit your [name] into the [J.name].")
- return
-
-/obj/item/storage/bag/trash/bluespace
- name = "trash bag of holding"
- desc = "The latest and greatest in custodial convenience, a trashbag that is capable of holding vast quantities of garbage."
- icon_state = "bluetrashbag"
- item_flags = NO_MAT_REDEMPTION
-
-/obj/item/storage/bag/trash/bluespace/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 60
- STR.max_items = 60
-
-/obj/item/storage/bag/trash/bluespace/cyborg
- insertable = FALSE
-
-// -----------------------------
-// Mining Satchel
-// -----------------------------
-
-/obj/item/storage/bag/ore
- name = "mining satchel"
- desc = "This little bugger can be used to store and transport ores."
- icon = 'icons/obj/mining.dmi'
- icon_state = "satchel"
- slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_POCKET
- w_class = WEIGHT_CLASS_NORMAL
- component_type = /datum/component/storage/concrete/stack
- var/spam_protection = FALSE //If this is TRUE, the holder won't receive any messages when they fail to pick up ore through crossing it
- var/datum/component/mobhook
-
-/obj/item/storage/bag/ore/ComponentInitialize()
- . = ..()
- var/datum/component/storage/concrete/stack/STR = GetComponent(/datum/component/storage/concrete/stack)
- STR.allow_quick_empty = TRUE
- STR.can_hold = typecacheof(list(/obj/item/stack/ore))
- STR.max_w_class = WEIGHT_CLASS_HUGE
- STR.max_combined_stack_amount = 50
-
-/obj/item/storage/bag/ore/equipped(mob/user)
- . = ..()
- if (mobhook && mobhook.parent != user)
- QDEL_NULL(mobhook)
- if (!mobhook)
- mobhook = user.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/Pickup_ores)))
-
-/obj/item/storage/bag/ore/dropped()
- . = ..()
- if (mobhook)
- QDEL_NULL(mobhook)
-
-/obj/item/storage/bag/ore/proc/Pickup_ores(mob/living/user)
- var/show_message = FALSE
- var/obj/structure/ore_box/box
- var/turf/tile = user.loc
- if (!isturf(tile))
- return
- if (istype(user.pulling, /obj/structure/ore_box))
- box = user.pulling
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- if(STR)
- for(var/A in tile)
- if (!is_type_in_typecache(A, STR.can_hold))
- continue
- if (box)
- user.transferItemToLoc(A, box)
- show_message = TRUE
- else if(SEND_SIGNAL(src, COMSIG_TRY_STORAGE_INSERT, A, user, TRUE))
- show_message = TRUE
- else
- if(!spam_protection)
- to_chat(user, "Your [name] is full and can't hold any more!")
- spam_protection = TRUE
- continue
- if(show_message)
- playsound(user, "rustle", 50, TRUE)
- if (box)
- user.visible_message("[user] offloads the ores beneath [user.p_them()] into [box].", \
- "You offload the ores beneath you into your [box].")
- else
- user.visible_message("[user] scoops up the ores beneath [user.p_them()].", \
- "You scoop up the ores beneath you with your [name].")
- spam_protection = FALSE
-
-/obj/item/storage/bag/ore/cyborg
- name = "cyborg mining satchel"
-
-/obj/item/storage/bag/ore/cyborg/ComponentInitialize()
- . = ..()
- var/datum/component/storage/concrete/stack/STR = GetComponent(/datum/component/storage/concrete/stack)
- STR.allow_quick_empty = TRUE
- STR.can_hold = typecacheof(list(/obj/item/stack/ore))
- STR.max_w_class = WEIGHT_CLASS_HUGE
- STR.max_combined_stack_amount = 150
-
-/obj/item/storage/bag/ore/large
- name = "large mining satchel"
- desc = "This bag can hold three times the ore in many small pockets. Shockingly foldable and compact for its volume."
-
-/obj/item/storage/bag/ore/large/ComponentInitialize()
- . = ..()
- var/datum/component/storage/concrete/stack/STR = GetComponent(/datum/component/storage/concrete/stack)
- STR.allow_quick_empty = TRUE
- STR.can_hold = typecacheof(list(/obj/item/stack/ore))
- STR.max_w_class = WEIGHT_CLASS_HUGE
- STR.max_combined_stack_amount = 150
-
-/obj/item/storage/bag/ore/holding //miners, your messiah has arrived
- name = "mining satchel of holding"
- desc = "A revolution in convenience, this satchel allows for huge amounts of ore storage. It's been outfitted with anti-malfunction safety measures."
- icon_state = "satchel_bspace"
-
-/obj/item/storage/bag/ore/holding/ComponentInitialize()
- . = ..()
- var/datum/component/storage/concrete/stack/STR = GetComponent(/datum/component/storage/concrete/stack)
- STR.max_items = INFINITY
- STR.max_combined_w_class = INFINITY
- STR.max_combined_stack_amount = INFINITY
-
-// -----------------------------
-// Plant bag
-// -----------------------------
-
-/obj/item/storage/bag/plants
- name = "plant bag"
- icon = 'icons/obj/hydroponics/equipment.dmi'
- icon_state = "plantbag"
- w_class = WEIGHT_CLASS_TINY
- resistance_flags = FLAMMABLE
-
-/obj/item/storage/bag/plants/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.max_combined_w_class = 100
- STR.max_items = 100
- STR.can_hold = typecacheof(list(/obj/item/reagent_containers/food/snacks/grown, /obj/item/seeds, /obj/item/grown, /obj/item/reagent_containers/honeycomb))
-
-////////
-
-/obj/item/storage/bag/plants/portaseeder
- name = "portable seed extractor"
- desc = "For the enterprising botanist on the go. Less efficient than the stationary model, it creates one seed per plant."
- icon_state = "portaseeder"
-
-/obj/item/storage/bag/plants/portaseeder/verb/dissolve_contents()
- set name = "Activate Seed Extraction"
- set category = "Object"
- set desc = "Activate to convert your plants into plantable seeds."
- if(usr.stat || !usr.canmove || usr.restrained())
- return
- for(var/obj/item/O in contents)
- seedify(O, 1)
-
-// -----------------------------
-// Sheet Snatcher
-// -----------------------------
-// Because it stacks stacks, this doesn't operate normally.
-// However, making it a storage/bag allows us to reuse existing code in some places. -Sayu
-
-/obj/item/storage/bag/sheetsnatcher
- name = "sheet snatcher"
- desc = "A patented Kinaris storage system designed for any kind of mineral sheet."
- icon = 'icons/obj/mining.dmi'
- icon_state = "sheetsnatcher"
-
- var/capacity = 300; //the number of sheets it can carry.
- w_class = WEIGHT_CLASS_NORMAL
- component_type = /datum/component/storage/concrete/stack
-
-/obj/item/storage/bag/sheetsnatcher/ComponentInitialize()
- . = ..()
- var/datum/component/storage/concrete/stack/STR = GetComponent(/datum/component/storage/concrete/stack)
- STR.allow_quick_empty = TRUE
- STR.can_hold = typecacheof(list(/obj/item/stack/sheet))
- STR.cant_hold = typecacheof(list(/obj/item/stack/sheet/mineral/sandstone, /obj/item/stack/sheet/mineral/wood))
- STR.max_combined_stack_amount = 300
-
-// -----------------------------
-// Sheet Snatcher (Cyborg)
-// -----------------------------
-
-/obj/item/storage/bag/sheetsnatcher/borg
- name = "sheet snatcher 9000"
- desc = ""
- capacity = 500//Borgs get more because >specialization
-
-/obj/item/storage/bag/sheetsnatcher/borg/ComponentInitialize()
- . = ..()
- var/datum/component/storage/concrete/stack/STR = GetComponent(/datum/component/storage/concrete/stack)
- STR.max_combined_stack_amount = 500
-
-// -----------------------------
-// Book bag
-// -----------------------------
-
-/obj/item/storage/bag/books
- name = "book bag"
- desc = "A bag for books."
- icon = 'icons/obj/library.dmi'
- icon_state = "bookbag"
- w_class = WEIGHT_CLASS_BULKY //Bigger than a book because physics
- resistance_flags = FLAMMABLE
-
-/obj/item/storage/bag/books/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_w_class = WEIGHT_CLASS_NORMAL
- STR.max_combined_w_class = 21
- STR.max_items = 7
- STR.display_numerical_stacking = FALSE
- STR.can_hold = typecacheof(list(/obj/item/book, /obj/item/storage/book, /obj/item/spellbook))
-
-/*
- * Trays - Agouri
- */
-/obj/item/storage/bag/tray
- name = "tray"
- icon = 'icons/obj/food/containers.dmi'
- icon_state = "tray"
- desc = "A metal tray to lay food on."
- force = 5
- throwforce = 10
- throw_speed = 3
- throw_range = 5
- w_class = WEIGHT_CLASS_BULKY
- flags_1 = CONDUCT_1
- materials = list(MAT_METAL=3000)
-
-/obj/item/storage/bag/tray/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_items = 15 //I want my sushi god damn it
- STR.insert_preposition = "on"
- STR.can_hold = typecacheof(list(
- /obj/item/reagent_containers/food,
- /obj/item/kitchen,
- /obj/item/storage/box/donkpockets))
-
-/obj/item/storage/bag/tray/pre_attack(atom/A, mob/living/user, params)
- if(istype(A, /obj/structure/table) && user.a_intent == INTENT_HELP) //I want my tray god damn it
- if(user.transferItemToLoc(src, get_turf(A)))
- var/list/click_params = params2list(params)
- if(!click_params || !click_params["icon-x"] || !click_params["icon-y"])
- return
- pixel_x = CLAMP(text2num(click_params["icon-x"]) - 16, -(world.icon_size/2), world.icon_size/2)
- pixel_y = CLAMP(text2num(click_params["icon-y"]) - 16, -(world.icon_size/2), world.icon_size/2)
- return
- ..()
-
-/obj/item/storage/bag/tray/attack(mob/living/M, mob/living/user)
- . = ..()
- // Drop all the things. All of them.
- var/list/obj/item/oldContents = contents.Copy()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.quick_empty()
- // Make each item scatter a bit
- for(var/obj/item/I in oldContents)
- spawn()
- for(var/i = 1, i <= rand(1,2), i++)
- if(I)
- step(I, pick(NORTH,SOUTH,EAST,WEST))
- sleep(rand(2,4))
-
- if(prob(50))
- playsound(M, 'sound/items/trayhit1.ogg', 50, 1)
- else
- playsound(M, 'sound/items/trayhit2.ogg', 50, 1)
-
- if(ishuman(M) || ismonkey(M))
- if(prob(10))
- M.Knockdown(40)
- update_icon()
-
-/obj/item/storage/bag/tray/update_icon()
- cut_overlays()
- for(var/obj/item/I in contents)
- var/mutable_appearance/MA = new (I) //I want my icons god damn it
- MA.pixel_x = rand(-6, 6)
- MA.pixel_y = rand(-6, 6)
- add_overlay(MA)
-
-/obj/item/storage/bag/tray/Entered()
- . = ..()
- update_icon()
-
-/obj/item/storage/bag/tray/Exited()
- . = ..()
- update_icon()
-
-/*
- * Chemistry bag
- */
-
-/obj/item/storage/bag/chemistry
- name = "chemistry bag"
- icon = 'icons/obj/chemical.dmi'
- icon_state = "bag"
- desc = "A bag for storing pills, patches, and bottles."
- w_class = WEIGHT_CLASS_TINY
- resistance_flags = FLAMMABLE
-
-/obj/item/storage/bag/chemistry/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 200
- STR.max_items = 50
- STR.insert_preposition = "in"
- STR.can_hold = typecacheof(list(/obj/item/reagent_containers/pill, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/syringe/dart, /obj/item/reagent_containers/chem_pack))
-
-/*
- * Biowaste bag (mostly for xenobiologists)
- */
-
-/obj/item/storage/bag/bio
- name = "bio bag"
- icon = 'icons/obj/chemical.dmi'
- icon_state = "biobag"
- desc = "A bag for the safe transportation and disposal of biowaste and other biological materials."
- w_class = WEIGHT_CLASS_TINY
- resistance_flags = FLAMMABLE
-
-/obj/item/storage/bag/bio/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 200
- STR.max_items = 25
- STR.insert_preposition = "in"
- STR.can_hold = typecacheof(list(/obj/item/slime_extract, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/blood, /obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/food/snacks/deadmouse, /obj/item/reagent_containers/food/snacks/monkeycube))
-
-/obj/item/storage/bag/bio/holding
- name = "bio bag of holding"
- icon = 'icons/obj/chemical.dmi'
- icon_state = "bspace_biobag"
- desc = "A bag for the safe transportation and disposal of biowaste and other biological materials."
-
-/obj/item/storage/bag/bio/holding/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = INFINITY
- STR.max_items = 100
-
-/*
- * Construction bag (for engineering, holds stock parts and electronics)
- */
-
-/obj/item/storage/bag/construction
- name = "construction bag"
- icon = 'icons/obj/tools.dmi'
- icon_state = "construction_bag"
- desc = "A bag for storing small construction components."
- w_class = WEIGHT_CLASS_TINY
- resistance_flags = FLAMMABLE
-
-/obj/item/storage/bag/construction/ComponentInitialize()
- . = ..()
- var/datum/component/storage/STR = GetComponent(/datum/component/storage)
- STR.max_combined_w_class = 100
- STR.max_items = 50
- STR.max_w_class = WEIGHT_CLASS_SMALL
- STR.insert_preposition = "in"
- STR.can_hold = typecacheof(list(/obj/item/stack/ore/bluespace_crystal, /obj/item/assembly, /obj/item/stock_parts, /obj/item/reagent_containers/glass/beaker, /obj/item/stack/cable_coil, /obj/item/circuitboard, /obj/item/electronics))
+/*
+ * These absorb the functionality of the plant bag, ore satchel, etc
+ * They use the use_to_pickup, quick_gather, and quick_empty functions
+ * that were already defined in weapon/storage, but which had been
+ * re-implemented in other classes.
+ *
+ * Contains:
+ * Trash Bag
+ * Mining Satchel
+ * Plant Bag
+ * Sheet Snatcher
+ * Book Bag
+ * Biowaste Bag
+ *
+ * -Sayu
+ */
+
+// Generic non-item
+/obj/item/storage/bag
+ slot_flags = ITEM_SLOT_BELT
+
+/obj/item/storage/bag/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.allow_quick_gather = TRUE
+ STR.allow_quick_empty = TRUE
+ STR.display_numerical_stacking = TRUE
+ STR.click_gather = TRUE
+
+// -----------------------------
+// Trash bag
+// -----------------------------
+/obj/item/storage/bag/trash
+ name = "trash bag"
+ desc = "It's the heavy-duty black polymer kind. Time to take out the trash!"
+ icon = 'icons/obj/janitor.dmi'
+ icon_state = "trashbag"
+ item_state = "trashbag"
+ lefthand_file = 'icons/mob/inhands/equipment/custodial_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/equipment/custodial_righthand.dmi'
+
+ w_class = WEIGHT_CLASS_BULKY
+ var/insertable = TRUE
+
+/obj/item/storage/bag/trash/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_w_class = WEIGHT_CLASS_SMALL
+ STR.max_combined_w_class = 30
+ STR.max_items = 30
+ STR.cant_hold = typecacheof(list(/obj/item/disk/nuclear))
+
+/obj/item/storage/bag/trash/suicide_act(mob/user)
+ user.visible_message("[user] puts [src] over [user.p_their()] head and starts chomping at the insides! Disgusting!")
+ playsound(loc, 'sound/items/eatfood.ogg', 50, 1, -1)
+ return (TOXLOSS)
+
+/obj/item/storage/bag/trash/update_icon()
+ if(contents.len == 0)
+ icon_state = "[initial(icon_state)]"
+ else if(contents.len < 12)
+ icon_state = "[initial(icon_state)]1"
+ else if(contents.len < 21)
+ icon_state = "[initial(icon_state)]2"
+ else icon_state = "[initial(icon_state)]3"
+
+/obj/item/storage/bag/trash/cyborg
+ insertable = FALSE
+
+/obj/item/storage/bag/trash/proc/janicart_insert(mob/user, obj/structure/janitorialcart/J)
+ if(insertable)
+ J.put_in_cart(src, user)
+ J.mybag=src
+ J.update_icon()
+ else
+ to_chat(user, "You are unable to fit your [name] into the [J.name].")
+ return
+
+/obj/item/storage/bag/trash/bluespace
+ name = "trash bag of holding"
+ desc = "The latest and greatest in custodial convenience, a trashbag that is capable of holding vast quantities of garbage."
+ icon_state = "bluetrashbag"
+ item_flags = NO_MAT_REDEMPTION
+
+/obj/item/storage/bag/trash/bluespace/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_combined_w_class = 60
+ STR.max_items = 60
+
+/obj/item/storage/bag/trash/bluespace/cyborg
+ insertable = FALSE
+
+// -----------------------------
+// Mining Satchel
+// -----------------------------
+
+/obj/item/storage/bag/ore
+ name = "mining satchel"
+ desc = "This little bugger can be used to store and transport ores."
+ icon = 'icons/obj/mining.dmi'
+ icon_state = "satchel"
+ slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_POCKET
+ w_class = WEIGHT_CLASS_NORMAL
+ component_type = /datum/component/storage/concrete/stack
+ var/spam_protection = FALSE //If this is TRUE, the holder won't receive any messages when they fail to pick up ore through crossing it
+ var/mob/listeningTo
+ rad_flags = RAD_PROTECT_CONTENTS | RAD_NO_CONTAMINATE
+
+/obj/item/storage/bag/ore/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/concrete/stack/STR = GetComponent(/datum/component/storage/concrete/stack)
+ STR.allow_quick_empty = TRUE
+ STR.can_hold = typecacheof(list(/obj/item/stack/ore))
+ STR.max_w_class = WEIGHT_CLASS_HUGE
+ STR.max_combined_stack_amount = 50
+
+/obj/item/storage/bag/ore/equipped(mob/user)
+ . = ..()
+ if(listeningTo == user)
+ return
+ if(listeningTo)
+ UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
+ RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/Pickup_ores)
+ listeningTo = user
+
+/obj/item/storage/bag/ore/dropped()
+ . = ..()
+ UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
+ listeningTo = null
+
+/obj/item/storage/bag/ore/proc/Pickup_ores(mob/living/user)
+ var/show_message = FALSE
+ var/obj/structure/ore_box/box
+ var/turf/tile = user.loc
+ if (!isturf(tile))
+ return
+ if (istype(user.pulling, /obj/structure/ore_box))
+ box = user.pulling
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ if(STR)
+ for(var/A in tile)
+ if (!is_type_in_typecache(A, STR.can_hold))
+ continue
+ if (box)
+ user.transferItemToLoc(A, box)
+ show_message = TRUE
+ else if(SEND_SIGNAL(src, COMSIG_TRY_STORAGE_INSERT, A, user, TRUE))
+ show_message = TRUE
+ else
+ if(!spam_protection)
+ to_chat(user, "Your [name] is full and can't hold any more!")
+ spam_protection = TRUE
+ continue
+ if(show_message)
+ playsound(user, "rustle", 50, TRUE)
+ if (box)
+ user.visible_message("[user] offloads the ores beneath [user.p_them()] into [box].", \
+ "You offload the ores beneath you into your [box].")
+ else
+ user.visible_message("[user] scoops up the ores beneath [user.p_them()].", \
+ "You scoop up the ores beneath you with your [name].")
+ spam_protection = FALSE
+
+/obj/item/storage/bag/ore/cyborg
+ name = "cyborg mining satchel"
+
+/obj/item/storage/bag/ore/cyborg/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/concrete/stack/STR = GetComponent(/datum/component/storage/concrete/stack)
+ STR.allow_quick_empty = TRUE
+ STR.can_hold = typecacheof(list(/obj/item/stack/ore))
+ STR.max_w_class = WEIGHT_CLASS_HUGE
+ STR.max_combined_stack_amount = 150
+
+/obj/item/storage/bag/ore/large
+ name = "large mining satchel"
+ desc = "This bag can hold three times the ore in many small pockets. Shockingly foldable and compact for its volume."
+
+/obj/item/storage/bag/ore/large/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/concrete/stack/STR = GetComponent(/datum/component/storage/concrete/stack)
+ STR.allow_quick_empty = TRUE
+ STR.can_hold = typecacheof(list(/obj/item/stack/ore))
+ STR.max_w_class = WEIGHT_CLASS_HUGE
+ STR.max_combined_stack_amount = 150
+
+/obj/item/storage/bag/ore/holding //miners, your messiah has arrived
+ name = "mining satchel of holding"
+ desc = "A revolution in convenience, this satchel allows for huge amounts of ore storage. It's been outfitted with anti-malfunction safety measures."
+ icon_state = "satchel_bspace"
+
+/obj/item/storage/bag/ore/holding/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/concrete/stack/STR = GetComponent(/datum/component/storage/concrete/stack)
+ STR.max_items = INFINITY
+ STR.max_combined_w_class = INFINITY
+ STR.max_combined_stack_amount = INFINITY
+
+// -----------------------------
+// Plant bag
+// -----------------------------
+
+/obj/item/storage/bag/plants
+ name = "plant bag"
+ icon = 'icons/obj/hydroponics/equipment.dmi'
+ icon_state = "plantbag"
+ w_class = WEIGHT_CLASS_TINY
+ resistance_flags = FLAMMABLE
+
+/obj/item/storage/bag/plants/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_w_class = WEIGHT_CLASS_NORMAL
+ STR.max_combined_w_class = 100
+ STR.max_items = 100
+ STR.can_hold = typecacheof(list(/obj/item/reagent_containers/food/snacks/grown, /obj/item/seeds, /obj/item/grown, /obj/item/reagent_containers/honeycomb))
+
+////////
+
+/obj/item/storage/bag/plants/portaseeder
+ name = "portable seed extractor"
+ desc = "For the enterprising botanist on the go. Less efficient than the stationary model, it creates one seed per plant."
+ icon_state = "portaseeder"
+
+/obj/item/storage/bag/plants/portaseeder/verb/dissolve_contents()
+ set name = "Activate Seed Extraction"
+ set category = "Object"
+ set desc = "Activate to convert your plants into plantable seeds."
+ if(usr.stat || !usr.canmove || usr.restrained())
+ return
+ for(var/obj/item/O in contents)
+ seedify(O, 1)
+
+// -----------------------------
+// Sheet Snatcher
+// -----------------------------
+// Because it stacks stacks, this doesn't operate normally.
+// However, making it a storage/bag allows us to reuse existing code in some places. -Sayu
+
+/obj/item/storage/bag/sheetsnatcher
+ name = "sheet snatcher"
+ desc = "A patented Kinaris storage system designed for any kind of mineral sheet."
+ icon = 'icons/obj/mining.dmi'
+ icon_state = "sheetsnatcher"
+
+ var/capacity = 300; //the number of sheets it can carry.
+ w_class = WEIGHT_CLASS_NORMAL
+ component_type = /datum/component/storage/concrete/stack
+
+/obj/item/storage/bag/sheetsnatcher/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/concrete/stack/STR = GetComponent(/datum/component/storage/concrete/stack)
+ STR.allow_quick_empty = TRUE
+ STR.can_hold = typecacheof(list(/obj/item/stack/sheet))
+ STR.cant_hold = typecacheof(list(/obj/item/stack/sheet/mineral/sandstone, /obj/item/stack/sheet/mineral/wood))
+ STR.max_combined_stack_amount = 300
+
+// -----------------------------
+// Sheet Snatcher (Cyborg)
+// -----------------------------
+
+/obj/item/storage/bag/sheetsnatcher/borg
+ name = "sheet snatcher 9000"
+ desc = ""
+ capacity = 500//Borgs get more because >specialization
+
+/obj/item/storage/bag/sheetsnatcher/borg/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/concrete/stack/STR = GetComponent(/datum/component/storage/concrete/stack)
+ STR.max_combined_stack_amount = 500
+
+// -----------------------------
+// Book bag
+// -----------------------------
+
+/obj/item/storage/bag/books
+ name = "book bag"
+ desc = "A bag for books."
+ icon = 'icons/obj/library.dmi'
+ icon_state = "bookbag"
+ w_class = WEIGHT_CLASS_BULKY //Bigger than a book because physics
+ resistance_flags = FLAMMABLE
+
+/obj/item/storage/bag/books/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_w_class = WEIGHT_CLASS_NORMAL
+ STR.max_combined_w_class = 21
+ STR.max_items = 7
+ STR.display_numerical_stacking = FALSE
+ STR.can_hold = typecacheof(list(/obj/item/book, /obj/item/storage/book, /obj/item/spellbook))
+
+/*
+ * Trays - Agouri
+ */
+/obj/item/storage/bag/tray
+ name = "tray"
+ icon = 'icons/obj/food/containers.dmi'
+ icon_state = "tray"
+ desc = "A metal tray to lay food on."
+ force = 5
+ throwforce = 10
+ throw_speed = 3
+ throw_range = 5
+ w_class = WEIGHT_CLASS_BULKY
+ flags_1 = CONDUCT_1
+ materials = list(MAT_METAL=3000)
+
+/obj/item/storage/bag/tray/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_items = 15 //I want my sushi god damn it
+ STR.insert_preposition = "on"
+ STR.can_hold = typecacheof(list(
+ /obj/item/reagent_containers/food,
+ /obj/item/kitchen,
+ /obj/item/storage/box/donkpockets))
+
+/obj/item/storage/bag/tray/pre_attack(atom/A, mob/living/user, params)
+ if(istype(A, /obj/structure/table) && user.a_intent == INTENT_HELP) //I want my tray god damn it
+ if(user.transferItemToLoc(src, get_turf(A)))
+ var/list/click_params = params2list(params)
+ if(!click_params || !click_params["icon-x"] || !click_params["icon-y"])
+ return
+ pixel_x = CLAMP(text2num(click_params["icon-x"]) - 16, -(world.icon_size/2), world.icon_size/2)
+ pixel_y = CLAMP(text2num(click_params["icon-y"]) - 16, -(world.icon_size/2), world.icon_size/2)
+ return
+ ..()
+
+/obj/item/storage/bag/tray/attack(mob/living/M, mob/living/user)
+ . = ..()
+ // Drop all the things. All of them.
+ var/list/obj/item/oldContents = contents.Copy()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.quick_empty()
+ // Make each item scatter a bit
+ for(var/obj/item/I in oldContents)
+ spawn()
+ for(var/i = 1, i <= rand(1,2), i++)
+ if(I)
+ step(I, pick(NORTH,SOUTH,EAST,WEST))
+ sleep(rand(2,4))
+
+ if(prob(50))
+ playsound(M, 'sound/items/trayhit1.ogg', 50, 1)
+ else
+ playsound(M, 'sound/items/trayhit2.ogg', 50, 1)
+
+ if(ishuman(M) || ismonkey(M))
+ if(prob(10))
+ M.Knockdown(40)
+ update_icon()
+
+/obj/item/storage/bag/tray/update_icon()
+ cut_overlays()
+ for(var/obj/item/I in contents)
+ var/mutable_appearance/MA = new (I) //I want my icons god damn it
+ MA.pixel_x = rand(-6, 6)
+ MA.pixel_y = rand(-6, 6)
+ add_overlay(MA)
+
+/obj/item/storage/bag/tray/Entered()
+ . = ..()
+ update_icon()
+
+/obj/item/storage/bag/tray/Exited()
+ . = ..()
+ update_icon()
+
+/*
+ * Chemistry bag
+ */
+
+/obj/item/storage/bag/chemistry
+ name = "chemistry bag"
+ icon = 'icons/obj/chemical.dmi'
+ icon_state = "bag"
+ desc = "A bag for storing pills, patches, and bottles."
+ w_class = WEIGHT_CLASS_TINY
+ resistance_flags = FLAMMABLE
+
+/obj/item/storage/bag/chemistry/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_combined_w_class = 200
+ STR.max_items = 50
+ STR.insert_preposition = "in"
+ STR.can_hold = typecacheof(list(/obj/item/reagent_containers/pill, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/syringe/dart, /obj/item/reagent_containers/chem_pack))
+
+/*
+ * Biowaste bag (mostly for xenobiologists)
+ */
+
+/obj/item/storage/bag/bio
+ name = "bio bag"
+ icon = 'icons/obj/chemical.dmi'
+ icon_state = "biobag"
+ desc = "A bag for the safe transportation and disposal of biowaste and other biological materials."
+ w_class = WEIGHT_CLASS_TINY
+ resistance_flags = FLAMMABLE
+
+/obj/item/storage/bag/bio/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_combined_w_class = 200
+ STR.max_items = 25
+ STR.insert_preposition = "in"
+ STR.can_hold = typecacheof(list(/obj/item/slime_extract, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/blood, /obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/food/snacks/deadmouse, /obj/item/reagent_containers/food/snacks/monkeycube))
+
+/obj/item/storage/bag/bio/holding
+ name = "bio bag of holding"
+ icon = 'icons/obj/chemical.dmi'
+ icon_state = "bspace_biobag"
+ desc = "A bag for the safe transportation and disposal of biowaste and other biological materials."
+
+/obj/item/storage/bag/bio/holding/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_combined_w_class = INFINITY
+ STR.max_items = 100
+
+/*
+ * Construction bag (for engineering, holds stock parts and electronics)
+ */
+
+/obj/item/storage/bag/construction
+ name = "construction bag"
+ icon = 'icons/obj/tools.dmi'
+ icon_state = "construction_bag"
+ desc = "A bag for storing small construction components."
+ w_class = WEIGHT_CLASS_TINY
+ resistance_flags = FLAMMABLE
+
+/obj/item/storage/bag/construction/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.max_combined_w_class = 100
+ STR.max_items = 50
+ STR.max_w_class = WEIGHT_CLASS_SMALL
+ STR.insert_preposition = "in"
+ STR.can_hold = typecacheof(list(/obj/item/stack/ore/bluespace_crystal, /obj/item/assembly, /obj/item/stock_parts, /obj/item/reagent_containers/glass/beaker, /obj/item/stack/cable_coil, /obj/item/circuitboard, /obj/item/electronics))
diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm
index 0b6bc6af..bbfa5c29 100644
--- a/code/game/objects/items/twohanded.dm
+++ b/code/game/objects/items/twohanded.dm
@@ -849,18 +849,20 @@
righthand_file = 'icons/mob/inhands/items_righthand.dmi'
slot_flags = ITEM_SLOT_BELT
w_class = WEIGHT_CLASS_SMALL
- var/datum/component/mobhook
+ var/mob/listeningTo
var/zoom_out_amt = 6
var/zoom_amt = 10
+/obj/item/twohanded/binoculars/Destroy()
+ listeningTo = null
+ return ..()
+
/obj/item/twohanded/binoculars/wield(mob/user)
. = ..()
if(!wielded)
return
- if(QDELETED(mobhook))
- mobhook = user.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/unwield)))
- else
- user.TakeComponent(mobhook)
+ RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/unwield)
+ listeningTo = user
user.visible_message("[user] holds [src] up to [user.p_their()] eyes.","You hold [src] up to your eyes.")
item_state = "binoculars_wielded"
user.regenerate_icons()
@@ -884,7 +886,8 @@
/obj/item/twohanded/binoculars/unwield(mob/user)
. = ..()
- mobhook.RemoveComponent()
+ UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
+ listeningTo = null
user.visible_message("[user] lowers [src].","You lower [src].")
item_state = "binoculars"
user.regenerate_icons()
diff --git a/code/modules/antagonists/disease/disease_mob.dm b/code/modules/antagonists/disease/disease_mob.dm
index 9c1f8720..a9992d05 100644
--- a/code/modules/antagonists/disease/disease_mob.dm
+++ b/code/modules/antagonists/disease/disease_mob.dm
@@ -31,7 +31,6 @@ the new instance inside the host to be updated to the template's stats.
var/browser_open = FALSE
var/mob/living/following_host
- var/datum/component/redirect/move_listener
var/list/disease_instances
var/list/hosts //this list is associative, affected_mob -> disease_instance
var/datum/disease/advance/sentient_disease/disease_template
@@ -260,16 +259,10 @@ the new instance inside the host to be updated to the template's stats.
refresh_adaptation_menu()
/mob/camera/disease/proc/set_following(mob/living/L)
+ if(following_host)
+ UnregisterSignal(following_host, COMSIG_MOVABLE_MOVED)
+ RegisterSignal(L, COMSIG_MOVABLE_MOVED, .proc/follow_mob)
following_host = L
- if(!move_listener)
- move_listener = L.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/follow_mob)))
- else
- if(L)
- L.TakeComponent(move_listener)
- if(QDELING(move_listener))
- move_listener = null
- else
- QDEL_NULL(move_listener)
follow_mob()
/mob/camera/disease/proc/follow_next(reverse = FALSE)
diff --git a/code/modules/antagonists/revenant/revenant_blight.dm b/code/modules/antagonists/revenant/revenant_blight.dm
index 89d8de28..3e05b8d9 100644
--- a/code/modules/antagonists/revenant/revenant_blight.dm
+++ b/code/modules/antagonists/revenant/revenant_blight.dm
@@ -12,8 +12,8 @@
disease_flags = CURABLE
permeability_mod = 1
severity = DISEASE_SEVERITY_DANGEROUS
- var/finalstage = 0 //Because we're spawning off the cure in the final stage, we need to check if we've done the final stage's effects.
- var/datum/mood_event/revenant_blight/depression
+ var/finalstage = FALSE //Because we're spawning off the cure in the final stage, we need to check if we've done the final stage's effects.
+ var/depression = FALSE
/datum/disease/revblight/cure()
if(affected_mob)
@@ -44,7 +44,8 @@
affected_mob.emote("pale")
if(3)
if(!depression)
- depression = SEND_SIGNAL(affected_mob, COMSIG_ADD_MOOD_EVENT, "rev_blight", /datum/mood_event/revenant_blight)
+ SEND_SIGNAL(affected_mob, COMSIG_ADD_MOOD_EVENT, "rev_blight", /datum/mood_event/revenant_blight)
+ depression = TRUE
SEND_SIGNAL(affected_mob, COMSIG_DECREASE_SANITY, 0.12, SANITY_CRAZY)
if(prob(10))
affected_mob.emote(pick("pale","shiver"))
diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm
index a023e0e3..2fbb1095 100644
--- a/code/modules/assembly/infrared.dm
+++ b/code/modules/assembly/infrared.dm
@@ -1,235 +1,239 @@
-/obj/item/assembly/infra
- name = "infrared emitter"
- desc = "Emits a visible or invisible beam and is triggered when the beam is interrupted."
- icon_state = "infrared"
- materials = list(MAT_METAL=1000, MAT_GLASS=500)
- is_position_sensitive = TRUE
-
- var/on = FALSE
- var/visible = FALSE
- var/maxlength = 8
- var/list/obj/effect/beam/i_beam/beams
- var/olddir = 0
- var/datum/component/redirect/listener
- var/hearing_range = 3
-
-/obj/item/assembly/infra/Initialize()
- . = ..()
- beams = list()
- START_PROCESSING(SSobj, src)
-
-/obj/item/assembly/infra/ComponentInitialize()
- . = ..()
- AddComponent(
- /datum/component/simple_rotation,
- ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_FLIP | ROTATION_VERBS,
- null,
- null,
- CALLBACK(src,.proc/after_rotation)
- )
-
-/obj/item/assembly/infra/proc/after_rotation()
- refreshBeam()
-
-/obj/item/assembly/infra/Destroy()
- STOP_PROCESSING(SSobj, src)
- QDEL_NULL(listener)
- QDEL_LIST(beams)
- . = ..()
-
-/obj/item/assembly/infra/examine(mob/user)
- . = ..()
- . += "The infrared trigger is [on?"on":"off"]."
-
-/obj/item/assembly/infra/activate()
- if(!..())
- return FALSE//Cooldown check
- on = !on
- refreshBeam()
- update_icon()
- return TRUE
-
-/obj/item/assembly/infra/toggle_secure()
- secured = !secured
- if(secured)
- START_PROCESSING(SSobj, src)
- refreshBeam()
- else
- QDEL_LIST(beams)
- STOP_PROCESSING(SSobj, src)
- update_icon()
- return secured
-
-/obj/item/assembly/infra/update_icon()
- cut_overlays()
- attached_overlays = list()
- if(on)
- add_overlay("infrared_on")
- attached_overlays += "infrared_on"
- if(visible && secured)
- add_overlay("infrared_visible")
- attached_overlays += "infrared_visible"
-
- if(holder)
- holder.update_icon()
- return
-
-/obj/item/assembly/infra/dropped()
- . = ..()
- if(holder)
- holder_movement() //sync the dir of the device as well if it's contained in a TTV or an assembly holder
- else
- refreshBeam()
-
-/obj/item/assembly/infra/process()
- if(!on || !secured)
- refreshBeam()
- return
-
-/obj/item/assembly/infra/proc/refreshBeam()
- QDEL_LIST(beams)
- if(throwing || !on || !secured)
- return
- if(holder)
- if(holder.master) //incase the sensor is part of an assembly that's contained in another item, such as a single tank bomb
- if(!holder.master.IsSpecialAssembly() || !isturf(holder.master.loc))
- return
- else if(!isturf(holder.loc)) //else just check where the holder is
- return
- else if(!isturf(loc)) //or just where the fuck we are in general
- return
- var/turf/T = get_turf(src)
- var/_dir = dir
- var/turf/_T = get_step(T, _dir)
- if(_T)
- for(var/i in 1 to maxlength)
- var/obj/effect/beam/i_beam/I = new(T)
- if(istype(holder, /obj/item/assembly_holder))
- var/obj/item/assembly_holder/assembly_holder = holder
- I.icon_state = "[initial(I.icon_state)]_[(assembly_holder.a_left == src) ? "l":"r"]" //Sync the offset of the beam with the position of the sensor.
- else if(istype(holder, /obj/item/transfer_valve))
- I.icon_state = "[initial(I.icon_state)]_ttv"
- I.density = TRUE
- if(!I.Move(_T))
- qdel(I)
- switchListener(_T)
- break
- I.density = FALSE
- beams += I
- I.master = src
- I.setDir(_dir)
- I.invisibility = visible? 0 : INVISIBILITY_ABSTRACT
- T = _T
- _T = get_step(_T, _dir)
- CHECK_TICK
-
-/obj/item/assembly/infra/on_detach()
- . = ..()
- if(!.)
- return
- refreshBeam()
-
-/obj/item/assembly/infra/attack_hand()
- . = ..()
- refreshBeam()
-
-/obj/item/assembly/infra/Moved()
- var/t = dir
- . = ..()
- setDir(t)
-
-/obj/item/assembly/infra/throw_at()
- . = ..()
- olddir = dir
-
-/obj/item/assembly/infra/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
- . = ..()
- if(!olddir)
- return
- setDir(olddir)
- olddir = null
-
-/obj/item/assembly/infra/proc/trigger_beam(atom/movable/AM, turf/location)
- refreshBeam()
- switchListener(location)
- if(!secured || !on || next_activate > world.time)
- return FALSE
- pulse(FALSE)
- audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*", null, hearing_range)
- for(var/CHM in get_hearers_in_view(hearing_range, src))
- if(ismob(CHM))
- var/mob/LM = CHM
- LM.playsound_local(get_turf(src), 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
- next_activate = world.time + 30
-
-/obj/item/assembly/infra/proc/switchListener(turf/newloc)
- QDEL_NULL(listener)
- listener = newloc.AddComponent(/datum/component/redirect, list(COMSIG_ATOM_EXITED = CALLBACK(src, .proc/check_exit)))
-
-/obj/item/assembly/infra/proc/check_exit(datum/source, atom/movable/offender)
- if(QDELETED(src))
- return
- if(offender == src || istype(offender,/obj/effect/beam/i_beam))
- return
- if (offender && isitem(offender))
- var/obj/item/I = offender
- if (I.item_flags & ABSTRACT)
- return
- return refreshBeam()
-
-/obj/item/assembly/infra/ui_interact(mob/user)//TODO: change this this to the wire control panel
- . = ..()
- if(is_secured(user))
- user.set_machine(src)
- var/dat = "Infrared Laser"
- dat += "
Status: [on ? "On" : "Off"]"
- dat += "
Visibility: [visible ? "Visible" : "Invisible"]"
- dat += "
Refresh"
- dat += "
Close"
- user << browse(dat, "window=infra")
- onclose(user, "infra")
- return
-
-/obj/item/assembly/infra/Topic(href, href_list)
- ..()
- if(usr.incapacitated() || !in_range(loc, usr))
- usr << browse(null, "window=infra")
- onclose(usr, "infra")
- return
- if(href_list["state"])
- on = !(on)
- update_icon()
- refreshBeam()
- if(href_list["visible"])
- visible = !(visible)
- update_icon()
- refreshBeam()
- if(href_list["close"])
- usr << browse(null, "window=infra")
- return
- if(usr)
- attack_self(usr)
-
-/obj/item/assembly/infra/setDir()
- . = ..()
- refreshBeam()
-
-/***************************IBeam*********************************/
-
-/obj/effect/beam/i_beam
- name = "infrared beam"
- icon = 'icons/obj/projectiles.dmi'
- icon_state = "ibeam"
- var/obj/item/assembly/infra/master
- anchored = TRUE
- density = FALSE
- pass_flags = PASSTABLE|PASSGLASS|PASSGRILLE|LETPASSTHROW
-
-/obj/effect/beam/i_beam/Crossed(atom/movable/AM as mob|obj)
- if(istype(AM, /obj/effect/beam))
- return
- if (isitem(AM))
- var/obj/item/I = AM
- if (I.item_flags & ABSTRACT)
- return
- master.trigger_beam(AM, get_turf(src))
+/obj/item/assembly/infra
+ name = "infrared emitter"
+ desc = "Emits a visible or invisible beam and is triggered when the beam is interrupted."
+ icon_state = "infrared"
+ materials = list(MAT_METAL=1000, MAT_GLASS=500)
+ is_position_sensitive = TRUE
+
+ var/on = FALSE
+ var/visible = FALSE
+ var/maxlength = 8
+ var/list/obj/effect/beam/i_beam/beams
+ var/olddir = 0
+ var/turf/listeningTo
+ var/hearing_range = 3
+
+/obj/item/assembly/infra/Initialize()
+ . = ..()
+ beams = list()
+ START_PROCESSING(SSobj, src)
+
+/obj/item/assembly/infra/ComponentInitialize()
+ . = ..()
+ AddComponent(
+ /datum/component/simple_rotation,
+ ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_FLIP | ROTATION_VERBS,
+ null,
+ null,
+ CALLBACK(src,.proc/after_rotation)
+ )
+
+/obj/item/assembly/infra/proc/after_rotation()
+ refreshBeam()
+
+/obj/item/assembly/infra/Destroy()
+ STOP_PROCESSING(SSobj, src)
+ listeningTo = null
+ QDEL_LIST(beams)
+ . = ..()
+
+/obj/item/assembly/infra/examine(mob/user)
+ . = ..()
+ . += "The infrared trigger is [on?"on":"off"]."
+
+/obj/item/assembly/infra/activate()
+ if(!..())
+ return FALSE//Cooldown check
+ on = !on
+ refreshBeam()
+ update_icon()
+ return TRUE
+
+/obj/item/assembly/infra/toggle_secure()
+ secured = !secured
+ if(secured)
+ START_PROCESSING(SSobj, src)
+ refreshBeam()
+ else
+ QDEL_LIST(beams)
+ STOP_PROCESSING(SSobj, src)
+ update_icon()
+ return secured
+
+/obj/item/assembly/infra/update_icon()
+ cut_overlays()
+ attached_overlays = list()
+ if(on)
+ add_overlay("infrared_on")
+ attached_overlays += "infrared_on"
+ if(visible && secured)
+ add_overlay("infrared_visible")
+ attached_overlays += "infrared_visible"
+
+ if(holder)
+ holder.update_icon()
+ return
+
+/obj/item/assembly/infra/dropped()
+ . = ..()
+ if(holder)
+ holder_movement() //sync the dir of the device as well if it's contained in a TTV or an assembly holder
+ else
+ refreshBeam()
+
+/obj/item/assembly/infra/process()
+ if(!on || !secured)
+ refreshBeam()
+ return
+
+/obj/item/assembly/infra/proc/refreshBeam()
+ QDEL_LIST(beams)
+ if(throwing || !on || !secured)
+ return
+ if(holder)
+ if(holder.master) //incase the sensor is part of an assembly that's contained in another item, such as a single tank bomb
+ if(!holder.master.IsSpecialAssembly() || !isturf(holder.master.loc))
+ return
+ else if(!isturf(holder.loc)) //else just check where the holder is
+ return
+ else if(!isturf(loc)) //or just where the fuck we are in general
+ return
+ var/turf/T = get_turf(src)
+ var/_dir = dir
+ var/turf/_T = get_step(T, _dir)
+ if(_T)
+ for(var/i in 1 to maxlength)
+ var/obj/effect/beam/i_beam/I = new(T)
+ if(istype(holder, /obj/item/assembly_holder))
+ var/obj/item/assembly_holder/assembly_holder = holder
+ I.icon_state = "[initial(I.icon_state)]_[(assembly_holder.a_left == src) ? "l":"r"]" //Sync the offset of the beam with the position of the sensor.
+ else if(istype(holder, /obj/item/transfer_valve))
+ I.icon_state = "[initial(I.icon_state)]_ttv"
+ I.density = TRUE
+ if(!I.Move(_T))
+ qdel(I)
+ switchListener(_T)
+ break
+ I.density = FALSE
+ beams += I
+ I.master = src
+ I.setDir(_dir)
+ I.invisibility = visible? 0 : INVISIBILITY_ABSTRACT
+ T = _T
+ _T = get_step(_T, _dir)
+ CHECK_TICK
+
+/obj/item/assembly/infra/on_detach()
+ . = ..()
+ if(!.)
+ return
+ refreshBeam()
+
+/obj/item/assembly/infra/attack_hand()
+ . = ..()
+ refreshBeam()
+
+/obj/item/assembly/infra/Moved()
+ var/t = dir
+ . = ..()
+ setDir(t)
+
+/obj/item/assembly/infra/throw_at()
+ . = ..()
+ olddir = dir
+
+/obj/item/assembly/infra/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
+ . = ..()
+ if(!olddir)
+ return
+ setDir(olddir)
+ olddir = null
+
+/obj/item/assembly/infra/proc/trigger_beam(atom/movable/AM, turf/location)
+ refreshBeam()
+ switchListener(location)
+ if(!secured || !on || next_activate > world.time)
+ return FALSE
+ pulse(FALSE)
+ audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*", null, hearing_range)
+ for(var/CHM in get_hearers_in_view(hearing_range, src))
+ if(ismob(CHM))
+ var/mob/LM = CHM
+ LM.playsound_local(get_turf(src), 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
+ next_activate = world.time + 30
+
+/obj/item/assembly/infra/proc/switchListener(turf/newloc)
+ if(listeningTo == newloc)
+ return
+ if(listeningTo)
+ UnregisterSignal(listeningTo, COMSIG_ATOM_EXITED)
+ RegisterSignal(newloc, COMSIG_ATOM_EXITED, .proc/check_exit)
+ listeningTo = newloc
+
+/obj/item/assembly/infra/proc/check_exit(datum/source, atom/movable/offender)
+ if(QDELETED(src))
+ return
+ if(offender == src || istype(offender,/obj/effect/beam/i_beam))
+ return
+ if (offender && isitem(offender))
+ var/obj/item/I = offender
+ if (I.item_flags & ABSTRACT)
+ return
+ return refreshBeam()
+
+/obj/item/assembly/infra/ui_interact(mob/user)//TODO: change this this to the wire control panel
+ . = ..()
+ if(is_secured(user))
+ user.set_machine(src)
+ var/dat = "Infrared Laser"
+ dat += "
Status: [on ? "On" : "Off"]"
+ dat += "
Visibility: [visible ? "Visible" : "Invisible"]"
+ dat += "
Refresh"
+ dat += "
Close"
+ user << browse(dat, "window=infra")
+ onclose(user, "infra")
+ return
+
+/obj/item/assembly/infra/Topic(href, href_list)
+ ..()
+ if(usr.incapacitated() || !in_range(loc, usr))
+ usr << browse(null, "window=infra")
+ onclose(usr, "infra")
+ return
+ if(href_list["state"])
+ on = !(on)
+ update_icon()
+ refreshBeam()
+ if(href_list["visible"])
+ visible = !(visible)
+ update_icon()
+ refreshBeam()
+ if(href_list["close"])
+ usr << browse(null, "window=infra")
+ return
+ if(usr)
+ attack_self(usr)
+
+/obj/item/assembly/infra/setDir()
+ . = ..()
+ refreshBeam()
+
+/***************************IBeam*********************************/
+
+/obj/effect/beam/i_beam
+ name = "infrared beam"
+ icon = 'icons/obj/projectiles.dmi'
+ icon_state = "ibeam"
+ var/obj/item/assembly/infra/master
+ anchored = TRUE
+ density = FALSE
+ pass_flags = PASSTABLE|PASSGLASS|PASSGRILLE|LETPASSTHROW
+
+/obj/effect/beam/i_beam/Crossed(atom/movable/AM as mob|obj)
+ if(istype(AM, /obj/effect/beam))
+ return
+ if (isitem(AM))
+ var/obj/item/I = AM
+ if (I.item_flags & ABSTRACT)
+ return
+ master.trigger_beam(AM, get_turf(src))
diff --git a/code/modules/clothing/gloves/_gloves.dm b/code/modules/clothing/gloves/_gloves.dm
index 00cb9b1e..c7067c64 100644
--- a/code/modules/clothing/gloves/_gloves.dm
+++ b/code/modules/clothing/gloves/_gloves.dm
@@ -1,43 +1,43 @@
-/obj/item/clothing/gloves
- name = "gloves"
- gender = PLURAL //Carn: for grammarically correct text-parsing
- w_class = WEIGHT_CLASS_SMALL
- icon = 'icons/obj/clothing/gloves.dmi'
- siemens_coefficient = 0.5
- body_parts_covered = HANDS
- slot_flags = ITEM_SLOT_GLOVES
- attack_verb = list("challenged")
- var/transfer_prints = FALSE
- var/transfer_blood = 0
- strip_delay = 20
- equip_delay_other = 40
-
-/obj/item/clothing/gloves/ComponentInitialize()
- . = ..()
- AddComponent(/datum/component/redirect, list(COMSIG_COMPONENT_CLEAN_ACT = CALLBACK(src, /obj/item/clothing/gloves/clean_blood)))
-
-/obj/item/clothing/gloves/clean_blood(datum/source, strength)
- . = ..()
- transfer_blood = 0
-
-/obj/item/clothing/gloves/suicide_act(mob/living/carbon/user)
- user.visible_message("\the [src] are forcing [user]'s hands around [user.p_their()] neck! It looks like the gloves are possessed!")
- return OXYLOSS
-
-/obj/item/clothing/gloves/worn_overlays(isinhands = FALSE)
- . = list()
- if(!isinhands)
- if(damaged_clothes)
- . += mutable_appearance('icons/effects/item_damage.dmi', "damagedgloves")
- if(blood_DNA)
- . += mutable_appearance('icons/effects/blood.dmi', "bloodyhands", color = blood_DNA_to_color())
-
-/obj/item/clothing/gloves/update_clothes_damaged_state(damaging = TRUE)
- ..()
- if(ismob(loc))
- var/mob/M = loc
- M.update_inv_gloves()
-
-// Called just before an attack_hand(), in mob/UnarmedAttack()
-/obj/item/clothing/gloves/proc/Touch(atom/A, proximity)
- return FALSE // return TRUE to cancel attack_hand()
+/obj/item/clothing/gloves
+ name = "gloves"
+ gender = PLURAL //Carn: for grammarically correct text-parsing
+ w_class = WEIGHT_CLASS_SMALL
+ icon = 'icons/obj/clothing/gloves.dmi'
+ siemens_coefficient = 0.5
+ body_parts_covered = HANDS
+ slot_flags = ITEM_SLOT_GLOVES
+ attack_verb = list("challenged")
+ var/transfer_prints = FALSE
+ var/transfer_blood = 0
+ strip_delay = 20
+ equip_delay_other = 40
+
+/obj/item/clothing/gloves/ComponentInitialize()
+ . = ..()
+ RegisterSignal(src, COMSIG_COMPONENT_CLEAN_ACT, /obj/item/clothing/gloves/clean_blood)
+
+/obj/item/clothing/gloves/clean_blood(datum/source, strength)
+ . = ..()
+ transfer_blood = 0
+
+/obj/item/clothing/gloves/suicide_act(mob/living/carbon/user)
+ user.visible_message("\the [src] are forcing [user]'s hands around [user.p_their()] neck! It looks like the gloves are possessed!")
+ return OXYLOSS
+
+/obj/item/clothing/gloves/worn_overlays(isinhands = FALSE)
+ . = list()
+ if(!isinhands)
+ if(damaged_clothes)
+ . += mutable_appearance('icons/effects/item_damage.dmi', "damagedgloves")
+ if(blood_DNA)
+ . += mutable_appearance('icons/effects/blood.dmi', "bloodyhands", color = blood_DNA_to_color())
+
+/obj/item/clothing/gloves/update_clothes_damaged_state(damaging = TRUE)
+ ..()
+ if(ismob(loc))
+ var/mob/M = loc
+ M.update_inv_gloves()
+
+// Called just before an attack_hand(), in mob/UnarmedAttack()
+/obj/item/clothing/gloves/proc/Touch(atom/A, proximity)
+ return FALSE // return TRUE to cancel attack_hand()
diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm
index b420ee67..7eaf05f2 100644
--- a/code/modules/clothing/shoes/_shoes.dm
+++ b/code/modules/clothing/shoes/_shoes.dm
@@ -1,113 +1,113 @@
-/obj/item/clothing/shoes
- name = "shoes"
- icon = 'icons/obj/clothing/shoes.dmi'
- desc = "Comfortable-looking shoes."
- gender = PLURAL //Carn: for grammarically correct text-parsing
- var/chained = 0
-
- body_parts_covered = FEET
- slot_flags = ITEM_SLOT_FEET
-
- permeability_coefficient = 0.5
- slowdown = SHOES_SLOWDOWN
- var/blood_state = BLOOD_STATE_NOT_BLOODY
- var/list/bloody_shoes = list(BLOOD_STATE_BLOOD = 0, BLOOD_STATE_OIL = 0, BLOOD_STATE_NOT_BLOODY = 0)
- var/offset = 0
- var/equipped_before_drop = FALSE
-
- //CITADEL EDIT Enables digitigrade shoe styles
- var/adjusted = NORMAL_STYLE
- mutantrace_variation = MUTANTRACE_VARIATION
- var/last_bloodtype = "" //used to track the last bloodtype to have graced these shoes; makes for better performing footprint shenanigans
- var/last_blood_DNA = "" //same as last one
-
-/obj/item/clothing/shoes/ComponentInitialize()
- . = ..()
- AddComponent(/datum/component/redirect, list(COMSIG_COMPONENT_CLEAN_ACT = CALLBACK(src, /obj/item/clothing/shoes/clean_blood)))
-
-/obj/item/clothing/shoes/suicide_act(mob/living/carbon/user)
- if(rand(2)>1)
- user.visible_message("[user] begins tying \the [src] up waaay too tightly! It looks like [user.p_theyre()] trying to commit suicide!")
- var/obj/item/bodypart/l_leg = user.get_bodypart(BODY_ZONE_L_LEG)
- var/obj/item/bodypart/r_leg = user.get_bodypart(BODY_ZONE_R_LEG)
- if(l_leg)
- l_leg.dismember()
- playsound(user,pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg') ,50, 1, -1)
- if(r_leg)
- r_leg.dismember()
- playsound(user,pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg') ,50, 1, -1)
- return BRUTELOSS
- else//didnt realize this suicide act existed (was in miscellaneous.dm) and didnt want to remove it, so made it a 50/50 chance. Why not!
- user.visible_message("[user] is bashing [user.p_their()] own head in with [src]! Ain't that a kick in the head?")
- for(var/i = 0, i < 3, i++)
- sleep(3)
- playsound(user, 'sound/weapons/genhit2.ogg', 50, 1)
- return(BRUTELOSS)
-
-/obj/item/clothing/shoes/transfer_blood_dna(list/blood_dna, diseases)
- ..()
- if(blood_dna.len)
- last_bloodtype = blood_dna[blood_dna[blood_dna.len]]//trust me this works
- last_blood_DNA = blood_dna[blood_dna.len]
-
-/obj/item/clothing/shoes/worn_overlays(isinhands = FALSE)
- . = list()
- if(!isinhands)
- var/bloody = FALSE
- if(blood_DNA)
- bloody = TRUE
- else
- bloody = bloody_shoes[BLOOD_STATE_BLOOD]
-
- if(damaged_clothes)
- . += mutable_appearance('icons/effects/item_damage.dmi', "damagedshoe")
- if(bloody)
- if(adjusted == NORMAL_STYLE)
- . += mutable_appearance('icons/effects/blood.dmi', "shoeblood", color = blood_DNA_to_color())
- else
- . += mutable_appearance('modular_citadel/icons/mob/digishoes.dmi', "shoeblood", color = blood_DNA_to_color())
-
-/obj/item/clothing/shoes/equipped(mob/user, slot)
- . = ..()
-
- if(mutantrace_variation && ishuman(user))
- var/mob/living/carbon/human/H = user
- if(DIGITIGRADE in H.dna.species.species_traits)
- adjusted = ALT_STYLE
- H.update_inv_shoes()
- else if(adjusted == ALT_STYLE)
- adjusted = NORMAL_STYLE
- H.update_inv_shoes()
-
- if(offset && slot_flags & slotdefine2slotbit(slot))
- user.pixel_y += offset
- worn_y_dimension -= (offset * 2)
- user.update_inv_shoes()
- equipped_before_drop = TRUE
-
-/obj/item/clothing/shoes/proc/restore_offsets(mob/user)
- equipped_before_drop = FALSE
- user.pixel_y -= offset
- worn_y_dimension = world.icon_size
-
-/obj/item/clothing/shoes/dropped(mob/user)
- if(offset && equipped_before_drop)
- restore_offsets(user)
- . = ..()
-
-/obj/item/clothing/shoes/update_clothes_damaged_state(damaging = TRUE)
- ..()
- if(ismob(loc))
- var/mob/M = loc
- M.update_inv_shoes()
-
-/obj/item/clothing/shoes/clean_blood(datum/source, strength)
- . = ..()
- bloody_shoes = list(BLOOD_STATE_BLOOD = 0, BLOOD_STATE_OIL = 0, BLOOD_STATE_NOT_BLOODY = 0)
- blood_state = BLOOD_STATE_NOT_BLOODY
- if(ismob(loc))
- var/mob/M = loc
- M.update_inv_shoes()
-
-/obj/item/proc/negates_gravity()
- return FALSE
+/obj/item/clothing/shoes
+ name = "shoes"
+ icon = 'icons/obj/clothing/shoes.dmi'
+ desc = "Comfortable-looking shoes."
+ gender = PLURAL //Carn: for grammarically correct text-parsing
+ var/chained = 0
+
+ body_parts_covered = FEET
+ slot_flags = ITEM_SLOT_FEET
+
+ permeability_coefficient = 0.5
+ slowdown = SHOES_SLOWDOWN
+ var/blood_state = BLOOD_STATE_NOT_BLOODY
+ var/list/bloody_shoes = list(BLOOD_STATE_BLOOD = 0, BLOOD_STATE_OIL = 0, BLOOD_STATE_NOT_BLOODY = 0)
+ var/offset = 0
+ var/equipped_before_drop = FALSE
+
+ //CITADEL EDIT Enables digitigrade shoe styles
+ var/adjusted = NORMAL_STYLE
+ mutantrace_variation = MUTANTRACE_VARIATION
+ var/last_bloodtype = "" //used to track the last bloodtype to have graced these shoes; makes for better performing footprint shenanigans
+ var/last_blood_DNA = "" //same as last one
+
+/obj/item/clothing/shoes/ComponentInitialize()
+ . = ..()
+ RegisterSignal(src, COMSIG_COMPONENT_CLEAN_ACT, /obj/item/clothing/shoes/clean_blood)
+
+/obj/item/clothing/shoes/suicide_act(mob/living/carbon/user)
+ if(rand(2)>1)
+ user.visible_message("[user] begins tying \the [src] up waaay too tightly! It looks like [user.p_theyre()] trying to commit suicide!")
+ var/obj/item/bodypart/l_leg = user.get_bodypart(BODY_ZONE_L_LEG)
+ var/obj/item/bodypart/r_leg = user.get_bodypart(BODY_ZONE_R_LEG)
+ if(l_leg)
+ l_leg.dismember()
+ playsound(user,pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg') ,50, 1, -1)
+ if(r_leg)
+ r_leg.dismember()
+ playsound(user,pick('sound/misc/desceration-01.ogg','sound/misc/desceration-02.ogg','sound/misc/desceration-01.ogg') ,50, 1, -1)
+ return BRUTELOSS
+ else//didnt realize this suicide act existed (was in miscellaneous.dm) and didnt want to remove it, so made it a 50/50 chance. Why not!
+ user.visible_message("[user] is bashing [user.p_their()] own head in with [src]! Ain't that a kick in the head?")
+ for(var/i = 0, i < 3, i++)
+ sleep(3)
+ playsound(user, 'sound/weapons/genhit2.ogg', 50, 1)
+ return(BRUTELOSS)
+
+/obj/item/clothing/shoes/transfer_blood_dna(list/blood_dna, diseases)
+ ..()
+ if(blood_dna.len)
+ last_bloodtype = blood_dna[blood_dna[blood_dna.len]]//trust me this works
+ last_blood_DNA = blood_dna[blood_dna.len]
+
+/obj/item/clothing/shoes/worn_overlays(isinhands = FALSE)
+ . = list()
+ if(!isinhands)
+ var/bloody = FALSE
+ if(blood_DNA)
+ bloody = TRUE
+ else
+ bloody = bloody_shoes[BLOOD_STATE_BLOOD]
+
+ if(damaged_clothes)
+ . += mutable_appearance('icons/effects/item_damage.dmi', "damagedshoe")
+ if(bloody)
+ if(adjusted == NORMAL_STYLE)
+ . += mutable_appearance('icons/effects/blood.dmi', "shoeblood", color = blood_DNA_to_color())
+ else
+ . += mutable_appearance('modular_citadel/icons/mob/digishoes.dmi', "shoeblood", color = blood_DNA_to_color())
+
+/obj/item/clothing/shoes/equipped(mob/user, slot)
+ . = ..()
+
+ if(mutantrace_variation && ishuman(user))
+ var/mob/living/carbon/human/H = user
+ if(DIGITIGRADE in H.dna.species.species_traits)
+ adjusted = ALT_STYLE
+ H.update_inv_shoes()
+ else if(adjusted == ALT_STYLE)
+ adjusted = NORMAL_STYLE
+ H.update_inv_shoes()
+
+ if(offset && slot_flags & slotdefine2slotbit(slot))
+ user.pixel_y += offset
+ worn_y_dimension -= (offset * 2)
+ user.update_inv_shoes()
+ equipped_before_drop = TRUE
+
+/obj/item/clothing/shoes/proc/restore_offsets(mob/user)
+ equipped_before_drop = FALSE
+ user.pixel_y -= offset
+ worn_y_dimension = world.icon_size
+
+/obj/item/clothing/shoes/dropped(mob/user)
+ if(offset && equipped_before_drop)
+ restore_offsets(user)
+ . = ..()
+
+/obj/item/clothing/shoes/update_clothes_damaged_state(damaging = TRUE)
+ ..()
+ if(ismob(loc))
+ var/mob/M = loc
+ M.update_inv_shoes()
+
+/obj/item/clothing/shoes/clean_blood(datum/source, strength)
+ . = ..()
+ bloody_shoes = list(BLOOD_STATE_BLOOD = 0, BLOOD_STATE_OIL = 0, BLOOD_STATE_NOT_BLOODY = 0)
+ blood_state = BLOOD_STATE_NOT_BLOODY
+ if(ismob(loc))
+ var/mob/M = loc
+ M.update_inv_shoes()
+
+/obj/item/proc/negates_gravity()
+ return FALSE
diff --git a/code/modules/clothing/spacesuits/hardsuit.dm b/code/modules/clothing/spacesuits/hardsuit.dm
index 5893e970..83315a40 100644
--- a/code/modules/clothing/spacesuits/hardsuit.dm
+++ b/code/modules/clothing/spacesuits/hardsuit.dm
@@ -604,7 +604,6 @@
armor = list("melee" = 30, "bullet" = 5, "laser" = 5, "energy" = 0, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 75)
item_color = "ancient"
resistance_flags = FIRE_PROOF
- var/datum/component/mobhook
/obj/item/clothing/suit/space/hardsuit/ancient
name = "prototype RIG hardsuit"
@@ -616,7 +615,7 @@
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ancient
resistance_flags = FIRE_PROOF
var/footstep = 1
- var/datum/component/mobhook
+ var/mob/listeningTo
/obj/item/clothing/suit/space/hardsuit/ancient/mason
name = "M.A.S.O.N RIG"
@@ -673,20 +672,24 @@
/obj/item/clothing/suit/space/hardsuit/ancient/equipped(mob/user, slot)
. = ..()
- if (slot == SLOT_WEAR_SUIT)
- if (mobhook && mobhook.parent != user)
- QDEL_NULL(mobhook)
- if (!mobhook)
- mobhook = user.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_mob_move)))
- else
- QDEL_NULL(mobhook)
+ if(slot != SLOT_WEAR_SUIT)
+ if(listeningTo)
+ UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
+ return
+ if(listeningTo == user)
+ return
+ if(listeningTo)
+ UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
+ RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/on_mob_move)
+ listeningTo = user
/obj/item/clothing/suit/space/hardsuit/ancient/dropped()
. = ..()
- QDEL_NULL(mobhook)
+ if(listeningTo)
+ UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
/obj/item/clothing/suit/space/hardsuit/ancient/Destroy()
- QDEL_NULL(mobhook) // mobhook is not our component
+ listeningTo = null
return ..()
/////////////SHIELDED//////////////////////////////////
diff --git a/code/modules/fields/fields.dm b/code/modules/fields/fields.dm
index 5e34c934..5b0b4bc1 100644
--- a/code/modules/fields/fields.dm
+++ b/code/modules/fields/fields.dm
@@ -283,7 +283,7 @@
var/field_type = /datum/proximity_monitor/advanced/debug
var/operating = FALSE
var/datum/proximity_monitor/advanced/current = null
- var/datum/component/mobhook
+ var/mob/listeningTo
/obj/item/multitool/field_debug/Initialize()
. = ..()
@@ -292,7 +292,7 @@
/obj/item/multitool/field_debug/Destroy()
STOP_PROCESSING(SSobj, src)
QDEL_NULL(current)
- QDEL_NULL(mobhook)
+ listeningTo = null
return ..()
/obj/item/multitool/field_debug/proc/setup_debug_field()
@@ -303,16 +303,20 @@
/obj/item/multitool/field_debug/attack_self(mob/user)
operating = !operating
to_chat(user, "You turn [src] [operating? "on":"off"].")
- QDEL_NULL(mobhook)
+ UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
+ listeningTo = null
if(!istype(current) && operating)
- mobhook = user.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_mob_move)))
+ RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/on_mob_move)
+ listeningTo = user
setup_debug_field()
else if(!operating)
QDEL_NULL(current)
/obj/item/multitool/field_debug/dropped()
. = ..()
- QDEL_NULL(mobhook)
+ if(listeningTo)
+ UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
+ listeningTo = null
/obj/item/multitool/field_debug/proc/on_mob_move()
check_turf(get_turf(src))
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 60113b98..798a73f4 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -29,7 +29,7 @@
. = ..()
- AddComponent(/datum/component/redirect, list(COMSIG_COMPONENT_CLEAN_ACT = CALLBACK(src, /mob/living/carbon/human/clean_blood)))
+ RegisterSignal(src, COMSIG_COMPONENT_CLEAN_ACT, /mob/living/carbon/human/clean_blood)
/mob/living/carbon/human/ComponentInitialize()
diff --git a/code/modules/projectiles/guns/misc/beam_rifle.dm b/code/modules/projectiles/guns/misc/beam_rifle.dm
index bcf44805..b6e6a01d 100644
--- a/code/modules/projectiles/guns/misc/beam_rifle.dm
+++ b/code/modules/projectiles/guns/misc/beam_rifle.dm
@@ -1,567 +1,570 @@
-
-#define ZOOM_LOCK_AUTOZOOM_FREEMOVE 0
-#define ZOOM_LOCK_AUTOZOOM_ANGLELOCK 1
-#define ZOOM_LOCK_CENTER_VIEW 2
-#define ZOOM_LOCK_OFF 3
-
-#define AUTOZOOM_PIXEL_STEP_FACTOR 48
-
-#define AIMING_BEAM_ANGLE_CHANGE_THRESHOLD 0.1
-
-/obj/item/gun/energy/beam_rifle
- name = "particle acceleration rifle"
- desc = "An energy-based anti material marksman rifle that uses highly charged particle beams moving at extreme velocities to decimate whatever is unfortunate enough to be targeted by one. \
- Hold down left click while scoped to aim, when weapon is fully aimed (Tracer goes from red to green as it charges), release to fire. Moving while aiming or \
- changing where you're pointing at while aiming will delay the aiming process depending on how much you changed."
- icon = 'icons/obj/guns/energy.dmi'
- icon_state = "esniper"
- item_state = "esniper"
- fire_sound = 'sound/weapons/beam_sniper.ogg'
- slot_flags = ITEM_SLOT_BACK
- force = 15
- materials = list()
- recoil = 4
- ammo_x_offset = 3
- ammo_y_offset = 3
- modifystate = FALSE
- weapon_weight = WEAPON_HEAVY
- w_class = WEIGHT_CLASS_BULKY
- ammo_type = list(/obj/item/ammo_casing/energy/beam_rifle/hitscan)
- cell_type = /obj/item/stock_parts/cell/beam_rifle
- canMouseDown = TRUE
- //Cit changes: beam rifle stats.
- slowdown = 1
- item_flags = NO_MAT_REDEMPTION | SLOWS_WHILE_IN_HAND | NEEDS_PERMIT
- pin = null
- var/aiming = FALSE
- var/aiming_time = 14
- var/aiming_time_fire_threshold = 5
- var/aiming_time_left = 14
- var/aiming_time_increase_user_movement = 7
- var/aiming_time_increase_angle_multiplier = 0.30
- var/last_process = 0
-
- var/lastangle = 0
- var/aiming_lastangle = 0
- var/mob/current_user = null
- var/list/obj/effect/projectile/tracer/current_tracers
-
- var/structure_piercing = 1
- var/structure_bleed_coeff = 0.7
- var/wall_pierce_amount = 0
- var/wall_devastate = 0
- var/aoe_structure_range = 1
- var/aoe_structure_damage = 35
- var/aoe_fire_range = 1
- var/aoe_fire_chance = 100
- var/aoe_mob_range = 1
- var/aoe_mob_damage = 20
- var/impact_structure_damage = 75
- var/projectile_damage = 40
- var/projectile_stun = 0
- var/projectile_setting_pierce = TRUE
- var/delay = 30
- var/lastfire = 0
-
- //ZOOMING
- var/zoom_current_view_increase = 0
- var/zoom_target_view_increase = 10
- var/zooming = FALSE
- var/zoom_lock = ZOOM_LOCK_OFF
- var/zooming_angle
- var/current_zoom_x = 0
- var/current_zoom_y = 0
-
- var/static/image/charged_overlay = image(icon = 'icons/obj/guns/energy.dmi', icon_state = "esniper_charged")
- var/static/image/drained_overlay = image(icon = 'icons/obj/guns/energy.dmi', icon_state = "esniper_empty")
-
- var/datum/action/item_action/zoom_lock_action/zoom_lock_action
- var/datum/component/mobhook
-
-/obj/item/gun/energy/beam_rifle/debug
- delay = 0
- cell_type = /obj/item/stock_parts/cell/infinite
- aiming_time = 0
- recoil = 0
- pin = /obj/item/firing_pin
-
-/obj/item/gun/energy/beam_rifle/equipped(mob/user)
- set_user(user)
- . = ..()
-
-/obj/item/gun/energy/beam_rifle/pickup(mob/user)
- set_user(user)
- . = ..()
-
-/obj/item/gun/energy/beam_rifle/dropped(mob/user)
- set_user()
- . = ..()
-
-/obj/item/gun/energy/beam_rifle/ui_action_click(owner, action)
- if(istype(action, /datum/action/item_action/zoom_lock_action))
- zoom_lock++
- if(zoom_lock > 3)
- zoom_lock = 0
- switch(zoom_lock)
- if(ZOOM_LOCK_AUTOZOOM_FREEMOVE)
- to_chat(owner, "You switch [src]'s zooming processor to free directional.")
- if(ZOOM_LOCK_AUTOZOOM_ANGLELOCK)
- to_chat(owner, "You switch [src]'s zooming processor to locked directional.")
- if(ZOOM_LOCK_CENTER_VIEW)
- to_chat(owner, "You switch [src]'s zooming processor to center mode.")
- if(ZOOM_LOCK_OFF)
- to_chat(owner, "You disable [src]'s zooming system.")
- reset_zooming()
-
-/obj/item/gun/energy/beam_rifle/proc/set_autozoom_pixel_offsets_immediate(current_angle)
- if(zoom_lock == ZOOM_LOCK_CENTER_VIEW || zoom_lock == ZOOM_LOCK_OFF)
- return
- current_zoom_x = sin(current_angle) + sin(current_angle) * AUTOZOOM_PIXEL_STEP_FACTOR * zoom_current_view_increase
- current_zoom_y = cos(current_angle) + cos(current_angle) * AUTOZOOM_PIXEL_STEP_FACTOR * zoom_current_view_increase
-
-/obj/item/gun/energy/beam_rifle/proc/handle_zooming()
- if(!zooming || !check_user())
- return
- set_autozoom_pixel_offsets_immediate(zooming_angle)
-
-/obj/item/gun/energy/beam_rifle/proc/start_zooming()
- if(zoom_lock == ZOOM_LOCK_OFF)
- return
- zooming = TRUE
- current_user.client.change_view(world.view + zoom_target_view_increase)
- zoom_current_view_increase = zoom_target_view_increase
-
-/obj/item/gun/energy/beam_rifle/proc/stop_zooming(mob/user)
- if(zooming)
- zooming = FALSE
- reset_zooming(user)
-
-/obj/item/gun/energy/beam_rifle/proc/reset_zooming(mob/user)
- if(!user)
- user = current_user
- if(!user || !user.client)
- return FALSE
- animate(user.client, pixel_x = 0, pixel_y = 0, 0, FALSE, LINEAR_EASING, ANIMATION_END_NOW)
- zoom_current_view_increase = 0
- user.client.change_view(CONFIG_GET(string/default_view))
- zooming_angle = 0
- current_zoom_x = 0
- current_zoom_y = 0
-
-/obj/item/gun/energy/beam_rifle/update_icon()
- cut_overlays()
- var/obj/item/ammo_casing/energy/primary_ammo = ammo_type[1]
- if(!QDELETED(cell) && (cell.charge > primary_ammo.e_cost))
- add_overlay(charged_overlay)
- else
- add_overlay(drained_overlay)
-
-/obj/item/gun/energy/beam_rifle/attack_self(mob/user)
- projectile_setting_pierce = !projectile_setting_pierce
- to_chat(user, "You set \the [src] to [projectile_setting_pierce? "pierce":"impact"] mode.")
- aiming_beam()
-
-/obj/item/gun/energy/beam_rifle/Initialize()
- . = ..()
- fire_delay = delay
- current_tracers = list()
- START_PROCESSING(SSfastprocess, src)
- zoom_lock_action = new(src)
-
-/obj/item/gun/energy/beam_rifle/Destroy()
- STOP_PROCESSING(SSfastprocess, src)
- set_user(null)
- QDEL_LIST(current_tracers)
- QDEL_NULL(mobhook)
- return ..()
-
-/obj/item/gun/energy/beam_rifle/emp_act(severity)
- . = ..()
- if(. & EMP_PROTECT_SELF)
- return
- chambered = null
- recharge_newshot()
-
-/obj/item/gun/energy/beam_rifle/proc/aiming_beam(force_update = FALSE)
- var/diff = abs(aiming_lastangle - lastangle)
- check_user()
- if(diff < AIMING_BEAM_ANGLE_CHANGE_THRESHOLD && !force_update)
- return
- aiming_lastangle = lastangle
- var/obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam/P = new
- P.gun = src
- P.wall_pierce_amount = wall_pierce_amount
- P.structure_pierce_amount = structure_piercing
- P.do_pierce = projectile_setting_pierce
- if(aiming_time)
- var/percent = ((100/aiming_time)*aiming_time_left)
- P.color = rgb(255 * percent,255 * ((100 - percent) / 100),0)
- else
- P.color = rgb(0, 255, 0)
- var/turf/curloc = get_turf(src)
- var/turf/targloc = get_turf(current_user.client.mouseObject)
- if(!istype(targloc))
- if(!istype(curloc))
- return
- targloc = get_turf_in_angle(lastangle, curloc, 10)
- P.preparePixelProjectile(targloc, current_user, current_user.client.mouseParams, 0)
- P.fire(lastangle)
-
-/obj/item/gun/energy/beam_rifle/process()
- if(!aiming)
- last_process = world.time
- return
- check_user()
- handle_zooming()
- aiming_time_left = max(0, aiming_time_left - (world.time - last_process))
- aiming_beam(TRUE)
- last_process = world.time
-
-/obj/item/gun/energy/beam_rifle/proc/check_user(automatic_cleanup = TRUE)
- if(!istype(current_user) || !isturf(current_user.loc) || !(src in current_user.held_items) || current_user.incapacitated()) //Doesn't work if you're not holding it!
- if(automatic_cleanup)
- stop_aiming()
- set_user(null)
- return FALSE
- return TRUE
-
-/obj/item/gun/energy/beam_rifle/proc/process_aim()
- if(istype(current_user) && current_user.client && current_user.client.mouseParams)
- var/angle = mouse_angle_from_client(current_user.client)
- current_user.setDir(angle2dir_cardinal(angle))
- var/difference = abs(closer_angle_difference(lastangle, angle))
- delay_penalty(difference * aiming_time_increase_angle_multiplier)
- lastangle = angle
-
-/obj/item/gun/energy/beam_rifle/proc/on_mob_move()
- check_user()
- if(aiming)
- delay_penalty(aiming_time_increase_user_movement)
- process_aim()
- aiming_beam(TRUE)
-
-/obj/item/gun/energy/beam_rifle/proc/start_aiming()
- aiming_time_left = aiming_time
- aiming = TRUE
- process_aim()
- aiming_beam(TRUE)
- zooming_angle = lastangle
- start_zooming()
-
-/obj/item/gun/energy/beam_rifle/proc/stop_aiming(mob/user)
- set waitfor = FALSE
- aiming_time_left = aiming_time
- aiming = FALSE
- QDEL_LIST(current_tracers)
- stop_zooming(user)
-
-/obj/item/gun/energy/beam_rifle/proc/set_user(mob/user)
- if(user == current_user)
- return
- stop_aiming(current_user)
- QDEL_NULL(mobhook)
- if(istype(current_user))
- LAZYREMOVE(current_user.mousemove_intercept_objects, src)
- current_user = null
- if(istype(user))
- current_user = user
- LAZYOR(current_user.mousemove_intercept_objects, src)
- mobhook = user.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_mob_move)))
-
-/obj/item/gun/energy/beam_rifle/onMouseDrag(src_object, over_object, src_location, over_location, params, mob)
- if(aiming)
- process_aim()
- aiming_beam()
- if(zoom_lock == ZOOM_LOCK_AUTOZOOM_FREEMOVE)
- zooming_angle = lastangle
- set_autozoom_pixel_offsets_immediate(zooming_angle)
- return ..()
-
-/obj/item/gun/energy/beam_rifle/onMouseDown(object, location, params, mob/mob)
- if(istype(mob))
- set_user(mob)
- if(istype(object, /obj/screen) && !istype(object, /obj/screen/click_catcher))
- return
- if((object in mob.contents) || (object == mob))
- return
- start_aiming()
- return ..()
-
-/obj/item/gun/energy/beam_rifle/onMouseUp(object, location, params, mob/M)
- if(istype(object, /obj/screen) && !istype(object, /obj/screen/click_catcher))
- return
- process_aim()
- if(aiming_time_left <= aiming_time_fire_threshold && check_user())
- sync_ammo()
- afterattack(M.client.mouseObject, M, FALSE, M.client.mouseParams, passthrough = TRUE)
- stop_aiming()
- QDEL_LIST(current_tracers)
- return ..()
-
-/obj/item/gun/energy/beam_rifle/afterattack(atom/target, mob/living/user, flag, params, passthrough = FALSE)
- if(flag) //It's adjacent, is the user, or is on the user's person
- if(target in user.contents) //can't shoot stuff inside us.
- return
- if(!ismob(target) || user.a_intent == INTENT_HARM) //melee attack
- return
- if(target == user && user.zone_selected != BODY_ZONE_PRECISE_MOUTH) //so we can't shoot ourselves (unless mouth selected)
- return
- if(!passthrough && (aiming_time > aiming_time_fire_threshold))
- return
- if(lastfire > world.time + delay)
- return
- lastfire = world.time
- . = ..()
- stop_aiming()
-
-/obj/item/gun/energy/beam_rifle/proc/sync_ammo()
- for(var/obj/item/ammo_casing/energy/beam_rifle/AC in contents)
- AC.sync_stats()
-
-/obj/item/gun/energy/beam_rifle/proc/delay_penalty(amount)
- aiming_time_left = CLAMP(aiming_time_left + amount, 0, aiming_time)
-
-/obj/item/ammo_casing/energy/beam_rifle
- name = "particle acceleration lens"
- desc = "Don't look into barrel!"
- var/wall_pierce_amount = 0
- var/wall_devastate = 0
- var/aoe_structure_range = 1
- var/aoe_structure_damage = 30
- var/aoe_fire_range = 2
- var/aoe_fire_chance = 66
- var/aoe_mob_range = 1
- var/aoe_mob_damage = 20
- var/impact_structure_damage = 50
- var/projectile_damage = 40
- var/projectile_stun = 0
- var/structure_piercing = 2
- var/structure_bleed_coeff = 0.7
- var/do_pierce = TRUE
- var/obj/item/gun/energy/beam_rifle/host
-
-/obj/item/ammo_casing/energy/beam_rifle/proc/sync_stats()
- var/obj/item/gun/energy/beam_rifle/BR = loc
- if(!istype(BR))
- stack_trace("Beam rifle syncing error")
- host = BR
- do_pierce = BR.projectile_setting_pierce
- wall_pierce_amount = BR.wall_pierce_amount
- wall_devastate = BR.wall_devastate
- aoe_structure_range = BR.aoe_structure_range
- aoe_structure_damage = BR.aoe_structure_damage
- aoe_fire_range = BR.aoe_fire_range
- aoe_fire_chance = BR.aoe_fire_chance
- aoe_mob_range = BR.aoe_mob_range
- aoe_mob_damage = BR.aoe_mob_damage
- impact_structure_damage = BR.impact_structure_damage
- projectile_damage = BR.projectile_damage
- projectile_stun = BR.projectile_stun
- delay = BR.delay
- structure_piercing = BR.structure_piercing
- structure_bleed_coeff = BR.structure_bleed_coeff
-
-/obj/item/ammo_casing/energy/beam_rifle/ready_proj(atom/target, mob/living/user, quiet, zone_override = "")
- . = ..()
- var/obj/item/projectile/beam/beam_rifle/hitscan/HS_BB = BB
- if(!istype(HS_BB))
- return
- HS_BB.impact_direct_damage = projectile_damage
- HS_BB.stun = projectile_stun
- HS_BB.impact_structure_damage = impact_structure_damage
- HS_BB.aoe_mob_damage = aoe_mob_damage
- HS_BB.aoe_mob_range = CLAMP(aoe_mob_range, 0, 15) //Badmin safety lock
- HS_BB.aoe_fire_chance = aoe_fire_chance
- HS_BB.aoe_fire_range = aoe_fire_range
- HS_BB.aoe_structure_damage = aoe_structure_damage
- HS_BB.aoe_structure_range = CLAMP(aoe_structure_range, 0, 15) //Badmin safety lock
- HS_BB.wall_devastate = wall_devastate
- HS_BB.wall_pierce_amount = wall_pierce_amount
- HS_BB.structure_pierce_amount = structure_piercing
- HS_BB.structure_bleed_coeff = structure_bleed_coeff
- HS_BB.do_pierce = do_pierce
- HS_BB.gun = host
-
-/obj/item/ammo_casing/energy/beam_rifle/throw_proj(atom/target, turf/targloc, mob/living/user, params, spread)
- var/turf/curloc = get_turf(user)
- if(!istype(curloc) || !BB)
- return FALSE
- var/obj/item/gun/energy/beam_rifle/gun = loc
- if(!targloc && gun)
- targloc = get_turf_in_angle(gun.lastangle, curloc, 10)
- else if(!targloc)
- return FALSE
- var/firing_dir
- if(BB.firer)
- firing_dir = BB.firer.dir
- if(!BB.suppressed && firing_effect_type)
- new firing_effect_type(get_turf(src), firing_dir)
- BB.preparePixelProjectile(target, user, params, spread)
- BB.fire(gun? gun.lastangle : null, null)
- BB = null
- return TRUE
-
-/obj/item/ammo_casing/energy/beam_rifle/hitscan
- projectile_type = /obj/item/projectile/beam/beam_rifle/hitscan
- select_name = "beam"
- e_cost = 5000
- fire_sound = 'sound/weapons/beam_sniper.ogg'
-
-/obj/item/projectile/beam/beam_rifle
- name = "particle beam"
- icon = null
- hitsound = 'sound/effects/explosion3.ogg'
- damage = 0 //Handled manually.
- damage_type = BURN
- flag = "energy"
- range = 150
- jitter = 10
- var/obj/item/gun/energy/beam_rifle/gun
- var/structure_pierce_amount = 0 //All set to 0 so the gun can manually set them during firing.
- var/structure_bleed_coeff = 0
- var/structure_pierce = 0
- var/do_pierce = TRUE
- var/wall_pierce_amount = 0
- var/wall_pierce = 0
- var/wall_devastate = 0
- var/aoe_structure_range = 0
- var/aoe_structure_damage = 0
- var/aoe_fire_range = 0
- var/aoe_fire_chance = 0
- var/aoe_mob_range = 0
- var/aoe_mob_damage = 0
- var/impact_structure_damage = 0
- var/impact_direct_damage = 0
- var/turf/cached
- var/list/pierced = list()
-
-/obj/item/projectile/beam/beam_rifle/proc/AOE(turf/epicenter)
- set waitfor = FALSE
- if(!epicenter)
- return
- new /obj/effect/temp_visual/explosion/fast(epicenter)
- for(var/mob/living/L in range(aoe_mob_range, epicenter)) //handle aoe mob damage
- L.adjustFireLoss(aoe_mob_damage)
- to_chat(L, "\The [src] sears you!")
- for(var/turf/T in range(aoe_fire_range, epicenter)) //handle aoe fire
- if(prob(aoe_fire_chance))
- new /obj/effect/hotspot(T)
- for(var/obj/O in range(aoe_structure_range, epicenter))
- if(!isitem(O))
- if(O.level == 1) //Please don't break underfloor items!
- continue
- O.take_damage(aoe_structure_damage * get_damage_coeff(O), BURN, "laser", FALSE)
-
-/obj/item/projectile/beam/beam_rifle/proc/check_pierce(atom/target)
- if(!do_pierce)
- return FALSE
- if(pierced[target]) //we already pierced them go away
- return TRUE
- if(isclosedturf(target))
- if(wall_pierce++ < wall_pierce_amount)
- if(prob(wall_devastate))
- if(iswallturf(target))
- var/turf/closed/wall/W = target
- W.dismantle_wall(TRUE, TRUE)
- else
- target.ex_act(EXPLODE_HEAVY)
- return TRUE
- if(ismovableatom(target))
- var/atom/movable/AM = target
- if(AM.density && !AM.CanPass(src, get_turf(target)) && !ismob(AM))
- if(structure_pierce < structure_pierce_amount)
- if(isobj(AM))
- var/obj/O = AM
- O.take_damage((impact_structure_damage + aoe_structure_damage) * structure_bleed_coeff * get_damage_coeff(AM), BURN, "energy", FALSE)
- pierced[AM] = TRUE
- structure_pierce++
- return TRUE
- return FALSE
-
-/obj/item/projectile/beam/beam_rifle/proc/get_damage_coeff(atom/target)
- if(istype(target, /obj/machinery/door))
- return 0.4
- if(istype(target, /obj/structure/window))
- return 0.5
- if(istype(target, /obj/structure/blob))
- return 0.65 //CIT CHANGE.
- return 1
-
-/obj/item/projectile/beam/beam_rifle/proc/handle_impact(atom/target)
- if(isobj(target))
- var/obj/O = target
- O.take_damage(impact_structure_damage * get_damage_coeff(target), BURN, "laser", FALSE)
- if(isliving(target))
- var/mob/living/L = target
- L.adjustFireLoss(impact_direct_damage)
- L.emote("scream")
-
-/obj/item/projectile/beam/beam_rifle/proc/handle_hit(atom/target)
- set waitfor = FALSE
- if(!cached && !QDELETED(target))
- cached = get_turf(target)
- if(nodamage)
- return FALSE
- playsound(cached, 'sound/effects/explosion3.ogg', 100, 1)
- AOE(cached)
- if(!QDELETED(target))
- handle_impact(target)
-
-/obj/item/projectile/beam/beam_rifle/Bump(atom/target)
- if(check_pierce(target))
- permutated += target
- trajectory_ignore_forcemove = TRUE
- forceMove(target.loc)
- trajectory_ignore_forcemove = FALSE
- return FALSE
- if(!QDELETED(target))
- cached = get_turf(target)
- . = ..()
-
-/obj/item/projectile/beam/beam_rifle/on_hit(atom/target, blocked = FALSE)
- if(!QDELETED(target))
- cached = get_turf(target)
- handle_hit(target)
- . = ..()
-
-/obj/item/projectile/beam/beam_rifle/hitscan
- icon_state = ""
- hitscan = TRUE
- tracer_type = /obj/effect/projectile/tracer/tracer/beam_rifle
- var/constant_tracer = FALSE
-
-/obj/item/projectile/beam/beam_rifle/hitscan/generate_hitscan_tracers(cleanup = TRUE, duration = 5, impacting = TRUE, highlander)
- set waitfor = FALSE
- if(isnull(highlander))
- highlander = constant_tracer
- if(highlander && istype(gun))
- QDEL_LIST(gun.current_tracers)
- for(var/datum/point/p in beam_segments)
- gun.current_tracers += generate_tracer_between_points(p, beam_segments[p], tracer_type, color, 0, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity)
- else
- for(var/datum/point/p in beam_segments)
- generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity)
- if(cleanup)
- QDEL_LIST(beam_segments)
- beam_segments = null
- QDEL_NULL(beam_index)
-
-/obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam
- tracer_type = /obj/effect/projectile/tracer/tracer/aiming
- name = "aiming beam"
- hitsound = null
- hitsound_wall = null
- nodamage = TRUE
- damage = 0
- constant_tracer = TRUE
- hitscan_light_range = 0
- hitscan_light_intensity = 0
- hitscan_light_color_override = "#99ff99"
-
-/obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam/prehit(atom/target)
- qdel(src)
- return FALSE
-
-/obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam/on_hit()
- qdel(src)
- return FALSE
+
+#define ZOOM_LOCK_AUTOZOOM_FREEMOVE 0
+#define ZOOM_LOCK_AUTOZOOM_ANGLELOCK 1
+#define ZOOM_LOCK_CENTER_VIEW 2
+#define ZOOM_LOCK_OFF 3
+
+#define AUTOZOOM_PIXEL_STEP_FACTOR 48
+
+#define AIMING_BEAM_ANGLE_CHANGE_THRESHOLD 0.1
+
+/obj/item/gun/energy/beam_rifle
+ name = "particle acceleration rifle"
+ desc = "An energy-based anti material marksman rifle that uses highly charged particle beams moving at extreme velocities to decimate whatever is unfortunate enough to be targeted by one. \
+ Hold down left click while scoped to aim, when weapon is fully aimed (Tracer goes from red to green as it charges), release to fire. Moving while aiming or \
+ changing where you're pointing at while aiming will delay the aiming process depending on how much you changed."
+ icon = 'icons/obj/guns/energy.dmi'
+ icon_state = "esniper"
+ item_state = "esniper"
+ fire_sound = 'sound/weapons/beam_sniper.ogg'
+ slot_flags = ITEM_SLOT_BACK
+ force = 15
+ materials = list()
+ recoil = 4
+ ammo_x_offset = 3
+ ammo_y_offset = 3
+ modifystate = FALSE
+ weapon_weight = WEAPON_HEAVY
+ w_class = WEIGHT_CLASS_BULKY
+ ammo_type = list(/obj/item/ammo_casing/energy/beam_rifle/hitscan)
+ cell_type = /obj/item/stock_parts/cell/beam_rifle
+ canMouseDown = TRUE
+ //Cit changes: beam rifle stats.
+ slowdown = 1
+ item_flags = NO_MAT_REDEMPTION | SLOWS_WHILE_IN_HAND | NEEDS_PERMIT
+ pin = null
+ var/aiming = FALSE
+ var/aiming_time = 14
+ var/aiming_time_fire_threshold = 5
+ var/aiming_time_left = 14
+ var/aiming_time_increase_user_movement = 7
+ var/aiming_time_increase_angle_multiplier = 0.30
+ var/last_process = 0
+
+ var/lastangle = 0
+ var/aiming_lastangle = 0
+ var/mob/current_user = null
+ var/list/obj/effect/projectile/tracer/current_tracers
+
+ var/structure_piercing = 1
+ var/structure_bleed_coeff = 0.7
+ var/wall_pierce_amount = 0
+ var/wall_devastate = 0
+ var/aoe_structure_range = 1
+ var/aoe_structure_damage = 35
+ var/aoe_fire_range = 1
+ var/aoe_fire_chance = 100
+ var/aoe_mob_range = 1
+ var/aoe_mob_damage = 20
+ var/impact_structure_damage = 75
+ var/projectile_damage = 40
+ var/projectile_stun = 0
+ var/projectile_setting_pierce = TRUE
+ var/delay = 30
+ var/lastfire = 0
+
+ //ZOOMING
+ var/zoom_current_view_increase = 0
+ var/zoom_target_view_increase = 10
+ var/zooming = FALSE
+ var/zoom_lock = ZOOM_LOCK_OFF
+ var/zooming_angle
+ var/current_zoom_x = 0
+ var/current_zoom_y = 0
+
+ var/static/image/charged_overlay = image(icon = 'icons/obj/guns/energy.dmi', icon_state = "esniper_charged")
+ var/static/image/drained_overlay = image(icon = 'icons/obj/guns/energy.dmi', icon_state = "esniper_empty")
+
+ var/datum/action/item_action/zoom_lock_action/zoom_lock_action
+ var/mob/listeningTo
+
+/obj/item/gun/energy/beam_rifle/debug
+ delay = 0
+ cell_type = /obj/item/stock_parts/cell/infinite
+ aiming_time = 0
+ recoil = 0
+ pin = /obj/item/firing_pin
+
+/obj/item/gun/energy/beam_rifle/equipped(mob/user)
+ set_user(user)
+ . = ..()
+
+/obj/item/gun/energy/beam_rifle/pickup(mob/user)
+ set_user(user)
+ . = ..()
+
+/obj/item/gun/energy/beam_rifle/dropped(mob/user)
+ set_user()
+ . = ..()
+
+/obj/item/gun/energy/beam_rifle/ui_action_click(owner, action)
+ if(istype(action, /datum/action/item_action/zoom_lock_action))
+ zoom_lock++
+ if(zoom_lock > 3)
+ zoom_lock = 0
+ switch(zoom_lock)
+ if(ZOOM_LOCK_AUTOZOOM_FREEMOVE)
+ to_chat(owner, "You switch [src]'s zooming processor to free directional.")
+ if(ZOOM_LOCK_AUTOZOOM_ANGLELOCK)
+ to_chat(owner, "You switch [src]'s zooming processor to locked directional.")
+ if(ZOOM_LOCK_CENTER_VIEW)
+ to_chat(owner, "You switch [src]'s zooming processor to center mode.")
+ if(ZOOM_LOCK_OFF)
+ to_chat(owner, "You disable [src]'s zooming system.")
+ reset_zooming()
+
+/obj/item/gun/energy/beam_rifle/proc/set_autozoom_pixel_offsets_immediate(current_angle)
+ if(zoom_lock == ZOOM_LOCK_CENTER_VIEW || zoom_lock == ZOOM_LOCK_OFF)
+ return
+ current_zoom_x = sin(current_angle) + sin(current_angle) * AUTOZOOM_PIXEL_STEP_FACTOR * zoom_current_view_increase
+ current_zoom_y = cos(current_angle) + cos(current_angle) * AUTOZOOM_PIXEL_STEP_FACTOR * zoom_current_view_increase
+
+/obj/item/gun/energy/beam_rifle/proc/handle_zooming()
+ if(!zooming || !check_user())
+ return
+ set_autozoom_pixel_offsets_immediate(zooming_angle)
+
+/obj/item/gun/energy/beam_rifle/proc/start_zooming()
+ if(zoom_lock == ZOOM_LOCK_OFF)
+ return
+ zooming = TRUE
+ current_user.client.change_view(world.view + zoom_target_view_increase)
+ zoom_current_view_increase = zoom_target_view_increase
+
+/obj/item/gun/energy/beam_rifle/proc/stop_zooming(mob/user)
+ if(zooming)
+ zooming = FALSE
+ reset_zooming(user)
+
+/obj/item/gun/energy/beam_rifle/proc/reset_zooming(mob/user)
+ if(!user)
+ user = current_user
+ if(!user || !user.client)
+ return FALSE
+ animate(user.client, pixel_x = 0, pixel_y = 0, 0, FALSE, LINEAR_EASING, ANIMATION_END_NOW)
+ zoom_current_view_increase = 0
+ user.client.change_view(CONFIG_GET(string/default_view))
+ zooming_angle = 0
+ current_zoom_x = 0
+ current_zoom_y = 0
+
+/obj/item/gun/energy/beam_rifle/update_icon()
+ cut_overlays()
+ var/obj/item/ammo_casing/energy/primary_ammo = ammo_type[1]
+ if(!QDELETED(cell) && (cell.charge > primary_ammo.e_cost))
+ add_overlay(charged_overlay)
+ else
+ add_overlay(drained_overlay)
+
+/obj/item/gun/energy/beam_rifle/attack_self(mob/user)
+ projectile_setting_pierce = !projectile_setting_pierce
+ to_chat(user, "You set \the [src] to [projectile_setting_pierce? "pierce":"impact"] mode.")
+ aiming_beam()
+
+/obj/item/gun/energy/beam_rifle/Initialize()
+ . = ..()
+ fire_delay = delay
+ current_tracers = list()
+ START_PROCESSING(SSfastprocess, src)
+ zoom_lock_action = new(src)
+
+/obj/item/gun/energy/beam_rifle/Destroy()
+ STOP_PROCESSING(SSfastprocess, src)
+ set_user(null)
+ QDEL_LIST(current_tracers)
+ listeningTo = null
+ return ..()
+
+/obj/item/gun/energy/beam_rifle/emp_act(severity)
+ . = ..()
+ if(. & EMP_PROTECT_SELF)
+ return
+ chambered = null
+ recharge_newshot()
+
+/obj/item/gun/energy/beam_rifle/proc/aiming_beam(force_update = FALSE)
+ var/diff = abs(aiming_lastangle - lastangle)
+ check_user()
+ if(diff < AIMING_BEAM_ANGLE_CHANGE_THRESHOLD && !force_update)
+ return
+ aiming_lastangle = lastangle
+ var/obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam/P = new
+ P.gun = src
+ P.wall_pierce_amount = wall_pierce_amount
+ P.structure_pierce_amount = structure_piercing
+ P.do_pierce = projectile_setting_pierce
+ if(aiming_time)
+ var/percent = ((100/aiming_time)*aiming_time_left)
+ P.color = rgb(255 * percent,255 * ((100 - percent) / 100),0)
+ else
+ P.color = rgb(0, 255, 0)
+ var/turf/curloc = get_turf(src)
+ var/turf/targloc = get_turf(current_user.client.mouseObject)
+ if(!istype(targloc))
+ if(!istype(curloc))
+ return
+ targloc = get_turf_in_angle(lastangle, curloc, 10)
+ P.preparePixelProjectile(targloc, current_user, current_user.client.mouseParams, 0)
+ P.fire(lastangle)
+
+/obj/item/gun/energy/beam_rifle/process()
+ if(!aiming)
+ last_process = world.time
+ return
+ check_user()
+ handle_zooming()
+ aiming_time_left = max(0, aiming_time_left - (world.time - last_process))
+ aiming_beam(TRUE)
+ last_process = world.time
+
+/obj/item/gun/energy/beam_rifle/proc/check_user(automatic_cleanup = TRUE)
+ if(!istype(current_user) || !isturf(current_user.loc) || !(src in current_user.held_items) || current_user.incapacitated()) //Doesn't work if you're not holding it!
+ if(automatic_cleanup)
+ stop_aiming()
+ set_user(null)
+ return FALSE
+ return TRUE
+
+/obj/item/gun/energy/beam_rifle/proc/process_aim()
+ if(istype(current_user) && current_user.client && current_user.client.mouseParams)
+ var/angle = mouse_angle_from_client(current_user.client)
+ current_user.setDir(angle2dir_cardinal(angle))
+ var/difference = abs(closer_angle_difference(lastangle, angle))
+ delay_penalty(difference * aiming_time_increase_angle_multiplier)
+ lastangle = angle
+
+/obj/item/gun/energy/beam_rifle/proc/on_mob_move()
+ check_user()
+ if(aiming)
+ delay_penalty(aiming_time_increase_user_movement)
+ process_aim()
+ aiming_beam(TRUE)
+
+/obj/item/gun/energy/beam_rifle/proc/start_aiming()
+ aiming_time_left = aiming_time
+ aiming = TRUE
+ process_aim()
+ aiming_beam(TRUE)
+ zooming_angle = lastangle
+ start_zooming()
+
+/obj/item/gun/energy/beam_rifle/proc/stop_aiming(mob/user)
+ set waitfor = FALSE
+ aiming_time_left = aiming_time
+ aiming = FALSE
+ QDEL_LIST(current_tracers)
+ stop_zooming(user)
+
+/obj/item/gun/energy/beam_rifle/proc/set_user(mob/user)
+ if(user == current_user)
+ return
+ stop_aiming(current_user)
+ if(listeningTo)
+ UnregisterSignal(listeningTo, COMSIG_MOVABLE_MOVED)
+ listeningTo = null
+ if(istype(current_user))
+ LAZYREMOVE(current_user.mousemove_intercept_objects, src)
+ current_user = null
+ if(istype(user))
+ current_user = user
+ LAZYOR(current_user.mousemove_intercept_objects, src)
+ RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/on_mob_move)
+ listeningTo = user
+
+/obj/item/gun/energy/beam_rifle/onMouseDrag(src_object, over_object, src_location, over_location, params, mob)
+ if(aiming)
+ process_aim()
+ aiming_beam()
+ if(zoom_lock == ZOOM_LOCK_AUTOZOOM_FREEMOVE)
+ zooming_angle = lastangle
+ set_autozoom_pixel_offsets_immediate(zooming_angle)
+ return ..()
+
+/obj/item/gun/energy/beam_rifle/onMouseDown(object, location, params, mob/mob)
+ if(istype(mob))
+ set_user(mob)
+ if(istype(object, /obj/screen) && !istype(object, /obj/screen/click_catcher))
+ return
+ if((object in mob.contents) || (object == mob))
+ return
+ start_aiming()
+ return ..()
+
+/obj/item/gun/energy/beam_rifle/onMouseUp(object, location, params, mob/M)
+ if(istype(object, /obj/screen) && !istype(object, /obj/screen/click_catcher))
+ return
+ process_aim()
+ if(aiming_time_left <= aiming_time_fire_threshold && check_user())
+ sync_ammo()
+ afterattack(M.client.mouseObject, M, FALSE, M.client.mouseParams, passthrough = TRUE)
+ stop_aiming()
+ QDEL_LIST(current_tracers)
+ return ..()
+
+/obj/item/gun/energy/beam_rifle/afterattack(atom/target, mob/living/user, flag, params, passthrough = FALSE)
+ if(flag) //It's adjacent, is the user, or is on the user's person
+ if(target in user.contents) //can't shoot stuff inside us.
+ return
+ if(!ismob(target) || user.a_intent == INTENT_HARM) //melee attack
+ return
+ if(target == user && user.zone_selected != BODY_ZONE_PRECISE_MOUTH) //so we can't shoot ourselves (unless mouth selected)
+ return
+ if(!passthrough && (aiming_time > aiming_time_fire_threshold))
+ return
+ if(lastfire > world.time + delay)
+ return
+ lastfire = world.time
+ . = ..()
+ stop_aiming()
+
+/obj/item/gun/energy/beam_rifle/proc/sync_ammo()
+ for(var/obj/item/ammo_casing/energy/beam_rifle/AC in contents)
+ AC.sync_stats()
+
+/obj/item/gun/energy/beam_rifle/proc/delay_penalty(amount)
+ aiming_time_left = CLAMP(aiming_time_left + amount, 0, aiming_time)
+
+/obj/item/ammo_casing/energy/beam_rifle
+ name = "particle acceleration lens"
+ desc = "Don't look into barrel!"
+ var/wall_pierce_amount = 0
+ var/wall_devastate = 0
+ var/aoe_structure_range = 1
+ var/aoe_structure_damage = 30
+ var/aoe_fire_range = 2
+ var/aoe_fire_chance = 66
+ var/aoe_mob_range = 1
+ var/aoe_mob_damage = 20
+ var/impact_structure_damage = 50
+ var/projectile_damage = 40
+ var/projectile_stun = 0
+ var/structure_piercing = 2
+ var/structure_bleed_coeff = 0.7
+ var/do_pierce = TRUE
+ var/obj/item/gun/energy/beam_rifle/host
+
+/obj/item/ammo_casing/energy/beam_rifle/proc/sync_stats()
+ var/obj/item/gun/energy/beam_rifle/BR = loc
+ if(!istype(BR))
+ stack_trace("Beam rifle syncing error")
+ host = BR
+ do_pierce = BR.projectile_setting_pierce
+ wall_pierce_amount = BR.wall_pierce_amount
+ wall_devastate = BR.wall_devastate
+ aoe_structure_range = BR.aoe_structure_range
+ aoe_structure_damage = BR.aoe_structure_damage
+ aoe_fire_range = BR.aoe_fire_range
+ aoe_fire_chance = BR.aoe_fire_chance
+ aoe_mob_range = BR.aoe_mob_range
+ aoe_mob_damage = BR.aoe_mob_damage
+ impact_structure_damage = BR.impact_structure_damage
+ projectile_damage = BR.projectile_damage
+ projectile_stun = BR.projectile_stun
+ delay = BR.delay
+ structure_piercing = BR.structure_piercing
+ structure_bleed_coeff = BR.structure_bleed_coeff
+
+/obj/item/ammo_casing/energy/beam_rifle/ready_proj(atom/target, mob/living/user, quiet, zone_override = "")
+ . = ..()
+ var/obj/item/projectile/beam/beam_rifle/hitscan/HS_BB = BB
+ if(!istype(HS_BB))
+ return
+ HS_BB.impact_direct_damage = projectile_damage
+ HS_BB.stun = projectile_stun
+ HS_BB.impact_structure_damage = impact_structure_damage
+ HS_BB.aoe_mob_damage = aoe_mob_damage
+ HS_BB.aoe_mob_range = CLAMP(aoe_mob_range, 0, 15) //Badmin safety lock
+ HS_BB.aoe_fire_chance = aoe_fire_chance
+ HS_BB.aoe_fire_range = aoe_fire_range
+ HS_BB.aoe_structure_damage = aoe_structure_damage
+ HS_BB.aoe_structure_range = CLAMP(aoe_structure_range, 0, 15) //Badmin safety lock
+ HS_BB.wall_devastate = wall_devastate
+ HS_BB.wall_pierce_amount = wall_pierce_amount
+ HS_BB.structure_pierce_amount = structure_piercing
+ HS_BB.structure_bleed_coeff = structure_bleed_coeff
+ HS_BB.do_pierce = do_pierce
+ HS_BB.gun = host
+
+/obj/item/ammo_casing/energy/beam_rifle/throw_proj(atom/target, turf/targloc, mob/living/user, params, spread)
+ var/turf/curloc = get_turf(user)
+ if(!istype(curloc) || !BB)
+ return FALSE
+ var/obj/item/gun/energy/beam_rifle/gun = loc
+ if(!targloc && gun)
+ targloc = get_turf_in_angle(gun.lastangle, curloc, 10)
+ else if(!targloc)
+ return FALSE
+ var/firing_dir
+ if(BB.firer)
+ firing_dir = BB.firer.dir
+ if(!BB.suppressed && firing_effect_type)
+ new firing_effect_type(get_turf(src), firing_dir)
+ BB.preparePixelProjectile(target, user, params, spread)
+ BB.fire(gun? gun.lastangle : null, null)
+ BB = null
+ return TRUE
+
+/obj/item/ammo_casing/energy/beam_rifle/hitscan
+ projectile_type = /obj/item/projectile/beam/beam_rifle/hitscan
+ select_name = "beam"
+ e_cost = 5000
+ fire_sound = 'sound/weapons/beam_sniper.ogg'
+
+/obj/item/projectile/beam/beam_rifle
+ name = "particle beam"
+ icon = null
+ hitsound = 'sound/effects/explosion3.ogg'
+ damage = 0 //Handled manually.
+ damage_type = BURN
+ flag = "energy"
+ range = 150
+ jitter = 10
+ var/obj/item/gun/energy/beam_rifle/gun
+ var/structure_pierce_amount = 0 //All set to 0 so the gun can manually set them during firing.
+ var/structure_bleed_coeff = 0
+ var/structure_pierce = 0
+ var/do_pierce = TRUE
+ var/wall_pierce_amount = 0
+ var/wall_pierce = 0
+ var/wall_devastate = 0
+ var/aoe_structure_range = 0
+ var/aoe_structure_damage = 0
+ var/aoe_fire_range = 0
+ var/aoe_fire_chance = 0
+ var/aoe_mob_range = 0
+ var/aoe_mob_damage = 0
+ var/impact_structure_damage = 0
+ var/impact_direct_damage = 0
+ var/turf/cached
+ var/list/pierced = list()
+
+/obj/item/projectile/beam/beam_rifle/proc/AOE(turf/epicenter)
+ set waitfor = FALSE
+ if(!epicenter)
+ return
+ new /obj/effect/temp_visual/explosion/fast(epicenter)
+ for(var/mob/living/L in range(aoe_mob_range, epicenter)) //handle aoe mob damage
+ L.adjustFireLoss(aoe_mob_damage)
+ to_chat(L, "\The [src] sears you!")
+ for(var/turf/T in range(aoe_fire_range, epicenter)) //handle aoe fire
+ if(prob(aoe_fire_chance))
+ new /obj/effect/hotspot(T)
+ for(var/obj/O in range(aoe_structure_range, epicenter))
+ if(!isitem(O))
+ if(O.level == 1) //Please don't break underfloor items!
+ continue
+ O.take_damage(aoe_structure_damage * get_damage_coeff(O), BURN, "laser", FALSE)
+
+/obj/item/projectile/beam/beam_rifle/proc/check_pierce(atom/target)
+ if(!do_pierce)
+ return FALSE
+ if(pierced[target]) //we already pierced them go away
+ return TRUE
+ if(isclosedturf(target))
+ if(wall_pierce++ < wall_pierce_amount)
+ if(prob(wall_devastate))
+ if(iswallturf(target))
+ var/turf/closed/wall/W = target
+ W.dismantle_wall(TRUE, TRUE)
+ else
+ target.ex_act(EXPLODE_HEAVY)
+ return TRUE
+ if(ismovableatom(target))
+ var/atom/movable/AM = target
+ if(AM.density && !AM.CanPass(src, get_turf(target)) && !ismob(AM))
+ if(structure_pierce < structure_pierce_amount)
+ if(isobj(AM))
+ var/obj/O = AM
+ O.take_damage((impact_structure_damage + aoe_structure_damage) * structure_bleed_coeff * get_damage_coeff(AM), BURN, "energy", FALSE)
+ pierced[AM] = TRUE
+ structure_pierce++
+ return TRUE
+ return FALSE
+
+/obj/item/projectile/beam/beam_rifle/proc/get_damage_coeff(atom/target)
+ if(istype(target, /obj/machinery/door))
+ return 0.4
+ if(istype(target, /obj/structure/window))
+ return 0.5
+ if(istype(target, /obj/structure/blob))
+ return 0.65 //CIT CHANGE.
+ return 1
+
+/obj/item/projectile/beam/beam_rifle/proc/handle_impact(atom/target)
+ if(isobj(target))
+ var/obj/O = target
+ O.take_damage(impact_structure_damage * get_damage_coeff(target), BURN, "laser", FALSE)
+ if(isliving(target))
+ var/mob/living/L = target
+ L.adjustFireLoss(impact_direct_damage)
+ L.emote("scream")
+
+/obj/item/projectile/beam/beam_rifle/proc/handle_hit(atom/target)
+ set waitfor = FALSE
+ if(!cached && !QDELETED(target))
+ cached = get_turf(target)
+ if(nodamage)
+ return FALSE
+ playsound(cached, 'sound/effects/explosion3.ogg', 100, 1)
+ AOE(cached)
+ if(!QDELETED(target))
+ handle_impact(target)
+
+/obj/item/projectile/beam/beam_rifle/Bump(atom/target)
+ if(check_pierce(target))
+ permutated += target
+ trajectory_ignore_forcemove = TRUE
+ forceMove(target.loc)
+ trajectory_ignore_forcemove = FALSE
+ return FALSE
+ if(!QDELETED(target))
+ cached = get_turf(target)
+ . = ..()
+
+/obj/item/projectile/beam/beam_rifle/on_hit(atom/target, blocked = FALSE)
+ if(!QDELETED(target))
+ cached = get_turf(target)
+ handle_hit(target)
+ . = ..()
+
+/obj/item/projectile/beam/beam_rifle/hitscan
+ icon_state = ""
+ hitscan = TRUE
+ tracer_type = /obj/effect/projectile/tracer/tracer/beam_rifle
+ var/constant_tracer = FALSE
+
+/obj/item/projectile/beam/beam_rifle/hitscan/generate_hitscan_tracers(cleanup = TRUE, duration = 5, impacting = TRUE, highlander)
+ set waitfor = FALSE
+ if(isnull(highlander))
+ highlander = constant_tracer
+ if(highlander && istype(gun))
+ QDEL_LIST(gun.current_tracers)
+ for(var/datum/point/p in beam_segments)
+ gun.current_tracers += generate_tracer_between_points(p, beam_segments[p], tracer_type, color, 0, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity)
+ else
+ for(var/datum/point/p in beam_segments)
+ generate_tracer_between_points(p, beam_segments[p], tracer_type, color, duration, hitscan_light_range, hitscan_light_color_override, hitscan_light_intensity)
+ if(cleanup)
+ QDEL_LIST(beam_segments)
+ beam_segments = null
+ QDEL_NULL(beam_index)
+
+/obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam
+ tracer_type = /obj/effect/projectile/tracer/tracer/aiming
+ name = "aiming beam"
+ hitsound = null
+ hitsound_wall = null
+ nodamage = TRUE
+ damage = 0
+ constant_tracer = TRUE
+ hitscan_light_range = 0
+ hitscan_light_intensity = 0
+ hitscan_light_color_override = "#99ff99"
+
+/obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam/prehit(atom/target)
+ qdel(src)
+ return FALSE
+
+/obj/item/projectile/beam/beam_rifle/hitscan/aiming_beam/on_hit()
+ qdel(src)
+ return FALSE
diff --git a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm
index eba26ad2..8b8cd096 100644
--- a/code/modules/research/xenobiology/crossbreeding/_status_effects.dm
+++ b/code/modules/research/xenobiology/crossbreeding/_status_effects.dm
@@ -64,10 +64,9 @@
var/interrupted = FALSE
var/mob/target
var/icon/bluespace
- var/datum/weakref/redirect_component
/datum/status_effect/slimerecall/on_apply()
- redirect_component = WEAKREF(owner.AddComponent(/datum/component/redirect, list(COMSIG_LIVING_RESIST = CALLBACK(src, .proc/resistField))))
+ RegisterSignal(owner, COMSIG_LIVING_RESIST, .proc/resistField)
to_chat(owner, "You feel a sudden tug from an unknown force, and feel a pull to bluespace!")
to_chat(owner, "Resist if you wish avoid the force!")
bluespace = icon('icons/effects/effects.dmi',"chronofield")
@@ -77,9 +76,9 @@
/datum/status_effect/slimerecall/proc/resistField()
interrupted = TRUE
owner.remove_status_effect(src)
+
/datum/status_effect/slimerecall/on_remove()
- qdel(redirect_component.resolve())
- redirect_component = null
+ UnregisterSignal(owner, COMSIG_LIVING_RESIST)
owner.cut_overlay(bluespace)
if(interrupted || !ismob(target))
to_chat(owner, "The bluespace tug fades away, and you feel that the force has passed you by.")
@@ -98,10 +97,9 @@
duration = -1 //Will remove self when block breaks.
alert_type = /obj/screen/alert/status_effect/freon/stasis
var/obj/structure/ice_stasis/cube
- var/datum/weakref/redirect_component
/datum/status_effect/frozenstasis/on_apply()
- redirect_component = WEAKREF(owner.AddComponent(/datum/component/redirect, list(COMSIG_LIVING_RESIST = CALLBACK(src, .proc/breakCube))))
+ RegisterSignal(owner, COMSIG_LIVING_RESIST, .proc/breakCube)
cube = new /obj/structure/ice_stasis(get_turf(owner))
owner.forceMove(cube)
owner.status_flags |= GODMODE
@@ -118,8 +116,7 @@
if(cube)
qdel(cube)
owner.status_flags &= ~GODMODE
- qdel(redirect_component.resolve())
- redirect_component = null
+ UnregisterSignal(owner, COMSIG_LIVING_RESIST)
/datum/status_effect/slime_clone
id = "slime_cloned"
diff --git a/code/modules/research/xenobiology/xenobio_camera.dm b/code/modules/research/xenobiology/xenobio_camera.dm
index d0a924ee..9de6384a 100644
--- a/code/modules/research/xenobiology/xenobio_camera.dm
+++ b/code/modules/research/xenobiology/xenobio_camera.dm
@@ -24,8 +24,6 @@
circuit = /obj/item/circuitboard/computer/xenobiology
var/datum/action/innate/hotkey_help/hotkey_help
- var/datum/component/redirect/listener
-
var/obj/machinery/monkey_recycler/connected_recycler
var/list/stored_slimes
var/obj/item/slimepotion/slime/current_potion
@@ -41,7 +39,7 @@
. = ..()
hotkey_help = new
stored_slimes = list()
- listener = AddComponent(/datum/component/redirect, list(COMSIG_ATOM_CONTENTS_DEL = CALLBACK(src, .proc/on_contents_del)))
+ RegisterSignal(src, COMSIG_ATOM_CONTENTS_DEL, .proc/on_contents_del)
for(var/obj/machinery/monkey_recycler/recycler in GLOB.monkey_recyclers)
if(get_area(src) == get_area(recycler))
connected_recycler = recycler
diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm
index c739d426..aae422bc 100644
--- a/code/modules/unit_tests/_unit_tests.dm
+++ b/code/modules/unit_tests/_unit_tests.dm
@@ -1,13 +1,13 @@
-//include unit test files in this module in this ifdef
-//Keep this sorted alphabetically
-
-#ifdef UNIT_TESTS
-#include "anchored_mobs.dm"
-#include "component_tests.dm"
-#include "reagent_id_typos.dm"
-#include "reagent_recipe_collisions.dm"
-#include "spawn_humans.dm"
-#include "subsystem_init.dm"
-#include "timer_sanity.dm"
-#include "unit_test.dm"
-#endif
+//include unit test files in this module in this ifdef
+//Keep this sorted alphabetically
+
+#ifdef UNIT_TESTS
+#include "anchored_mobs.dm"
+#include "component_tests.dm"
+#include "reagent_id_typos.dm"
+#include "reagent_recipe_collisions.dm"
+#include "spawn_humans.dm"
+#include "subsystem_init.dm"
+#include "timer_sanity.dm"
+#include "unit_test.dm"
+#endif
diff --git a/code/modules/unit_tests/component_tests.dm b/code/modules/unit_tests/component_tests.dm
new file mode 100644
index 00000000..409d7f43
--- /dev/null
+++ b/code/modules/unit_tests/component_tests.dm
@@ -0,0 +1,12 @@
+/datum/unit_test/component_duping/Run()
+ var/list/bad_dms = list()
+ var/list/bad_dts = list()
+ for(var/t in typesof(/datum/component))
+ var/datum/component/comp = t
+ if(!isnum(initial(comp.dupe_mode)))
+ bad_dms += t
+ var/dupe_type = initial(comp.dupe_type)
+ if(dupe_type && !ispath(dupe_type))
+ bad_dts += t
+ if(length(bad_dms) || length(bad_dts))
+ Fail("Components with invalid dupe modes: ([bad_dms.Join(",")]) ||| Components with invalid dupe types: ([bad_dts.Join(",")])")
\ No newline at end of file
diff --git a/hyperstation/code/obj/leash.dm b/hyperstation/code/obj/leash.dm
index cc057c92..46dcc35c 100644
--- a/hyperstation/code/obj/leash.dm
+++ b/hyperstation/code/obj/leash.dm
@@ -35,7 +35,7 @@ Icons, maybe?
/datum/status_effect/leash_pet
id = "leashed"
status_type = STATUS_EFFECT_UNIQUE
- var/datum/weakref/redirect_component
+ var/mob/redirect_component
alert_type = /obj/screen/alert/status_effect/leash_pet
/obj/screen/alert/status_effect/leash_pet
@@ -45,7 +45,9 @@ Icons, maybe?
/datum/status_effect/leash_pet/on_apply()
- redirect_component = WEAKREF(owner.AddComponent(/datum/component/redirect, list(COMSIG_LIVING_RESIST = CALLBACK(src, .proc/owner_resist))))
+ //redirect_component = WEAKREF(owner.AddComponent(/datum/component/redirect, list(COMSIG_LIVING_RESIST = CALLBACK(src, .proc/owner_resist))))
+ RegisterSignal(owner, COMSIG_LIVING_RESIST, .proc/owner_resist)
+ redirect_component = owner
if(!owner.stat)
to_chat(owner, "You have been leashed!")
return ..()
@@ -80,9 +82,9 @@ Icons, maybe?
var/leash_used = 0 //A flag to see if the leash has been used yet, because for some reason picking up an unused leash is weird
var/mob/living/leash_pet = "null" //Variable to store our pet later
var/mob/living/leash_master = "null" //And our master too
- var/datum/component/mobhook_leash_pet
- var/datum/component/mobhook_leash_master //Needed to watch for these entities to move
- var/datum/component/mobhook_leash_freepet
+ var/mob/mobhook_leash_pet
+ var/mob/mobhook_leash_master //Needed to watch for these entities to move
+ var/mob/mobhook_leash_freepet
var/leash_location[3] //Three digit list for us to store coordinates later
//Called when someone is clicked with the leash
@@ -101,8 +103,12 @@ Icons, maybe?
user.apply_status_effect(/datum/status_effect/leash_dom) //Is the leasher
leash_pet = C //Save pet reference for later
leash_master = user //Save dom reference for later
- mobhook_leash_pet = leash_pet.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_pet_move)))
- mobhook_leash_master = leash_master.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_master_move)))
+ //mobhook_leash_pet = leash_pet.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_pet_move)))
+ RegisterSignal(leash_pet, COMSIG_MOVABLE_MOVED, .proc/on_pet_move)
+ mobhook_leash_pet = leash_pet
+ //mobhook_leash_master = leash_master.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_master_move)))
+ RegisterSignal(leash_master, COMSIG_MOVABLE_MOVED, .proc/on_master_move)
+ mobhook_leash_master = leash_master
leash_used = 1
if(!leash_pet.has_status_effect(/datum/status_effect/leash_dom)) //Add slowdown if the pet didn't leash themselves
leash_pet.add_movespeed_modifier(MOVESPEED_ID_LEASH, multiplicative_slowdown = 5)
@@ -126,9 +132,12 @@ Icons, maybe?
leash_pet.remove_status_effect(/datum/status_effect/leash_pet)
if(!leash_pet.has_status_effect(/datum/status_effect/leash_pet)) //If there is no pet, there is no dom. Loop breaks.
- QDEL_NULL(mobhook_leash_master)
- QDEL_NULL(mobhook_leash_pet)
- QDEL_NULL(mobhook_leash_freepet)
+ //QDEL_NULL(mobhook_leash_master)
+ UnregisterSignal(mobhook_leash_master, COMSIG_MOVABLE_MOVED)
+ //QDEL_NULL(mobhook_leash_pet)
+ UnregisterSignal(mobhook_leash_pet, COMSIG_MOVABLE_MOVED)
+ //QDEL_NULL(mobhook_leash_freepet)
+ UnregisterSignal(mobhook_leash_freepet, COMSIG_MOVABLE_MOVED)
if(leash_pet.has_status_effect(/datum/status_effect/leash_freepet))
leash_pet.remove_status_effect(/datum/status_effect/leash_freepet)
if(leash_pet.has_movespeed_modifier(MOVESPEED_ID_LEASH))
@@ -183,7 +192,8 @@ Icons, maybe?
if(leash_pet == leash_master) //Pet is the master
return
if(!leash_pet.has_status_effect(/datum/status_effect/leash_pet))
- QDEL_NULL(mobhook_leash_master) //Probably redundant, but it's nice to be safe
+ //QDEL_NULL(mobhook_leash_master) //Probably redundant, but it's nice to be safe
+ UnregisterSignal(mobhook_leash_master, COMSIG_MOVABLE_MOVED)
leash_master.remove_status_effect(/datum/status_effect/leash_dom)
return
@@ -252,8 +262,10 @@ Icons, maybe?
leash_pet.remove_status_effect(/datum/status_effect/leash_pet)
leash_pet.remove_movespeed_modifier(MOVESPEED_ID_LEASH)
leash_master.remove_status_effect(/datum/status_effect/leash_dom)
- QDEL_NULL(mobhook_leash_master)
- QDEL_NULL(mobhook_leash_pet)
+ //QDEL_NULL(mobhook_leash_master)
+ UnregisterSignal(mobhook_leash_master, COMSIG_MOVABLE_MOVED)
+ //QDEL_NULL(mobhook_leash_pet)
+ UnregisterSignal(mobhook_leash_pet, COMSIG_MOVABLE_MOVED)
leash_pet = "null"
leash_master = "null"
leash_used = 0
@@ -267,7 +279,8 @@ Icons, maybe?
return
//Make sure the pet is still a pet
if(!leash_pet.has_status_effect(/datum/status_effect/leash_pet))
- QDEL_NULL(mobhook_leash_pet) //Probably redundant, but it's nice to be safe
+ //QDEL_NULL(mobhook_leash_pet) //Probably redundant, but it's nice to be safe
+ UnregisterSignal(mobhook_leash_pet, COMSIG_MOVABLE_MOVED)
return
//The pet has escaped. There is no DOM. GO PET RUN.
@@ -356,8 +369,10 @@ Icons, maybe?
leash_pet.adjustOxyLoss(5)
leash_pet.remove_status_effect(/datum/status_effect/leash_pet)
leash_pet.remove_status_effect(/datum/status_effect/leash_freepet)
- QDEL_NULL(mobhook_leash_pet)
- QDEL_NULL(mobhook_leash_freepet)
+ //QDEL_NULL(mobhook_leash_pet)
+ UnregisterSignal(mobhook_leash_pet, COMSIG_MOVABLE_MOVED)
+ //QDEL_NULL(mobhook_leash_freepet)
+ UnregisterSignal(mobhook_leash_freepet, COMSIG_MOVABLE_MOVED)
leash_pet = "null"
leash_used = 0
@@ -375,10 +390,13 @@ Icons, maybe?
viewing.show_message("[leash_master] has dropped the leash.", 1)
//DOM HAS DROPPED LEASH. PET IS FREE. SCP HAS BREACHED CONTAINMENT.
leash_pet.remove_movespeed_modifier(MOVESPEED_ID_LEASH)
- mobhook_leash_freepet = leash_pet.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_freepet_move)))
+ //mobhook_leash_freepet = leash_pet.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_freepet_move)))
+ RegisterSignal(leash_pet, COMSIG_MOVABLE_MOVED, .proc/on_freepet_move)
+ mobhook_leash_freepet = leash_pet
leash_master.remove_status_effect(/datum/status_effect/leash_dom) //No dom with no leash. We will get a new dom if the leash is picked back up.
leash_master = "null"
- QDEL_NULL(mobhook_leash_master)
+ //QDEL_NULL(mobhook_leash_master)
+ UnregisterSignal(mobhook_leash_master, COMSIG_MOVABLE_MOVED)
/obj/item/leash/equipped(mob/user)
. = ..()
@@ -392,9 +410,12 @@ Icons, maybe?
leash_master = "null"
return
leash_master.apply_status_effect(/datum/status_effect/leash_dom)
- mobhook_leash_master = leash_master.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_master_move)))
+ //mobhook_leash_master = leash_master.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED = CALLBACK(src, .proc/on_master_move)))
+ RegisterSignal(leash_master, COMSIG_MOVABLE_MOVED, .proc/on_master_move)
+ mobhook_leash_master = leash_master
leash_pet.remove_status_effect(/datum/status_effect/leash_freepet)
- QDEL_NULL(mobhook_leash_freepet)
+ //QDEL_NULL(mobhook_leash_freepet)
+ UnregisterSignal(mobhook_leash_freepet, COMSIG_MOVABLE_MOVED)
leash_pet.add_movespeed_modifier(MOVESPEED_ID_LEASH, multiplicative_slowdown = 5)
/datum/crafting_recipe/leash
@@ -403,4 +424,4 @@ Icons, maybe?
time = 40
reqs = list(/obj/item/stack/sheet/metal = 1,
/obj/item/stack/sheet/cloth = 3)
- category = CAT_MISC
\ No newline at end of file
+ category = CAT_MISC
diff --git a/modular_citadel/code/datums/components/phantomthief.dm b/modular_citadel/code/datums/components/phantomthief.dm
deleted file mode 100644
index d34e16f6..00000000
--- a/modular_citadel/code/datums/components/phantomthief.dm
+++ /dev/null
@@ -1,49 +0,0 @@
-//This component applies a customizable drop_shadow filter to its wearer when they toggle combat mode on or off. This can stack.
-
-/datum/component/phantomthief
- dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS
-
- var/filter_x
- var/filter_y
- var/filter_size
- var/filter_border
- var/filter_color
-
- var/datum/component/redirect/combattoggle_redir
-
-/datum/component/phantomthief/Initialize(_x = -2, _y = 0, _size = 0, _border = 0, _color = "#E62111")
- filter_x = _x
- filter_y = _y
- filter_size = _size
- filter_border = _border
- filter_color = _color
-
- RegisterSignal(parent, COMSIG_COMBAT_TOGGLED, .proc/handlefilterstuff)
- RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/OnEquipped)
- RegisterSignal(parent, COMSIG_ITEM_DROPPED, .proc/OnDropped)
-
-/datum/component/phantomthief/proc/handlefilterstuff(mob/user, combatmodestate)
- if(istype(user))
- var/thefilter = filter(type = "drop_shadow", x = filter_x, y = filter_y, size = filter_size, border = filter_border, color = filter_color)
- if(!combatmodestate)
- user.filters -= thefilter
- else
- user.filters += thefilter
-
-/datum/component/phantomthief/proc/stripdesiredfilter(mob/user)
- if(istype(user))
- var/thefilter = filter(type = "drop_shadow", x = filter_x, y = filter_y, size = filter_size, border = filter_border, color = filter_color)
- user.filters -= thefilter
-
-/datum/component/phantomthief/proc/OnEquipped(mob/user, slot)
- if(!istype(user))
- return
- if(!combattoggle_redir)
- combattoggle_redir = user.AddComponent(/datum/component/redirect, list(COMSIG_COMBAT_TOGGLED = CALLBACK(src, .proc/handlefilterstuff)))
-
-/datum/component/phantomthief/proc/OnDropped(mob/user)
- if(!istype(user))
- return
- if(combattoggle_redir)
- QDEL_NULL(combattoggle_redir)
- stripdesiredfilter(user)
diff --git a/modular_citadel/code/datums/status_effects/chems.dm b/modular_citadel/code/datums/status_effects/chems.dm
index ef45e07b..a35a5f50 100644
--- a/modular_citadel/code/datums/status_effects/chems.dm
+++ b/modular_citadel/code/datums/status_effects/chems.dm
@@ -201,7 +201,6 @@
var/enthrallGender //Use master or mistress
var/mental_capacity //Higher it is, lower the cooldown on commands, capacity reduces with resistance.
- var/datum/weakref/redirect_component //resistance
var/distancelist = list(2,1.5,1,0.8,0.6,0.5,0.4,0.3,0.2) //Distance multipliers
@@ -231,7 +230,7 @@
master = get_mob_by_key(enthrallID)
//if(M.ckey == enthrallID)
// owner.remove_status_effect(src)//At the moment, a user can enthrall themselves, toggle this back in if that should be removed.
- redirect_component = WEAKREF(owner.AddComponent(/datum/component/redirect, list(COMSIG_LIVING_RESIST = CALLBACK(src, .proc/owner_resist)))) //Do resistance calc if resist is pressed#
+ RegisterSignal(owner, COMSIG_LIVING_RESIST, .proc/owner_resist) //Do resistance calc if resist is pressed#
RegisterSignal(owner, COMSIG_MOVABLE_HEAR, .proc/owner_hear)
//var/obj/item/organ/brain/B = M.getorganslot(ORGAN_SLOT_BRAIN) //removing this, may cause some issues unsure.
mental_capacity = 500 - M.getOrganLoss(ORGAN_SLOT_BRAIN)//It's their brain!
@@ -570,8 +569,7 @@
SEND_SIGNAL(M, COMSIG_CLEAR_MOOD_EVENT, "EnthMissing2")
SEND_SIGNAL(M, COMSIG_CLEAR_MOOD_EVENT, "EnthMissing3")
SEND_SIGNAL(M, COMSIG_CLEAR_MOOD_EVENT, "EnthMissing4")
- qdel(redirect_component.resolve())
- redirect_component = null
+ UnregisterSignal(M, COMSIG_LIVING_RESIST)
UnregisterSignal(owner, COMSIG_MOVABLE_HEAR)
REMOVE_TRAIT(owner, TRAIT_PACIFISM, "MKUltra")
to_chat(owner, "You're now free of [master]'s influence, and fully independent!'")
diff --git a/modular_citadel/code/modules/clothing/glasses/phantomthief.dm b/modular_citadel/code/modules/clothing/glasses/phantomthief.dm
index 716e33c7..669697c6 100644
--- a/modular_citadel/code/modules/clothing/glasses/phantomthief.dm
+++ b/modular_citadel/code/modules/clothing/glasses/phantomthief.dm
@@ -6,19 +6,18 @@
icon_state = "s-ninja"
item_state = "s-ninja"
-/obj/item/clothing/glasses/phantomthief/Initialize()
+/obj/item/clothing/glasses/phantomthief/ComponentInitialize()
. = ..()
- AddComponent(/datum/component/phantomthief)
+ AddComponent(/datum/component/wearertargeting/phantomthief)
/obj/item/clothing/glasses/phantomthief/syndicate
name = "suspicious plastic mask"
desc = "A cheap, bulky, Syndicate-branded plastic face mask. You have to break in to break out."
var/nextadrenalinepop
- var/datum/component/redirect/combattoggle_redir
-/obj/item/clothing/glasses/phantomthief/syndicate/examine(user)
+/obj/item/clothing/glasses/phantomthief/syndicate/examine(mob/user)
. = ..()
- if(combattoggle_redir)
+ if(user.get_item_by_slot(SLOT_GLASSES) == src)
if(world.time >= nextadrenalinepop)
. += "The built-in adrenaline injector is ready for use."
else
@@ -34,12 +33,12 @@
. = ..()
if(!istype(user))
return
- if(!combattoggle_redir)
- combattoggle_redir = user.AddComponent(/datum/component/redirect, list(COMSIG_COMBAT_TOGGLED = CALLBACK(src, .proc/injectadrenaline)))
+ if(slot != SLOT_GLASSES)
+ return
+ RegisterSignal(user, COMSIG_COMBAT_TOGGLED, .proc/injectadrenaline)
/obj/item/clothing/glasses/phantomthief/syndicate/dropped(mob/user)
. = ..()
if(!istype(user))
return
- if(combattoggle_redir)
- QDEL_NULL(combattoggle_redir)
+ UnregisterSignal(user, COMSIG_COMBAT_TOGGLED)
diff --git a/tgstation.dme b/tgstation.dme
index 115184bd..4e0c3088 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -381,12 +381,12 @@
#include "code\datums\components\ntnet_interface.dm"
#include "code\datums\components\orbiter.dm"
#include "code\datums\components\paintable.dm"
+#include "code\datums\components\phantomthief.dm"
#include "code\datums\components\rad_insulation.dm"
#include "code\datums\components\radioactive.dm"
#include "code\datums\components\remote_materials.dm"
#include "code\datums\components\riding.dm"
#include "code\datums\components\rotation.dm"
-#include "code\datums\components\signal_redirect.dm"
#include "code\datums\components\sizzle.dm"
#include "code\datums\components\slippery.dm"
#include "code\datums\components\spawner.dm"
@@ -3171,7 +3171,6 @@
#include "modular_citadel\code\controllers\configuration\entries\general.dm"
#include "modular_citadel\code\controllers\subsystem\job.dm"
#include "modular_citadel\code\datums\components\material_container.dm"
-#include "modular_citadel\code\datums\components\phantomthief.dm"
#include "modular_citadel\code\datums\components\souldeath.dm"
#include "modular_citadel\code\datums\mood_events\chem_events.dm"
#include "modular_citadel\code\datums\mood_events\generic_negative_events.dm"