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"